Skip to content

Commit de68ca8

Browse files
committed
pci: add device enumeration code
Add probe_pci, probe_pci_bus, and init_pci functions. init_pci is called from kernel_start to setup the PCI subsystem. In turn, init_pci calls probe_pci, which initializes the host bridge and then recursively enumerates the rest of the PCI devices on the system by calling probe_pci_bus. Each device's basic config space is stored in the pcidev_t structure, along with a pointer to the device's parent bridge and a list_head_t node. When a device is found, it is added to the global pci_list structure for later reference. Signed-off-by: Connor Davis <[email protected]>
1 parent 1eb18b6 commit de68ca8

File tree

3 files changed

+274
-0
lines changed

3 files changed

+274
-0
lines changed

common/pci.c

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright © 2022 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
* 2. Redistributions in binary form must reproduce the above copyright notice,
11+
* this list of conditions and the following disclaimer in the documentation
12+
* and/or other materials provided with the distribution.
13+
*
14+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
#include <list.h>
26+
#include <mm/slab.h>
27+
#include <pci.h>
28+
#include <pci_cfg.h>
29+
#include <string.h>
30+
31+
static list_head_t pci_list = LIST_INIT(pci_list);
32+
33+
static inline uint8_t pci_dev_hdr_type(pcidev_t *dev) {
34+
return dev->hdr & PCI_HDR_TYPE;
35+
}
36+
37+
static inline bool pci_dev_is_multifunc(pcidev_t *dev) {
38+
return ((dev->hdr & PCI_HDR_MULTIFUNC) != 0);
39+
}
40+
41+
static pcidev_t *probe_pci_dev(uint8_t bus, uint8_t dev, uint8_t func, uint32_t device_vendor) {
42+
uint32_t cfg_val;
43+
pcidev_t *new_dev = kzalloc(sizeof(*new_dev));
44+
45+
if (NULL == new_dev)
46+
return NULL;
47+
48+
new_dev->segment = 0;
49+
new_dev->bus = bus;
50+
new_dev->dev = dev;
51+
new_dev->func = func;
52+
53+
new_dev->vendor_id = PCI_VENDOR(device_vendor);
54+
new_dev->device_id = PCI_DEVICE(device_vendor);
55+
56+
cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_COMMAND);
57+
new_dev->command = PCI_COMMAND(cfg_val);
58+
new_dev->status = PCI_STATUS(cfg_val);
59+
60+
cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_CLASS);
61+
new_dev->class = PCI_CLASS(cfg_val);
62+
new_dev->subclass = PCI_SUBCLASS(cfg_val);
63+
64+
cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_HDR);
65+
new_dev->hdr = PCI_HDR(cfg_val);
66+
67+
if (new_dev->status & PCI_STATUS_CAP_LIST)
68+
new_dev->cap_ptr = pci_cfg_read8(bus, dev, func, PCI_REG_CAP_PTR);
69+
70+
return new_dev;
71+
}
72+
73+
static void probe_pci_bus(uint8_t bus, uint8_t start_dev, pcidev_t *bridge) {
74+
for (uint8_t dev = start_dev; dev < PCI_NR_DEV; dev++) {
75+
for (uint8_t func = 0; func < PCI_NR_FUNC; func++) {
76+
pcidev_t *new_dev;
77+
uint32_t cfg_val;
78+
79+
cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_VENDOR);
80+
if (!PCI_DEV_EXISTS(cfg_val)) {
81+
continue;
82+
}
83+
84+
new_dev = probe_pci_dev(bus, dev, func, cfg_val);
85+
BUG_ON(!new_dev);
86+
87+
new_dev->bridge = bridge;
88+
snprintf(&new_dev->bdf_str[0], sizeof(new_dev->bdf_str), "%02x:%1x.%1x", bus,
89+
dev, func);
90+
91+
list_add_tail(&new_dev->list, &pci_list);
92+
93+
if (pci_dev_hdr_type(new_dev) == PCI_HDR_TYPE_PCI_BRIDGE) {
94+
uint8_t secondary, subordinate;
95+
96+
cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_BUS);
97+
secondary = PCI_SEC_BUS(cfg_val);
98+
subordinate = PCI_SUB_BUS(cfg_val);
99+
100+
for (uint8_t b = secondary; b <= subordinate; b++) {
101+
probe_pci_bus(b, 0, new_dev);
102+
}
103+
}
104+
105+
if (!pci_dev_is_multifunc(new_dev))
106+
break;
107+
}
108+
}
109+
}
110+
111+
static void probe_pci(void) {
112+
pcidev_t *host_bridge;
113+
uint32_t cfg_val;
114+
uint32_t class_reg;
115+
const uint8_t bus = 0;
116+
const uint8_t dev = 0;
117+
const uint8_t func = 0;
118+
const uint32_t vendor_reg = pci_cfg_read(bus, dev, func, PCI_REG_VENDOR);
119+
120+
if (!PCI_DEV_EXISTS(vendor_reg)) {
121+
printk("pci: non-existent host bridge @ 00.0.0\n");
122+
return;
123+
}
124+
125+
class_reg = pci_cfg_read(bus, dev, func, PCI_REG_CLASS);
126+
if (PCI_CLASS(class_reg) != PCI_CLASS_BRIDGE && PCI_SUBCLASS(class_reg) != PCI_SUBCLASS_HOST_BRIDGE) {
127+
printk("pci: expected host bridge class code @ 00.0.0\n");
128+
return;
129+
}
130+
131+
/* Found the host bridge, initialize it */
132+
host_bridge = kzalloc(sizeof(*host_bridge));
133+
BUG_ON(!host_bridge);
134+
135+
host_bridge->segment = 0;
136+
host_bridge->vendor_id = PCI_VENDOR(vendor_reg);
137+
host_bridge->device_id = PCI_DEVICE(vendor_reg);
138+
139+
cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_COMMAND);
140+
host_bridge->command = PCI_COMMAND(cfg_val);
141+
host_bridge->status = PCI_STATUS(cfg_val);
142+
143+
host_bridge->class = PCI_CLASS_BRIDGE;
144+
host_bridge->subclass = PCI_SUBCLASS_HOST_BRIDGE;
145+
host_bridge->bridge = NULL;
146+
147+
strncpy(&host_bridge->bdf_str[0], "00:0.0", sizeof(host_bridge->bdf_str));
148+
list_add_tail(&host_bridge->list, &pci_list);
149+
150+
/* Probe the rest of bus 0 */
151+
probe_pci_bus(0, 1, host_bridge);
152+
153+
return;
154+
}
155+
156+
void init_pci(void) {
157+
pcidev_t *dev;
158+
159+
printk("Initializing PCI\n");
160+
probe_pci();
161+
162+
list_for_each_entry (dev, &pci_list, list) {
163+
printk("pci: found device at %s\n", &dev->bdf_str[0]);
164+
}
165+
}

