blob: dd9e34e57d864e1879fb9a3dbef44917b5da8d65 [file] [log] [blame]
Andrew Walbran19690632022-12-07 16:41:30 +00001// 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
Andrew Walbran0a8dac72022-12-21 13:49:06 +000015//! Functions to scan the PCI bus for VirtIO devices.
Andrew Walbran19690632022-12-07 16:41:30 +000016
Andrew Walbran848decf2022-12-15 14:39:38 +000017use super::hal::HalImpl;
Andrew Walbran730375d2022-12-21 14:04:34 +000018use crate::{entry::RebootReason, memory::MemoryTracker};
Andrew Walbranb398fc82023-01-24 14:45:46 +000019use alloc::boxed::Box;
Andrew Walbran730375d2022-12-21 14:04:34 +000020use fdtpci::{PciError, PciInfo};
Andrew Walbran848decf2022-12-15 14:39:38 +000021use log::{debug, error, info};
Andrew Walbranb398fc82023-01-24 14:45:46 +000022use once_cell::race::OnceBox;
Andrew Walbran848decf2022-12-15 14:39:38 +000023use virtio_drivers::{
24 device::blk::VirtIOBlk,
25 transport::{
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +000026 pci::{
27 bus::{BusDeviceIterator, PciRoot},
28 virtio_device_type, PciTransport,
29 },
Andrew Walbran848decf2022-12-15 14:39:38 +000030 DeviceType, Transport,
31 },
32};
Andrew Walbran19690632022-12-07 16:41:30 +000033
Andrew Walbranb398fc82023-01-24 14:45:46 +000034pub(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.
45pub 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
Andrew Walbran730375d2022-12-21 14:04:34 +000055/// Maps the CAM and BAR range in the page table and MMIO guard.
Andrew Walbranb398fc82023-01-24 14:45:46 +000056fn map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
Andrew Walbran730375d2022-12-21 14:04:34 +000057 memory.map_mmio_range(pci_info.cam_range.clone()).map_err(|e| {
58 error!("Failed to map PCI CAM: {}", e);
59 RebootReason::InternalError
60 })?;
Andrew Walbran19690632022-12-07 16:41:30 +000061
Andrew Walbran730375d2022-12-21 14:04:34 +000062 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);
Andrew Walbran0d8b54d2022-12-08 16:32:33 +000066 RebootReason::InternalError
67 })?;
68
Andrew Walbran730375d2022-12-21 14:04:34 +000069 Ok(())
Andrew Walbran19690632022-12-07 16:41:30 +000070}
Andrew Walbrand1d03182022-12-09 18:20:01 +000071
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +000072struct VirtIOBlkIterator<'a> {
73 pci_root: &'a mut PciRoot,
74 bus: BusDeviceIterator,
75}
76
77impl<'a> VirtIOBlkIterator<'a> {
78 pub fn new(pci_root: &'a mut PciRoot) -> Self {
79 let bus = pci_root.enumerate_bus(0);
80 Self { pci_root, bus }
81 }
82}
83
84impl<'a> Iterator for VirtIOBlkIterator<'a> {
85 type Item = VirtIOBlk<HalImpl, PciTransport>;
86
87 fn next(&mut self) -> Option<Self::Item> {
88 loop {
89 let (device_function, info) = self.bus.next()?;
90 let (status, command) = self.pci_root.get_status_command(device_function);
91 debug!(
92 "Found PCI device {} at {}, status {:?} command {:?}",
93 info, device_function, status, command
94 );
95
96 let virtio_type = if let Some(t) = virtio_device_type(&info) {
97 t
98 } else {
99 continue;
100 };
Andrew Walbrand1d03182022-12-09 18:20:01 +0000101 debug!(" VirtIO {:?}", virtio_type);
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000102
103 let mut transport =
104 PciTransport::new::<HalImpl>(self.pci_root, device_function).unwrap();
105 debug!(
Andrew Walbran848decf2022-12-15 14:39:38 +0000106 "Detected virtio PCI device with device type {:?}, features {:#018x}",
107 transport.device_type(),
108 transport.read_device_features(),
109 );
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000110
Andrew Walbran848decf2022-12-15 14:39:38 +0000111 if virtio_type == DeviceType::Block {
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000112 return Some(Self::Item::new(transport).expect("failed to create blk driver"));
Andrew Walbran848decf2022-12-15 14:39:38 +0000113 }
Andrew Walbrand1d03182022-12-09 18:20:01 +0000114 }
115 }
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000116}
117
118/// Finds VirtIO PCI devices.
119pub fn find_virtio_devices(pci_root: &mut PciRoot) -> Result<(), PciError> {
120 for mut blk in VirtIOBlkIterator::new(pci_root) {
121 info!("Found {} KiB block device.", blk.capacity() * 512 / 1024);
122 let mut data = [0; 512];
123 blk.read_block(0, &mut data).expect("Failed to read block device");
124 }
Andrew Walbrand1d03182022-12-09 18:20:01 +0000125
126 Ok(())
127}