include: one folder per subsystem, and includes that name it

Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-23 15:05:12 +02:00
commit a1c965da1e
267 changed files with 1560 additions and 1440 deletions

182
include/dev/pci.h Normal file
View file

@ -0,0 +1,182 @@
#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
// Devices we will remember. A laptop has 30 to 60 functions, a desktop with
// a couple of cards and Thunderbolt more than 64, and every one past the limit
// was ignored -- including whatever disk or NIC came last in bus order.
#define NPCI 256
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
// How that line is signalled, an IRQ_* mode from irq.h. PCI's level/low
// unless ACPI's routing says otherwise -- see kernel/acpi/aml.c.
uint8 irq_mode;
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 "dev/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 "core/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);
};