1 //! Module for dealing with a PCI bus in general, without anything specific to VirtIO. 2 3 use bitflags::bitflags; 4 use core::{ 5 convert::TryFrom, 6 fmt::{self, Display, Formatter}, 7 }; 8 use log::warn; 9 10 const INVALID_READ: u32 = 0xffffffff; 11 12 /// The maximum number of devices on a bus. 13 const MAX_DEVICES: u8 = 32; 14 /// The maximum number of functions on a device. 15 const MAX_FUNCTIONS: u8 = 8; 16 17 /// The offset in bytes to the status and command fields within PCI configuration space. 18 const STATUS_COMMAND_OFFSET: u8 = 0x04; 19 /// The offset in bytes to BAR0 within PCI configuration space. 20 const BAR0_OFFSET: u8 = 0x10; 21 22 /// ID for vendor-specific PCI capabilities. 23 pub const PCI_CAP_ID_VNDR: u8 = 0x09; 24 25 bitflags! { 26 /// The status register in PCI configuration space. 27 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 28 pub struct Status: u16 { 29 // Bits 0-2 are reserved. 30 /// The state of the device's INTx# signal. 31 const INTERRUPT_STATUS = 1 << 3; 32 /// The device has a linked list of capabilities. 33 const CAPABILITIES_LIST = 1 << 4; 34 /// The device is capabile of running at 66 MHz rather than 33 MHz. 35 const MHZ_66_CAPABLE = 1 << 5; 36 // Bit 6 is reserved. 37 /// The device can accept fast back-to-back transactions not from the same agent. 38 const FAST_BACK_TO_BACK_CAPABLE = 1 << 7; 39 /// The bus agent observed a parity error (if parity error handling is enabled). 40 const MASTER_DATA_PARITY_ERROR = 1 << 8; 41 // Bits 9-10 are DEVSEL timing. 42 /// A target device terminated a transaction with target-abort. 43 const SIGNALED_TARGET_ABORT = 1 << 11; 44 /// A master device transaction was terminated with target-abort. 45 const RECEIVED_TARGET_ABORT = 1 << 12; 46 /// A master device transaction was terminated with master-abort. 47 const RECEIVED_MASTER_ABORT = 1 << 13; 48 /// A device asserts SERR#. 49 const SIGNALED_SYSTEM_ERROR = 1 << 14; 50 /// The device detects a parity error, even if parity error handling is disabled. 51 const DETECTED_PARITY_ERROR = 1 << 15; 52 } 53 } 54 55 bitflags! { 56 /// The command register in PCI configuration space. 57 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] 58 pub struct Command: u16 { 59 /// The device can respond to I/O Space accesses. 60 const IO_SPACE = 1 << 0; 61 /// The device can respond to Memory Space accesses. 62 const MEMORY_SPACE = 1 << 1; 63 /// The device can behave as a bus master. 64 const BUS_MASTER = 1 << 2; 65 /// The device can monitor Special Cycle operations. 66 const SPECIAL_CYCLES = 1 << 3; 67 /// The device can generate the Memory Write and Invalidate command. 68 const MEMORY_WRITE_AND_INVALIDATE_ENABLE = 1 << 4; 69 /// The device will snoop palette register data. 70 const VGA_PALETTE_SNOOP = 1 << 5; 71 /// The device should take its normal action when a parity error is detected. 72 const PARITY_ERROR_RESPONSE = 1 << 6; 73 // Bit 7 is reserved. 74 /// The SERR# driver is enabled. 75 const SERR_ENABLE = 1 << 8; 76 /// The device is allowed to generate fast back-to-back transactions. 77 const FAST_BACK_TO_BACK_ENABLE = 1 << 9; 78 /// Assertion of the device's INTx# signal is disabled. 79 const INTERRUPT_DISABLE = 1 << 10; 80 } 81 } 82 83 /// Errors accessing a PCI device. 84 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 85 pub enum PciError { 86 /// The device reported an invalid BAR type. 87 InvalidBarType, 88 } 89 90 impl Display for PciError { fmt(&self, f: &mut Formatter) -> fmt::Result91 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 92 match self { 93 Self::InvalidBarType => write!(f, "Invalid PCI BAR type."), 94 } 95 } 96 } 97 98 /// The root complex of a PCI bus. 99 #[derive(Debug)] 100 pub struct PciRoot { 101 mmio_base: *mut u32, 102 cam: Cam, 103 } 104 105 /// A PCI Configuration Access Mechanism. 106 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 107 pub enum Cam { 108 /// The PCI memory-mapped Configuration Access Mechanism. 109 /// 110 /// This provides access to 256 bytes of configuration space per device function. 111 MmioCam, 112 /// The PCIe memory-mapped Enhanced Configuration Access Mechanism. 113 /// 114 /// This provides access to 4 KiB of configuration space per device function. 115 Ecam, 116 } 117 118 impl Cam { 119 /// Returns the total size in bytes of the memory-mapped region. size(self) -> u32120 pub const fn size(self) -> u32 { 121 match self { 122 Self::MmioCam => 0x1000000, 123 Self::Ecam => 0x10000000, 124 } 125 } 126 } 127 128 impl PciRoot { 129 /// Wraps the PCI root complex with the given MMIO base address. 130 /// 131 /// Panics if the base address is not aligned to a 4-byte boundary. 132 /// 133 /// # Safety 134 /// 135 /// `mmio_base` must be a valid pointer to an appropriately-mapped MMIO region of at least 136 /// 16 MiB (if `cam == Cam::MmioCam`) or 256 MiB (if `cam == Cam::Ecam`). The pointer must be 137 /// valid for the entire lifetime of the program (i.e. `'static`), which implies that no Rust 138 /// references may be used to access any of the memory region at any point. new(mmio_base: *mut u8, cam: Cam) -> Self139 pub unsafe fn new(mmio_base: *mut u8, cam: Cam) -> Self { 140 assert!(mmio_base as usize & 0x3 == 0); 141 Self { 142 mmio_base: mmio_base as *mut u32, 143 cam, 144 } 145 } 146 147 /// Makes a clone of the `PciRoot`, pointing at the same MMIO region. 148 /// 149 /// # Safety 150 /// 151 /// This function allows concurrent mutable access to the PCI CAM. To avoid this causing 152 /// problems, the returned `PciRoot` instance must only be used to read read-only fields. unsafe_clone(&self) -> Self153 unsafe fn unsafe_clone(&self) -> Self { 154 Self { 155 mmio_base: self.mmio_base, 156 cam: self.cam, 157 } 158 } 159 cam_offset(&self, device_function: DeviceFunction, register_offset: u8) -> u32160 fn cam_offset(&self, device_function: DeviceFunction, register_offset: u8) -> u32 { 161 assert!(device_function.valid()); 162 163 let bdf = (device_function.bus as u32) << 8 164 | (device_function.device as u32) << 3 165 | device_function.function as u32; 166 let address = 167 bdf << match self.cam { 168 Cam::MmioCam => 8, 169 Cam::Ecam => 12, 170 } | register_offset as u32; 171 // Ensure that address is within range. 172 assert!(address < self.cam.size()); 173 // Ensure that address is word-aligned. 174 assert!(address & 0x3 == 0); 175 address 176 } 177 178 /// Reads 4 bytes from configuration space using the appropriate CAM. config_read_word( &self, device_function: DeviceFunction, register_offset: u8, ) -> u32179 pub(crate) fn config_read_word( 180 &self, 181 device_function: DeviceFunction, 182 register_offset: u8, 183 ) -> u32 { 184 let address = self.cam_offset(device_function, register_offset); 185 // Safe because both the `mmio_base` and the address offset are properly aligned, and the 186 // resulting pointer is within the MMIO range of the CAM. 187 unsafe { 188 // Right shift to convert from byte offset to word offset. 189 (self.mmio_base.add((address >> 2) as usize)).read_volatile() 190 } 191 } 192 193 /// Writes 4 bytes to configuration space using the appropriate CAM. config_write_word( &mut self, device_function: DeviceFunction, register_offset: u8, data: u32, )194 pub(crate) fn config_write_word( 195 &mut self, 196 device_function: DeviceFunction, 197 register_offset: u8, 198 data: u32, 199 ) { 200 let address = self.cam_offset(device_function, register_offset); 201 // Safe because both the `mmio_base` and the address offset are properly aligned, and the 202 // resulting pointer is within the MMIO range of the CAM. 203 unsafe { 204 // Right shift to convert from byte offset to word offset. 205 let ptr = self.mmio_base.add((address >> 2) as usize); 206 #[cfg(not(target_arch = "aarch64"))] 207 { 208 ptr.write_volatile(data) 209 } 210 #[cfg(target_arch = "aarch64")] 211 { 212 core::arch::asm!( 213 "str {value:w}, [{ptr}]", 214 value = in(reg) data, 215 ptr = in(reg) ptr, 216 ) 217 } 218 } 219 } 220 221 /// Enumerates PCI devices on the given bus. enumerate_bus(&self, bus: u8) -> BusDeviceIterator222 pub fn enumerate_bus(&self, bus: u8) -> BusDeviceIterator { 223 // Safe because the BusDeviceIterator only reads read-only fields. 224 let root = unsafe { self.unsafe_clone() }; 225 BusDeviceIterator { 226 root, 227 next: DeviceFunction { 228 bus, 229 device: 0, 230 function: 0, 231 }, 232 } 233 } 234 235 /// Reads the status and command registers of the given device function. get_status_command(&self, device_function: DeviceFunction) -> (Status, Command)236 pub fn get_status_command(&self, device_function: DeviceFunction) -> (Status, Command) { 237 let status_command = self.config_read_word(device_function, STATUS_COMMAND_OFFSET); 238 let status = Status::from_bits_truncate((status_command >> 16) as u16); 239 let command = Command::from_bits_truncate(status_command as u16); 240 (status, command) 241 } 242 243 /// Sets the command register of the given device function. set_command(&mut self, device_function: DeviceFunction, command: Command)244 pub fn set_command(&mut self, device_function: DeviceFunction, command: Command) { 245 self.config_write_word( 246 device_function, 247 STATUS_COMMAND_OFFSET, 248 command.bits().into(), 249 ); 250 } 251 252 /// Gets an iterator over the capabilities of the given device function. capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator253 pub fn capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator { 254 CapabilityIterator { 255 root: self, 256 device_function, 257 next_capability_offset: self.capabilities_offset(device_function), 258 } 259 } 260 261 /// Gets information about the given BAR of the given device function. bar_info( &mut self, device_function: DeviceFunction, bar_index: u8, ) -> Result<BarInfo, PciError>262 pub fn bar_info( 263 &mut self, 264 device_function: DeviceFunction, 265 bar_index: u8, 266 ) -> Result<BarInfo, PciError> { 267 let bar_orig = self.config_read_word(device_function, BAR0_OFFSET + 4 * bar_index); 268 269 // Get the size of the BAR. 270 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, 0xffffffff); 271 let size_mask = self.config_read_word(device_function, BAR0_OFFSET + 4 * bar_index); 272 // A wrapping add is necessary to correctly handle the case of unused BARs, which read back 273 // as 0, and should be treated as size 0. 274 let size = (!(size_mask & 0xfffffff0)).wrapping_add(1); 275 276 // Restore the original value. 277 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, bar_orig); 278 279 if bar_orig & 0x00000001 == 0x00000001 { 280 // I/O space 281 let address = bar_orig & 0xfffffffc; 282 Ok(BarInfo::IO { address, size }) 283 } else { 284 // Memory space 285 let mut address = u64::from(bar_orig & 0xfffffff0); 286 let prefetchable = bar_orig & 0x00000008 != 0; 287 let address_type = MemoryBarType::try_from(((bar_orig & 0x00000006) >> 1) as u8)?; 288 if address_type == MemoryBarType::Width64 { 289 if bar_index >= 5 { 290 return Err(PciError::InvalidBarType); 291 } 292 let address_top = 293 self.config_read_word(device_function, BAR0_OFFSET + 4 * (bar_index + 1)); 294 address |= u64::from(address_top) << 32; 295 } 296 Ok(BarInfo::Memory { 297 address_type, 298 prefetchable, 299 address, 300 size, 301 }) 302 } 303 } 304 305 /// Sets the address of the given 32-bit memory or I/O BAR of the given device function. set_bar_32(&mut self, device_function: DeviceFunction, bar_index: u8, address: u32)306 pub fn set_bar_32(&mut self, device_function: DeviceFunction, bar_index: u8, address: u32) { 307 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, address); 308 } 309 310 /// Sets the address of the given 64-bit memory BAR of the given device function. set_bar_64(&mut self, device_function: DeviceFunction, bar_index: u8, address: u64)311 pub fn set_bar_64(&mut self, device_function: DeviceFunction, bar_index: u8, address: u64) { 312 self.config_write_word(device_function, BAR0_OFFSET + 4 * bar_index, address as u32); 313 self.config_write_word( 314 device_function, 315 BAR0_OFFSET + 4 * (bar_index + 1), 316 (address >> 32) as u32, 317 ); 318 } 319 320 /// Gets the capabilities 'pointer' for the device function, if any. capabilities_offset(&self, device_function: DeviceFunction) -> Option<u8>321 fn capabilities_offset(&self, device_function: DeviceFunction) -> Option<u8> { 322 let (status, _) = self.get_status_command(device_function); 323 if status.contains(Status::CAPABILITIES_LIST) { 324 Some((self.config_read_word(device_function, 0x34) & 0xFC) as u8) 325 } else { 326 None 327 } 328 } 329 } 330 331 // SAFETY: `mmio_base` is only used for MMIO, which can happen from any thread or CPU core. 332 unsafe impl Send for PciRoot {} 333 334 // SAFETY: `&PciRoot` only allows MMIO reads, which are fine to happen concurrently on different CPU 335 // cores. 336 unsafe impl Sync for PciRoot {} 337 338 /// Information about a PCI Base Address Register. 339 #[derive(Clone, Debug, Eq, PartialEq)] 340 pub enum BarInfo { 341 /// The BAR is for a memory region. 342 Memory { 343 /// The size of the BAR address and where it can be located. 344 address_type: MemoryBarType, 345 /// If true, then reading from the region doesn't have side effects. The CPU may cache reads 346 /// and merge repeated stores. 347 prefetchable: bool, 348 /// The memory address, always 16-byte aligned. 349 address: u64, 350 /// The size of the BAR in bytes. 351 size: u32, 352 }, 353 /// The BAR is for an I/O region. 354 IO { 355 /// The I/O address, always 4-byte aligned. 356 address: u32, 357 /// The size of the BAR in bytes. 358 size: u32, 359 }, 360 } 361 362 impl BarInfo { 363 /// Returns whether this BAR is a 64-bit memory region, and so takes two entries in the table in 364 /// configuration space. takes_two_entries(&self) -> bool365 pub fn takes_two_entries(&self) -> bool { 366 matches!( 367 self, 368 BarInfo::Memory { 369 address_type: MemoryBarType::Width64, 370 .. 371 } 372 ) 373 } 374 375 /// Returns the address and size of this BAR if it is a memory bar, or `None` if it is an IO 376 /// BAR. memory_address_size(&self) -> Option<(u64, u32)>377 pub fn memory_address_size(&self) -> Option<(u64, u32)> { 378 if let Self::Memory { address, size, .. } = self { 379 Some((*address, *size)) 380 } else { 381 None 382 } 383 } 384 } 385 386 impl Display for BarInfo { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result387 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 388 match self { 389 Self::Memory { 390 address_type, 391 prefetchable, 392 address, 393 size, 394 } => write!( 395 f, 396 "Memory space at {:#010x}, size {}, type {:?}, prefetchable {}", 397 address, size, address_type, prefetchable 398 ), 399 Self::IO { address, size } => { 400 write!(f, "I/O space at {:#010x}, size {}", address, size) 401 } 402 } 403 } 404 } 405 406 /// The location allowed for a memory BAR. 407 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 408 pub enum MemoryBarType { 409 /// The BAR has a 32-bit address and can be mapped anywhere in 32-bit address space. 410 Width32, 411 /// The BAR must be mapped below 1MiB. 412 Below1MiB, 413 /// The BAR has a 64-bit address and can be mapped anywhere in 64-bit address space. 414 Width64, 415 } 416 417 impl From<MemoryBarType> for u8 { from(bar_type: MemoryBarType) -> Self418 fn from(bar_type: MemoryBarType) -> Self { 419 match bar_type { 420 MemoryBarType::Width32 => 0, 421 MemoryBarType::Below1MiB => 1, 422 MemoryBarType::Width64 => 2, 423 } 424 } 425 } 426 427 impl TryFrom<u8> for MemoryBarType { 428 type Error = PciError; 429 try_from(value: u8) -> Result<Self, Self::Error>430 fn try_from(value: u8) -> Result<Self, Self::Error> { 431 match value { 432 0 => Ok(Self::Width32), 433 1 => Ok(Self::Below1MiB), 434 2 => Ok(Self::Width64), 435 _ => Err(PciError::InvalidBarType), 436 } 437 } 438 } 439 440 /// Iterator over capabilities for a device. 441 #[derive(Debug)] 442 pub struct CapabilityIterator<'a> { 443 root: &'a PciRoot, 444 device_function: DeviceFunction, 445 next_capability_offset: Option<u8>, 446 } 447 448 impl<'a> Iterator for CapabilityIterator<'a> { 449 type Item = CapabilityInfo; 450 next(&mut self) -> Option<Self::Item>451 fn next(&mut self) -> Option<Self::Item> { 452 let offset = self.next_capability_offset?; 453 454 // Read the first 4 bytes of the capability. 455 let capability_header = self.root.config_read_word(self.device_function, offset); 456 let id = capability_header as u8; 457 let next_offset = (capability_header >> 8) as u8; 458 let private_header = (capability_header >> 16) as u16; 459 460 self.next_capability_offset = if next_offset == 0 { 461 None 462 } else if next_offset < 64 || next_offset & 0x3 != 0 { 463 warn!("Invalid next capability offset {:#04x}", next_offset); 464 None 465 } else { 466 Some(next_offset) 467 }; 468 469 Some(CapabilityInfo { 470 offset, 471 id, 472 private_header, 473 }) 474 } 475 } 476 477 /// Information about a PCI device capability. 478 #[derive(Debug, Copy, Clone, Eq, PartialEq)] 479 pub struct CapabilityInfo { 480 /// The offset of the capability in the PCI configuration space of the device function. 481 pub offset: u8, 482 /// The ID of the capability. 483 pub id: u8, 484 /// The third and fourth bytes of the capability, to save reading them again. 485 pub private_header: u16, 486 } 487 488 /// An iterator which enumerates PCI devices and functions on a given bus. 489 #[derive(Debug)] 490 pub struct BusDeviceIterator { 491 /// This must only be used to read read-only fields, and must not be exposed outside this 492 /// module, because it uses the same CAM as the main `PciRoot` instance. 493 root: PciRoot, 494 next: DeviceFunction, 495 } 496 497 impl Iterator for BusDeviceIterator { 498 type Item = (DeviceFunction, DeviceFunctionInfo); 499 next(&mut self) -> Option<Self::Item>500 fn next(&mut self) -> Option<Self::Item> { 501 while self.next.device < MAX_DEVICES { 502 // Read the header for the current device and function. 503 let current = self.next; 504 let device_vendor = self.root.config_read_word(current, 0); 505 506 // Advance to the next device or function. 507 self.next.function += 1; 508 if self.next.function >= MAX_FUNCTIONS { 509 self.next.function = 0; 510 self.next.device += 1; 511 } 512 513 if device_vendor != INVALID_READ { 514 let class_revision = self.root.config_read_word(current, 8); 515 let device_id = (device_vendor >> 16) as u16; 516 let vendor_id = device_vendor as u16; 517 let class = (class_revision >> 24) as u8; 518 let subclass = (class_revision >> 16) as u8; 519 let prog_if = (class_revision >> 8) as u8; 520 let revision = class_revision as u8; 521 let bist_type_latency_cache = self.root.config_read_word(current, 12); 522 let header_type = HeaderType::from((bist_type_latency_cache >> 16) as u8 & 0x7f); 523 return Some(( 524 current, 525 DeviceFunctionInfo { 526 vendor_id, 527 device_id, 528 class, 529 subclass, 530 prog_if, 531 revision, 532 header_type, 533 }, 534 )); 535 } 536 } 537 None 538 } 539 } 540 541 /// An identifier for a PCI bus, device and function. 542 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 543 pub struct DeviceFunction { 544 /// The PCI bus number, between 0 and 255. 545 pub bus: u8, 546 /// The device number on the bus, between 0 and 31. 547 pub device: u8, 548 /// The function number of the device, between 0 and 7. 549 pub function: u8, 550 } 551 552 impl DeviceFunction { 553 /// Returns whether the device and function numbers are valid, i.e. the device is between 0 and 554 /// 31, and the function is between 0 and 7. valid(&self) -> bool555 pub fn valid(&self) -> bool { 556 self.device < 32 && self.function < 8 557 } 558 } 559 560 impl Display for DeviceFunction { fmt(&self, f: &mut Formatter) -> fmt::Result561 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 562 write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function) 563 } 564 } 565 566 /// Information about a PCI device function. 567 #[derive(Clone, Debug, Eq, PartialEq)] 568 pub struct DeviceFunctionInfo { 569 /// The PCI vendor ID. 570 pub vendor_id: u16, 571 /// The PCI device ID. 572 pub device_id: u16, 573 /// The PCI class. 574 pub class: u8, 575 /// The PCI subclass. 576 pub subclass: u8, 577 /// The PCI programming interface byte. 578 pub prog_if: u8, 579 /// The PCI revision ID. 580 pub revision: u8, 581 /// The type of PCI device. 582 pub header_type: HeaderType, 583 } 584 585 impl Display for DeviceFunctionInfo { fmt(&self, f: &mut Formatter) -> fmt::Result586 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 587 write!( 588 f, 589 "{:04x}:{:04x} (class {:02x}.{:02x}, rev {:02x}) {:?}", 590 self.vendor_id, 591 self.device_id, 592 self.class, 593 self.subclass, 594 self.revision, 595 self.header_type, 596 ) 597 } 598 } 599 600 /// The type of a PCI device function header. 601 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 602 pub enum HeaderType { 603 /// A normal PCI device. 604 Standard, 605 /// A PCI to PCI bridge. 606 PciPciBridge, 607 /// A PCI to CardBus bridge. 608 PciCardbusBridge, 609 /// Unrecognised header type. 610 Unrecognised(u8), 611 } 612 613 impl From<u8> for HeaderType { from(value: u8) -> Self614 fn from(value: u8) -> Self { 615 match value { 616 0x00 => Self::Standard, 617 0x01 => Self::PciPciBridge, 618 0x02 => Self::PciCardbusBridge, 619 _ => Self::Unrecognised(value), 620 } 621 } 622 } 623