blob: 301ecfcce0812cff63c2ec5bcac8a0e90837be14 [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 Walbran0d8b54d2022-12-08 16:32:33 +000017use crate::{
18 entry::RebootReason,
19 memory::{MemoryRange, MemoryTracker},
20};
Andrew Walbrand1d03182022-12-09 18:20:01 +000021use core::{
22 ffi::CStr,
23 fmt::{self, Display, Formatter},
24 ops::Range,
25};
26use libfdt::{AddressRange, Fdt, FdtError, FdtNode};
Andrew Walbran19690632022-12-07 16:41:30 +000027use log::{debug, error};
Andrew Walbrand1d03182022-12-09 18:20:01 +000028use virtio_drivers::pci::{
Andrew Walbran0a8dac72022-12-21 13:49:06 +000029 bus::{Cam, PciRoot},
Andrew Walbrand1d03182022-12-09 18:20:01 +000030 virtio_device_type,
31};
Andrew Walbran19690632022-12-07 16:41:30 +000032
33/// PCI MMIO configuration region size.
34const PCI_CFG_SIZE: usize = 0x100_0000;
35
Andrew Walbrand1d03182022-12-09 18:20:01 +000036#[derive(Clone, Debug, Eq, PartialEq)]
37pub enum PciError {
38 FdtErrorPci(FdtError),
39 FdtNoPci,
40 FdtErrorReg(FdtError),
41 FdtMissingReg,
42 FdtRegEmpty,
43 FdtRegMissingSize,
44 CamWrongSize(usize),
45 FdtErrorRanges(FdtError),
46 FdtMissingRanges,
47 RangeAddressMismatch { bus_address: u64, cpu_physical: u64 },
48 NoSuitableRange,
Andrew Walbrand1d03182022-12-09 18:20:01 +000049}
50
51impl Display for PciError {
52 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
53 match self {
54 Self::FdtErrorPci(e) => write!(f, "Error getting PCI node from FDT: {}", e),
55 Self::FdtNoPci => write!(f, "Failed to find PCI bus in FDT."),
56 Self::FdtErrorReg(e) => write!(f, "Error getting reg property from PCI node: {}", e),
57 Self::FdtMissingReg => write!(f, "PCI node missing reg property."),
58 Self::FdtRegEmpty => write!(f, "Empty reg property on PCI node."),
59 Self::FdtRegMissingSize => write!(f, "PCI reg property missing size."),
60 Self::CamWrongSize(cam_size) => write!(
61 f,
62 "FDT says PCI CAM is {} bytes but we expected {}.",
63 cam_size, PCI_CFG_SIZE
64 ),
65 Self::FdtErrorRanges(e) => {
66 write!(f, "Error getting ranges property from PCI node: {}", e)
67 }
68 Self::FdtMissingRanges => write!(f, "PCI node missing ranges property."),
69 Self::RangeAddressMismatch { bus_address, cpu_physical } => {
70 write!(
71 f,
72 "bus address {:#018x} != CPU physical address {:#018x}",
73 bus_address, cpu_physical
74 )
75 }
76 Self::NoSuitableRange => write!(f, "No suitable PCI memory range found."),
Andrew Walbrand1d03182022-12-09 18:20:01 +000077 }
78 }
79}
80
Andrew Walbran0d8b54d2022-12-08 16:32:33 +000081/// Information about the PCI bus parsed from the device tree.
82#[derive(Debug)]
83pub struct PciInfo {
84 /// The MMIO range used by the memory-mapped PCI CAM.
85 cam_range: MemoryRange,
86 /// The MMIO range from which 32-bit PCI BARs should be allocated.
87 bar_range: Range<u32>,
88}
89
90impl PciInfo {
91 /// Finds the PCI node in the FDT, parses its properties and validates it.
Andrew Walbrand1d03182022-12-09 18:20:01 +000092 pub fn from_fdt(fdt: &Fdt) -> Result<Self, PciError> {
Andrew Walbran0d8b54d2022-12-08 16:32:33 +000093 let pci_node = pci_node(fdt)?;
94
95 let cam_range = parse_cam_range(&pci_node)?;
96 let bar_range = parse_ranges(&pci_node)?;
97
98 Ok(Self { cam_range, bar_range })
99 }
100
101 /// Maps the CAM and BAR range in the page table and MMIO guard.
102 pub fn map(&self, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
103 memory.map_mmio_range(self.cam_range.clone()).map_err(|e| {
104 error!("Failed to map PCI CAM: {}", e);
105 RebootReason::InternalError
106 })?;
107
108 memory.map_mmio_range(self.bar_range.start as usize..self.bar_range.end as usize).map_err(
109 |e| {
110 error!("Failed to map PCI MMIO range: {}", e);
111 RebootReason::InternalError
112 },
113 )?;
114
115 Ok(())
116 }
Andrew Walbrand1d03182022-12-09 18:20:01 +0000117
118 /// Returns the `PciRoot` for the memory-mapped CAM found in the FDT. The CAM should be mapped
119 /// before this is called, by calling [`PciInfo::map`].
120 ///
121 /// # Safety
122 ///
123 /// To prevent concurrent access, only one `PciRoot` should exist in the program. Thus this
124 /// method must only be called once, and there must be no other `PciRoot` constructed using the
125 /// same CAM.
126 pub unsafe fn make_pci_root(&self) -> PciRoot {
127 PciRoot::new(self.cam_range.start as *mut u8, Cam::MmioCam)
128 }
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000129}
130
Andrew Walbran19690632022-12-07 16:41:30 +0000131/// Finds an FDT node with compatible=pci-host-cam-generic.
Andrew Walbrand1d03182022-12-09 18:20:01 +0000132fn pci_node(fdt: &Fdt) -> Result<FdtNode, PciError> {
Andrew Walbran19690632022-12-07 16:41:30 +0000133 fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
Andrew Walbrand1d03182022-12-09 18:20:01 +0000134 .map_err(PciError::FdtErrorPci)?
Andrew Walbran19690632022-12-07 16:41:30 +0000135 .next()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000136 .ok_or(PciError::FdtNoPci)
Andrew Walbran19690632022-12-07 16:41:30 +0000137}
138
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000139/// Parses the "reg" property of the given PCI FDT node to find the MMIO CAM range.
Andrew Walbrand1d03182022-12-09 18:20:01 +0000140fn parse_cam_range(pci_node: &FdtNode) -> Result<MemoryRange, PciError> {
Andrew Walbran19690632022-12-07 16:41:30 +0000141 let pci_reg = pci_node
142 .reg()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000143 .map_err(PciError::FdtErrorReg)?
144 .ok_or(PciError::FdtMissingReg)?
Andrew Walbran19690632022-12-07 16:41:30 +0000145 .next()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000146 .ok_or(PciError::FdtRegEmpty)?;
Andrew Walbran19690632022-12-07 16:41:30 +0000147 let cam_addr = pci_reg.addr as usize;
Andrew Walbrand1d03182022-12-09 18:20:01 +0000148 let cam_size = pci_reg.size.ok_or(PciError::FdtRegMissingSize)? as usize;
Andrew Walbran19690632022-12-07 16:41:30 +0000149 debug!("Found PCI CAM at {:#x}-{:#x}", cam_addr, cam_addr + cam_size);
150 // Check that the CAM is the size we expect, so we don't later try accessing it beyond its
151 // bounds. If it is a different size then something is very wrong and we shouldn't continue to
152 // access it; maybe there is some new version of PCI we don't know about.
153 if cam_size != PCI_CFG_SIZE {
Andrew Walbrand1d03182022-12-09 18:20:01 +0000154 return Err(PciError::CamWrongSize(cam_size));
Andrew Walbran19690632022-12-07 16:41:30 +0000155 }
156
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000157 Ok(cam_addr..cam_addr + cam_size)
158}
Andrew Walbran19690632022-12-07 16:41:30 +0000159
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000160/// Parses the "ranges" property of the given PCI FDT node, and returns the largest suitable range
161/// to use for non-prefetchable 32-bit memory BARs.
Andrew Walbrand1d03182022-12-09 18:20:01 +0000162fn parse_ranges(pci_node: &FdtNode) -> Result<Range<u32>, PciError> {
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000163 let mut memory_address = 0;
164 let mut memory_size = 0;
165
166 for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
167 .ranges::<(u32, u64), u64, u64>()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000168 .map_err(PciError::FdtErrorRanges)?
169 .ok_or(PciError::FdtMissingRanges)?
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000170 {
171 let flags = PciMemoryFlags(flags);
172 let prefetchable = flags.prefetchable();
173 let range_type = flags.range_type();
174 debug!(
175 "range: {:?} {}prefetchable bus address: {:#018x} CPU physical address: {:#018x} size: {:#018x}",
176 range_type,
177 if prefetchable { "" } else { "non-" },
178 bus_address,
179 cpu_physical,
180 size,
181 );
182
183 // Use a 64-bit range for 32-bit memory, if it is low enough, because crosvm doesn't
184 // currently provide any 32-bit ranges.
185 if !prefetchable
186 && matches!(range_type, PciRangeType::Memory32 | PciRangeType::Memory64)
187 && size > memory_size.into()
188 && bus_address + size < u32::MAX.into()
189 {
190 if bus_address != cpu_physical {
Andrew Walbrand1d03182022-12-09 18:20:01 +0000191 return Err(PciError::RangeAddressMismatch { bus_address, cpu_physical });
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000192 }
193 memory_address = u32::try_from(cpu_physical).unwrap();
194 memory_size = u32::try_from(size).unwrap();
195 }
196 }
197
198 if memory_size == 0 {
Andrew Walbrand1d03182022-12-09 18:20:01 +0000199 return Err(PciError::NoSuitableRange);
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000200 }
201
202 Ok(memory_address..memory_address + memory_size)
203}
204
205#[derive(Copy, Clone, Debug, Eq, PartialEq)]
206struct PciMemoryFlags(u32);
207
208impl PciMemoryFlags {
209 pub fn prefetchable(self) -> bool {
210 self.0 & 0x80000000 != 0
211 }
212
213 pub fn range_type(self) -> PciRangeType {
214 PciRangeType::from((self.0 & 0x3000000) >> 24)
215 }
216}
217
218#[derive(Copy, Clone, Debug, Eq, PartialEq)]
219enum PciRangeType {
220 ConfigurationSpace,
221 IoSpace,
222 Memory32,
223 Memory64,
224}
225
226impl From<u32> for PciRangeType {
227 fn from(value: u32) -> Self {
228 match value {
229 0 => Self::ConfigurationSpace,
230 1 => Self::IoSpace,
231 2 => Self::Memory32,
232 3 => Self::Memory64,
233 _ => panic!("Tried to convert invalid range type {}", value),
234 }
235 }
Andrew Walbran19690632022-12-07 16:41:30 +0000236}
Andrew Walbrand1d03182022-12-09 18:20:01 +0000237
Andrew Walbran0a8dac72022-12-21 13:49:06 +0000238/// Finds VirtIO PCI devices.
239pub fn find_virtio_devices(pci_root: &mut PciRoot) -> Result<(), PciError> {
Andrew Walbrand1d03182022-12-09 18:20:01 +0000240 for (device_function, info) in pci_root.enumerate_bus(0) {
241 let (status, command) = pci_root.get_status_command(device_function);
242 debug!(
243 "Found PCI device {} at {}, status {:?} command {:?}",
244 info, device_function, status, command
245 );
246 if let Some(virtio_type) = virtio_device_type(&info) {
247 debug!(" VirtIO {:?}", virtio_type);
Andrew Walbrand1d03182022-12-09 18:20:01 +0000248 }
249 }
250
251 Ok(())
252}