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