pci_types/lib.rs
1#![no_std]
2
3pub mod capability;
4pub mod device_type;
5mod register;
6
7pub use register::{CommandRegister, DevselTiming, StatusRegister};
8
9use crate::capability::CapabilityIterator;
10use bit_field::BitField;
11use core::fmt;
12
13/// The address of a PCIe function.
14///
15/// PCIe supports 65536 segments, each with 256 buses, each with 32 slots, each with 8 possible functions. We pack this into a `u32`:
16///
17/// ```ignore
18/// 32 16 8 3 0
19/// +-------------------------------+---------------+---------+------+
20/// | segment | bus | device | func |
21/// +-------------------------------+---------------+---------+------+
22/// ```
23#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
24pub struct PciAddress(u32);
25
26impl PciAddress {
27 pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> PciAddress {
28 let mut result = 0;
29 result.set_bits(0..3, function as u32);
30 result.set_bits(3..8, device as u32);
31 result.set_bits(8..16, bus as u32);
32 result.set_bits(16..32, segment as u32);
33 PciAddress(result)
34 }
35
36 pub fn segment(&self) -> u16 {
37 self.0.get_bits(16..32) as u16
38 }
39
40 pub fn bus(&self) -> u8 {
41 self.0.get_bits(8..16) as u8
42 }
43
44 pub fn device(&self) -> u8 {
45 self.0.get_bits(3..8) as u8
46 }
47
48 pub fn function(&self) -> u8 {
49 self.0.get_bits(0..3) as u8
50 }
51}
52
53impl fmt::Display for PciAddress {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(f, "{:02x}-{:02x}:{:02x}.{}", self.segment(), self.bus(), self.device(), self.function())
56 }
57}
58
59impl fmt::Debug for PciAddress {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "{}", self)
62 }
63}
64
65pub type VendorId = u16;
66pub type DeviceId = u16;
67pub type DeviceRevision = u8;
68pub type BaseClass = u8;
69pub type SubClass = u8;
70pub type Interface = u8;
71pub type SubsystemId = u16;
72pub type SubsystemVendorId = u16;
73pub type InterruptLine = u8;
74pub type InterruptPin = u8;
75
76// TODO: documentation
77pub trait ConfigRegionAccess {
78 /// Performs a PCI read at `address` with `offset`.
79 ///
80 /// # Safety
81 ///
82 /// `address` and `offset` must be valid for PCI reads.
83 unsafe fn read(&self, address: PciAddress, offset: u16) -> u32;
84
85 /// Performs a PCI write at `address` with `offset`.
86 ///
87 /// # Safety
88 ///
89 /// `address` and `offset` must be valid for PCI writes.
90 unsafe fn write(&self, address: PciAddress, offset: u16, value: u32);
91}
92
93impl<T: ConfigRegionAccess + ?Sized> ConfigRegionAccess for &T {
94 #[inline]
95 unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
96 (**self).read(address, offset)
97 }
98
99 #[inline]
100 unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
101 (**self).write(address, offset, value)
102 }
103}
104
105#[non_exhaustive]
106#[derive(Clone, Copy, PartialEq, Eq, Debug)]
107pub enum HeaderType {
108 Endpoint,
109 PciPciBridge,
110 CardBusBridge,
111 Unknown(u8),
112}
113
114/// Every PCI configuration region starts with a header made up of two parts:
115/// - a predefined region that identify the function (bytes `0x00..0x10`)
116/// - a device-dependent region that depends on the Header Type field
117///
118/// The predefined region is of the form:
119/// ```ignore
120/// 32 16 0
121/// +-----------------------------+------------------------------+
122/// | Device ID | Vendor ID | 0x00
123/// | | |
124/// +-----------------------------+------------------------------+
125/// | Status | Command | 0x04
126/// | | |
127/// +-----------------------------+---------------+--------------+
128/// | Class Code | Revision | 0x08
129/// | | ID |
130/// +--------------+--------------+---------------+--------------+
131/// | BIST | Header | Latency | Cacheline | 0x0c
132/// | | type | timer | size |
133/// +--------------+--------------+---------------+--------------+
134/// ```
135pub struct PciHeader(PciAddress);
136
137impl PciHeader {
138 pub fn new(address: PciAddress) -> PciHeader {
139 PciHeader(address)
140 }
141
142 pub fn address(&self) -> PciAddress {
143 self.0
144 }
145
146 pub fn id(&self, access: impl ConfigRegionAccess) -> (VendorId, DeviceId) {
147 let id = unsafe { access.read(self.0, 0x00) };
148 (id.get_bits(0..16) as VendorId, id.get_bits(16..32) as DeviceId)
149 }
150
151 pub fn header_type(&self, access: impl ConfigRegionAccess) -> HeaderType {
152 /*
153 * Read bits 0..=6 of the Header Type. Bit 7 dictates whether the device has multiple functions and so
154 * isn't returned here.
155 */
156 match unsafe { access.read(self.0, 0x0c) }.get_bits(16..23) {
157 0x00 => HeaderType::Endpoint,
158 0x01 => HeaderType::PciPciBridge,
159 0x02 => HeaderType::CardBusBridge,
160 t => HeaderType::Unknown(t as u8),
161 }
162 }
163
164 pub fn has_multiple_functions(&self, access: impl ConfigRegionAccess) -> bool {
165 /*
166 * Reads bit 7 of the Header Type, which is 1 if the device has multiple functions.
167 */
168 unsafe { access.read(self.0, 0x0c) }.get_bit(23)
169 }
170
171 pub fn revision_and_class(
172 &self,
173 access: impl ConfigRegionAccess,
174 ) -> (DeviceRevision, BaseClass, SubClass, Interface) {
175 let field = unsafe { access.read(self.0, 0x08) };
176 (
177 field.get_bits(0..8) as DeviceRevision,
178 field.get_bits(24..32) as BaseClass,
179 field.get_bits(16..24) as SubClass,
180 field.get_bits(8..16) as Interface,
181 )
182 }
183
184 pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
185 let data = unsafe { access.read(self.0, 0x4).get_bits(16..32) };
186 StatusRegister::new(data as u16)
187 }
188
189 pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
190 let data = unsafe { access.read(self.0, 0x4).get_bits(0..16) };
191 CommandRegister::from_bits_retain(data as u16)
192 }
193
194 pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
195 where
196 F: FnOnce(CommandRegister) -> CommandRegister,
197 {
198 let mut data = unsafe { access.read(self.0, 0x4) };
199 let new_command = f(CommandRegister::from_bits_retain(data.get_bits(0..16) as u16));
200 data.set_bits(0..16, new_command.bits() as u32);
201 unsafe {
202 access.write(self.0, 0x4, data);
203 }
204 }
205}
206
207/// Endpoints have a Type-0 header, so the remainder of the header is of the form:
208/// ```ignore
209/// 32 16 0
210/// +-----------------------------------------------------------+ 0x00
211/// | |
212/// | Predefined region of header |
213/// | |
214/// | |
215/// +-----------------------------------------------------------+
216/// | Base Address Register 0 | 0x10
217/// | |
218/// +-----------------------------------------------------------+
219/// | Base Address Register 1 | 0x14
220/// | |
221/// +-----------------------------------------------------------+
222/// | Base Address Register 2 | 0x18
223/// | |
224/// +-----------------------------------------------------------+
225/// | Base Address Register 3 | 0x1c
226/// | |
227/// +-----------------------------------------------------------+
228/// | Base Address Register 4 | 0x20
229/// | |
230/// +-----------------------------------------------------------+
231/// | Base Address Register 5 | 0x24
232/// | |
233/// +-----------------------------------------------------------+
234/// | CardBus CIS Pointer | 0x28
235/// | |
236/// +----------------------------+------------------------------+
237/// | Subsystem ID | Subsystem vendor ID | 0x2c
238/// | | |
239/// +----------------------------+------------------------------+
240/// | Expansion ROM Base Address | 0x30
241/// | |
242/// +--------------------------------------------+--------------+
243/// | Reserved | Capabilities | 0x34
244/// | | Pointer |
245/// +--------------------------------------------+--------------+
246/// | Reserved | 0x38
247/// | |
248/// +--------------+--------------+--------------+--------------+
249/// | Max_Lat | Min_Gnt | Interrupt | Interrupt | 0x3c
250/// | | | pin | line |
251/// +--------------+--------------+--------------+--------------+
252/// ```
253pub struct EndpointHeader(PciAddress);
254
255impl EndpointHeader {
256 pub fn from_header(header: PciHeader, access: impl ConfigRegionAccess) -> Option<EndpointHeader> {
257 match header.header_type(access) {
258 HeaderType::Endpoint => Some(EndpointHeader(header.0)),
259 _ => None,
260 }
261 }
262
263 pub fn header(&self) -> PciHeader {
264 PciHeader(self.0)
265 }
266
267 pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
268 self.header().status(access)
269 }
270
271 pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
272 self.header().command(access)
273 }
274
275 pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
276 where
277 F: FnOnce(CommandRegister) -> CommandRegister,
278 {
279 self.header().update_command(access, f);
280 }
281
282 pub fn capability_pointer(&self, access: impl ConfigRegionAccess) -> u16 {
283 let status = self.status(&access);
284 if status.has_capability_list() {
285 unsafe { access.read(self.0, 0x34).get_bits(0..8) as u16 }
286 } else {
287 0
288 }
289 }
290
291 pub fn capabilities<T: ConfigRegionAccess>(&self, access: T) -> CapabilityIterator<T> {
292 let pointer = self.capability_pointer(&access);
293 CapabilityIterator::new(self.0, pointer, access)
294 }
295
296 pub fn subsystem(&self, access: impl ConfigRegionAccess) -> (SubsystemId, SubsystemVendorId) {
297 let data = unsafe { access.read(self.0, 0x2c) };
298 (data.get_bits(16..32) as u16, data.get_bits(0..16) as u16)
299 }
300
301 /// Get the contents of a BAR in a given slot. Empty bars will return `None`.
302 ///
303 /// ### Note
304 /// 64-bit memory BARs use two slots, so if one is decoded in e.g. slot #0, this method should not be called
305 /// for slot #1
306 pub fn bar(&self, slot: u8, access: impl ConfigRegionAccess) -> Option<Bar> {
307 if slot >= 6 {
308 return None;
309 }
310
311 let offset = 0x10 + (slot as u16) * 4;
312 let bar = unsafe { access.read(self.0, offset) };
313
314 /*
315 * If bit 0 is `0`, the BAR is in memory. If it's `1`, it's in I/O.
316 */
317 if !bar.get_bit(0) {
318 let prefetchable = bar.get_bit(3);
319 let address = bar.get_bits(4..32) << 4;
320
321 match bar.get_bits(1..3) {
322 0b00 => {
323 let size = unsafe {
324 access.write(self.0, offset, 0xfffffff0);
325 let mut readback = access.read(self.0, offset);
326 access.write(self.0, offset, address);
327
328 /*
329 * If the entire readback value is zero, the BAR is not implemented, so we return `None`.
330 */
331 if readback == 0x0 {
332 return None;
333 }
334
335 readback.set_bits(0..4, 0);
336 1 << readback.trailing_zeros()
337 };
338 Some(Bar::Memory32 { address, size, prefetchable })
339 }
340
341 0b10 => {
342 /*
343 * If the BAR is 64 bit-wide and this slot is the last, there is no second slot to read.
344 */
345 if slot >= 5 {
346 return None;
347 }
348
349 let address_upper = unsafe { access.read(self.0, offset + 4) };
350
351 let size = unsafe {
352 access.write(self.0, offset, 0xfffffff0);
353 access.write(self.0, offset + 4, 0xffffffff);
354 let mut readback_low = access.read(self.0, offset);
355 let readback_high = access.read(self.0, offset + 4);
356 access.write(self.0, offset, address);
357 access.write(self.0, offset + 4, address_upper);
358
359 /*
360 * If the readback from the first slot is not 0, the size of the BAR is less than 4GiB.
361 */
362 readback_low.set_bits(0..4, 0);
363 if readback_low != 0 {
364 (1 << readback_low.trailing_zeros()) as u64
365 } else {
366 1u64 << ((readback_high.trailing_zeros() + 32) as u64)
367 }
368 };
369
370 let address = {
371 let mut address = address as u64;
372 // TODO: do we need to mask off the lower bits on this?
373 address.set_bits(32..64, address_upper as u64);
374 address
375 };
376
377 Some(Bar::Memory64 { address, size, prefetchable })
378 }
379 // TODO: should we bother to return an error here?
380 _ => panic!("BAR Memory type is reserved!"),
381 }
382 } else {
383 Some(Bar::Io { port: bar.get_bits(2..32) << 2 })
384 }
385 }
386
387 /// Write to a BAR, setting the address for a device to use.
388 ///
389 /// # Safety
390 ///
391 /// The supplied value must be a valid BAR value (refer to the PCIe specification for
392 /// requirements) and must be of the correct size (i.e. no larger than `u32::MAX` for 32-bit
393 /// BARs). In the case of a 64-bit BAR, the supplied slot should be the first slot of the pair.
394 pub unsafe fn write_bar(
395 &mut self,
396 slot: u8,
397 access: impl ConfigRegionAccess,
398 value: usize,
399 ) -> Result<(), BarWriteError> {
400 match self.bar(slot, &access) {
401 Some(Bar::Memory64 { .. }) => {
402 let offset = 0x10 + (slot as u16) * 4;
403 unsafe {
404 access.write(self.0, offset, value.get_bits(0..32) as u32);
405 access.write(self.0, offset + 4, value.get_bits(32..64) as u32);
406 }
407 Ok(())
408 }
409 Some(Bar::Memory32 { .. }) | Some(Bar::Io { .. }) => {
410 if value > u32::MAX as usize {
411 return Err(BarWriteError::InvalidValue);
412 }
413
414 let offset = 0x10 + (slot as u16) * 4;
415 unsafe {
416 access.write(self.0, offset, value as u32);
417 }
418 Ok(())
419 }
420 None => Err(BarWriteError::NoSuchBar),
421 }
422 }
423
424 pub fn interrupt(&self, access: impl ConfigRegionAccess) -> (InterruptPin, InterruptLine) {
425 // According to the PCI Express Specification 4.0, Min_Gnt/Max_Lat registers
426 // must be read-only and hardwired to 00h.
427 let data = unsafe { access.read(self.0, 0x3c) };
428 (data.get_bits(8..16) as u8, data.get_bits(0..8) as u8)
429 }
430
431 pub fn update_interrupt<F>(&mut self, access: impl ConfigRegionAccess, f: F)
432 where
433 F: FnOnce((InterruptPin, InterruptLine)) -> (InterruptPin, InterruptLine),
434 {
435 let mut data = unsafe { access.read(self.0, 0x3c) };
436 let (new_pin, new_line) = f((data.get_bits(8..16) as u8, data.get_bits(0..8) as u8));
437 data.set_bits(8..16, new_pin.into());
438 data.set_bits(0..8, new_line.into());
439 unsafe {
440 access.write(self.0, 0x3c, data);
441 }
442 }
443}
444
445/// PCI-PCI Bridges have a Type-1 header, so the remainder of the header is of the form:
446/// ```ignore
447/// 32 16 0
448/// +-----------------------------------------------------------+ 0x00
449/// | |
450/// | Predefined region of header |
451/// | |
452/// | |
453/// +-----------------------------------------------------------+
454/// | Base Address Register 0 | 0x10
455/// | |
456/// +-----------------------------------------------------------+
457/// | Base Address Register 1 | 0x14
458/// | |
459/// +--------------+--------------+--------------+--------------+
460/// | Secondary | Subordinate | Secondary | Primary Bus | 0x18
461/// |Latency Timer | Bus Number | Bus Number | Number |
462/// +--------------+--------------+--------------+--------------+
463/// | Secondary Status | I/O Limit | I/O Base | 0x1C
464/// | | | |
465/// +-----------------------------+--------------+--------------+
466/// | Memory Limit | Memory Base | 0x20
467/// | | |
468/// +-----------------------------+-----------------------------+
469/// | Prefetchable Memory Limit | Prefetchable Memory Base | 0x24
470/// | | |
471/// +-----------------------------+-----------------------------+
472/// | Prefetchable Base Upper 32 Bits | 0x28
473/// | |
474/// +-----------------------------------------------------------+
475/// | Prefetchable Limit Upper 32 Bits | 0x2C
476/// | |
477/// +-----------------------------+-----------------------------+
478/// | I/O Limit Upper 16 Bits | I/O Base Upper 16 Bits | 0x30
479/// | | |
480/// +-----------------------------+--------------+--------------+
481/// | Reserved | Capability | 0x34
482/// | | Pointer |
483/// +--------------------------------------------+--------------+
484/// | Expansion ROM base address | 0x38
485/// | |
486/// +-----------------------------+--------------+--------------+
487/// | Bridge Control | Interrupt | Interrupt | 0x3C
488/// | | PIN | Line |
489/// +-----------------------------+--------------+--------------+
490/// ```
491pub struct PciPciBridgeHeader(PciAddress);
492
493impl PciPciBridgeHeader {
494 pub fn from_header(header: PciHeader, access: impl ConfigRegionAccess) -> Option<PciPciBridgeHeader> {
495 match header.header_type(access) {
496 HeaderType::PciPciBridge => Some(PciPciBridgeHeader(header.0)),
497 _ => None,
498 }
499 }
500
501 pub fn header(&self) -> PciHeader {
502 PciHeader(self.0)
503 }
504
505 pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
506 self.header().status(access)
507 }
508
509 pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
510 self.header().command(access)
511 }
512
513 pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
514 where
515 F: FnOnce(CommandRegister) -> CommandRegister,
516 {
517 self.header().update_command(access, f);
518 }
519
520 pub fn primary_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
521 let data = unsafe { access.read(self.0, 0x18).get_bits(0..8) };
522 data as u8
523 }
524
525 pub fn secondary_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
526 let data = unsafe { access.read(self.0, 0x18).get_bits(8..16) };
527 data as u8
528 }
529
530 pub fn subordinate_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
531 let data = unsafe { access.read(self.0, 0x18).get_bits(16..24) };
532 data as u8
533 }
534}
535
536pub const MAX_BARS: usize = 6;
537
538#[derive(Clone, Copy, Debug)]
539pub enum Bar {
540 Memory32 { address: u32, size: u32, prefetchable: bool },
541 Memory64 { address: u64, size: u64, prefetchable: bool },
542 Io { port: u32 },
543}
544
545impl Bar {
546 /// Return the IO port of this BAR or panic if not an IO BAR.
547 pub fn unwrap_io(self) -> u32 {
548 match self {
549 Bar::Io { port } => port,
550 Bar::Memory32 { .. } | Bar::Memory64 { .. } => panic!("expected IO BAR, found memory BAR"),
551 }
552 }
553
554 /// Return the address and size of this BAR or panic if not a memory BAR.
555 pub fn unwrap_mem(self) -> (usize, usize) {
556 match self {
557 Bar::Memory32 { address, size, prefetchable: _ } => (address as usize, size as usize),
558 Bar::Memory64 { address, size, prefetchable: _ } => (
559 address.try_into().expect("conversion from 64bit BAR to usize failed"),
560 size.try_into().expect("conversion from 64bit BAR to usize failed"),
561 ),
562 Bar::Io { .. } => panic!("expected memory BAR, found IO BAR"),
563 }
564 }
565}
566
567#[derive(Clone, Copy, PartialEq, Eq, Debug)]
568pub enum BarWriteError {
569 NoSuchBar,
570 InvalidValue,
571}