tartan_pci/config.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
use crate::INVALID_VENDOR;
use tartan_bitfield::bitfield;
/// A register type that is always located at a specific offset
pub trait FixedConfigRegister: From<u32> + Into<u32> {
/// Fixed offset for this register type
const REGISTER_NUMBER: u8;
}
bitfield! {
/// The first register of the standard configuration header for all PCI devices.
pub struct HeaderRegister0(u32) {
/// Vendor-defined device ID
[16..32] pub device: u16,
/// ID of the chipset manufacturer for this function, assigned by PCI-SIG
[ 0..16] pub vendor: u16,
}
}
impl HeaderRegister0 {
/// Return false if the function is not present, as indicated by an invalid vendor ID.
pub fn valid(self) -> bool {
self.vendor() != INVALID_VENDOR
}
}
impl FixedConfigRegister for HeaderRegister0 {
const REGISTER_NUMBER: u8 = 0;
}
bitfield! {
/// The second register of the standard configuration header for all PCI devices.
pub struct HeaderRegister1(u32) {
/// Status bits indicated by the function
[16..32] pub status: u16 as StatusRegister,
/// Control bits set by the host
[ 0..16] pub command: u16 as CommandRegister,
}
}
impl FixedConfigRegister for HeaderRegister1 {
const REGISTER_NUMBER: u8 = 1;
}
bitfield! {
/// The third register of the standard configuration header for all PCI devices.
pub struct HeaderRegister2(u32) {
/// Indicates the general purpose of the function, from a list of IDs defined by
/// PCI-SIG.
[24..32] pub class: u8,
/// Indicates the purpose of the function more specifically, from a list of IDs
/// defined by PCI-SIG for the corresponding [`class`]. Some classes do not have
/// any meaningful subclasses defined.
[16..24] pub subclass: u8,
/// Indicates a standard programming interface that can be used by a generic
/// driver. The possible IDs are defined by PCI-SIG for the corresponding
/// [`class`] and [`subclass`]. Some subclasses do not have any meaningful
/// interfaces defined.
[ 8..16] pub interface: u8,
/// Vendor-defined revision of the hardware
[ 0.. 8] pub revision: u8,
}
}
impl FixedConfigRegister for HeaderRegister2 {
const REGISTER_NUMBER: u8 = 2;
}
bitfield! {
/// The fourth register of the standard configuration header for all PCI devices.
pub struct HeaderRegister3(u32) {
/// Control and status flags for a function's self test capability, if present.
[24..32] pub self_test: u8 as SelfTest,
/// If true, then this device responds to at least some function numbers >= 1.
/// The flag is only meaningful on function zero.
[23 ] pub multi_function,
/// Indicates which of the standard PCI configuration header formats that this
/// function supports, and indirectly, whether this function is a PCI or CardBus
/// bridge.
///
/// Currently, the defined values are:
/// * `0x00` - general purpose
/// * `0x01` - PCI bridge
/// * `0x02` - PCI-to-CardBus bridge
[16..23] pub header_type: u8,
/// The value of the bus master's latency timer in PCI clock cycles
[ 8..16] pub latency_timer: u8,
/// Informs the device of the size of the system's cache lines, in 32-bit units.
[ 0.. 8] pub cache_line_size: u8,
}
}
impl FixedConfigRegister for HeaderRegister3 {
const REGISTER_NUMBER: u8 = 3;
}
bitfield! {
/// Twelfth register of the standard configuration header for general-purpose devices
/// (header type `0x00`).
pub struct Type0HeaderRegister11(u32) {
/// ID of the device as defined by the card manufacturer.
[16..32] pub subsystem: u16,
/// ID of the card manufacturer, which may be different than the chipset
/// manufacturer. Uses the same values defined by PCI-SIG for the vendor ID.
[ 0..16] pub subsystem_vendor: u16,
}
}
impl FixedConfigRegister for Type0HeaderRegister11 {
const REGISTER_NUMBER: u8 = 11;
}
bitfield! {
/// Fourtheenth register of the standard configuration header for general-purpose
/// devices.
pub struct Type0HeaderRegister13(u32) {
/// Offset within this function's configuration space that points to the first
/// of a linked list of "capabilities" supported by the device. Only valid if the
/// appropriate bit is set in the [`StatusRegister`].
[0..8] pub capabilities_offset: u8,
}
}
impl FixedConfigRegister for Type0HeaderRegister13 {
const REGISTER_NUMBER: u8 = 13;
}
bitfield! {
/// Sixteenth register of the standard configuration header for general-purpose
/// devices.
pub struct Type0HeaderRegister15(u32) {
/// Indicates the longest period the device can wait to access the PCI bus, in
/// units of 0.25 µs.
[24..32] pub max_latency: u8,
/// Indicates the desired length of bursts in units of 0.25 µs.
[16..24] pub min_grant: u8,
/// Indicates which of the four interrupt pins on the PCI bus that the device uses
/// (0 for `INTA#` through 3 for `INTC#`). Single-function devices always use
/// `INTA#`.
[ 8..16] pub interrupt_pin: u8,
/// Indicates which system interrupt number the function uses, as defined by the
/// system's interrupt controller.
[ 0.. 8] pub interrupt_line: u8,
}
}
impl FixedConfigRegister for Type0HeaderRegister15 {
const REGISTER_NUMBER: u8 = 15;
}
bitfield! {
/// Control bits set by the host
pub struct CommandRegister(u16) {
/// Controls whether the device is prohibited from asserting its interrupt pin.
/// Does not apply to MSI interrupts.
[10] pub interrupt_disabled,
/// Controls whether the device may use fast back-to-back transactions to multiple
/// devices when acting as a bus master.
[ 9] pub fast_back_to_back_enabled,
/// Controls whether the device may signal critical errors in addresses or
/// special cycle operations.
[ 8] pub system_error_enabled,
/// Controls whether the device responds normally to parity errors (`true`) or
/// whether it must simply set the parity error bit in the status register and
/// ignore it (`false`).
[ 6] pub parity_error_response,
/// Controls whether a VGA or graphics card device may "snoop" on VGA palette
/// buffer writes sent to a different device.
[ 5] pub vga_palette_snoop,
/// Controls whether the device may send a "Memory Write and Invalidate" command
/// when acting as a bus master.
[ 4] pub write_and_invalidate_enable,
/// Controls whether the device may respond to "special cycle" messages, used to
/// communicate system state to the device (e.g., shutting down, halted)
[ 3] pub special_cycle,
/// Controls whether the device may act as a master of the PCI bus
[ 2] pub bus_master,
/// Controls whether the device may respond to accesses through memory space
[ 1] pub memory_space,
/// Controls whether the device may respond to accesses through I/O space
[ 0] pub io_space,
}
}
bitfield! {
/// Status bits indicated by the function
pub struct StatusRegister(u16) {
/// Indicates taht the device has detected a parity error, even if it is ignored.
[15 ] pub parity_error_detected,
/// Indicates that the device has detected a critical error in an address or
/// special cycle operation.
[14 ] pub system_error_signaled,
/// Indicates whether the transaction was aborted with a Target-Abort while this
/// device was acting as the bus master.
[13 ] pub master_abort_received,
/// Indicates whether the transaction was aborted with a Target-Abort.
[12 ] pub target_abort_received,
/// Indicates that this function generated an abort on the transaction (as opposed
/// to a master abort).
[11 ] pub target_abort_signaled,
/// Indicates how quickly the device can assert the `DEVSEL#` pin.
///
/// Valid values are:
/// * `0x0` - fast
/// * `0x1` - medium
/// * `0x2` - slow
[ 9..11] pub device_select_timing: u8,
/// Indicates whether a partity error was detected while this device was acting as
/// the bus master.
[ 8 ] pub master_parity_error,
/// Indicates whether the device supports receiving back-to-back transactions that
/// are targeted at multiple devices.
[ 7 ] pub fast_back_to_back_capable,
/// Indicates whether the device can run at 66 MHz (true).
[ 5 ] pub double_clock_capable,
/// Indicates whether the configuration space contains a linked list of
/// capabilities, beginning at the offset listed in [`Type0HeaderRegister13`].
[ 4 ] pub capabilities_list_available,
/// Indicates whether the device wants to signal an interrupt, even if it is
/// prohibited from asserting its interrupt pin.
[ 3 ] pub interrupt_status,
}
}
bitfield! {
/// Control and status flags for a function's self test capability, if present.
pub struct SelfTest(u8) {
/// Indicates whether the function has a self-test capability.
[7 ] pub capable,
/// Setting this bit triggers the device to start its self test. Once the test is
/// finished (successful or not), this is reset by the device.
[6 ] pub start,
/// If a self-test is complete, then any non-zero value in this field indicates
/// that the test failed.
[0..4] pub completion_code: u8,
}
}
/// A pointer to a memory or I/O space that is used to interact with the function.
///
/// The meaning of the addressed space is defined by the specific function's interface.
#[repr(transparent)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct BaseAddressRegister(u32);
impl BaseAddressRegister {
/// Indicates whether this BAR points to memory or I/O space
pub fn address_space(self) -> AddressSpace {
if self.0 & 1 == 0 {
AddressSpace::Memory
} else {
AddressSpace::IO
}
}
/// Indicates whether the host can safely cache the memory addressed by this BAR
pub fn prefetchable(self) -> bool {
if self.address_space() == AddressSpace::Memory {
self.0 & 0b1000 != 0
} else {
false
}
}
/// Size of address bus supported by this PCI function.
///
/// Addresses in I/O space are always 32-bits wide.
pub fn address_width(self) -> AddressWidth {
if self.address_space() == AddressSpace::IO {
AddressWidth::U32
} else {
match (self.0 & 0b0110) >> 1 {
0 => AddressWidth::U32,
2 => AddressWidth::U64,
_ => AddressWidth::Invalid,
}
}
}
/// The address as a 32-bit number.
///
/// If the [`address_width`](Self::address_width) for this BAR is 64 bits, then this
/// will only return the lower half of the address.
pub fn address_u32(self) -> u32 {
if self.address_space() == AddressSpace::IO {
self.0 & 0xffff_fffc
} else {
self.0 & 0xffff_fff0
}
}
/// The address as a 64-bit number, when combined with the value of the following
/// register.
///
/// If the `address_width` of this BAR is 32 bits, then the value of the next register
/// will be ignored. Thus, this is safe to use with any address width.
pub fn address_u64(self, next_register: u32) -> u64 {
let mut address = u64::from(self.address_u32());
if self.address_width() == AddressWidth::U64 {
address &= u64::from(next_register) << 32;
}
address
}
}
/// Differentiates between memory and I/O addresses
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressSpace {
/// This address is in the normal memory address space
Memory,
/// This address is in I/O space (supported on x86/x86-64 only)
IO,
}
/// Size of address bus supported by a PCI function
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressWidth {
/// 32-bit address bus
U32,
/// 64-bit address bus
U64,
/// Unknown address bus width
Invalid,
}
bitfield! {
/// Structure shared by all capability registers
pub struct GenericCapabilityRegister(u32) {
/// Capability ID defined by PCI-SIG
[ 0.. 8] pub id: u8,
/// Byte offset within capability space of next capability register, or 0 if none.
[ 8..16] pub next_offset: u8,
}
}