pci_types/
lib.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
#![no_std]

pub mod capability;
pub mod device_type;
mod register;

pub use register::{CommandRegister, DevselTiming, StatusRegister};

use crate::capability::CapabilityIterator;
use bit_field::BitField;
use core::fmt;

/// The address of a PCIe function.
///
/// PCIe supports 65536 segments, each with 256 buses, each with 32 slots, each with 8 possible functions. We pack this into a `u32`:
///
/// ```ignore
/// 32                              16               8         3      0
///  +-------------------------------+---------------+---------+------+
///  |            segment            |      bus      | device  | func |
///  +-------------------------------+---------------+---------+------+
/// ```
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct PciAddress(u32);

impl PciAddress {
    pub fn new(segment: u16, bus: u8, device: u8, function: u8) -> PciAddress {
        let mut result = 0;
        result.set_bits(0..3, function as u32);
        result.set_bits(3..8, device as u32);
        result.set_bits(8..16, bus as u32);
        result.set_bits(16..32, segment as u32);
        PciAddress(result)
    }

    pub fn segment(&self) -> u16 {
        self.0.get_bits(16..32) as u16
    }

    pub fn bus(&self) -> u8 {
        self.0.get_bits(8..16) as u8
    }

    pub fn device(&self) -> u8 {
        self.0.get_bits(3..8) as u8
    }

    pub fn function(&self) -> u8 {
        self.0.get_bits(0..3) as u8
    }
}

impl fmt::Display for PciAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:02x}-{:02x}:{:02x}.{}", self.segment(), self.bus(), self.device(), self.function())
    }
}

impl fmt::Debug for PciAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self)
    }
}

pub type VendorId = u16;
pub type DeviceId = u16;
pub type DeviceRevision = u8;
pub type BaseClass = u8;
pub type SubClass = u8;
pub type Interface = u8;
pub type SubsystemId = u16;
pub type SubsystemVendorId = u16;
pub type InterruptLine = u8;
pub type InterruptPin = u8;

// TODO: documentation
pub trait ConfigRegionAccess {
    /// Performs a PCI read at `address` with `offset`.
    ///
    /// # Safety
    ///
    /// `address` and `offset` must be valid for PCI reads.
    unsafe fn read(&self, address: PciAddress, offset: u16) -> u32;

    /// Performs a PCI write at `address` with `offset`.
    ///
    /// # Safety
    ///
    /// `address` and `offset` must be valid for PCI writes.
    unsafe fn write(&self, address: PciAddress, offset: u16, value: u32);
}

impl<T: ConfigRegionAccess + ?Sized> ConfigRegionAccess for &T {
    #[inline]
    unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 {
        (**self).read(address, offset)
    }

    #[inline]
    unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) {
        (**self).write(address, offset, value)
    }
}

#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum HeaderType {
    Endpoint,
    PciPciBridge,
    CardBusBridge,
    Unknown(u8),
}

/// Every PCI configuration region starts with a header made up of two parts:
///    - a predefined region that identify the function (bytes `0x00..0x10`)
///    - a device-dependent region that depends on the Header Type field
///
/// The predefined region is of the form:
/// ```ignore
///     32                            16                              0
///      +-----------------------------+------------------------------+
///      |       Device ID             |       Vendor ID              | 0x00
///      |                             |                              |
///      +-----------------------------+------------------------------+
///      |         Status              |       Command                | 0x04
///      |                             |                              |
///      +-----------------------------+---------------+--------------+
///      |               Class Code                    |   Revision   | 0x08
///      |                                             |      ID      |
///      +--------------+--------------+---------------+--------------+
///      |     BIST     |    Header    |    Latency    |  Cacheline   | 0x0c
///      |              |     type     |     timer     |    size      |
///      +--------------+--------------+---------------+--------------+
/// ```
pub struct PciHeader(PciAddress);

impl PciHeader {
    pub fn new(address: PciAddress) -> PciHeader {
        PciHeader(address)
    }

    pub fn address(&self) -> PciAddress {
        self.0
    }

    pub fn id(&self, access: impl ConfigRegionAccess) -> (VendorId, DeviceId) {
        let id = unsafe { access.read(self.0, 0x00) };
        (id.get_bits(0..16) as VendorId, id.get_bits(16..32) as DeviceId)
    }

    pub fn header_type(&self, access: impl ConfigRegionAccess) -> HeaderType {
        /*
         * Read bits 0..=6 of the Header Type. Bit 7 dictates whether the device has multiple functions and so
         * isn't returned here.
         */
        match unsafe { access.read(self.0, 0x0c) }.get_bits(16..23) {
            0x00 => HeaderType::Endpoint,
            0x01 => HeaderType::PciPciBridge,
            0x02 => HeaderType::CardBusBridge,
            t => HeaderType::Unknown(t as u8),
        }
    }

