1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Functions to scan the PCI bus for VirtIO devices.
16 
17 use crate::{
18     fdt::pci::PciInfo,
19     memory::{map_device, MemoryTrackerError},
20 };
21 use alloc::boxed::Box;
22 use core::fmt;
23 use core::marker::PhantomData;
24 use log::debug;
25 use once_cell::race::OnceBox;
26 use virtio_drivers::{
27     device::{blk, socket},
28     transport::pci::{
29         bus::{BusDeviceIterator, PciRoot},
30         virtio_device_type, PciTransport,
31     },
32     Hal,
33 };
34 
35 pub(super) static PCI_INFO: OnceBox<PciInfo> = OnceBox::new();
36 
37 /// PCI errors.
38 #[derive(Debug, Clone)]
39 pub enum PciError {
40     /// Attempted to initialize the PCI more than once.
41     DuplicateInitialization,
42     /// Failed to map PCI CAM.
43     CamMapFailed(MemoryTrackerError),
44     /// Failed to map PCI BAR.
45     BarMapFailed(MemoryTrackerError),
46 }
47 
48 impl fmt::Display for PciError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result49     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50         match self {
51             Self::DuplicateInitialization => {
52                 write!(f, "Attempted to initialize the PCI more than once.")
53             }
54             Self::CamMapFailed(e) => write!(f, "Failed to map PCI CAM: {e}"),
55             Self::BarMapFailed(e) => write!(f, "Failed to map PCI BAR: {e}"),
56         }
57     }
58 }
59 
60 /// Prepares to use VirtIO PCI devices.
61 ///
62 /// In particular:
63 ///
64 /// 1. Maps the PCI CAM and BAR range in the page table and MMIO guard.
65 /// 2. Stores the `PciInfo` for the VirtIO HAL to use later.
66 /// 3. Creates and returns a `PciRoot`.
67 ///
68 /// This must only be called once and after having switched to the dynamic page tables.
initialize(pci_info: PciInfo) -> Result<PciRoot, PciError>69 pub fn initialize(pci_info: PciInfo) -> Result<PciRoot, PciError> {
70     PCI_INFO.set(Box::new(pci_info.clone())).map_err(|_| PciError::DuplicateInitialization)?;
71 
72     let cam_start = pci_info.cam_range.start;
73     let cam_size = pci_info.cam_range.len().try_into().unwrap();
74     map_device(cam_start, cam_size).map_err(PciError::CamMapFailed)?;
75 
76     let bar_start = pci_info.bar_range.start.try_into().unwrap();
77     let bar_size = pci_info.bar_range.len().try_into().unwrap();
78     map_device(bar_start, bar_size).map_err(PciError::BarMapFailed)?;
79 
80     // SAFETY: This is the only place where we call make_pci_root, validated by `PCI_INFO.set`.
81     Ok(unsafe { pci_info.make_pci_root() })
82 }
83 
84 /// Virtio Block device.
85 pub type VirtIOBlk<T> = blk::VirtIOBlk<T, PciTransport>;
86 
87 /// Virtio Socket device.
88 ///
89 /// Spec: https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html 5.10
90 pub type VirtIOSocket<T> = socket::VirtIOSocket<T, PciTransport>;
91 
92 /// An iterator that iterates over the PCI transport for each device.
93 pub struct PciTransportIterator<'a, T: Hal> {
94     pci_root: &'a mut PciRoot,
95     bus: BusDeviceIterator,
96     _hal: PhantomData<T>,
97 }
98 
99 impl<'a, T: Hal> PciTransportIterator<'a, T> {
100     /// Creates a new iterator.
new(pci_root: &'a mut PciRoot) -> Self101     pub fn new(pci_root: &'a mut PciRoot) -> Self {
102         let bus = pci_root.enumerate_bus(0);
103         Self { pci_root, bus, _hal: PhantomData }
104     }
105 }
106 
107 impl<'a, T: Hal> Iterator for PciTransportIterator<'a, T> {
108     type Item = PciTransport;
109 
next(&mut self) -> Option<Self::Item>110     fn next(&mut self) -> Option<Self::Item> {
111         loop {
112             let (device_function, info) = self.bus.next()?;
113             let (status, command) = self.pci_root.get_status_command(device_function);
114             debug!(
115                 "Found PCI device {} at {}, status {:?} command {:?}",
116                 info, device_function, status, command
117             );
118 
119             let Some(virtio_type) = virtio_device_type(&info) else {
120                 continue;
121             };
122             debug!("  VirtIO {:?}", virtio_type);
123 
124             return PciTransport::new::<T>(self.pci_root, device_function).ok();
125         }
126     }
127 }
128