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 super::hal::HalImpl;
18 use crate::{entry::RebootReason, memory::MemoryTracker};
19 use alloc::boxed::Box;
20 use fdtpci::PciInfo;
21 use log::{debug, error};
22 use once_cell::race::OnceBox;
23 use virtio_drivers::{
24 device::blk,
25 transport::{
26 pci::{
27 bus::{BusDeviceIterator, PciRoot},
28 virtio_device_type, PciTransport,
29 },
30 DeviceType, Transport,
31 },
32 };
33
34 pub(super) static PCI_INFO: OnceBox<PciInfo> = OnceBox::new();
35
36 /// Prepares to use VirtIO PCI devices.
37 ///
38 /// In particular:
39 ///
40 /// 1. Maps the PCI CAM and BAR range in the page table and MMIO guard.
41 /// 2. Stores the `PciInfo` for the VirtIO HAL to use later.
42 /// 3. Creates and returns a `PciRoot`.
43 ///
44 /// This must only be called once; it will panic if it is called a second time.
initialise(pci_info: PciInfo, memory: &mut MemoryTracker) -> Result<PciRoot, RebootReason>45 pub fn initialise(pci_info: PciInfo, memory: &mut MemoryTracker) -> Result<PciRoot, RebootReason> {
46 map_mmio(&pci_info, memory)?;
47
48 PCI_INFO.set(Box::new(pci_info.clone())).expect("Tried to set PCI_INFO a second time");
49
50 // Safety: This is the only place where we call make_pci_root, and `PCI_INFO.set` above will
51 // panic if it is called a second time.
52 Ok(unsafe { pci_info.make_pci_root() })
53 }
54
55 /// Maps the CAM and BAR range in the page table and MMIO guard.
map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason>56 fn map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
57 memory.map_mmio_range(pci_info.cam_range.clone()).map_err(|e| {
58 error!("Failed to map PCI CAM: {}", e);
59 RebootReason::InternalError
60 })?;
61
62 memory
63 .map_mmio_range(pci_info.bar_range.start as usize..pci_info.bar_range.end as usize)
64 .map_err(|e| {
65 error!("Failed to map PCI MMIO range: {}", e);
66 RebootReason::InternalError
67 })?;
68
69 Ok(())
70 }
71
72 pub type VirtIOBlk = blk::VirtIOBlk<HalImpl, PciTransport>;
73
74 pub struct VirtIOBlkIterator<'a> {
75 pci_root: &'a mut PciRoot,
76 bus: BusDeviceIterator,
77 }
78
79 impl<'a> VirtIOBlkIterator<'a> {
new(pci_root: &'a mut PciRoot) -> Self80 pub fn new(pci_root: &'a mut PciRoot) -> Self {
81 let bus = pci_root.enumerate_bus(0);
82 Self { pci_root, bus }
83 }
84 }
85
86 impl<'a> Iterator for VirtIOBlkIterator<'a> {
87 type Item = VirtIOBlk;
88
next(&mut self) -> Option<Self::Item>89 fn next(&mut self) -> Option<Self::Item> {
90 loop {
91 let (device_function, info) = self.bus.next()?;
92 let (status, command) = self.pci_root.get_status_command(device_function);
93 debug!(
94 "Found PCI device {} at {}, status {:?} command {:?}",
95 info, device_function, status, command
96 );
97
98 let Some(virtio_type) = virtio_device_type(&info) else {
99 continue;
100 };
101 debug!(" VirtIO {:?}", virtio_type);
102
103 let mut transport =
104 PciTransport::new::<HalImpl>(self.pci_root, device_function).unwrap();
105 debug!(
106 "Detected virtio PCI device with device type {:?}, features {:#018x}",
107 transport.device_type(),
108 transport.read_device_features(),
109 );
110
111 if virtio_type == DeviceType::Block {
112 return Some(Self::Item::new(transport).expect("failed to create blk driver"));
113 }
114 }
115 }
116 }
117