diff --git a/kexts/drivers/bus/pci/driver.c b/kexts/drivers/bus/pci/driver.c new file mode 100644 index 0000000..18b2f27 --- /dev/null +++ b/kexts/drivers/bus/pci/driver.c @@ -0,0 +1,109 @@ +#include +#include + +static struct vm_cache pci_driver_cache = { + .c_name = "pci_driver", + .c_obj_size = sizeof(struct pci_driver), +}; + +static struct queue pci_drivers; +static spin_lock_t pci_drivers_lock; + +extern kern_status_t init_pci_driver_cache(void) +{ + vm_cache_init(&pci_driver_cache); + pci_drivers = QUEUE_INIT; + pci_drivers_lock = SPIN_LOCK_INIT; + + return KERN_OK; +} + +struct pci_driver *pci_driver_create(struct kext *self, const char *name, const struct pci_device_id *device_ids) +{ + struct pci_driver *driver = vm_cache_alloc(&pci_driver_cache, VM_NORMAL); + if (!driver) { + return NULL; + } + + kern_status_t status = driver_init(&driver->pci_base, self, name); + if (status != KERN_OK) { + vm_cache_free(&pci_driver_cache, driver); + return NULL; + } + + driver->pci_target_devices = device_ids; + return driver; +} + +kern_status_t pci_driver_destroy(struct pci_driver *driver) +{ + /* TODO */ + return KERN_UNIMPLEMENTED; +} + +kern_status_t pci_driver_register(struct pci_driver *driver) +{ + kern_status_t status = driver_register(&driver->pci_base); + if (status != KERN_OK) { + return status; + } + + unsigned long flags; + spin_lock_irqsave(&pci_drivers_lock, &flags); + queue_push_back(&pci_drivers, &driver->pci_head); + spin_unlock_irqrestore(&pci_drivers_lock, flags); + + return KERN_OK; +} + +kern_status_t pci_driver_unregister(struct pci_driver *driver) +{ + if (driver->pci_base.drv_major == DEV_MAJOR_INVALID) { + return KERN_INVALID_ARGUMENT; + } + + unsigned long flags; + spin_lock_irqsave(&pci_drivers_lock, &flags); + queue_delete(&pci_drivers, &driver->pci_head); + spin_unlock_irqrestore(&pci_drivers_lock, flags); + + return driver_unregister(&driver->pci_base); +} + +static bool scan_device_id_list(const struct pci_device_id *device_ids, uint16_t vendor_id, uint16_t device_id) +{ + for (unsigned int i = 0; ; i++) { + if (device_ids[i].pci_device_id == PCI_NONE && device_ids[i].pci_vendor_id == PCI_NONE) { + break; + } + + if (device_ids[i].pci_device_id == device_id && device_ids[i].pci_vendor_id == vendor_id) { + return true; + } + } + + return false; +} + +struct pci_driver *find_driver_for_pci_device(uint16_t vendor_id, uint16_t device_id) +{ + struct pci_driver *out = NULL; + + unsigned long flags; + spin_lock_irqsave(&pci_drivers_lock, &flags); + + queue_foreach (struct pci_driver, driver, &pci_drivers, pci_head) { + const struct pci_device_id *device_ids = driver->pci_target_devices; + if (!device_ids) { + continue; + } + + if (scan_device_id_list(device_ids, vendor_id, device_id)) { + out = driver; + break; + } + } + + spin_unlock_irqrestore(&pci_drivers_lock, flags); + return out; +} diff --git a/kexts/drivers/bus/pci/extension.yaml b/kexts/drivers/bus/pci/extension.yaml index 1d14dec..6ff2085 100644 --- a/kexts/drivers/bus/pci/extension.yaml +++ b/kexts/drivers/bus/pci/extension.yaml @@ -6,3 +6,5 @@ license: BSD-3-Clause copyright: Copyright © Max Wash 2023 sources: - main.c + - pci.c + - driver.c diff --git a/kexts/drivers/bus/pci/include/socks/pci.h b/kexts/drivers/bus/pci/include/socks/pci.h index 2f075df..1df5784 100644 --- a/kexts/drivers/bus/pci/include/socks/pci.h +++ b/kexts/drivers/bus/pci/include/socks/pci.h @@ -1,32 +1,33 @@ #ifndef SOCKS_PCI_H_ #define SOCKS_PCI_H_ +#include #include -#define PCI_VENDOR_ID 0x00 // 2 -#define PCI_DEVICE_ID 0x02 // 2 -#define PCI_COMMAND 0x04 // 2 -#define PCI_STATUS 0x06 // 2 -#define PCI_REVISION_ID 0x08 // 1 +#define PCI_REG_VENDOR_ID 0x00 // 2 +#define PCI_REG_DEVICE_ID 0x02 // 2 +#define PCI_REG_COMMAND 0x04 // 2 +#define PCI_REG_STATUS 0x06 // 2 +#define PCI_REG_REVISION_ID 0x08 // 1 -#define PCI_PROG_IF 0x09 // 1 -#define PCI_SUBCLASS 0x0a // 1 -#define PCI_CLASS 0x0b // 1 -#define PCI_CACHE_LINE_SIZE 0x0c // 1 -#define PCI_LATENCY_TIMER 0x0d // 1 -#define PCI_HEADER_TYPE 0x0e // 1 -#define PCI_BIST 0x0f // 1 -#define PCI_BAR0 0x10 // 4 -#define PCI_BAR1 0x14 // 4 -#define PCI_BAR2 0x18 // 4 -#define PCI_BAR3 0x1C // 4 -#define PCI_BAR4 0x20 // 4 -#define PCI_BAR5 0x24 // 4 +#define PCI_REG_PROG_IF 0x09 // 1 +#define PCI_REG_SUBCLASS 0x0a // 1 +#define PCI_REG_CLASS 0x0b // 1 +#define PCI_REG_CACHE_LINE_SIZE 0x0c // 1 +#define PCI_REG_LATENCY_TIMER 0x0d // 1 +#define PCI_REG_HEADER_TYPE 0x0e // 1 +#define PCI_REG_BIST 0x0f // 1 +#define PCI_REG_BAR0 0x10 // 4 +#define PCI_REG_BAR1 0x14 // 4 +#define PCI_REG_BAR2 0x18 // 4 +#define PCI_REG_BAR3 0x1C // 4 +#define PCI_REG_BAR4 0x20 // 4 +#define PCI_REG_BAR5 0x24 // 4 -#define PCI_INTERRUPT_LINE 0x3C // 1 -#define PCI_INTERRUPT_PIN 0x3D +#define PCI_REG_INTERRUPT_LINE 0x3C // 1 +#define PCI_REG_INTERRUPT_PIN 0x3D -#define PCI_SECONDARY_BUS 0x19 // 1 +#define PCI_REG_SECONDARY_BUS 0x19 // 1 #define PCI_HEADER_TYPE_DEVICE 0 #define PCI_HEADER_TYPE_BRIDGE 1 @@ -40,7 +41,29 @@ #define PCI_NONE 0xFFFF -typedef void (*pci_func_t)(uint32_t device, uint16_t vendor_id, uint16_t device_id, void *arg); +#define PCI_SUBSYSTEM_KEXT_ID "net.doorstuck.socks.pci" + +#define PCI_DEVICE_ID(vid, did) { .pci_vendor_id = (vid), .pci_device_id = (did) } +#define PCI_DEVICE_ID_INVALID { .pci_vendor_id = PCI_NONE, .pci_device_id = PCI_NONE } + +struct pci_device_id { + uint16_t pci_vendor_id; + uint16_t pci_device_id; +}; + +struct pci_device { + struct pci_device_id pci_id; + unsigned int pci_bus, pci_slot, pci_func; +}; + +struct pci_driver { + struct driver pci_base; + struct queue_entry pci_head; + const struct pci_device_id *pci_target_devices; + + kern_status_t(*probe)(struct pci_driver *, struct device *); + kern_status_t(*remove)(struct pci_driver *, struct device *); +}; static inline int pci_get_bus(uint32_t device) { @@ -67,8 +90,20 @@ static inline uint32_t pci_box_device(int bus, int slot, int func) return (uint32_t)((bus << 16) | (slot << 8) | func); } +extern struct pci_driver *pci_driver_create(struct kext *self, const char *name, const struct pci_device_id *device_ids); +extern kern_status_t pci_driver_destroy(struct pci_driver *driver); +extern kern_status_t pci_driver_register(struct pci_driver *driver); +extern kern_status_t pci_driver_unregister(struct pci_driver *driver); +static inline struct driver *pci_driver_base(struct pci_driver *driver) +{ + return &driver->pci_base; +} + extern uint32_t pci_read_field(uint32_t device, int field, int size); extern void pci_write_field(uint32_t device, int field, int size, uint32_t value); extern uint16_t pci_find_type(uint32_t dev); +extern uint32_t pci_device_read_field(struct device *dev, int field, int size); +extern void pci_device_write_field(struct device *dev, int field, int size, uint32_t value); + #endif diff --git a/kexts/drivers/bus/pci/main.c b/kexts/drivers/bus/pci/main.c index a4bb4bc..1cd8976 100644 --- a/kexts/drivers/bus/pci/main.c +++ b/kexts/drivers/bus/pci/main.c @@ -1,119 +1,14 @@ #include +#include #include #include #include #include #include +#include "pci.h" -static struct driver *pci_driver = NULL; -static struct bus_device *pci_bus = NULL; - -static void pci_scan_bus(pci_func_t f, int type, int bus, void *arg); - -void pci_write_field(uint32_t device, int field, int size, uint32_t value) -{ - outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); - outportl(PCI_VALUE_PORT, value); -} - -uint32_t pci_read_field(uint32_t device, int field, int size) -{ - outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); - - if (size == 4) { - uint32_t t = inportl(PCI_VALUE_PORT); - return t; - } else if (size == 2) { - uint16_t t = inportw(PCI_VALUE_PORT + (field & 2)); - return t; - } else if (size == 1) { - uint8_t t = inportb(PCI_VALUE_PORT + (field & 3)); - return t; - } - - return 0xFFFF; -} - -uint16_t pci_find_type(uint32_t dev) -{ - return (pci_read_field(dev, PCI_CLASS, 1) << 8) | pci_read_field(dev, PCI_SUBCLASS, 1); -} - -static void pci_scan_hit(pci_func_t f, uint32_t dev, void *arg) -{ - int dev_vend = (int)pci_read_field(dev, PCI_VENDOR_ID, 2); - int dev_dvid = (int)pci_read_field(dev, PCI_DEVICE_ID, 2); - - f(dev, dev_vend, dev_dvid, arg); -} - -static void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, void *arg) -{ - uint32_t dev = pci_box_device(bus, slot, func); - if (type == -1 || type == pci_find_type(dev)) { - pci_scan_hit(f, dev, arg); - } - - if (pci_find_type(dev) == PCI_TYPE_BRIDGE) { - pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), arg); - } -} - -static void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void *arg) -{ - uint32_t dev = pci_box_device(bus, slot, 0); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { - return; - } - - pci_scan_func(f, type, bus, slot, 0, arg); - if (!pci_read_field(dev, PCI_HEADER_TYPE, 1)) { - return; - } - - for (int func = 1; func < 8; func++) { - uint32_t dev = pci_box_device(bus, slot, func); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) != PCI_NONE) { - pci_scan_func(f, type, bus, slot, func, arg); - } - } -} - -static void pci_scan_bus(pci_func_t f, int type, int bus, void *arg) -{ - for (int slot = 0; slot < 32; ++slot) { - pci_scan_slot(f, type, bus, slot, arg); - } -} - -void pci_scan(pci_func_t f, int type, void *arg) -{ - if ((pci_read_field(0, PCI_HEADER_TYPE, 1) & 0x80) == 0) { - pci_scan_bus(f, type, 0, arg); - return; - } - - int hit = 0; - for (int func = 0; func < 8; ++func) { - uint32_t dev = pci_box_device(0, 0, func); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { - break; - } - - hit = 1; - pci_scan_bus(f, type, func, arg); - } - - if (hit) { - return; - } - - for (int bus = 0; bus < 256; ++bus) { - for (int slot = 0; slot < 32; ++slot) { - pci_scan_slot(f, type, bus, slot, arg); - } - } -} +struct driver *pci_driver = NULL; +struct bus_device *pci_bus = NULL; static void init_pci_device(uint32_t device, uint16_t vendid, uint16_t devid, void *arg) { @@ -123,13 +18,36 @@ static void init_pci_device(uint32_t device, uint16_t vendid, uint16_t devid, vo pci_get_slot(device), pci_get_func(device)); + printk("pci: found device %s (vend:%04x, dev:%04x)", dev->dev_name, vendid, devid); + + struct pci_device *pci_dev = kmalloc(sizeof *pci_dev, VM_NORMAL); + if (!pci_dev) { + /* TODO destroy device */ + return; + } + + pci_dev->pci_id.pci_vendor_id = vendid; + pci_dev->pci_id.pci_device_id = devid; + pci_dev->pci_bus = pci_get_bus(device); + pci_dev->pci_slot = pci_get_slot(device); + pci_dev->pci_func = pci_get_func(device); + + dev->dev_bus_priv = pci_dev; + + /* register as a generic device under the PCI driver. + if we find a suitable driver for this device, that device will re-register it as theirs. */ device_register(dev, pci_driver, bus_device_base(pci_bus)); + + struct pci_driver *driver = find_driver_for_pci_device(vendid, devid); + if (driver && driver->probe) { + driver->probe(driver, dev); + } } static kern_status_t pci_bus_scan(struct device *bus) { printk("pci: scanning for devices..."); - pci_scan(init_pci_device, -1, NULL); + pci_enumerate_devices(init_pci_device, -1, NULL); return KERN_OK; } @@ -144,6 +62,11 @@ static kern_status_t online(struct kext *self) return KERN_NO_MEMORY; } + kern_status_t status = init_pci_driver_cache(); + if (status != KERN_OK) { + return status; + } + driver_register(pci_driver); pci_bus = bus_device_create(); @@ -153,9 +76,11 @@ static kern_status_t online(struct kext *self) device_register(pci_base, pci_driver, root_device()); + printk("pci: subsystem initialised"); + return KERN_OK; } -DEFINE_KEXT("net.doorstuck.socks.pci", +DEFINE_KEXT(PCI_SUBSYSTEM_KEXT_ID, online, NULL, KEXT_NO_DEPENDENCIES); diff --git a/kexts/drivers/bus/pci/pci.c b/kexts/drivers/bus/pci/pci.c new file mode 100644 index 0000000..0acf483 --- /dev/null +++ b/kexts/drivers/bus/pci/pci.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include "pci.h" + +static void pci_scan_bus(pci_func_t f, int type, int bus, void *arg); + +void pci_write_field(uint32_t device, int field, int size, uint32_t value) +{ + outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); + outportl(PCI_VALUE_PORT, value); +} + +uint32_t pci_read_field(uint32_t device, int field, int size) +{ + outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); + + if (size == 4) { + uint32_t t = inportl(PCI_VALUE_PORT); + return t; + } else if (size == 2) { + uint16_t t = inportw(PCI_VALUE_PORT + (field & 2)); + return t; + } else if (size == 1) { + uint8_t t = inportb(PCI_VALUE_PORT + (field & 3)); + return t; + } + + return 0xFFFF; +} + +uint32_t pci_device_read_field(struct device *dev, int field, int size) +{ + struct pci_device *pci_dev = dev->dev_bus_priv; + if (!pci_dev) { + return 0; + } + + return pci_read_field(pci_box_device(pci_dev->pci_bus, pci_dev->pci_slot, pci_dev->pci_func), field, size); +} + +void pci_device_write_field(struct device *dev, int field, int size, uint32_t value) +{ + struct pci_device *pci_dev = dev->dev_bus_priv; + if (!pci_dev) { + return; + } + + pci_write_field(pci_box_device(pci_dev->pci_bus, pci_dev->pci_slot, pci_dev->pci_func), field, size, value); +} + +uint16_t pci_find_type(uint32_t dev) +{ + return (pci_read_field(dev, PCI_REG_CLASS, 1) << 8) | pci_read_field(dev, PCI_REG_SUBCLASS, 1); +} + +static void pci_scan_hit(pci_func_t f, uint32_t dev, void *arg) +{ + int dev_vend = (int)pci_read_field(dev, PCI_REG_VENDOR_ID, 2); + int dev_dvid = (int)pci_read_field(dev, PCI_REG_DEVICE_ID, 2); + + f(dev, dev_vend, dev_dvid, arg); +} + +static void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, void *arg) +{ + uint32_t dev = pci_box_device(bus, slot, func); + if (type == -1 || type == pci_find_type(dev)) { + pci_scan_hit(f, dev, arg); + } + + if (pci_find_type(dev) == PCI_TYPE_BRIDGE) { + pci_scan_bus(f, type, pci_read_field(dev, PCI_REG_SECONDARY_BUS, 1), arg); + } +} + +static void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void *arg) +{ + uint32_t dev = pci_box_device(bus, slot, 0); + if (pci_read_field(dev, PCI_REG_VENDOR_ID, 2) == PCI_NONE) { + return; + } + + pci_scan_func(f, type, bus, slot, 0, arg); + if (!pci_read_field(dev, PCI_REG_HEADER_TYPE, 1)) { + return; + } + + for (int func = 1; func < 8; func++) { + uint32_t dev = pci_box_device(bus, slot, func); + if (pci_read_field(dev, PCI_REG_VENDOR_ID, 2) != PCI_NONE) { + pci_scan_func(f, type, bus, slot, func, arg); + } + } +} + +static void pci_scan_bus(pci_func_t f, int type, int bus, void *arg) +{ + for (int slot = 0; slot < 32; ++slot) { + pci_scan_slot(f, type, bus, slot, arg); + } +} + +void pci_enumerate_devices(pci_func_t f, int type, void *arg) +{ + if ((pci_read_field(0, PCI_REG_HEADER_TYPE, 1) & 0x80) == 0) { + pci_scan_bus(f, type, 0, arg); + return; + } + + int hit = 0; + for (int func = 0; func < 8; ++func) { + uint32_t dev = pci_box_device(0, 0, func); + if (pci_read_field(dev, PCI_REG_VENDOR_ID, 2) == PCI_NONE) { + break; + } + + hit = 1; + pci_scan_bus(f, type, func, arg); + } + + if (hit) { + return; + } + + for (int bus = 0; bus < 256; ++bus) { + for (int slot = 0; slot < 32; ++slot) { + pci_scan_slot(f, type, bus, slot, arg); + } + } +} diff --git a/kexts/drivers/bus/pci/pci.h b/kexts/drivers/bus/pci/pci.h new file mode 100644 index 0000000..7406511 --- /dev/null +++ b/kexts/drivers/bus/pci/pci.h @@ -0,0 +1,16 @@ +#ifndef PCI_H_ +#define PCI_H_ + +#include +#include + +typedef void (*pci_func_t)(uint32_t device, uint16_t vendor_id, uint16_t device_id, void *arg); + +extern struct driver *pci_driver; +extern struct bus_device *pci_bus; + +extern struct pci_driver *find_driver_for_pci_device(unsigned int vendor_id, unsigned int device_id); +extern kern_status_t init_pci_driver_cache(void); +extern void pci_enumerate_devices(pci_func_t f, int type, void *arg); + +#endif