diff options
Diffstat (limited to 'src/kernel/pci.c')
-rw-r--r-- | src/kernel/pci.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/kernel/pci.c b/src/kernel/pci.c new file mode 100644 index 0000000..fe96c4c --- /dev/null +++ b/src/kernel/pci.c @@ -0,0 +1,77 @@ +#include "boot.h" +#include "mem.h" +#include "util.h" +#include "pci.h" +#include "panic.h" + +enum { + PCP_SELECT = 0x0cf8, + PCP_READ = 0x0cfc +}; + +uint16_t n_pci_devices = 0; +struct pci_device *pci_device_pages[256]; + +#define PCI_DEVICES_PER_PAGE (4096 / sizeof(struct pci_device)) + +struct pci_device *nth_pci_device(uint16_t n) { + return pci_device_pages[n / PCI_DEVICES_PER_PAGE] + (n % PCI_DEVICES_PER_PAGE); +} + +struct pci_device *find_pci_device_from_class_and_subclass(uint8_t class, uint8_t subclass, uint16_t start, uint16_t *index) { + for (uint16_t n = start; n < n_pci_devices; ++n) { + struct pci_device *p = nth_pci_device(n); + if ((p->class == class) && (p->subclass == subclass)) { + *index = n; + return p; + } + } + return 0; +} + +struct pci_device *next_pci_device() { + if (!(n_pci_devices % PCI_DEVICES_PER_PAGE)) + pci_device_pages[n_pci_devices / PCI_DEVICES_PER_PAGE] = allocate_pages(1); + return nth_pci_device(n_pci_devices++); +} + +static inline uint32_t pci_read_config(uint16_t number, uint8_t reg) { + uint32_t cspace_addr = 0x80000000 | (number << 8) | (reg << 2); + outd(PCP_SELECT, cspace_addr); + + return ind(PCP_READ); +} + +void pci_device_check(uint16_t number) { + uint32_t id = pci_read_config(number, 0); + if ((id & 0xffff) == 0xffff) + return; + + struct pci_device *next_device = next_pci_device(); + + next_device->number = number; + + next_device->id_vendor = id; + next_device->id_device = id >> 16; + + uint32_t class = pci_read_config(number, 2); + + next_device->class = class >> 24; + next_device->subclass = class >> 16; + next_device->iface = class >> 8; + + next_device->bar0 = pci_read_config(number, 4); + next_device->bar1 = pci_read_config(number, 5); + next_device->bar2 = pci_read_config(number, 6); + next_device->bar3 = pci_read_config(number, 7); +} + +void pci_init() { + if (!(BOOT_INFO->support_flags & BIS_PCI)) + panic("No PCI support detected."); + if (!(BOOT_INFO->pci_hw_char & PHC_CS_M1)) + panic("No PCI Mechanism 1 support"); + + for (uint32_t number = 0; number < (BOOT_INFO->last_pci_bus + 1) * 256; ++number) + pci_device_check(number); +}
\ No newline at end of file |