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, 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]>
  • Loading branch information
cjams committed Jan 11, 2022
1 parent 1eb18b6 commit 2c52337
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 0 deletions.
160 changes: 160 additions & 0 deletions common/pci.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* 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>

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

#define PCI_HDR_NORMAL 0x00
#define PCI_HDR_PCI_BRIDGE 0x01
#define PCI_HDR_MASK 0x7F
#define PCI_HDR_MULTIFUNC 0x80

#define PCI_STATUS_CAP_LIST (1U << 4)

static list_head_t pci_list = LIST_INIT(pci_list);

static inline bool pci_dev_hdr_type(pcidev_t *dev) {
return (dev->hdr_type & PCI_HDR_MASK);
}

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

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, 0);
if ((cfg_val & 0xFFFF) == 0xFFFF) {
if (0 == func)
break;
continue;
}

new_dev = kzalloc(sizeof(*new_dev));
BUG_ON(!new_dev);

new_dev->segment = 0;
new_dev->bus = bus;
new_dev->dev = dev;
new_dev->func = func;
new_dev->vendor_id = cfg_val & 0xFFFF;
new_dev->device_id = cfg_val >> 16;

cfg_val = pci_cfg_read(bus, dev, func, 0x1);
new_dev->command = cfg_val & 0xFFFF;
new_dev->status = cfg_val >> 16;

cfg_val = pci_cfg_read(bus, dev, func, 0x2);
new_dev->subclass = (cfg_val & 0xFF0000) >> 16;
new_dev->class = cfg_val >> 24;

cfg_val = pci_cfg_read(bus, dev, func, 0x3);
new_dev->hdr_type = (cfg_val & 0xFF0000) >> 16;

if (new_dev->status & PCI_STATUS_CAP_LIST) {
new_dev->cap_ptr = pci_cfg_read8(bus, dev, func, 0xD);
}

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);

printk("pci: found device @ %s\n", &new_dev->bdf_str);

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

cfg_val = pci_cfg_read(bus, dev, func, 0x6);
secondary = (cfg_val & 0xFF00) >> 8;
subordinate = (cfg_val & 0xFF0000) >> 16;

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;

if ((pci_cfg_read(0, 0, 0, 0) & 0xFFFF) == 0xFFFF) {
printk("pci: non-existent host bridge @ 00.0.0\n");
return;
}

if ((pci_cfg_read(0, 0, 0, 2) & 0xFFFF0000) != 0x06000000) {
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_cfg_read(0, 0, 0, 0) & 0xFFFF;
host_bridge->device_id = pci_cfg_read(0, 0, 0, 0) >> 16;

cfg_val = pci_cfg_read(0, 0, 0, 1);
host_bridge->command = cfg_val & 0xFFFF;
host_bridge->status = cfg_val >> 16;

host_bridge->class = 0x06;
host_bridge->subclass = 0x00;
host_bridge->bridge = NULL;

snprintf(&host_bridge->bdf_str[0], sizeof(host_bridge->bdf_str), "00:0.0");

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

printk("pci: found host bridge @ %s\n", &host_bridge->bdf_str);

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

void init_pci(void) {
printk("Initializing PCI\n");

probe_pci();
}
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
51 changes: 51 additions & 0 deletions include/pci.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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>

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_type;
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 2c52337

Please sign in to comment.