    pub fn has_multiple_functions(&self, access: impl ConfigRegionAccess) -> bool {
        /*
         * Reads bit 7 of the Header Type, which is 1 if the device has multiple functions.
         */
        unsafe { access.read(self.0, 0x0c) }.get_bit(23)
    }

    pub fn revision_and_class(
        &self,
        access: impl ConfigRegionAccess,
    ) -> (DeviceRevision, BaseClass, SubClass, Interface) {
        let field = unsafe { access.read(self.0, 0x08) };
        (
            field.get_bits(0..8) as DeviceRevision,
            field.get_bits(24..32) as BaseClass,
            field.get_bits(16..24) as SubClass,
            field.get_bits(8..16) as Interface,
        )
    }

    pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
        let data = unsafe { access.read(self.0, 0x4).get_bits(16..32) };
        StatusRegister::new(data as u16)
    }

    pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
        let data = unsafe { access.read(self.0, 0x4).get_bits(0..16) };
        CommandRegister::from_bits_retain(data as u16)
    }

    pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
    where
        F: FnOnce(CommandRegister) -> CommandRegister,
    {
        let mut data = unsafe { access.read(self.0, 0x4) };
        let new_command = f(CommandRegister::from_bits_retain(data.get_bits(0..16) as u16));
        data.set_bits(0..16, new_command.bits() as u32);
        unsafe {
            access.write(self.0, 0x4, data);
        }
    }
}

/// Endpoints have a Type-0 header, so the remainder of the header is of the form:
/// ```ignore
///     32                           16                              0
///     +-----------------------------------------------------------+ 0x00
///     |                                                           |
///     |                Predefined region of header                |
///     |                                                           |
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 0                  | 0x10
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 1                  | 0x14
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 2                  | 0x18
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 3                  | 0x1c
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 4                  | 0x20
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 5                  | 0x24
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  CardBus CIS Pointer                      | 0x28
///     |                                                           |
///     +----------------------------+------------------------------+
///     |       Subsystem ID         |    Subsystem vendor ID       | 0x2c
///     |                            |                              |
///     +----------------------------+------------------------------+
///     |               Expansion ROM Base Address                  | 0x30
///     |                                                           |
///     +--------------------------------------------+--------------+
///     |                 Reserved                   | Capabilities | 0x34
///     |                                            |   Pointer    |
///     +--------------------------------------------+--------------+
///     |                         Reserved                          | 0x38
///     |                                                           |
///     +--------------+--------------+--------------+--------------+
///     |   Max_Lat    |   Min_Gnt    |  Interrupt   |  Interrupt   | 0x3c
///     |              |              |   pin        |   line       |
///     +--------------+--------------+--------------+--------------+
/// ```
pub struct EndpointHeader(PciAddress);

impl EndpointHeader {
    pub fn from_header(header: PciHeader, access: impl ConfigRegionAccess) -> Option<EndpointHeader> {
        match header.header_type(access) {
            HeaderType::Endpoint => Some(EndpointHeader(header.0)),
            _ => None,
        }
    }

    pub fn header(&self) -> PciHeader {
        PciHeader(self.0)
    }

    pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
        self.header().status(access)
    }

    pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
        self.header().command(access)
    }

    pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
    where
        F: FnOnce(CommandRegister) -> CommandRegister,
    {
        self.header().update_command(access, f);
    }

    pub fn capability_pointer(&self, access: impl ConfigRegionAccess) -> u16 {
        let status = self.status(&access);
        if status.has_capability_list() {
            unsafe { access.read(self.0, 0x34).get_bits(0..8) as u16 }
        } else {
            0
        }
    }

    pub fn capabilities<T: ConfigRegionAccess>(&self, access: T) -> CapabilityIterator<T> {
        let pointer = self.capability_pointer(&access);
        CapabilityIterator::new(self.0, pointer, access)
    }

    pub fn subsystem(&self, access: impl ConfigRegionAccess) -> (SubsystemId, SubsystemVendorId) {
        let data = unsafe { access.read(self.0, 0x2c) };
        (data.get_bits(16..32) as u16, data.get_bits(0..16) as u16)
    }

    /// Get the contents of a BAR in a given slot. Empty bars will return `None`.
    ///
    /// ### Note
    /// 64-bit memory BARs use two slots, so if one is decoded in e.g. slot #0, this method should not be called
    /// for slot #1
    pub fn bar(&self, slot: u8, access: impl ConfigRegionAccess) -> Option<Bar> {
        if slot >= 6 {
            return None;
        }

        let offset = 0x10 + (slot as u16) * 4;
        let bar = unsafe { access.read(self.0, offset) };

        /*
         * If bit 0 is `0`, the BAR is in memory. If it's `1`, it's in I/O.
         */
        if !bar.get_bit(0) {
            let prefetchable = bar.get_bit(3);
            let address = bar.get_bits(4..32) << 4;

            match bar.get_bits(1..3) {
                0b00 => {
                    let size = unsafe {
                        access.write(self.0, offset, 0xfffffff0);
                        let mut readback = access.read(self.0, offset);
                        access.write(self.0, offset, address);

                        /*
                         * If the entire readback value is zero, the BAR is not implemented, so we return `None`.
                         */
                        if readback == 0x0 {
                            return None;
                        }

                        readback.set_bits(0..4, 0);
                        1 << readback.trailing_zeros()
                    };
                    Some(Bar::Memory32 { address, size, prefetchable })
                }

                0b10 => {
                    /*
                     * If the BAR is 64 bit-wide and this slot is the last, there is no second slot to read.
                     */
                    if slot >= 5 {
                        return None;
                    }

                    let address_upper = unsafe { access.read(self.0, offset + 4) };

                    let size = unsafe {
                        access.write(self.0, offset, 0xfffffff0);
                        access.write(self.0, offset + 4, 0xffffffff);
                        let mut readback_low = access.read(self.0, offset);
                        let readback_high = access.read(self.0, offset + 4);
                        access.write(self.0, offset, address);
                        access.write(self.0, offset + 4, address_upper);

                        /*
                         * If the readback from the first slot is not 0, the size of the BAR is less than 4GiB.
                         */
                        readback_low.set_bits(0..4, 0);
                        if readback_low != 0 {
                            (1 << readback_low.trailing_zeros()) as u64
                        } else {
                            1u64 << ((readback_high.trailing_zeros() + 32) as u64)
                        }
                    };

                    let address = {
                        let mut address = address as u64;
                        // TODO: do we need to mask off the lower bits on this?
                        address.set_bits(32..64, address_upper as u64);
                        address
                    };

                    Some(Bar::Memory64 { address, size, prefetchable })
                }
                // TODO: should we bother to return an error here?
                _ => panic!("BAR Memory type is reserved!"),
            }
        } else {
            Some(Bar::Io { port: bar.get_bits(2..32) << 2 })
        }
    }

    /// Write to a BAR, setting the address for a device to use.
    ///
    /// # Safety
    ///
    /// The supplied value must be a valid BAR value (refer to the PCIe specification for
    /// requirements) and must be of the correct size (i.e. no larger than `u32::MAX` for 32-bit
    /// BARs). In the case of a 64-bit BAR, the supplied slot should be the first slot of the pair.
    pub unsafe fn write_bar(
        &mut self,
        slot: u8,
        access: impl ConfigRegionAccess,
        value: usize,
    ) -> Result<(), BarWriteError> {
        match self.bar(slot, &access) {
            Some(Bar::Memory64 { .. }) => {
                let offset = 0x10 + (slot as u16) * 4;
                unsafe {
                    access.write(self.0, offset, value.get_bits(0..32) as u32);
                    access.write(self.0, offset + 4, value.get_bits(32..64) as u32);
                }
                Ok(())
            }
            Some(Bar::Memory32 { .. }) | Some(Bar::Io { .. }) => {
                if value > u32::MAX as usize {
                    return Err(BarWriteError::InvalidValue);
                }

                let offset = 0x10 + (slot as u16) * 4;
                unsafe {
                    access.write(self.0, offset, value as u32);
                }
                Ok(())
            }
            None => Err(BarWriteError::NoSuchBar),
        }
    }

    pub fn interrupt(&self, access: impl ConfigRegionAccess) -> (InterruptPin, InterruptLine) {
        // According to the PCI Express Specification 4.0, Min_Gnt/Max_Lat registers
        // must be read-only and hardwired to 00h.
        let data = unsafe { access.read(self.0, 0x3c) };
        (data.get_bits(8..16) as u8, data.get_bits(0..8) as u8)
    }

    pub fn update_interrupt<F>(&mut self, access: impl ConfigRegionAccess, f: F)
    where
        F: FnOnce((InterruptPin, InterruptLine)) -> (InterruptPin, InterruptLine),
    {
        let mut data = unsafe { access.read(self.0, 0x3c) };
        let (new_pin, new_line) = f((data.get_bits(8..16) as u8, data.get_bits(0..8) as u8));
        data.set_bits(8..16, new_pin.into());
        data.set_bits(0..8, new_line.into());
        unsafe {
            access.write(self.0, 0x3c, data);
        }
    }
}

