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