SchOS/include/arch/memlayout.h
Nexoniarz a1c965da1e 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
2026-10-23 15:05:12 +02:00

98 lines
4 KiB
C

#pragma once
// Memory layout
#define EXTMEM 0x100000 // Start of extended memory
// Top of usable physical memory. A *variable*, read from the firmware's E820
// map at boot -- see kernel/arch/x86_64/e820.c. It used to be the constant
// below, which was not a limit on what the kernel used but an assertion that
// exactly 224 MB existed: less and the allocator walked off the end of RAM,
// more and it was invisible.
//
// The macro spelling is kept so that the twenty-odd call sites read the same as
// they always did. It must not be used before e820_init(), which runs first
// thing in main() for that reason.
#ifndef __ASSEMBLER__
extern uint64 phystop;
#define PHYSTOP phystop
#endif
// PHYSTOP alone describes memory as one run ending at a top address, which is
// what a BIOS map usually looks like and what UEFI never does: OVMF drops an
// ACPI NVS region at 0x800000, so the run containing the kernel ends at 8 MB
// and everything above the hole was invisible. The usable regions are
// therefore kept as a list as well, and kinit1() frees each one rather than
// walking a single range from the kernel's end to the top.
//
// PHYSTOP still means what it always did -- the top of what kalloc may hand
// out, and the bound kfree() rejects pages above -- so every existing call
// site is unaffected.
#define MEMREGION_MAX 64
#ifndef __ASSEMBLER__
struct memregion {
uint64 base; // first byte, page aligned
uint64 end; // one past the last byte, page aligned
};
extern struct memregion mem_usable[MEMREGION_MAX];
extern int nmem_usable;
// The top of the low region: where the DMA arena is carved from, and the
// ceiling for kinit1(). Always below DIRECTMAP_BOOT, and separate from
// `phystop` because a device is not obliged to be able to reach the top of
// memory -- an RTL8139's buffer address register is thirty-two bits wide.
extern uint64 lowtop;
#endif
// What to assume when the firmware gives us nothing usable: the old constant,
// so that path behaves exactly as the kernel always did.
#define PHYSTOP_FALLBACK 0xE000000ULL
// The most RAM the kernel will use.
//
// This was 0xC0000000 -- three gigabytes -- because kvmalloc() mapped exactly
// four 1 GB pages and the fourth was marked uncached for the PCI hole. On a
// laptop with sixteen gigabytes that threw away thirteen of them, silently.
//
// kvmalloc() now maps as many 1 GB pages as the machine has RAM, so the limit
// is the page table rather than a constant: the kernel PDPT has 512 entries
// and ioremap takes what the direct map does not, so 256 GB of RAM still
// leaves 248 GB of device window. A machine with more than this gets the rest
// ignored, with a line saying so.
//
// It was 64 GB while handing memory to the allocator meant writing every page
// of it at boot. kalloc.c keeps never-used memory as ranges now, so a bigger
// machine costs nothing until its memory is used.
#define PHYSTOP_MAX (256ULL << 30)
// What the *loader's* page tables cover, and therefore the ceiling on anything
// that runs before kvmalloc() builds the kernel's own.
//
// kinit1() frees pages by writing a free-list pointer into each one, so every
// page it touches has to be mapped at the time. Before kvmalloc that means the
// low four gigabytes and nothing else, which is why freeing memory happens in
// two passes -- see kinit_high().
#define DIRECTMAP_BOOT (4ULL << 30)
// Below this the kernel image, its page tables and the DMA arena do not fit.
#define PHYSTOP_MIN (24ULL * 1024 * 1024)
// Key addresses for address space layout
#define KERNBASE 0xFFFF800000000000 // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked
#ifndef __ASSEMBLER__
static inline addr_t v2p(void *a) {
return ((addr_t)(a)) - ((addr_t)KERNBASE);
}
static inline void *p2v(addr_t a) {
return (void *)((a) + ((addr_t)KERNBASE));
}
#endif
#define V2P(a) (((addr_t) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)
#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts