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

255
include/arch/x86.h Normal file
View file

@ -0,0 +1,255 @@
#pragma once
struct dtr {
ushort limit;
uint64 base;
} __attribute__((packed));
static inline uchar
inb(ushort port)
{
uchar data;
asm volatile("in %1,%0" : "=a" (data) : "d" (port));
return data;
}
// Cycle counter. Not a clock -- it does not convert to time without knowing the
// frequency, and it is not synchronised across cores -- but for "which of these
// two paths costs more" it is exactly the right instrument, and the only one
// with fine enough resolution to see inside a single syscall.
static inline uint64
rdtsc(void)
{
uint lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64)hi << 32) | lo;
}
// The x87/MMX/SSE register file, to and from a 512-byte, 16-byte-aligned area.
static inline void
fxsave(void *area)
{
asm volatile("fxsave64 (%0)" : : "r"(area) : "memory");
}
static inline void
fxrstor(const void *area)
{
asm volatile("fxrstor64 (%0)" : : "r"(area) : "memory");
}
static inline void
insl(int port, void *addr, int cnt)
{
asm volatile("cld; rep insl" :
"=D" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"memory", "cc");
}
static inline void
outb(ushort port, uchar data)
{
asm volatile("out %0,%1" : : "a" (data), "d" (port));
}
// 16 bits. Added for AC'97, whose mixer is entirely 16-bit registers: reading
// one of them a byte at a time returns the low half twice on some codecs,
// because the read of the low byte is what latches the pair.
static inline ushort
inw(ushort port)
{
ushort data;
asm volatile("inw %1,%0" : "=a" (data) : "d" (port));
return data;
}
static inline uint
inl(ushort port)
{
uint data;
asm volatile("inl %1,%0" : "=a" (data) : "d" (port));
return data;
}
static inline void
outw(ushort port, ushort data)
{
asm volatile("out %0,%1" : : "a" (data), "d" (port));
}
// PCI configuration space is reached through a pair of 32-bit ports, so this
// is not optional decoration -- see kernel/dev/pci.c.
static inline void
outl(ushort port, uint data)
{
asm volatile("outl %0,%1" : : "a" (data), "d" (port));
}
static inline void
outsl(int port, const void *addr, int cnt)
{
asm volatile("cld; rep outsl" :
"=S" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"cc");
}
static inline void
stosb(void *addr, int data, int cnt)
{
asm volatile("cld; rep stosb" :
"=D" (addr), "=c" (cnt) :
"0" (addr), "1" (cnt), "a" (data) :
"memory", "cc");
}
// 64-bit-only helpers. `rep stosq` and `xchgq` do not exist in 32-bit mode, so
// these must not even be parsed by the -m32 bootloader build -- they compiled
// there before only because uint64 was accidentally 32 bits wide, which is the
// bug include/core/types.h now asserts against.
#ifdef __x86_64__
static inline void
stosq(void *addr, uint64 data, int cnt)
{
asm volatile("cld; rep stosq" :
"=D" (addr), "=c" (cnt) :
"0" (addr), "1" (cnt), "a" (data) :
"memory", "cc");
}
struct segdesc;
static inline void
lgdt(struct segdesc *p, int size)
{
struct dtr d;
d.limit = size - 1;
d.base = (uint64)p;
asm volatile("lgdt (%0)" : : "r" (&d));
}
struct gatedesc;
static inline void
lidt(struct gatedesc *p, int size)
{
struct dtr d;
d.limit = size - 1;
d.base = (uint64)p;
asm volatile("lidt (%0)" : : "r" (&d));
}
static inline void
ltr(ushort sel)
{
asm volatile("ltr %0" : : "r" (sel));
}
static inline uint64
readeflags(void)
{
uint64 flags;
asm volatile("pushfq; pop %0" : "=r" (flags));
return flags;
}
static inline void
cli(void)
{
asm volatile("cli");
}
static inline void
sti(void)
{
asm volatile("sti");
}
static inline void
hlt(void)
{
asm volatile("hlt");
}
static inline uint64
xchg(volatile uint64 *addr, uint64 newval)
{
uint64 result;
asm volatile("lock; xchgq %0, %1" :
"+m" (*addr), "=a" (result) :
"1" (newval) :
"cc");
return result;
}
#endif /* __x86_64__ */
static inline uint64
rcr2(void)
{
uint64 val;
asm volatile("mov %%cr2,%0" : "=r" (val));
return val;
}
static inline void
lcr3(uint64 val)
{
asm volatile("mov %0,%%cr3" : : "r" (val));
}
// Invalidate the TLB entry for one page. Needed any time a PTE is changed
// or cleared for an address space that may still be the active CR3 —
// otherwise the CPU can keep translating through the stale entry until the
// next full CR3 reload (context switch), silently reading/writing the
// wrong physical page.
static inline void
invlpg(void *va)
{
asm volatile("invlpg (%0)" : : "r" (va) : "memory");
}
static inline void
wrmsr(uint32 msr, uint64 val)
{
uint32 low = (uint32)val;
uint32 high = (uint32)(val >> 32);
asm volatile("wrmsr" : : "c" (msr), "a" (low), "d" (high));
}
static inline uint64
rdmsr(uint32 msr)
{
uint32 low, high;
asm volatile("rdmsr" : "=a" (low), "=d" (high) : "c" (msr));
return ((uint64)high << 32) | low;
}
static inline uint64
readcs(void)
{
uint64 val;
asm volatile("mov %%cs, %0" : "=r" (val));
return val;
}
// CPUID. `leaf` in EAX, results in EAX/EBX/ECX/EDX.
//
// %rbx is callee-saved *and* the PIC base register on some builds, so it is
// exchanged out rather than named as an operand -- naming it directly is the
// classic way to get "inline asm clobbers reserved register" or, worse, silent
// corruption in the caller.
static inline void
cpuid_leaf(uint32 leaf, uint32 *a, uint32 *b, uint32 *c, uint32 *d)
{
uint32 ra, rb, rc, rd;
asm volatile("xchgq %%rbx, %q1\n\t"
"cpuid\n\t"
"xchgq %%rbx, %q1"
: "=a"(ra), "=&r"(rb), "=c"(rc), "=d"(rd)
: "0"(leaf), "2"(0));
if (a) *a = ra;
if (b) *b = rb;
if (c) *c = rc;
if (d) *d = rd;
}