From 2e9bb0a47635479914532cc77160549fca985589 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sun, 6 Sep 2026 14:51:04 +0200 Subject: [PATCH] feat: add pageAlignDown and pageAlignUp --- src/mmap.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 src/mmap.c diff --git a/src/mmap.c b/src/mmap.c new file mode 100644 index 0000000..ebbd741 --- /dev/null +++ b/src/mmap.c @@ -0,0 +1,161 @@ +#include "mmap.h" +#include "sched.h" +#include "elf.h" +#include "paging.h" +#include "pmm.h" +#include "vfs.h" +#include "string.h" + +/* + * Real demand-paged mmap(2)/munmap(2) on top of this kernel's single + * shared page table: a Vma only ever RESERVES address space (mmapReserve) + * -- no physical frame is allocated and no file byte is read until the + * very first access to a given page actually traps into isr.c's #PF + * handler, which resolves it here (mmapFaultHandler). This is the same + * "lazy" contract pagingMapUserRange's own eager sibling does NOT have + * (that one maps and zeroes every page up front, for an ELF image/stack + * that is about to be fully used anyway) -- mmap's whole point is a + * mapping the caller may only ever touch a fraction of. + * + * Every mapping is placed inside the calling thread's own elfSlotSize + * slot (elf.h), specifically the sub-range [userSlotBase + + * elfSlotStackOffset, userSlotBase + elfSlotSize) -- everything from the + * top of the small, fixed-size user stack up to the end of the slot, 128 + * MiB that no ELF LOAD segment or brk-growth ever reaches in this + * kernel's own binaries (elfSlotBrkOffset caps brk's own growth at 64 + * MiB in, elfSlotStackOffset itself at 128 MiB in -- see elf.h). There is + * no address-space-wide mmap arena the way a real kernel's own mm_struct + * has one: with a single page table shared by every thread in the + * system, "the whole address space" is not a per-thread concept to begin + * with, so mmap here is scoped exactly the way brk/the stack already are. + */ + +static uint64 pageAlignDown(uint64 x) +{ + return x & ~(uint64)(frameSize - 1); +} + +static uint64 pageAlignUp(uint64 x) +{ + return (x + frameSize - 1) & ~(uint64)(frameSize - 1); +} + +static uint64 mmapRegionStart(struct Thread* thread) +{ + return thread->userSlotBase + elfSlotStackOffset; +} + +static uint64 mmapRegionEnd(struct Thread* thread) +{ + return thread->userSlotBase + elfSlotSize; +} + +static bool rangesOverlap(uint64 aStart, uint64 aEnd, uint64 bStart, uint64 bEnd) +{ + return aStart < bEnd && bStart < aEnd; +} + +Vma* mmapFind(struct Thread* thread, uint64 addr) +{ + for (int i = 0; i < maxVmasPerThread; i++) { + Vma* v = &thread->vmas[i]; + if (v->used && addr >= v->start && addr < v->end) + return v; + } + return nullptr; +} + +static Vma* findFreeVmaSlot(struct Thread* thread) +{ + for (int i = 0; i < maxVmasPerThread; i++) + if (!thread->vmas[i].used) + return &thread->vmas[i]; + return nullptr; +} + +static bool regionFree(struct Thread* thread, uint64 start, uint64 end) +{ + for (int i = 0; i < maxVmasPerThread; i++) { + Vma* v = &thread->vmas[i]; + if (v->used && rangesOverlap(start, end, v->start, v->end)) + return false; + } + return true; +} + +/* First-fit search for `length` free bytes inside thread's own mmap + region: candidate gaps are the region's own start plus every existing + Vma's own end, sorted into address order (a plain insertion sort -- + maxVmasPerThread's small fixed bound makes anything fancier pointless), + then the first candidate `length` bytes actually fits at wins. */ +static uint64 findFreeRegion(struct Thread* thread, uint64 length) +{ + uint64 regionStart = mmapRegionStart(thread); + uint64 regionEnd = mmapRegionEnd(thread); + + uint64 candidates[maxVmasPerThread + 1]; + int candidateCount = 0; + candidates[candidateCount++] = regionStart; + for (int i = 0; i < maxVmasPerThread; i++) + if (thread->vmas[i].used) + candidates[candidateCount++] = thread->vmas[i].end; + + for (int i = 1; i < candidateCount; i++) { + uint64 key = candidates[i]; + int j = i - 1; + while (j >= 0 && candidates[j] > key) { + candidates[j + 1] = candidates[j]; + j--; + } + candidates[j + 1] = key; + } + + for (int i = 0; i < candidateCount; i++) { + uint64 candidate = candidates[i]; + if (candidate < regionStart) + continue; + if (candidate + length < candidate || candidate + length > regionEnd) + continue; + if (regionFree(thread, candidate, candidate + length)) + return candidate; + } + return 0; +} + +bool mmapReserve(struct Thread* thread, uint64 addr, uint64 length, uint32 prot, + bool anonymous, int fd, uint64 fileOffset, uint64* outAddr) +{ + if (length == 0) + return false; + if (addr % frameSize != 0 || length % frameSize != 0) + return false; + if (!anonymous && fileOffset % frameSize != 0) + return false; + + uint64 start = addr; + if (start == 0) { + start = findFreeRegion(thread, length); + if (start == 0) + return false; + } + + uint64 end = start + length; + if (end < start) + return false; + if (start < mmapRegionStart(thread) || end > mmapRegionEnd(thread)) + return false; + if (!regionFree(thread, start, end)) + return false; + + Vma* slot = findFreeVmaSlot(thread); + if (!slot) + return false; + + slot->used = true; + slot->start = start; + slot->end = end; + slot->prot = prot; + slot->anonymous = anonymous; + slot->fd = anonymous ? -1 : fd; + slot->fileOffset = anonymous ? 0 : fileOffset; +