/// PCI-PCI Bridges have a Type-1 header, so the remainder of the header is of the form:
/// ```ignore
///     32                           16                              0
///     +-----------------------------------------------------------+ 0x00
///     |                                                           |
///     |                Predefined region of header                |
///     |                                                           |
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 0                  | 0x10
///     |                                                           |
///     +-----------------------------------------------------------+
///     |                  Base Address Register 1                  | 0x14
///     |                                                           |
///     +--------------+--------------+--------------+--------------+
///     | Secondary    | Subordinate  |  Secondary   | Primary Bus  | 0x18
///     |Latency Timer | Bus Number   |  Bus Number  |   Number     |
///     +--------------+--------------+--------------+--------------+
///     |      Secondary Status       |  I/O Limit   |   I/O Base   | 0x1C
///     |                             |              |              |
///     +-----------------------------+--------------+--------------+
///     |        Memory Limit         |         Memory Base         | 0x20
///     |                             |                             |
///     +-----------------------------+-----------------------------+
///     |  Prefetchable Memory Limit  |  Prefetchable Memory Base   | 0x24
///     |                             |                             |
///     +-----------------------------+-----------------------------+
///     |             Prefetchable Base Upper 32 Bits               | 0x28
///     |                                                           |
///     +-----------------------------------------------------------+
///     |             Prefetchable Limit Upper 32 Bits              | 0x2C
///     |                                                           |
///     +-----------------------------+-----------------------------+
///     |   I/O Limit Upper 16 Bits   |   I/O Base Upper 16 Bits    | 0x30
///     |                             |                             |
///     +-----------------------------+--------------+--------------+
///     |              Reserved                      |  Capability  | 0x34
///     |                                            |   Pointer    |
///     +--------------------------------------------+--------------+
///     |                  Expansion ROM base address               | 0x38
///     |                                                           |
///     +-----------------------------+--------------+--------------+
///     |    Bridge Control           |  Interrupt   | Interrupt    | 0x3C
///     |                             |     PIN      |   Line       |
///     +-----------------------------+--------------+--------------+
/// ```
pub struct PciPciBridgeHeader(PciAddress);

impl PciPciBridgeHeader {
    pub fn from_header(header: PciHeader, access: impl ConfigRegionAccess) -> Option<PciPciBridgeHeader> {
        match header.header_type(access) {
            HeaderType::PciPciBridge => Some(PciPciBridgeHeader(header.0)),
            _ => None,
        }
    }

    pub fn header(&self) -> PciHeader {
        PciHeader(self.0)
    }

    pub fn status(&self, access: impl ConfigRegionAccess) -> StatusRegister {
        self.header().status(access)
    }

    pub fn command(&self, access: impl ConfigRegionAccess) -> CommandRegister {
        self.header().command(access)
    }

    pub fn update_command<F>(&mut self, access: impl ConfigRegionAccess, f: F)
    where
        F: FnOnce(CommandRegister) -> CommandRegister,
    {
        self.header().update_command(access, f);
    }

    pub fn primary_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
        let data = unsafe { access.read(self.0, 0x18).get_bits(0..8) };
        data as u8
    }

    pub fn secondary_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
        let data = unsafe { access.read(self.0, 0x18).get_bits(8..16) };
        data as u8
    }

    pub fn subordinate_bus_number(&self, access: impl ConfigRegionAccess) -> u8 {
        let data = unsafe { access.read(self.0, 0x18).get_bits(16..24) };
        data as u8
    }
}

pub const MAX_BARS: usize = 6;

#[derive(Clone, Copy, Debug)]
pub enum Bar {
    Memory32 { address: u32, size: u32, prefetchable: bool },
    Memory64 { address: u64, size: u64, prefetchable: bool },
    Io { port: u32 },
}

impl Bar {
    /// Return the IO port of this BAR or panic if not an IO BAR.
    pub fn unwrap_io(self) -> u32 {
        match self {
            Bar::Io { port } => port,
            Bar::Memory32 { .. } | Bar::Memory64 { .. } => panic!("expected IO BAR, found memory BAR"),
        }
    }

    /// Return the address and size of this BAR or panic if not a memory BAR.
    pub fn unwrap_mem(self) -> (usize, usize) {
        match self {
            Bar::Memory32 { address, size, prefetchable: _ } => (address as usize, size as usize),
            Bar::Memory64 { address, size, prefetchable: _ } => (
                address.try_into().expect("conversion from 64bit BAR to usize failed"),
                size.try_into().expect("conversion from 64bit BAR to usize failed"),
            ),
            Bar::Io { .. } => panic!("expected memory BAR, found IO BAR"),
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BarWriteError {
    NoSuchBar,
    InvalidValue,
}