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