blob: d971c7b344516f7484cd5e474963c86c923b458d [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
15//! Functions to scan the PCI bus for VirtIO device and allocate BARs.
16
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::{
29 bus::{self, BarInfo, Cam, Command, DeviceFunction, MemoryBarType, PciRoot},
30 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,
49 BarInfoFailed(bus::PciError),
50 BarAllocationFailed { size: u32, device_function: DeviceFunction },
51 UnsupportedBarType(MemoryBarType),
52}
53
54impl Display for PciError {
55 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
56 match self {
57 Self::FdtErrorPci(e) => write!(f, "Error getting PCI node from FDT: {}", e),
58 Self::FdtNoPci => write!(f, "Failed to find PCI bus in FDT."),
59 Self::FdtErrorReg(e) => write!(f, "Error getting reg property from PCI node: {}", e),
60 Self::FdtMissingReg => write!(f, "PCI node missing reg property."),
61 Self::FdtRegEmpty => write!(f, "Empty reg property on PCI node."),
62 Self::FdtRegMissingSize => write!(f, "PCI reg property missing size."),
63 Self::CamWrongSize(cam_size) => write!(
64 f,
65 "FDT says PCI CAM is {} bytes but we expected {}.",
66 cam_size, PCI_CFG_SIZE
67 ),
68 Self::FdtErrorRanges(e) => {
69 write!(f, "Error getting ranges property from PCI node: {}", e)
70 }
71 Self::FdtMissingRanges => write!(f, "PCI node missing ranges property."),
72 Self::RangeAddressMismatch { bus_address, cpu_physical } => {
73 write!(
74 f,
75 "bus address {:#018x} != CPU physical address {:#018x}",
76 bus_address, cpu_physical
77 )
78 }
79 Self::NoSuitableRange => write!(f, "No suitable PCI memory range found."),
80 Self::BarInfoFailed(e) => write!(f, "Error getting PCI BAR information: {}", e),
81 Self::BarAllocationFailed { size, device_function } => write!(
82 f,
83 "Failed to allocate memory BAR of size {} for PCI device {}.",
84 size, device_function
85 ),
86 Self::UnsupportedBarType(address_type) => {
87 write!(f, "Memory BAR address type {:?} not supported.", address_type)
88 }
89 }
90 }
91}
92
Andrew Walbran0d8b54d2022-12-08 16:32:33 +000093/// Information about the PCI bus parsed from the device tree.
94#[derive(Debug)]
95pub struct PciInfo {
96 /// The MMIO range used by the memory-mapped PCI CAM.
97 cam_range: MemoryRange,
98 /// The MMIO range from which 32-bit PCI BARs should be allocated.
99 bar_range: Range<u32>,
100}
101
102impl PciInfo {
103 /// Finds the PCI node in the FDT, parses its properties and validates it.
Andrew Walbrand1d03182022-12-09 18:20:01 +0000104 pub fn from_fdt(fdt: &Fdt) -> Result<Self, PciError> {
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000105 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 /// Maps the CAM and BAR range in the page table and MMIO guard.
114 pub fn map(&self, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
115 memory.map_mmio_range(self.cam_range.clone()).map_err(|e| {
116 error!("Failed to map PCI CAM: {}", e);
117 RebootReason::InternalError
118 })?;
119
120 memory.map_mmio_range(self.bar_range.start as usize..self.bar_range.end as usize).map_err(
121 |e| {
122 error!("Failed to map PCI MMIO range: {}", e);
123 RebootReason::InternalError
124 },
125 )?;
126
127 Ok(())
128 }
Andrew Walbrand1d03182022-12-09 18:20:01 +0000129
130 /// Returns the `PciRoot` for the memory-mapped CAM found in the FDT. The CAM should be mapped
131 /// before this is called, by calling [`PciInfo::map`].
132 ///
133 /// # Safety
134 ///
135 /// To prevent concurrent access, only one `PciRoot` should exist in the program. Thus this
136 /// method must only be called once, and there must be no other `PciRoot` constructed using the
137 /// same CAM.
138 pub unsafe fn make_pci_root(&self) -> PciRoot {
139 PciRoot::new(self.cam_range.start as *mut u8, Cam::MmioCam)
140 }
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000141}
142
Andrew Walbran19690632022-12-07 16:41:30 +0000143/// Finds an FDT node with compatible=pci-host-cam-generic.
Andrew Walbrand1d03182022-12-09 18:20:01 +0000144fn pci_node(fdt: &Fdt) -> Result<FdtNode, PciError> {
Andrew Walbran19690632022-12-07 16:41:30 +0000145 fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
Andrew Walbrand1d03182022-12-09 18:20:01 +0000146 .map_err(PciError::FdtErrorPci)?
Andrew Walbran19690632022-12-07 16:41:30 +0000147 .next()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000148 .ok_or(PciError::FdtNoPci)
Andrew Walbran19690632022-12-07 16:41:30 +0000149}
150
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000151/// Parses the "reg" property of the given PCI FDT node to find the MMIO CAM range.
Andrew Walbrand1d03182022-12-09 18:20:01 +0000152fn parse_cam_range(pci_node: &FdtNode) -> Result<MemoryRange, PciError> {
Andrew Walbran19690632022-12-07 16:41:30 +0000153 let pci_reg = pci_node
154 .reg()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000155 .map_err(PciError::FdtErrorReg)?
156 .ok_or(PciError::FdtMissingReg)?
Andrew Walbran19690632022-12-07 16:41:30 +0000157 .next()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000158 .ok_or(PciError::FdtRegEmpty)?;
Andrew Walbran19690632022-12-07 16:41:30 +0000159 let cam_addr = pci_reg.addr as usize;
Andrew Walbrand1d03182022-12-09 18:20:01 +0000160 let cam_size = pci_reg.size.ok_or(PciError::FdtRegMissingSize)? as usize;
Andrew Walbran19690632022-12-07 16:41:30 +0000161 debug!("Found PCI CAM at {:#x}-{:#x}", cam_addr, cam_addr + cam_size);
162 // Check that the CAM is the size we expect, so we don't later try accessing it beyond its
163 // bounds. If it is a different size then something is very wrong and we shouldn't continue to
164 // access it; maybe there is some new version of PCI we don't know about.
165 if cam_size != PCI_CFG_SIZE {
Andrew Walbrand1d03182022-12-09 18:20:01 +0000166 return Err(PciError::CamWrongSize(cam_size));
Andrew Walbran19690632022-12-07 16:41:30 +0000167 }
168
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000169 Ok(cam_addr..cam_addr + cam_size)
170}
Andrew Walbran19690632022-12-07 16:41:30 +0000171
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000172/// Parses the "ranges" property of the given PCI FDT node, and returns the largest suitable range
173/// to use for non-prefetchable 32-bit memory BARs.
Andrew Walbrand1d03182022-12-09 18:20:01 +0000174fn parse_ranges(pci_node: &FdtNode) -> Result<Range<u32>, PciError> {
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000175 let mut memory_address = 0;
176 let mut memory_size = 0;
177
178 for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
179 .ranges::<(u32, u64), u64, u64>()
Andrew Walbrand1d03182022-12-09 18:20:01 +0000180 .map_err(PciError::FdtErrorRanges)?
181 .ok_or(PciError::FdtMissingRanges)?
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000182 {
183 let flags = PciMemoryFlags(flags);
184 let prefetchable = flags.prefetchable();
185 let range_type = flags.range_type();
186 debug!(
187 "range: {:?} {}prefetchable bus address: {:#018x} CPU physical address: {:#018x} size: {:#018x}",
188 range_type,
189 if prefetchable { "" } else { "non-" },
190 bus_address,
191 cpu_physical,
192 size,
193 );
194
195 // Use a 64-bit range for 32-bit memory, if it is low enough, because crosvm doesn't
196 // currently provide any 32-bit ranges.
197 if !prefetchable
198 && matches!(range_type, PciRangeType::Memory32 | PciRangeType::Memory64)
199 && size > memory_size.into()
200 && bus_address + size < u32::MAX.into()
201 {
202 if bus_address != cpu_physical {
Andrew Walbrand1d03182022-12-09 18:20:01 +0000203 return Err(PciError::RangeAddressMismatch { bus_address, cpu_physical });
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000204 }
205 memory_address = u32::try_from(cpu_physical).unwrap();
206 memory_size = u32::try_from(size).unwrap();
207 }
208 }
209
210 if memory_size == 0 {
Andrew Walbrand1d03182022-12-09 18:20:01 +0000211 return Err(PciError::NoSuitableRange);
Andrew Walbran0d8b54d2022-12-08 16:32:33 +0000212 }
213
214 Ok(memory_address..memory_address + memory_size)
215}
216
217#[derive(Copy, Clone, Debug, Eq, PartialEq)]
218struct PciMemoryFlags(u32);
219
220impl PciMemoryFlags {
221 pub fn prefetchable(self) -> bool {
222 self.0 & 0x80000000 != 0
223 }
224
225 pub fn range_type(self) -> PciRangeType {
226 PciRangeType::from((self.0 & 0x3000000) >> 24)
227 }
228}
229
230#[derive(Copy, Clone, Debug, Eq, PartialEq)]
231enum PciRangeType {
232 ConfigurationSpace,
233 IoSpace,
234 Memory32,
235 Memory64,
236}
237
238impl From<u32> for PciRangeType {
239 fn from(value: u32) -> Self {
240 match value {
241 0 => Self::ConfigurationSpace,
242 1 => Self::IoSpace,
243 2 => Self::Memory32,
244 3 => Self::Memory64,
245 _ => panic!("Tried to convert invalid range type {}", value),
246 }
247 }
Andrew Walbran19690632022-12-07 16:41:30 +0000248}
Andrew Walbrand1d03182022-12-09 18:20:01 +0000249
250/// Allocates BARs for all VirtIO PCI devices.
251pub fn allocate_all_virtio_bars(
252 pci_root: &mut PciRoot,
253 allocator: &mut PciMemory32Allocator,
254) -> Result<(), PciError> {
255 for (device_function, info) in pci_root.enumerate_bus(0) {
256 let (status, command) = pci_root.get_status_command(device_function);
257 debug!(
258 "Found PCI device {} at {}, status {:?} command {:?}",
259 info, device_function, status, command
260 );
261 if let Some(virtio_type) = virtio_device_type(&info) {
262 debug!(" VirtIO {:?}", virtio_type);
263 allocate_bars(pci_root, device_function, allocator)?;
264 }
265 }
266
267 Ok(())
268}
269
270/// Allocates 32-bit memory addresses for PCI BARs.
271#[derive(Debug)]
272pub struct PciMemory32Allocator {
273 /// The start of the available (not yet allocated) address space for PCI BARs.
274 start: u32,
275 /// The end of the available address space.
276 end: u32,
277}
278
279impl PciMemory32Allocator {
280 pub fn new(pci_info: &PciInfo) -> Self {
281 Self { start: pci_info.bar_range.start, end: pci_info.bar_range.end }
282 }
283
284 /// Allocates a 32-bit memory address region for a PCI BAR of the given power-of-2 size.
285 ///
286 /// It will have alignment matching the size. The size must be a power of 2.
287 pub fn allocate_memory_32(&mut self, size: u32) -> Option<u32> {
288 assert!(size.is_power_of_two());
289 let allocated_address = align_up(self.start, size);
290 if allocated_address + size <= self.end {
291 self.start = allocated_address + size;
292 Some(allocated_address)
293 } else {
294 None
295 }
296 }
297}
298
299/// Allocates appropriately-sized memory regions and assigns them to the device's BARs.
300fn allocate_bars(
301 root: &mut PciRoot,
302 device_function: DeviceFunction,
303 allocator: &mut PciMemory32Allocator,
304) -> Result<(), PciError> {
305 let mut bar_index = 0;
306 while bar_index < 6 {
307 let info = root.bar_info(device_function, bar_index).map_err(PciError::BarInfoFailed)?;
308 debug!("BAR {}: {}", bar_index, info);
309 // Ignore I/O bars, as they aren't required for the VirtIO driver.
310 if let BarInfo::Memory { address_type, size, .. } = info {
311 match address_type {
312 _ if size == 0 => {}
313 MemoryBarType::Width32 => {
314 let address = allocator
315 .allocate_memory_32(size)
316 .ok_or(PciError::BarAllocationFailed { size, device_function })?;
317 debug!("Allocated address {:#010x}", address);
318 root.set_bar_32(device_function, bar_index, address);
319 }
320 _ => {
321 return Err(PciError::UnsupportedBarType(address_type));
322 }
323 }
324 }
325
326 bar_index += 1;
327 if info.takes_two_entries() {
328 bar_index += 1;
329 }
330 }
331
332 // Enable the device to use its BARs.
333 root.set_command(
334 device_function,
335 Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
336 );
337 let (status, command) = root.get_status_command(device_function);
338 debug!("Allocated BARs and enabled device, status {:?} command {:?}", status, command);
339
340 Ok(())
341}
342
343// TODO: Make the alignment functions in the helpers module generic once const_trait_impl is stable.
344const fn align_up(value: u32, alignment: u32) -> u32 {
345 ((value - 1) | (alignment - 1)) + 1
346}