aboutsummaryrefslogtreecommitdiff
path: root/kernel/drivers/pcie.c
blob: 6003eaba473731d61d064935ae5192b3cf9b0b2f (plain)
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
#include "drivers/pcie.h"
#include <drivers/pcie.h>
#include <main/acpi.h>
#include <mm/kmalloc.h>
#include <mm/pagetable.h>
#include <util/debug.h>

#define MCFG_SIGNATURE (*(uint32_t *)"MCFG")
static uintptr_t pcie_base_addr;

typedef struct pcie_table
{
    pcie_device_t devices[PCI_NUM_BUSES][PCI_NUM_DEVICES_PER_BUS]
                         [PCI_NUM_FUNCTIONS_PER_DEVICE];
} pcie_table_t;

static pcie_table_t *pcie_table;

#define PCIE_DEV(bus, device, func) \
    (&pcie_table->devices[(bus)][(device)][(func)])
static list_t pcie_wrapper_list;

void pci_init(void)
{
    // TODO document; needs -machine type=q35 flag in qemu!
    void *table = acpi_table(MCFG_SIGNATURE, 0);
    KASSERT(table);
    pcie_base_addr = *(uintptr_t *)((uintptr_t)table + 44) + PHYS_OFFSET;
    pcie_table = (pcie_table_t *)pcie_base_addr;
    pt_map_range(pt_get(), pcie_base_addr - PHYS_OFFSET, pcie_base_addr,
                 pcie_base_addr + PAGE_SIZE_1GB, PT_WRITE | PT_PRESENT,
                 PT_WRITE | PT_PRESENT);

    list_init(&pcie_wrapper_list);
    for (unsigned bus = 0; bus < PCI_NUM_BUSES; bus++)
    {
        for (unsigned device = 0; device < PCI_NUM_DEVICES_PER_BUS; device++)
        {
            unsigned int max_functions =
                (PCIE_DEV(bus, device, 0)->standard.header_type & 0x80)
                    ? PCI_NUM_DEVICES_PER_BUS
                    : 1;
            for (unsigned function = 0; function < max_functions; function++)
            {
                pcie_device_t *dev = PCIE_DEV(bus, device, function);
                if (!dev->standard.vendor_id ||
                    dev->standard.vendor_id == (uint16_t)-1)
                    continue;
                pcie_device_wrapper_t *wrapper =
                    kmalloc(sizeof(pcie_device_wrapper_t));
                wrapper->dev = dev;
                wrapper->class = dev->standard.class;
                wrapper->subclass = dev->standard.subclass;
                wrapper->interface = dev->standard.prog_if;
                list_link_init(&wrapper->link);
                list_insert_tail(&pcie_wrapper_list, &wrapper->link);
            }
        }
    }
}

pcie_device_t *pcie_lookup(uint8_t class, uint8_t subclass, uint8_t interface)
{
    list_iterate(&pcie_wrapper_list, wrapper, pcie_device_wrapper_t, link)
    {
        /* verify the class subclass and interface are correct */
        if (((class == PCI_LOOKUP_WILDCARD) || (wrapper->class == class)) &&
            ((subclass == PCI_LOOKUP_WILDCARD) ||
             (wrapper->subclass == subclass)) &&
            ((interface == PCI_LOOKUP_WILDCARD) ||
             (wrapper->interface == interface)))
        {
            return wrapper->dev;
        }
    }
    return NULL;
}