blob: 1d05c18d50d13f9938e235ab5ff47200565d552b [file] [log] [blame]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Functions to scan the PCI bus for VirtIO devices.
use crate::memory::{MemoryTracker, MemoryTrackerError};
use alloc::boxed::Box;
use core::fmt;
use core::marker::PhantomData;
use fdtpci::PciInfo;
use log::debug;
use once_cell::race::OnceBox;
use virtio_drivers::{
device::{blk, socket},
transport::pci::{
bus::{BusDeviceIterator, PciRoot},
virtio_device_type, PciTransport,
},
Hal,
};
pub(super) static PCI_INFO: OnceBox<PciInfo> = OnceBox::new();
/// PCI errors.
#[derive(Debug, Clone)]
pub enum PciError {
/// Attempted to initialize the PCI more than once.
DuplicateInitialization,
/// Failed to map PCI CAM.
CamMapFailed(MemoryTrackerError),
/// Failed to map PCI BAR.
BarMapFailed(MemoryTrackerError),
}
impl fmt::Display for PciError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::DuplicateInitialization => {
write!(f, "Attempted to initialize the PCI more than once.")
}
Self::CamMapFailed(e) => write!(f, "Failed to map PCI CAM: {e}"),
Self::BarMapFailed(e) => write!(f, "Failed to map PCI BAR: {e}"),
}
}
}
/// Prepares to use VirtIO PCI devices.
///
/// In particular:
///
/// 1. Maps the PCI CAM and BAR range in the page table and MMIO guard.
/// 2. Stores the `PciInfo` for the VirtIO HAL to use later.
/// 3. Creates and returns a `PciRoot`.
///
/// This must only be called once; it will panic if it is called a second time.
pub fn initialize(pci_info: PciInfo, memory: &mut MemoryTracker) -> Result<PciRoot, PciError> {
PCI_INFO.set(Box::new(pci_info.clone())).map_err(|_| PciError::DuplicateInitialization)?;
memory.map_mmio_range(pci_info.cam_range.clone()).map_err(PciError::CamMapFailed)?;
let bar_range = pci_info.bar_range.start as usize..pci_info.bar_range.end as usize;
memory.map_mmio_range(bar_range).map_err(PciError::BarMapFailed)?;
// Safety: This is the only place where we call make_pci_root, and `PCI_INFO.set` above will
// panic if it is called a second time.
Ok(unsafe { pci_info.make_pci_root() })
}
/// Virtio Block device.
pub type VirtIOBlk<T> = blk::VirtIOBlk<T, PciTransport>;
/// Virtio Socket device.
///
/// Spec: https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html 5.10
pub type VirtIOSocket<T> = socket::VirtIOSocket<T, PciTransport>;
/// An iterator that iterates over the PCI transport for each device.
pub struct PciTransportIterator<'a, T: Hal> {
pci_root: &'a mut PciRoot,
bus: BusDeviceIterator,
_hal: PhantomData<T>,
}
impl<'a, T: Hal> PciTransportIterator<'a, T> {
/// Creates a new iterator.
pub fn new(pci_root: &'a mut PciRoot) -> Self {
let bus = pci_root.enumerate_bus(0);
Self { pci_root, bus, _hal: PhantomData }
}
}
impl<'a, T: Hal> Iterator for PciTransportIterator<'a, T> {
type Item = PciTransport;
fn next(&mut self) -> Option<Self::Item> {
loop {
let (device_function, info) = self.bus.next()?;
let (status, command) = self.pci_root.get_status_command(device_function);
debug!(
"Found PCI device {} at {}, status {:?} command {:?}",
info, device_function, status, command
);
let Some(virtio_type) = virtio_device_type(&info) else {
continue;
};
debug!(" VirtIO {:?}", virtio_type);
return PciTransport::new::<T>(self.pci_root, device_function).ok();
}
}
}