feat: add pageAlignDown and pageAlignUp

This commit is contained in:
dualfroz committed 2026-09-06 14:51:04 +02:00
1 parent 8101704cc3
commit 2e9bb0a476
1 file changed
+161
+161
View File
@@ -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;