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
// SPDX-License-Identifier: MPL-2.0
//! The PCI configuration space.
//!
//! Reference: <https://wiki.osdev.org/PCI>
use alloc::sync::Arc;
use core::mem::size_of;
use bitflags::bitflags;
use super::PciDeviceLocation;
use crate::{
arch::device::io_port::{PortRead, PortWrite},
io_mem::IoMem,
Error, Result,
};
/// Offset in PCI device's common configuration space(Not the PCI bridge).
#[repr(u16)]
pub enum PciDeviceCommonCfgOffset {
/// Vendor ID
VendorId = 0x00,
/// Device ID
DeviceId = 0x02,
/// PCI Command
Command = 0x04,
/// PCI Status
Status = 0x06,
/// Revision ID
RevisionId = 0x08,
/// Class code
ClassCode = 0x09,
/// Cache Line Size
CacheLineSize = 0x0C,
/// Latency Timer
LatencyTimer = 0x0D,
/// Header Type: Identifies the layout of the header.
HeaderType = 0x0E,
/// BIST: Represents the status and allows control of a devices BIST(built-in self test).
Bist = 0x0F,
/// Base Address Register #0
Bar0 = 0x10,
/// Base Address Register #1
Bar1 = 0x14,
/// Base Address Register #2
Bar2 = 0x18,
/// Base Address Register #3
Bar3 = 0x1C,
/// Base Address Register #4
Bar4 = 0x20,
/// Base Address Register #5
Bar5 = 0x24,
/// Cardbus CIS Pointer
CardbusCisPtr = 0x28,
/// Subsystem Vendor ID
SubsystemVendorId = 0x2C,
/// Subsystem ID
SubsystemId = 0x2E,
/// Expansion ROM base address
XromBar = 0x30,
/// Capabilities pointer
CapabilitiesPointer = 0x34,
/// Interrupt Line
InterruptLine = 0x3C,
/// INterrupt PIN
InterruptPin = 0x3D,
/// Min Grant
MinGrant = 0x3E,
/// Max latency
MaxLatency = 0x3F,
}
bitflags! {
/// PCI device common config space command register.
pub struct Command: u16 {
/// Sets to 1 if the device can respond to I/O Space accesses.
const IO_SPACE = 1 << 0;
/// Sets to 1 if the device can respond to Memory SPace accesses.
const MEMORY_SPACE = 1 << 1;
/// Sets to 1 if the device can behave as a bus master.
const BUS_MASTER = 1 << 2;
/// Sets to 1 if the device can monitor Special Cycle operations.
const SPECIAL_CYCLES = 1 << 3;
/// Memory Write and Invalidate Enable. Set to 1 if the device can
/// generate the Memory Write and Invalidate command.
const MWI_ENABLE = 1 << 4;
/// Sets to 1 if the device does not respond to palette register writes
/// and will snoop the data.
const VGA_PALETTE_SNOOP = 1 << 5;
/// Sets to 1 if the device will takes its normal action when a parity
/// error is detected.
const PARITY_ERROR_RESPONSE = 1 << 6;
/// Sets to 1 if the SERR# driver is enabled.
const SERR_ENABLE = 1 << 8;
/// Sets to 1 if the device is allowed to generate fast back-to-back
/// transactions
const FAST_BACK_TO_BACK_ENABLE = 1 << 9;
/// Sets to 1 if the assertion of the devices INTx# signal is disabled.
const INTERRUPT_DISABLE = 1 << 10;
}
}
bitflags! {
/// PCI device common config space status register.
pub struct Status: u16 {
/// The status of the device's INTx# signal.
const INTERRUPT_STATUS = 1 << 3;
/// Sets to 1 if the device support capabilities.
const CAPABILITIES_LIST = 1 << 4;
/// Sets to 1 if the device is capable of running at 66 MHz.
const MHZ66_CAPABLE = 1 << 5;
/// Sets to 1 if the device can accpet fast back-to-back transactions
/// that are not from the same agent.
const FAST_BACK_TO_BACK_CAPABLE = 1 << 7;
/// This bit is only set when the following conditions are met:
/// 1. The bus agent asserted PERR# on a read or observed an assertion
/// of PERR# on a write
/// 2. The agent setting the bit acted as the bus master for the
/// operation in which the error occurred
/// 3. Bit 6 of the Command register (Parity Error Response bit) is set
/// to 1.
const MASTER_DATA_PARITY_ERROR = 1 << 8;
/// The read-only bit that represent the slowest time that a device will
/// assert DEVSEL# for any bus command except Configuration Space read
/// and writes.
///
/// If both `DEVSEL_MEDIUM_TIMING` and `DEVSEL_SLOW_TIMING` are not set,
/// then it represents fast timing
const DEVSEL_MEDIUM_TIMING = 1 << 9;
/// Check `DEVSEL_MEDIUM_TIMING`
const DEVSEL_SLOW_TIMING = 1 << 10;
/// Sets to 1 when a target device terminates a transaction with Target-
/// Abort.
const SIGNALED_TARGET_ABORT = 1 << 11;
/// Sets to 1 by a master device when its transaction is terminated with
/// Target-Abort
const RECEIVED_TARGET_ABORT = 1 << 12;
/// Sets to 1 by a master device when its transcation (except for Special
/// Cycle transactions) is terminated with Master-Abort.
const RECEIVED_MASTER_ABORT = 1 << 13;
/// Sets to 1 when the device asserts SERR#
const SIGNALED_SYSTEM_ERROR = 1 << 14;
/// Sets to 1 when the device detects a parity error, even if parity error
/// handling is disabled.
const DETECTED_PARITY_ERROR = 1 << 15;
}
}
/// BAR space in PCI common config space.
#[derive(Debug, Clone)]
pub enum Bar {
/// Memory BAR
Memory(Arc<MemoryBar>),
/// I/O BAR
Io(Arc<IoBar>),
}
impl Bar {
pub(super) fn new(location: PciDeviceLocation, index: u8) -> Result<Self> {
if index >= 6 {
return Err(Error::InvalidArgs);
}
// Get the original value first, then write all 1 to the register to get the length
let raw = location.read32(index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16);
if raw == 0 {
// no BAR
return Err(Error::InvalidArgs);
}
Ok(if raw & 1 == 0 {
Self::Memory(Arc::new(MemoryBar::new(&location, index)?))
} else {
// IO BAR
Self::Io(Arc::new(IoBar::new(&location, index)?))
})
}
}
/// Memory BAR
#[derive(Debug, Clone)]
pub struct MemoryBar {
base: u64,
size: u32,
prefetchable: bool,
address_length: AddrLen,
io_memory: IoMem,
}
impl MemoryBar {
/// Memory BAR bits type
pub fn address_length(&self) -> AddrLen {
self.address_length
}
/// Whether this bar is prefetchable, allowing the CPU to get the data
/// in advance.
pub fn prefetchable(&self) -> bool {
self.prefetchable
}
/// Base address
pub fn base(&self) -> u64 {
self.base
}
/// Size of the memory
pub fn size(&self) -> u32 {
self.size
}
/// Grants I/O memory access
pub fn io_mem(&self) -> &IoMem {
&self.io_memory
}
/// Creates a memory BAR structure.
fn new(location: &PciDeviceLocation, index: u8) -> Result<Self> {
// Get the original value first, then write all 1 to the register to get the length
let offset = index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16;
let raw = location.read32(offset);
location.write32(offset, !0);
let len_encoded = location.read32(offset);
location.write32(offset, raw);
let mut address_length = AddrLen::Bits32;
// base address, it may be bit64 or bit32
let base: u64 = match (raw & 0b110) >> 1 {
// bits32
0 => (raw & !0xF) as u64,
// bits64
2 => {
address_length = AddrLen::Bits64;
((raw & !0xF) as u64) | ((location.read32(offset + 4) as u64) << 32)
}
_ => {
return Err(Error::InvalidArgs);
}
};
// length
let size = !(len_encoded & !0xF).wrapping_add(1);
let prefetchable = raw & 0b1000 != 0;
// The BAR is located in I/O memory region
Ok(MemoryBar {
base,
size,
prefetchable,
address_length,
io_memory: unsafe { IoMem::new((base as usize)..((base + size as u64) as usize)) },
})
}
}
/// Whether this BAR is 64bit address or 32bit address
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AddrLen {
/// 32 bits
Bits32,
/// 64 bits
Bits64,
}
/// I/O port BAR.
#[derive(Debug, Clone, Copy)]
pub struct IoBar {
base: u32,
size: u32,
}
impl IoBar {
/// Base port
pub fn base(&self) -> u32 {
self.base
}
/// Size of the port
pub fn size(&self) -> u32 {
self.size
}
/// Reads from port
pub fn read<T: PortRead>(&self, offset: u32) -> Result<T> {
// Check alignment
if (self.base + offset) % size_of::<T>() as u32 != 0 {
return Err(Error::InvalidArgs);
}
// Check overflow
if self.size < size_of::<T>() as u32 || offset > self.size - size_of::<T>() as u32 {
return Err(Error::InvalidArgs);
}
// SAFETY: The range of ports accessed is within the scope managed by the IoBar and
// an out-of-bounds check is performed.
unsafe { Ok(T::read_from_port((self.base + offset) as u16)) }
}
/// Writes to port
pub fn write<T: PortWrite>(&self, offset: u32, value: T) -> Result<()> {
// Check alignment
if (self.base + offset) % size_of::<T>() as u32 != 0 {
return Err(Error::InvalidArgs);
}
// Check overflow
if size_of::<T>() as u32 > self.size || offset > self.size - size_of::<T>() as u32 {
return Err(Error::InvalidArgs);
}
// SAFETY: The range of ports accessed is within the scope managed by the IoBar and
// an out-of-bounds check is performed.
unsafe { T::write_to_port((self.base + offset) as u16, value) }
Ok(())
}
fn new(location: &PciDeviceLocation, index: u8) -> Result<Self> {
let offset = index as u16 * 4 + PciDeviceCommonCfgOffset::Bar0 as u16;
let raw = location.read32(offset);
location.write32(offset, !0);
let len_encoded = location.read32(offset);
location.write32(offset, raw);
let len = !(len_encoded & !0x3) + 1;
Ok(Self {
base: raw & !0x3,
size: len,
})
}
}