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