#pragma once // PCI bus enumeration and driver binding. // // Until this existed, every driver in SchOS found its hardware by knowing // where it was: IDE at 0x1F0 because IDE has always been at 0x1F0, the // framebuffer wherever the BIOS's VBE call said. That works for exactly the // devices that predate PCI and for nothing else. A NIC, an AHCI controller, // a virtio device or a real GPU has no fixed address at all -- you find it by // asking the bus what is plugged into it, which is what this does. // --------------------------------------------------------------------------- // Config space // --------------------------------------------------------------------------- #define PCI_CONFIG_ADDR 0xCF8 #define PCI_CONFIG_DATA 0xCFC #define PCI_VENDOR_ID 0x00 #define PCI_DEVICE_ID 0x02 #define PCI_COMMAND 0x04 #define PCI_STATUS 0x06 #define PCI_REVISION 0x08 #define PCI_PROG_IF 0x09 #define PCI_SUBCLASS 0x0A #define PCI_CLASS 0x0B #define PCI_HEADER_TYPE 0x0E #define PCI_BAR0 0x10 #define PCI_SECONDARY_BUS 0x19 #define PCI_INTERRUPT_LINE 0x3C #define PCI_INTERRUPT_PIN 0x3D // PCI_COMMAND bits a driver usually wants set before touching its device. #define PCI_CMD_IO 0x0001 #define PCI_CMD_MEMORY 0x0002 #define PCI_CMD_BUS_MASTER 0x0004 // Base classes worth naming. The full list is long and mostly irrelevant here. #define PCI_CLASS_STORAGE 0x01 #define PCI_CLASS_NETWORK 0x02 #define PCI_CLASS_DISPLAY 0x03 #define PCI_CLASS_MULTIMEDIA 0x04 #define PCI_CLASS_BRIDGE 0x06 #define PCI_CLASS_SERIAL_BUS 0x0C #define PCI_STATUS_CAPLIST 0x0010 // device has a capability list #define PCI_CAP_PTR 0x34 // offset of the first capability #define PCI_CAP_MSI 0x05 #define PCI_CAP_MSIX 0x11 #define PCI_CAP_VENDOR 0x09 // virtio's configuration lives here #define PCI_CAP_PCIE 0x10 #define PCI_ANY 0xFFFF // wildcard in a match table #define NPCI 64 // devices we will remember struct pci_bar { uint64 base; // already masked; 0 means "no BAR here" uint64 size; // determined by write-ones-and-read-back int is_io; // I/O port range rather than memory int is_64; // consumed the following BAR slot too int prefetch; }; struct pci_dev { uint8 bus, slot, func; uint16 vendor, device; uint8 class, subclass, prog_if, revision; uint8 irq; // interrupt line, 0xFF when unrouted uint8 header_type; struct pci_bar bar[6]; const char *driver; // name of whatever claimed it, or 0 // The driver itself, so it can be asked to release the device. The name // alone cannot be called. const struct pci_driver *drv; // How pci_irq_attach() connected the interrupt: PCI_IRQ_INTX, _MSI or // _MSIX, and on which line. See kernel/dev/pci.c. uint8 irq_kind; int irq_line; }; #define PCI_IRQ_NONE 0 #define PCI_IRQ_INTX 1 #define PCI_IRQ_MSI 2 #define PCI_IRQ_MSIX 3 // pci_irq_attach() flags. MSI is always tried first; MSI-X only when the // driver says it can take it, because some parts change how they report // interrupt causes once MSI-X is on (the 82574's IVAR, for one). #define PCI_IRQF_MSIX 0x1 // One row of a driver's match table. Terminate the table with a row whose // vendor is 0. struct pci_match { uint16 vendor, device; // PCI_ANY to ignore uint8 class, subclass; // used only when vendor is PCI_ANY }; // Declared here rather than defs.h because both take a struct pci_dev, which // only this header defines. See kernel/dev/device.c for the model they feed. // device.h is included rather than forward-declaring `enum dev_class`, which // is a compiler extension and warns. #include "device.h" enum dev_class pci_class_of(const struct pci_dev *); // Connect a device's interrupt to a handler: an MSI vector if the device has // one, MSI-X if allowed and it has that instead, and its INTx line otherwise. // Returns the line, or -1 if there is no way to interrupt at all -- which is // not fatal; a driver with a poll path should say so and carry on. #include "irq.h" int pci_irq_attach(struct pci_dev *, irq_handler_t, void *arg, const char *name, int flags); void pci_irq_detach(struct pci_dev *, irq_handler_t, void *arg); const char *pci_irq_kind(const struct pci_dev *); void pci_describe(const struct pci_dev *, char *out, int cap); struct pci_driver { const char *name; // Base for the device's name in /proc/devices, if the driver's own name // makes a bad one. `e1000` plus an index reads as "e10000"; a NIC wants to // be `eth0`. Optional -- null means use `name`. const char *devname; const struct pci_match *match; // Class-specific vtable, published on the device when this driver claims // it -- struct display_ops for a display, and so on. Optional; a driver // with nothing to offer a consumer leaves it null. // // It lives on the driver rather than being set inside probe() because // pci_bind() registers the device *after* probe returns, so a driver has // nothing to attach it to at the point it would want to. const void *ops; // Return 0 to claim the device. A driver that returns non-zero leaves it // unclaimed for anything else that matches. int (*probe)(struct pci_dev *); // Called right after the device is published, for a driver that supports // more than one instance and needs to say which of its own is which. // Optional -- a single-instance driver has nothing to attach. // // It exists because probe() runs *before* device_add(), so a driver has // nowhere to put its per-instance pointer at the point it would want to. // Without this the second card of the same model is either refused or // silently shares the first one's registers. void (*attached)(struct pci_dev *, struct device *); // Give the device up, so a userspace driver can take it. // // docs/plan/hybrid.md is built on the kernel *handing over* a device // rather than a process wrestling it away: two drivers writing one set of // registers is not a subtle failure, it is hardware that appears to have // started lying. So the transfer is the incumbent's decision, and this is // where it is made. // // A driver that implements this must leave the device in a state the next // one can start from, and must stop touching it. A driver that does not // implement it keeps its device: refusing is always safe, and for anything // on the path to a shell it is also correct. int (*release)(struct pci_dev *); // Put one last frame on a device this driver was told to let go of. // // Called from panic(), and only from there: interrupts are off, no other // CPU is coming back, and the process that was driving the device will not // run again. The framebuffer is one the userspace driver registered with // PCIIOC_PANICFB and the kernel has already painted the report into; all // that is left is to make the device show it. // // The contract is what panic context allows: **no allocation, no locks, no // sleeping.** Whatever memory this needs has to already exist, because the // allocator may be exactly what was being held when the machine died. A // driver that cannot meet that leaves this null, and the screen it handed // over stays frozen -- which is what happens today and is not a // regression. // // Returns 0 if something was put on screen. int (*panic_present)(struct pci_dev *, uint64 fb_paddr, int w, int h); };