summaryrefslogtreecommitdiff
path: root/src/kernel/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/pci.c')
-rw-r--r--src/kernel/pci.c77
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