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 //! Library for working with (VirtIO) PCI devices discovered from a device tree.
16
17 use core::{
18 ffi::CStr,
19 fmt::{self, Display, Formatter},
20 ops::Range,
21 };
22 use libfdt::{AddressRange, Fdt, FdtError, FdtNode};
23 use log::debug;
24 use virtio_drivers::transport::pci::bus::{Cam, PciRoot};
25
26 /// PCI MMIO configuration region size.
27 const PCI_CFG_SIZE: usize = 0x100_0000;
28
29 /// An error parsing a PCI node from an FDT.
30 #[derive(Clone, Debug, Eq, PartialEq)]
31 pub enum PciError {
32 /// Error getting PCI node from FDT.
33 FdtErrorPci(FdtError),
34 /// Failed to find PCI bus in FDT.
35 FdtNoPci,
36 /// Error getting `reg` property from PCI node.
37 FdtErrorReg(FdtError),
38 /// PCI node missing `reg` property.
39 FdtMissingReg,
40 /// Empty `reg property on PCI node.
41 FdtRegEmpty,
42 /// PCI `reg` property missing size.
43 FdtRegMissingSize,
44 /// PCI CAM size reported by FDT is not what we expected.
45 CamWrongSize(usize),
46 /// Error getting `ranges` property from PCI node.
47 FdtErrorRanges(FdtError),
48 /// PCI node missing `ranges` property.
49 FdtMissingRanges,
50 /// Bus address is not equal to CPU physical address in `ranges` property.
51 RangeAddressMismatch {
52 /// A bus address from the `ranges` property.
53 bus_address: u64,
54 /// The corresponding CPU physical address from the `ranges` property.
55 cpu_physical: u64,
56 },
57 /// No suitable PCI memory range found.
58 NoSuitableRange,
59 }
60
61 impl Display for PciError {
fmt(&self, f: &mut Formatter) -> fmt::Result62 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
63 match self {
64 Self::FdtErrorPci(e) => write!(f, "Error getting PCI node from FDT: {}", e),
65 Self::FdtNoPci => write!(f, "Failed to find PCI bus in FDT."),
66 Self::FdtErrorReg(e) => write!(f, "Error getting reg property from PCI node: {}", e),
67 Self::FdtMissingReg => write!(f, "PCI node missing reg property."),
68 Self::FdtRegEmpty => write!(f, "Empty reg property on PCI node."),
69 Self::FdtRegMissingSize => write!(f, "PCI reg property missing size."),
70 Self::CamWrongSize(cam_size) => write!(
71 f,
72 "FDT says PCI CAM is {} bytes but we expected {}.",
73 cam_size, PCI_CFG_SIZE
74 ),
75 Self::FdtErrorRanges(e) => {
76 write!(f, "Error getting ranges property from PCI node: {}", e)
77 }
78 Self::FdtMissingRanges => write!(f, "PCI node missing ranges property."),
79 Self::RangeAddressMismatch { bus_address, cpu_physical } => {
80 write!(
81 f,
82 "bus address {:#018x} != CPU physical address {:#018x}",
83 bus_address, cpu_physical
84 )
85 }
86 Self::NoSuitableRange => write!(f, "No suitable PCI memory range found."),
87 }
88 }
89 }
90
91 /// Information about the PCI bus parsed from the device tree.
92 #[derive(Clone, Debug)]
93 pub struct PciInfo {
94 /// The MMIO range used by the memory-mapped PCI CAM.
95 pub cam_range: Range<usize>,
96 /// The MMIO range from which 32-bit PCI BARs should be allocated.
97 pub bar_range: Range<u32>,
98 }
99
100 impl PciInfo {
101 /// Finds the PCI node in the FDT, parses its properties and validates it.
from_fdt(fdt: &Fdt) -> Result<Self, PciError>102 pub fn from_fdt(fdt: &Fdt) -> Result<Self, PciError> {
103 let pci_node = pci_node(fdt)?;
104
105 let cam_range = parse_cam_range(&pci_node)?;
106 let bar_range = parse_ranges(&pci_node)?;
107
108 Ok(Self { cam_range, bar_range })
109 }
110
111 /// Returns the `PciRoot` for the memory-mapped CAM found in the FDT. The CAM should be mapped
112 /// before this is called, by calling [`PciInfo::map`].
113 ///
114 /// # Safety
115 ///
116 /// To prevent concurrent access, only one `PciRoot` should exist in the program. Thus this
117 /// method must only be called once, and there must be no other `PciRoot` constructed using the
118 /// same CAM.
make_pci_root(&self) -> PciRoot119 pub unsafe fn make_pci_root(&self) -> PciRoot {
120 // SAFETY: We trust that the FDT gave us a valid MMIO base address for the CAM. The caller
121 // guarantees to only call us once, so there are no other references to it.
122 unsafe { PciRoot::new(self.cam_range.start as *mut u8, Cam::MmioCam) }
123 }
124 }
125
126 /// Finds an FDT node with compatible=pci-host-cam-generic.
pci_node(fdt: &Fdt) -> Result<FdtNode, PciError>127 fn pci_node(fdt: &Fdt) -> Result<FdtNode, PciError> {
128 fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
129 .map_err(PciError::FdtErrorPci)?
130 .next()
131 .ok_or(PciError::FdtNoPci)
132 }
133
134 /// Parses the "reg" property of the given PCI FDT node to find the MMIO CAM range.
parse_cam_range(pci_node: &FdtNode) -> Result<Range<usize>, PciError>135 fn parse_cam_range(pci_node: &FdtNode) -> Result<Range<usize>, PciError> {
136 let pci_reg = pci_node
137 .reg()
138 .map_err(PciError::FdtErrorReg)?
139 .ok_or(PciError::FdtMissingReg)?
140 .next()
141 .ok_or(PciError::FdtRegEmpty)?;
142 let cam_addr = pci_reg.addr as usize;
143 let cam_size = pci_reg.size.ok_or(PciError::FdtRegMissingSize)? as usize;
144 debug!("Found PCI CAM at {:#x}-{:#x}", cam_addr, cam_addr + cam_size);
145 // Check that the CAM is the size we expect, so we don't later try accessing it beyond its
146 // bounds. If it is a different size then something is very wrong and we shouldn't continue to
147 // access it; maybe there is some new version of PCI we don't know about.
148 if cam_size != PCI_CFG_SIZE {
149 return Err(PciError::CamWrongSize(cam_size));
150 }
151
152 Ok(cam_addr..cam_addr + cam_size)
153 }
154
155 /// Parses the "ranges" property of the given PCI FDT node, and returns the largest suitable range
156 /// to use for non-prefetchable 32-bit memory BARs.
parse_ranges(pci_node: &FdtNode) -> Result<Range<u32>, PciError>157 fn parse_ranges(pci_node: &FdtNode) -> Result<Range<u32>, PciError> {
158 let mut memory_address = 0;
159 let mut memory_size = 0;
160
161 for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
162 .ranges::<(u32, u64), u64, u64>()
163 .map_err(PciError::FdtErrorRanges)?
164 .ok_or(PciError::FdtMissingRanges)?
165 {
166 let flags = PciMemoryFlags(flags);
167 let prefetchable = flags.prefetchable();
168 let range_type = flags.range_type();
169 debug!(
170 "range: {:?} {}prefetchable bus address: {:#018x} CPU physical address: {:#018x} size: {:#018x}",
171 range_type,
172 if prefetchable { "" } else { "non-" },
173 bus_address,
174 cpu_physical,
175 size,
176 );
177
178 // Use a 64-bit range for 32-bit memory, if it is low enough, because crosvm doesn't
179 // currently provide any 32-bit ranges.
180 if !prefetchable
181 && matches!(range_type, PciRangeType::Memory32 | PciRangeType::Memory64)
182 && size > memory_size.into()
183 && bus_address + size < u32::MAX.into()
184 {
185 if bus_address != cpu_physical {
186 return Err(PciError::RangeAddressMismatch { bus_address, cpu_physical });
187 }
188 memory_address = u32::try_from(cpu_physical).unwrap();
189 memory_size = u32::try_from(size).unwrap();
190 }
191 }
192
193 if memory_size == 0 {
194 return Err(PciError::NoSuitableRange);
195 }
196
197 Ok(memory_address..memory_address + memory_size)
198 }
199
200 /// Encodes memory flags of a PCI range
201 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
202 pub struct PciMemoryFlags(pub u32);
203
204 impl PciMemoryFlags {
205 /// Returns whether this PCI range is prefetchable
prefetchable(self) -> bool206 pub fn prefetchable(self) -> bool {
207 self.0 & 0x40000000 != 0
208 }
209
210 /// Returns the type of this PCI range
range_type(self) -> PciRangeType211 pub fn range_type(self) -> PciRangeType {
212 PciRangeType::from((self.0 & 0x3000000) >> 24)
213 }
214 }
215
216 /// Type of a PCI range
217 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
218 pub enum PciRangeType {
219 /// Range represents the PCI configuration space
220 ConfigurationSpace,
221 /// Range is on IO space
222 IoSpace,
223 /// Range is on 32-bit MMIO space
224 Memory32,
225 /// Range is on 64-bit MMIO space
226 Memory64,
227 }
228
229 impl From<u32> for PciRangeType {
from(value: u32) -> Self230 fn from(value: u32) -> Self {
231 match value {
232 0 => Self::ConfigurationSpace,
233 1 => Self::IoSpace,
234 2 => Self::Memory32,
235 3 => Self::Memory64,
236 _ => panic!("Tried to convert invalid range type {}", value),
237 }
238 }
239 }
240