summaryrefslogtreecommitdiffstats
path: root/rsnix/src/pci.rs
blob: 2d201f339217548db99dc40fe475f8f521ada475 (plain) (blame)
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
// ---------------------------------------------------------------------------
// PCI config space — legacy port-based access (0xCF8 / 0xCFC).
//
// Enumeration walks buses 0-255 × devices 0-31 × functions 0-7.  Port I/O
// stays usable after ExitBootServices, so the shell can list devices and
// future drivers (AHCI, network, ...) can find their controllers.
// ---------------------------------------------------------------------------

const CONFIG_ADDRESS: u16 = 0xCF8;
const CONFIG_DATA: u16 = 0xCFC;

#[derive(Debug, Clone, Copy)]
pub struct DeviceInfo {
    pub bus: u8,
    pub device: u8,
    pub func: u8,
    pub vendor: u16,
    pub device_id: u16,
    pub class: u8,
    pub subclass: u8,
    pub prog_if: u8,
    pub header_type: u8,
}

fn outl(port: u16, val: u32) {
    unsafe {
        core::arch::asm!(
            "out dx, eax",
            in("dx") port,
            in("eax") val,
            options(nomem, nostack, preserves_flags)
        );
    }
}

fn inl(port: u16) -> u32 {
    let val: u32;
    unsafe {
        core::arch::asm!(
            "in eax, dx",
            out("eax") val,
            in("dx") port,
            options(nomem, nostack, preserves_flags)
        );
    }
    val
}

/// Read a dword from a device's config space (`reg` must be dword-aligned).
pub fn config_read(bus: u8, device: u8, func: u8, reg: u8) -> u32 {
    let addr = 0x8000_0000
        | ((bus as u32) << 16)
        | ((device as u32) << 11)
        | ((func as u32) << 8)
        | ((reg as u32) & 0xFC);
    outl(CONFIG_ADDRESS, addr);
    inl(CONFIG_DATA)
}

/// Enumerate every device/function present on all buses.
pub fn probe() -> alloc::vec::Vec<DeviceInfo> {
    let mut out = alloc::vec::Vec::new();
    for bus in 0..=255u8 {
        for dev in 0..32u8 {
            for func in 0..8u8 {
                let id = config_read(bus, dev, func, 0);
                let vendor = (id & 0xFFFF) as u16;
                if vendor == 0xFFFF {
                    if func == 0 {
                        break; // nothing on this device number
                    }
                    continue;
                }
                let hdr = config_read(bus, dev, func, 0x0C);
                let header_type = (hdr & 0xFF) as u8;
                let cc = config_read(bus, dev, func, 0x08);
                out.push(DeviceInfo {
                    bus,
                    device: dev,
                    func,
                    vendor,
                    device_id: (id >> 16) as u16,
                    class: (cc >> 24) as u8,
                    subclass: (cc >> 16) as u8,
                    prog_if: (cc >> 8) as u8,
                    header_type,
                });
                // A non-multifunction device has no functions 1-7.
                if func == 0 && header_type & 0x80 == 0 {
                    break;
                }
            }
        }
    }
    out
}

/// Short human name for a (class, subclass) pair.
pub fn class_name(class: u8, subclass: u8) -> &'static str {
    match (class, subclass) {
        (0x00, 0x01) => "VGA-compatible",
        (0x00, _) => "Unclassified",
        (0x01, 0x01) => "IDE controller",
        (0x01, 0x06) => "SATA controller",
        (0x01, 0x08) => "NVM controller",
        (0x01, _) => "Mass storage",
        (0x02, 0x00) => "Ethernet",
        (0x02, _) => "Network",
        (0x03, 0x00) => "VGA",
        (0x03, _) => "Display",
        (0x04, _) => "Multimedia",
        (0x05, _) => "Memory controller",
        (0x06, 0x00) => "Host bridge",
        (0x06, 0x01) => "ISA bridge",
        (0x06, 0x04) => "PCI-PCI bridge",
        (0x06, _) => "Bridge",
        (0x08, 0x80) => "PIC",
        (0x0C, 0x03) => "USB controller",
        (0x0C, 0x05) => "SMBus",
        (0x0C, _) => "Serial bus",
        (0x11, _) => "Signal processing",
        (0xFF, _) => "Unassigned",
        _ => "Other",
    }
}