blob: c2e33010ec26786851c8a365047ef3097f68b35c [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
Pierre-Clément Tosif2c19d42024-10-01 17:42:04 +010017use crate::{
18 fdt::pci::PciInfo,
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000019 memory::{map_device, MemoryTrackerError},
Pierre-Clément Tosif2c19d42024-10-01 17:42:04 +010020};
Andrew Walbranb398fc82023-01-24 14:45:46 +000021use alloc::boxed::Box;
Alice Wang287de622023-06-08 13:17:03 +000022use core::fmt;
Alice Wang7c55c7d2023-07-05 14:51:40 +000023use core::marker::PhantomData;
Alice Wang287de622023-06-08 13:17:03 +000024use log::debug;
Andrew Walbranb398fc82023-01-24 14:45:46 +000025use once_cell::race::OnceBox;
Andrew Walbran848decf2022-12-15 14:39:38 +000026use virtio_drivers::{
Alice Wang16e7c3f2023-07-06 08:06:00 +000027 device::{blk, socket},
Alice Wang0e086232023-06-12 13:47:40 +000028 transport::pci::{
Andrew Walbraneb75fb92024-12-13 15:37:30 +000029 bus::{BusDeviceIterator, ConfigurationAccess, PciRoot},
Alice Wang0e086232023-06-12 13:47:40 +000030 virtio_device_type, PciTransport,
Andrew Walbran848decf2022-12-15 14:39:38 +000031 },
Alice Wang7c55c7d2023-07-05 14:51:40 +000032 Hal,
Andrew Walbran848decf2022-12-15 14:39:38 +000033};
Andrew Walbran19690632022-12-07 16:41:30 +000034
Andrew Walbranb398fc82023-01-24 14:45:46 +000035pub(super) static PCI_INFO: OnceBox<PciInfo> = OnceBox::new();
36
Alice Wang287de622023-06-08 13:17:03 +000037/// PCI errors.
38#[derive(Debug, Clone)]
39pub 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
48impl fmt::Display for PciError {
49 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
Andrew Walbranb398fc82023-01-24 14:45:46 +000060/// 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///
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000068/// This must only be called once and after having switched to the dynamic page tables.
Andrew Walbraneb75fb92024-12-13 15:37:30 +000069pub fn initialize(pci_info: PciInfo) -> Result<PciRoot<impl ConfigurationAccess>, PciError> {
Alice Wang287de622023-06-08 13:17:03 +000070 PCI_INFO.set(Box::new(pci_info.clone())).map_err(|_| PciError::DuplicateInitialization)?;
Andrew Walbranb398fc82023-01-24 14:45:46 +000071
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000072 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)?;
Andrew Walbranb398fc82023-01-24 14:45:46 +000075
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000076 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`.
Andrew Walbranb398fc82023-01-24 14:45:46 +000081 Ok(unsafe { pci_info.make_pci_root() })
82}
83
Alice Wangeade1672023-06-08 14:56:20 +000084/// Virtio Block device.
Alice Wang7c55c7d2023-07-05 14:51:40 +000085pub type VirtIOBlk<T> = blk::VirtIOBlk<T, PciTransport>;
Pierre-Clément Tosi1cc5eb72023-02-02 11:09:18 +000086
Alice Wang16e7c3f2023-07-06 08:06:00 +000087/// Virtio Socket device.
88///
89/// Spec: https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html 5.10
90pub type VirtIOSocket<T> = socket::VirtIOSocket<T, PciTransport>;
91
Alice Wang0e086232023-06-12 13:47:40 +000092/// An iterator that iterates over the PCI transport for each device.
Andrew Walbraneb75fb92024-12-13 15:37:30 +000093pub struct PciTransportIterator<'a, T: Hal, C: ConfigurationAccess> {
94 pci_root: &'a mut PciRoot<C>,
95 bus: BusDeviceIterator<C>,
Alice Wang7c55c7d2023-07-05 14:51:40 +000096 _hal: PhantomData<T>,
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +000097}
98
Andrew Walbraneb75fb92024-12-13 15:37:30 +000099impl<'a, T: Hal, C: ConfigurationAccess> PciTransportIterator<'a, T, C> {
Alice Wangeade1672023-06-08 14:56:20 +0000100 /// Creates a new iterator.
Andrew Walbraneb75fb92024-12-13 15:37:30 +0000101 pub fn new(pci_root: &'a mut PciRoot<C>) -> Self {
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000102 let bus = pci_root.enumerate_bus(0);
Alice Wang7c55c7d2023-07-05 14:51:40 +0000103 Self { pci_root, bus, _hal: PhantomData }
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000104 }
105}
106
Chris Wailes52358e92025-01-27 17:04:40 -0800107impl<T: Hal, C: ConfigurationAccess> Iterator for PciTransportIterator<'_, T, C> {
Alice Wang0e086232023-06-12 13:47:40 +0000108 type Item = PciTransport;
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000109
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
Pierre-Clément Tosiebb37602023-02-17 14:57:26 +0000119 let Some(virtio_type) = virtio_device_type(&info) else {
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000120 continue;
121 };
Andrew Walbrand1d03182022-12-09 18:20:01 +0000122 debug!(" VirtIO {:?}", virtio_type);
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000123
Andrew Walbraneb75fb92024-12-13 15:37:30 +0000124 return PciTransport::new::<T, C>(self.pci_root, device_function).ok();
Andrew Walbrand1d03182022-12-09 18:20:01 +0000125 }
126 }
Pierre-Clément Tosie8ec0392023-01-16 15:38:31 +0000127}