Skip to content

Commit

Permalink
pci: add device enumeration code
Browse files Browse the repository at this point in the history
Add probe_pci, probe_pci_bus, probe_pci_dev 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/probe_pci_dev.

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]>
  • Loading branch information
cjams authored and wipawel committed Jan 18, 2022
1 parent 0dc23a9 commit 18f5903
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 0 deletions.
163 changes: 163 additions & 0 deletions common/pci.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright © 2022 Amazon.com, Inc. or its affiliates.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <list.h>
#include <mm/slab.h>
#include <pci.h>
#include <pci_cfg.h>
#include <string.h>

static list_head_t pci_list = LIST_INIT(pci_list);

static inline uint8_t pci_dev_hdr_type(pcidev_t *dev) { return dev->hdr & PCI_HDR_TYPE; }

static inline bool pci_dev_is_multifunc(pcidev_t *dev) {
return ((dev->hdr & PCI_HDR_MULTIFUNC) != 0);
}

static pcidev_t *probe_pci_dev(uint8_t bus, uint8_t dev, uint8_t func,
uint32_t device_vendor) {
uint32_t cfg_val;
pcidev_t *new_dev = kzalloc(sizeof(*new_dev));

if (NULL == new_dev)
return NULL;

new_dev->segment = 0;
new_dev->bus = bus;
new_dev->dev = dev;
new_dev->func = func;

new_dev->vendor_id = PCI_VENDOR(device_vendor);
new_dev->device_id = PCI_DEVICE(device_vendor);

cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_COMMAND);
new_dev->command = PCI_COMMAND(cfg_val);
new_dev->status = PCI_STATUS(cfg_val);

cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_CLASS);
new_dev->class = PCI_CLASS(cfg_val);
new_dev->subclass = PCI_SUBCLASS(cfg_val);

cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_HDR);
new_dev->hdr = PCI_HDR(cfg_val);

if (new_dev->status & PCI_STATUS_CAP_LIST)
new_dev->cap_ptr = pci_cfg_read8(bus, dev, func, PCI_REG_CAP_PTR);

return new_dev;
}

static void probe_pci_bus(uint8_t bus, uint8_t start_dev, pcidev_t *bridge) {
for (uint8_t dev = start_dev; dev < PCI_NR_DEV; dev++) {
for (uint8_t func = 0; func < PCI_NR_FUNC; func++) {
pcidev_t *new_dev;
uint32_t cfg_val;

cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_VENDOR);
if (!PCI_DEV_EXISTS(cfg_val)) {
continue;
}

new_dev = probe_pci_dev(bus, dev, func, cfg_val);
BUG_ON(!new_dev);

new_dev->bridge = bridge;
snprintf(&new_dev->bdf_str[0], sizeof(new_dev->bdf_str), "%02x:%1x.%1x", bus,
dev, func);

list_add_tail(&new_dev->list, &pci_list);

if (pci_dev_hdr_type(new_dev) == PCI_HDR_TYPE_PCI_BRIDGE) {
uint8_t secondary, subordinate;

cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_BUS);
secondary = PCI_SEC_BUS(cfg_val);
subordinate = PCI_SUB_BUS(cfg_val);

for (uint8_t b = secondary; b <= subordinate; b++) {
probe_pci_bus(b, 0, new_dev);
}
}

if (!pci_dev_is_multifunc(new_dev))
break;
}
}
}

static void probe_pci(void) {
pcidev_t *host_bridge;
uint32_t cfg_val;
uint32_t class_reg;
const uint8_t bus = 0;
const uint8_t dev = 0;
const uint8_t func = 0;
const uint32_t vendor_reg = pci_cfg_read(bus, dev, func, PCI_REG_VENDOR);

if (!PCI_DEV_EXISTS(vendor_reg)) {
printk("pci: non-existent host bridge @ 00.0.0\n");
return;
}

class_reg = pci_cfg_read(bus, dev, func, PCI_REG_CLASS);
if (PCI_CLASS(class_reg) != PCI_CLASS_BRIDGE &&
PCI_SUBCLASS(class_reg) != PCI_SUBCLASS_HOST_BRIDGE) {
printk("pci: expected host bridge class code @ 00.0.0\n");
return;
}

/* Found the host bridge, initialize it */
host_bridge = kzalloc(sizeof(*host_bridge));
BUG_ON(!host_bridge);

host_bridge->segment = 0;
host_bridge->vendor_id = PCI_VENDOR(vendor_reg);
host_bridge->device_id = PCI_DEVICE(vendor_reg);

cfg_val = pci_cfg_read(bus, dev, func, PCI_REG_COMMAND);
host_bridge->command = PCI_COMMAND(cfg_val);
host_bridge->status = PCI_STATUS(cfg_val);

host_bridge->class = PCI_CLASS_BRIDGE;
host_bridge->subclass = PCI_SUBCLASS_HOST_BRIDGE;
host_bridge->bridge = NULL;

strncpy(&host_bridge->bdf_str[0], "00:0.0", sizeof(host_bridge->bdf_str));
list_add_tail(&host_bridge->list, &pci_list);

/* Probe the rest of bus 0 */
probe_pci_bus(0, 1, host_bridge);
}