common/setup.c

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <multiboot.h>
3535
#include <page.h>
3636
#include <pagetable.h>
37+
#include <pci.h>
3738
#include <percpu.h>
3839
#include <real_mode.h>
3940
#include <sched.h>
@@ -290,6 +291,8 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic,
290291

291292
init_ioapic();
292293

294+
init_pci();
295+
293296
/* Initialize console input */
294297
uart_input_init(get_bsp_cpu_id());
295298

include/pci.h

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright © 2022 Amazon.com, Inc. or its affiliates.
3+
* All Rights Reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
* 2. Redistributions in binary form must reproduce the above copyright notice,
11+
* this list of conditions and the following disclaimer in the documentation
12+
* and/or other materials provided with the distribution.
13+
*
14+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
#ifndef KTF_PCI_H
26+
#define KTF_PCI_H
27+
28+
#include <list.h>
29+
30+
#define PCI_NR_BUS 256
31+
#define PCI_NR_DEV 32
32+
#define PCI_NR_FUNC 8
33+
34+
#define PCI_VENDOR_MASK 0x0000FFFF
35+
#define PCI_VENDOR_INVALID 0xFFFF
36+
37+
#define PCI_DEVICE_MASK 0xFFFF0000
38+
#define PCI_DEVICE_SHIFT 16
39+
40+
#define PCI_COMMAND_MASK 0x0000FFFF
41+
42+
#define PCI_STATUS_MASK 0xFFFF0000
43+
#define PCI_STATUS_SHIFT 16
44+
#define PCI_STATUS_CAP_LIST (1U << 4)
45+
46+
#define PCI_CLASS_MASK 0xFF000000
47+
#define PCI_CLASS_SHIFT 24
48+
#define PCI_CLASS_BRIDGE 0x06
49+
50+
#define PCI_SUBCLASS_MASK 0x00FF0000
51+
#define PCI_SUBCLASS_SHIFT 16
52+
#define PCI_SUBCLASS_HOST_BRIDGE 0x0
53+
54+
#define PCI_HDR_MASK 0x00FF0000
55+
#define PCI_HDR_SHIFT 16
56+
#define PCI_HDR_TYPE 0x7F
57+
#define PCI_HDR_TYPE_NORMAL 0x00
58+
#define PCI_HDR_TYPE_PCI_BRIDGE 0x01
59+
#define PCI_HDR_MULTIFUNC 0x80
60+
61+
#define PCI_SEC_BUS_MASK 0x0000FF00
62+
#define PCI_SEC_BUS_SHIFT 8
63+
#define PCI_SUB_BUS_MASK 0x00FF0000
64+
#define PCI_SUB_BUS_SHIFT 16
65+
66+
#define PCI_REG_VENDOR 0x0
67+
#define PCI_REG_COMMAND 0x1
68+
#define PCI_REG_CLASS 0x2
69+
#define PCI_REG_HDR 0x3
70+
#define PCI_REG_BUS 0x6
71+
#define PCI_REG_CAP_PTR 0xD
72+
73+
#define PCI_VENDOR(cfg_reg0) (cfg_reg0 & PCI_VENDOR_MASK)
74+
#define PCI_DEVICE(cfg_reg0) ((cfg_reg0 & PCI_DEVICE_MASK) >> PCI_DEVICE_SHIFT)
75+
#define PCI_COMMAND(cfg_reg1) (cfg_reg1 & PCI_COMMAND_MASK)
76+
#define PCI_STATUS(cfg_reg1) ((cfg_reg1 & PCI_STATUS_MASK) >> PCI_STATUS_SHIFT)
77+
#define PCI_CLASS(cfg_reg2) ((cfg_reg2 & PCI_CLASS_MASK) >> PCI_CLASS_SHIFT)
78+
#define PCI_SUBCLASS(cfg_reg2) ((cfg_reg2 & PCI_SUBCLASS_MASK) >> PCI_SUBCLASS_SHIFT)
79+
#define PCI_HDR(cfg_reg3) ((cfg_reg3 & PCI_HDR_MASK) >> PCI_HDR_SHIFT)
80+
#define PCI_SEC_BUS(cfg_reg6) ((cfg_reg6 & PCI_SEC_BUS_MASK) >> PCI_SEC_BUS_SHIFT)
81+
#define PCI_SUB_BUS(cfg_reg6) ((cfg_reg6 & PCI_SUB_BUS_MASK) >> PCI_SUB_BUS_SHIFT)
82+
83+
#define PCI_DEV_EXISTS(cfg_reg0) (PCI_VENDOR(cfg_reg0) != PCI_VENDOR_INVALID)
84+
85+
struct pcidev {
86+
list_head_t list;
87+
uint32_t segment : 16;
88+
uint32_t bus : 8;
89+
uint32_t dev : 5;
90+
uint32_t func : 3;
91+
uint16_t vendor_id;
92+
uint16_t device_id;
93+
uint16_t command;
94+
uint16_t status;
95+
uint8_t subclass;
96+
uint8_t class;
97+
uint8_t hdr;
98+
uint8_t cap_ptr;
99+
struct pcidev *bridge;
100+
char bdf_str[7];
101+
};
102+
typedef struct pcidev pcidev_t;
103+
104+
extern void init_pci(void);
105+
106+
#endif /* KTF_PCI_H */

0 commit comments

Comments
 (0)