// 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. #include #include // --------------------------------------------------------------------------- // Physical-to-virtual translation via the direct map window. // --------------------------------------------------------------------------- static uint64_t gPhysBase = 0; // set by MmInitializePhysical static inline void* PhysToVirt(PHYSICAL_ADDRESS phys) { return (void *)(phys + gPhysBase); } // --------------------------------------------------------------------------- // Frame stack // --------------------------------------------------------------------------- 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; static FRAME_STACK gFrameStack; // Push a physical frame address onto the stack. static void PushFrame(PHYSICAL_ADDRESS frame) { uintptr_t flags = KeAcquireSpinlock(&gFrameStack.Lock); if (gFrameStack.Top < gFrameStack.Capacity) { gFrameStack.Stack[gFrameStack.Top++] = frame; } KeReleaseSpinlock(&gFrameStack.Lock, flags); } // --------------------------------------------------------------------------- // Initialisation // --------------------------------------------------------------------------- bool MmInitializePhysical(const MEMORY_MAP* map, uint64_t physicalWindowBase) { 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 *)PhysToVirt(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); 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; for (PHYSICAL_ADDRESS frame = base; frame < end; frame += PAGE_SIZE) { if (frame >= stackPhys && frame < stackEnd) continue; if (gFrameStack.Top < gFrameStack.Capacity) { gFrameStack.Stack[gFrameStack.Top++] = frame; gFrameStack.Total++; } } } return true; } // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- 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; } 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); } __declspec(dllexport) PHYSICAL_ADDRESS MmAllocateContiguous(size_t count) { if (!count) return (PHYSICAL_ADDRESS)0; uintptr_t flags = KeAcquireSpinlock(&gFrameStack.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; } } 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; } size_t MmFreeFrameCount(void) { return gFrameStack.Top; } size_t MmTotalFrameCount(void) { return gFrameStack.Total; }