completely redesign how user mode memory is allocated, fix a few smp and scheduler bugs, terminating a process now terminates the process, telemetry, optimize text editor, new font, new color scheme

This commit is contained in:
maksw committed 2026-06-09 18:02:28 +02:00
1 parent 27b31f62b6
commit 325149006c
45 files changed
+2091 -760

No files matched your search

+247 -137
View File
@@ -1,13 +1,12 @@
// mm_phys.c - physical memory manager
//
// The free-frame stack is a flat array of PHYSICAL_ADDRESS values carved out of
// the first suitable usable region before any allocation is served. All access
// to physical memory goes through the direct physical window established by the
// bootloader at PhysicalWindowBase (always 0xFFFF880000000000).
//
// Lock: a single spinlock guards Push/Pop. O(1) for both operations.
// Bitmap allocator over 4 KB physical frames. One bitmap records which PFNs
// exist in usable RAM, and one bitmap records which usable PFNs are currently
// free. This keeps single-frame allocation simple while making contiguous
// allocation and memory visualization deterministic.
#include <kernel/mm_phys.h>
#include <kernel/ke_spinlock.h>
#include <string.h>
// ---------------------------------------------------------------------------
// Physical-to-virtual translation via the direct map window.
@@ -19,122 +18,198 @@ static inline void* KiPhysToVirt(PHYSICAL_ADDRESS phys) {
}
// ---------------------------------------------------------------------------
// Frame stack
// Frame bitmap
// ---------------------------------------------------------------------------
typedef struct {
PHYSICAL_ADDRESS* Stack; // pointer into physical window
size_t Top; // index of next free slot (stack grows up)
size_t Capacity;
size_t Total; // total frames ever pushed
SPINLOCK Lock;
} FRAME_STACK;
uint8_t* Usable; // bit = 1 when PFN belongs to usable RAM
uint8_t* Free; // bit = 1 when PFN is currently free
size_t BitmapBytes;
size_t MaxPfn;
size_t TotalFrames; // usable frames available to the allocator
size_t FreeFrames;
SPINLOCK Lock;
} FRAME_BITMAP;
static FRAME_STACK gFrameStack;
static FRAME_BITMAP gFrameBitmap;
// Push a physical frame address onto the stack.
static void KiPushFrame(PHYSICAL_ADDRESS frame) {
uintptr_t flags = KeAcquireSpinlock(&gFrameStack.Lock);
if (gFrameStack.Top < gFrameStack.Capacity) {
gFrameStack.Stack[gFrameStack.Top++] = frame;
gFrameStack.Total++;
static inline PHYSICAL_ADDRESS KiAlignUp(PHYSICAL_ADDRESS value) {
return (value + PAGE_SIZE - 1) & PAGE_MASK;
}
static inline PHYSICAL_ADDRESS KiAlignDown(PHYSICAL_ADDRESS value) {
return value & PAGE_MASK;
}
static inline int KiBitTest(const uint8_t* bits, size_t index) {
return (bits[index >> 3] & (uint8_t)(1u << (index & 7))) != 0;
}
static inline void KiBitSet(uint8_t* bits, size_t index) {
bits[index >> 3] |= (uint8_t)(1u << (index & 7));
}
static inline void KiBitClear(uint8_t* bits, size_t index) {
bits[index >> 3] &= (uint8_t)~(1u << (index & 7));
}
static inline int KiPfnUsableLocked(size_t pfn) {
return pfn < gFrameBitmap.MaxPfn && KiBitTest(gFrameBitmap.Usable, pfn);
}
static inline int KiPfnFreeLocked(size_t pfn) {
return KiPfnUsableLocked(pfn) && KiBitTest(gFrameBitmap.Free, pfn);
}
static void KiMarkUsableFreeRange(size_t firstPfn, size_t count) {
for (size_t i = 0; i < count; i++) {
size_t pfn = firstPfn + i;
if (pfn >= gFrameBitmap.MaxPfn) break;
if (!KiBitTest(gFrameBitmap.Usable, pfn)) {
KiBitSet(gFrameBitmap.Usable, pfn);
gFrameBitmap.TotalFrames++;
}
if (!KiBitTest(gFrameBitmap.Free, pfn)) {
KiBitSet(gFrameBitmap.Free, pfn);
gFrameBitmap.FreeFrames++;
}
}
}
static void KiRemoveUsablePfn(size_t pfn) {
if (pfn >= gFrameBitmap.MaxPfn) return;
if (KiBitTest(gFrameBitmap.Free, pfn)) {
KiBitClear(gFrameBitmap.Free, pfn);
if (gFrameBitmap.FreeFrames) gFrameBitmap.FreeFrames--;
}
if (KiBitTest(gFrameBitmap.Usable, pfn)) {
KiBitClear(gFrameBitmap.Usable, pfn);
if (gFrameBitmap.TotalFrames) gFrameBitmap.TotalFrames--;
}
KeReleaseSpinlock(&gFrameStack.Lock, flags);
}
// ---------------------------------------------------------------------------
// Initialisation
// ---------------------------------------------------------------------------
bool MmInitializePhysical(const MEMORY_MAP* map, uint64_t physicalWindowBase) {
if (!map) return false;
gPhysBase = physicalWindowBase;
// --- Phase 0: Count total usable frames so we can size the stack
// dynamically instead of capping at a hardcoded 1 GB. ---
uint64_t totalUsableBytes = 0;
for (uint32_t i = 0; i < map->RegionCount; i++) {
if (map->Regions[i].Type == MemoryUsable)
totalUsableBytes += map->Regions[i].Length;
}
const size_t maxFrames = (size_t)(totalUsableBytes / PAGE_SIZE);
const size_t stackBytes = maxFrames * sizeof(PHYSICAL_ADDRESS);
// --- Phase 1: Find a usable region large enough to hold the stack. ---
PHYSICAL_ADDRESS stackPhys = 0;
for (uint32_t i = 0; i < map->RegionCount; i++) {
const MEMORY_REGION* r = &map->Regions[i];
if (r->Type == MemoryUsable && r->Length >= stackBytes + PAGE_SIZE) {
stackPhys = r->Base;
break;
}
}
if (!stackPhys) {
return false;
}
gFrameStack.Stack = (PHYSICAL_ADDRESS *)KiPhysToVirt(stackPhys);
gFrameStack.Top = 0;
gFrameStack.Capacity = maxFrames;
gFrameStack.Total = 0;
gFrameStack.Lock = (SPINLOCK)SPINLOCK_INIT;
// --- Phase 2: Push all free 4 KB frames, skipping the stack pages. ---
const PHYSICAL_ADDRESS stackEnd =
stackPhys + ((stackBytes + PAGE_SIZE - 1) & PAGE_MASK);
PHYSICAL_ADDRESS maxEnd = 0;
for (uint32_t i = 0; i < map->RegionCount; i++) {
const MEMORY_REGION* r = &map->Regions[i];
if (r->Type != MemoryUsable) continue;
PHYSICAL_ADDRESS base = r->Base;
PHYSICAL_ADDRESS end = r->Base + r->Length;
PHYSICAL_ADDRESS base = KiAlignUp(r->Base);
PHYSICAL_ADDRESS end = r->Base + r->Length;
if (end < r->Base) end = (PHYSICAL_ADDRESS)-1;
end = KiAlignDown(end);
if (end <= base) continue;
if (end > maxEnd) maxEnd = end;
}
for (PHYSICAL_ADDRESS frame = base; frame < end; frame += PAGE_SIZE) {
if (frame >= stackPhys && frame < stackEnd) continue;
KiPushFrame(frame);
size_t maxPfn = (size_t)(maxEnd / PAGE_SIZE);
if (!maxPfn) return false;
size_t bitmapBytes = (maxPfn + 7) / 8;
size_t metadataBytes = bitmapBytes * 2;
size_t metadataPages = (metadataBytes + PAGE_SIZE - 1) / PAGE_SIZE;
PHYSICAL_ADDRESS metadataSize = (PHYSICAL_ADDRESS)metadataPages * PAGE_SIZE;
// Keep allocator metadata out of low memory. The bitmap is much smaller
// than the old free-frame stack, so a first-fit search can otherwise choose
// a conventional-memory hole below 1 MiB. That range is still useful for
// AP startup/legacy scratch, and stealing it makes early boot failures
// unnecessarily silent because PMM initializes before normal debug output.
const PHYSICAL_ADDRESS metadataMin = 0x100000ULL;
PHYSICAL_ADDRESS metadataPhys = 0;
int metadataFound = 0;
for (uint32_t i = 0; i < map->RegionCount; i++) {
const MEMORY_REGION* r = &map->Regions[i];
if (r->Type != MemoryUsable) continue;
PHYSICAL_ADDRESS base = KiAlignUp(r->Base);
if (base < metadataMin) base = metadataMin;
PHYSICAL_ADDRESS end = r->Base + r->Length;
if (end < r->Base) end = (PHYSICAL_ADDRESS)-1;
end = KiAlignDown(end);
if (end <= base) continue;
if (end - base >= metadataSize) {
metadataPhys = base;
metadataFound = 1;
break;
}
}
if (!metadataFound) return false;
gFrameBitmap.BitmapBytes = bitmapBytes;
gFrameBitmap.MaxPfn = maxPfn;
gFrameBitmap.TotalFrames = 0;
gFrameBitmap.FreeFrames = 0;
gFrameBitmap.Lock = (SPINLOCK)SPINLOCK_INIT;
gFrameBitmap.Usable = (uint8_t*)KiPhysToVirt(metadataPhys);
gFrameBitmap.Free = gFrameBitmap.Usable + bitmapBytes;
memset(gFrameBitmap.Usable, 0, metadataBytes);
// Mark page-aligned usable RAM as free. Unaligned tails are ignored rather
// than handing malformed frame addresses to the rest of the kernel.
for (uint32_t i = 0; i < map->RegionCount; i++) {
const MEMORY_REGION* r = &map->Regions[i];
if (r->Type != MemoryUsable) continue;
PHYSICAL_ADDRESS base = KiAlignUp(r->Base);
PHYSICAL_ADDRESS end = r->Base + r->Length;
if (end < r->Base) end = (PHYSICAL_ADDRESS)-1;
end = KiAlignDown(end);
if (end <= base) continue;
size_t firstPfn = (size_t)(base / PAGE_SIZE);
size_t count = (size_t)((end - base) / PAGE_SIZE);
KiMarkUsableFreeRange(firstPfn, count);
}
// PFN 0 is never a valid allocation result because 0 is the failure value.
KiRemoveUsablePfn(0);
// Hide the PMM metadata pages from the allocator entirely. They are not
// merely allocated; they are structural storage that must not become free if
// some caller accidentally passes their PFNs to MmFreeFrame().
size_t metadataFirstPfn = (size_t)(metadataPhys / PAGE_SIZE);
for (size_t i = 0; i < metadataPages; i++)
KiRemoveUsablePfn(metadataFirstPfn + i);
return true;
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
static PHYSICAL_ADDRESS KiPopFrameAtIndexLocked(size_t idx) {
PHYSICAL_ADDRESS frame = gFrameStack.Stack[idx];
size_t tail = gFrameStack.Top - idx - 1;
for (size_t j = 0; j < tail; j++)
gFrameStack.Stack[idx + j] = gFrameStack.Stack[idx + 1 + j];
gFrameStack.Top--;
return frame;
}
PHYSICAL_ADDRESS MmAllocateFrameInRange(PHYSICAL_ADDRESS minInclusive,
PHYSICAL_ADDRESS maxExclusive) {
minInclusive = (minInclusive + PAGE_SIZE - 1) & PAGE_MASK;
maxExclusive &= PAGE_MASK;
minInclusive = KiAlignUp(minInclusive);
maxExclusive = KiAlignDown(maxExclusive);
if (maxExclusive <= minInclusive) return (PHYSICAL_ADDRESS)0;
if (maxExclusive <= minInclusive)
return (PHYSICAL_ADDRESS)0;
size_t firstPfn = (size_t)(minInclusive / PAGE_SIZE);
size_t limitPfn = (size_t)(maxExclusive / PAGE_SIZE);
if (limitPfn > gFrameBitmap.MaxPfn) limitPfn = gFrameBitmap.MaxPfn;
if (firstPfn >= limitPfn) return (PHYSICAL_ADDRESS)0;
uintptr_t flags = KeAcquireSpinlock(&gFrameStack.Lock);
uintptr_t flags = KeAcquireSpinlock(&gFrameBitmap.Lock);
// Search high-to-low within the requested window. For low-memory users
// such as the AP trampoline this naturally prefers the traditional
// 0x70000-0x9f000 area and avoids consuming lower real-mode space first.
for (size_t i = gFrameStack.Top; i > 0; i--) {
size_t idx = i - 1;
PHYSICAL_ADDRESS frame = gFrameStack.Stack[idx];
if (frame >= minInclusive && frame < maxExclusive &&
!(frame & (PAGE_SIZE - 1))) {
frame = KiPopFrameAtIndexLocked(idx);
KeReleaseSpinlock(&gFrameStack.Lock, flags);
return frame;
for (size_t pfn = limitPfn; pfn > firstPfn; pfn--) {
size_t candidate = pfn - 1;
if (KiPfnFreeLocked(candidate)) {
KiBitClear(gFrameBitmap.Free, candidate);
if (gFrameBitmap.FreeFrames) gFrameBitmap.FreeFrames--;
KeReleaseSpinlock(&gFrameBitmap.Lock, flags);
return (PHYSICAL_ADDRESS)candidate * PAGE_SIZE;
}
}
KeReleaseSpinlock(&gFrameStack.Lock, flags);
KeReleaseSpinlock(&gFrameBitmap.Lock, flags);
return (PHYSICAL_ADDRESS)0;
}
@@ -143,74 +218,109 @@ PHYSICAL_ADDRESS MmAllocateFrameBelow(PHYSICAL_ADDRESS maxExclusive) {
}
__declspec(dllexport) PHYSICAL_ADDRESS MmAllocateFrame(void) {
uintptr_t flags = KeAcquireSpinlock(&gFrameStack.Lock);
if (gFrameStack.Top == 0) {
KeReleaseSpinlock(&gFrameStack.Lock, flags);
return (PHYSICAL_ADDRESS)0;
}
PHYSICAL_ADDRESS frame = gFrameStack.Stack[--gFrameStack.Top];
KeReleaseSpinlock(&gFrameStack.Lock, flags);
return frame;
return MmAllocateFrameInRange(PAGE_SIZE, (PHYSICAL_ADDRESS)gFrameBitmap.MaxPfn * PAGE_SIZE);
}
void MmFreeFrame(PHYSICAL_ADDRESS frame) {
if (!frame || (frame & (PAGE_SIZE - 1))) return;
uintptr_t flags = KeAcquireSpinlock(&gFrameStack.Lock);
if (gFrameStack.Top < gFrameStack.Capacity)
gFrameStack.Stack[gFrameStack.Top++] = frame;
// else: stack full - leak rather than corrupt
KeReleaseSpinlock(&gFrameStack.Lock, flags);
size_t pfn = (size_t)(frame / PAGE_SIZE);
uintptr_t flags = KeAcquireSpinlock(&gFrameBitmap.Lock);
if (KiPfnUsableLocked(pfn) && !KiBitTest(gFrameBitmap.Free, pfn)) {
KiBitSet(gFrameBitmap.Free, pfn);
gFrameBitmap.FreeFrames++;
}
KeReleaseSpinlock(&gFrameBitmap.Lock, flags);
}
__declspec(dllexport) PHYSICAL_ADDRESS MmAllocateContiguous(size_t count) {
if (!count) return (PHYSICAL_ADDRESS)0;
if (!count || count > gFrameBitmap.MaxPfn) return (PHYSICAL_ADDRESS)0;
uintptr_t flags = KeAcquireSpinlock(&gFrameStack.Lock);
uintptr_t flags = KeAcquireSpinlock(&gFrameBitmap.Lock);
if (gFrameStack.Top < count) {
KeReleaseSpinlock(&gFrameStack.Lock, flags);
return (PHYSICAL_ADDRESS)0;
}
// Frames are pushed in ascending address order by MmInitializePhysical,
// so physically consecutive pages sit at consecutive stack indices.
// Scan from bottom upward for a run of 'count' adjacent frames.
size_t run = (size_t)-1;
for (size_t i = 0; i + count <= gFrameStack.Top; i++) {
int ok = 1;
for (size_t j = 1; j < count; j++) {
if (gFrameStack.Stack[i + j] != gFrameStack.Stack[i] + j * PAGE_SIZE) {
ok = 0;
// Skip past this broken run; the next candidate can't start
// before the frame that broke the sequence.
i += j - 1; // outer loop adds 1 more
break;
size_t runStart = 0;
size_t runLength = 0;
for (size_t pfn = 1; pfn < gFrameBitmap.MaxPfn; pfn++) {
if (KiPfnFreeLocked(pfn)) {
if (!runLength) runStart = pfn;
runLength++;
if (runLength == count) {
for (size_t i = 0; i < count; i++)
KiBitClear(gFrameBitmap.Free, runStart + i);
if (gFrameBitmap.FreeFrames >= count) gFrameBitmap.FreeFrames -= count;
else gFrameBitmap.FreeFrames = 0;
KeReleaseSpinlock(&gFrameBitmap.Lock, flags);
return (PHYSICAL_ADDRESS)runStart * PAGE_SIZE;
}
} else {
runLength = 0;
}
if (ok) { run = i; break; }
}
if (run == (size_t)-1) {
KeReleaseSpinlock(&gFrameStack.Lock, flags);
return (PHYSICAL_ADDRESS)0;
}
PHYSICAL_ADDRESS base = gFrameStack.Stack[run];
// Splice out the 'count' entries by sliding the tail down.
size_t tail = gFrameStack.Top - run - count;
for (size_t i = 0; i < tail; i++)
gFrameStack.Stack[run + i] = gFrameStack.Stack[run + count + i];
gFrameStack.Top -= count;
KeReleaseSpinlock(&gFrameStack.Lock, flags);
return base;
KeReleaseSpinlock(&gFrameBitmap.Lock, flags);
return (PHYSICAL_ADDRESS)0;
}
size_t MmFreeFrameCount(void) {
return gFrameStack.Top;
uintptr_t flags = KeAcquireSpinlock(&gFrameBitmap.Lock);
size_t count = gFrameBitmap.FreeFrames;
KeReleaseSpinlock(&gFrameBitmap.Lock, flags);
return count;
}
size_t MmTotalFrameCount(void) {
return gFrameStack.Total;
return gFrameBitmap.TotalFrames;
}
void MmQueryPhysicalStats(MM_PHYSICAL_STATS* out) {
if (!out) return;
MM_PHYSICAL_STATS stats;
memset(&stats, 0, sizeof(stats));
uintptr_t flags = KeAcquireSpinlock(&gFrameBitmap.Lock);
stats.TotalFrames = (uint64_t)gFrameBitmap.TotalFrames;
stats.FreeFrames = (uint64_t)gFrameBitmap.FreeFrames;
stats.UsedFrames = stats.TotalFrames - stats.FreeFrames;
uint64_t run = 0;
for (size_t pfn = 1; pfn < gFrameBitmap.MaxPfn; pfn++) {
if (KiPfnFreeLocked(pfn)) {
run++;
continue;
}
if (run) {
stats.FreeRunCount++;
if (run > stats.LargestFreeRun)
stats.LargestFreeRun = run;
if (run == 1) stats.FreeRunHistogram[0]++;
else if (run <= 3) stats.FreeRunHistogram[1]++;
else if (run <= 7) stats.FreeRunHistogram[2]++;
else if (run <= 15) stats.FreeRunHistogram[3]++;
else if (run <= 63) stats.FreeRunHistogram[4]++;
else if (run <= 255) stats.FreeRunHistogram[5]++;
else if (run <= 1023) stats.FreeRunHistogram[6]++;
else stats.FreeRunHistogram[7]++;
run = 0;
}
}
if (run) {
stats.FreeRunCount++;
if (run > stats.LargestFreeRun)
stats.LargestFreeRun = run;
if (run == 1) stats.FreeRunHistogram[0]++;
else if (run <= 3) stats.FreeRunHistogram[1]++;
else if (run <= 7) stats.FreeRunHistogram[2]++;
else if (run <= 15) stats.FreeRunHistogram[3]++;
else if (run <= 63) stats.FreeRunHistogram[4]++;
else if (run <= 255) stats.FreeRunHistogram[5]++;
else if (run <= 1023) stats.FreeRunHistogram[6]++;
else stats.FreeRunHistogram[7]++;
}
KeReleaseSpinlock(&gFrameBitmap.Lock, flags);
*out = stats;
}