void init_pci(void) {
pcidev_t *dev;

printk("Initializing PCI\n");
probe_pci();

list_for_each_entry (dev, &pci_list, list) {
printk("pci: found device at %s\n", &dev->bdf_str[0]);
}
}
3 changes: 3 additions & 0 deletions common/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <multiboot.h>
#include <page.h>
#include <pagetable.h>
#include <pci.h>
#include <percpu.h>
#include <real_mode.h>
#include <sched.h>
Expand Down Expand Up @@ -290,6 +291,8 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic,

init_ioapic();

init_pci();

/* Initialize console input */
uart_input_init(get_bsp_cpu_id());

Expand Down
106 changes: 106 additions & 0 deletions include/pci.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright © 2022 Amazon.com, Inc. or its affiliates.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef KTF_PCI_H
#define KTF_PCI_H

#include <list.h>

#define PCI_NR_BUS 256
#define PCI_NR_DEV 32
#define PCI_NR_FUNC 8

#define PCI_VENDOR_MASK 0x0000FFFF
#define PCI_VENDOR_INVALID 0xFFFF

#define PCI_DEVICE_MASK 0xFFFF0000
#define PCI_DEVICE_SHIFT 16

#define PCI_COMMAND_MASK 0x0000FFFF

#define PCI_STATUS_MASK 0xFFFF0000
#define PCI_STATUS_SHIFT 16
#define PCI_STATUS_CAP_LIST (1U << 4)

#define PCI_CLASS_MASK 0xFF000000
#define PCI_CLASS_SHIFT 24
#define PCI_CLASS_BRIDGE 0x06

#define PCI_SUBCLASS_MASK 0x00FF0000
#define PCI_SUBCLASS_SHIFT 16
#define PCI_SUBCLASS_HOST_BRIDGE 0x0

#define PCI_HDR_MASK 0x00FF0000
#define PCI_HDR_SHIFT 16
#define PCI_HDR_TYPE 0x7F
#define PCI_HDR_TYPE_NORMAL 0x00
#define PCI_HDR_TYPE_PCI_BRIDGE 0x01
#define PCI_HDR_MULTIFUNC 0x80

#define PCI_SEC_BUS_MASK 0x0000FF00
#define PCI_SEC_BUS_SHIFT 8
#define PCI_SUB_BUS_MASK 0x00FF0000
#define PCI_SUB_BUS_SHIFT 16

#define PCI_REG_VENDOR 0x0
#define PCI_REG_COMMAND 0x1
#define PCI_REG_CLASS 0x2
#define PCI_REG_HDR 0x3
#define PCI_REG_BUS 0x6
#define PCI_REG_CAP_PTR 0xD

#define PCI_VENDOR(cfg_reg0) (cfg_reg0 & PCI_VENDOR_MASK)
#define PCI_DEVICE(cfg_reg0) ((cfg_reg0 & PCI_DEVICE_MASK) >> PCI_DEVICE_SHIFT)
#define PCI_COMMAND(cfg_reg1) (cfg_reg1 & PCI_COMMAND_MASK)
#define PCI_STATUS(cfg_reg1) ((cfg_reg1 & PCI_STATUS_MASK) >> PCI_STATUS_SHIFT)
#define PCI_CLASS(cfg_reg2) ((cfg_reg2 & PCI_CLASS_MASK) >> PCI_CLASS_SHIFT)
#define PCI_SUBCLASS(cfg_reg2) ((cfg_reg2 & PCI_SUBCLASS_MASK) >> PCI_SUBCLASS_SHIFT)
#define PCI_HDR(cfg_reg3) ((cfg_reg3 & PCI_HDR_MASK) >> PCI_HDR_SHIFT)
#define PCI_SEC_BUS(cfg_reg6) ((cfg_reg6 & PCI_SEC_BUS_MASK) >> PCI_SEC_BUS_SHIFT)
#define PCI_SUB_BUS(cfg_reg6) ((cfg_reg6 & PCI_SUB_BUS_MASK) >> PCI_SUB_BUS_SHIFT)

#define PCI_DEV_EXISTS(cfg_reg0) (PCI_VENDOR(cfg_reg0) != PCI_VENDOR_INVALID)

struct pcidev {
list_head_t list;
uint32_t segment : 16;
uint32_t bus : 8;
uint32_t dev : 5;
uint32_t func : 3;
uint16_t vendor_id;
uint16_t device_id;
uint16_t command;
uint16_t status;
uint8_t subclass;
uint8_t class;
uint8_t hdr;
uint8_t cap_ptr;
struct pcidev *bridge;
char bdf_str[7];
};
typedef struct pcidev pcidev_t;

extern void init_pci(void);

#endif /* KTF_PCI_H */

0 comments on commit 18f5903

Please sign in to comment.