refactor(memory): complete PMM/VMM/LMM rewrite - bitmap allocators, bug fixes, hardening
PMM Rewrite:
- Replace ~8KB record array with compact bitmaps
(PhysPresent/PhysAlloc/PhysScanned, 128B each)
- Add PhysOwner[0..1023] for per-block ownership tracking
- new_block: O(1) amortised via NextFreeHint + dword-level BSF scan,
returns 0 as OOM sentinel
- free_block: O(1) direct bit clear with double-free detection
- alloc_block: validates PhysPresent and PhysAlloc bits before allocation
- Add pmm_free_blocks/pmm_total_blocks stats functions
VMM Correctness Fixes:
- Fix free_page block extraction (Address SHR 10, was using
raw Address field)
- Fix invlpg operand (use virtual address, was passing PDE index)
- Clear PDE entry on free_page before returning block to PMM
- Remove map_page double-write (delegate to map_page_ex only)
- Add OOM propagation (new_page returns false if PMM returns 0)
- Fix missing pop_trace in new_page error path
LMM Rewrite:
- Replace per-entry record array with bitmap allocator
(93% vs 43% page efficiency)
- Layout: Header(24B) + Bitmap(64512B) + Padding + Data at 0x10000
- SIZE_PREFIX=16 for 16-byte aligned returns (SSE MOVAPS requirement)
- Next-fit bitmap scan with roving hint (NextFree) and dword-level
fast skip
- Large alloc sentinel (LARGE_ALLOC_MAGIC=$FFFFFFFE) so kfree detects
klalloc'd memory
- Implement klfree (was previously a no-op - pages could never
be returned)
- Implement try_release_page (return fully-free heap pages to VMM/PMM)
- Fix interrupt safety: stack-balanced pushfd/cli/pop +
restore_if (sti only)
Fixes Bad TSS caused by popfd restoring dangerous EFLAGS bits (NT, IOPL)
- Zero memory via rep stosd instead of byte-by-byte loop
Hardening:
- kalloc OOM now triggers BSOD instead of returning nil
(90% of callers don't check)
- Double-free detection in kfree (verify bitmap bits set before clearing)
- Guard bytes ({$IFDEF DEBUG_LMM}): $DEADBEEF sentinel for buffer overrun detection
- kpalloc: bounds check (block >= 1024 -> GPF) + syslog MMIO mapping audit log
Observability:
- Add MEMINFO command as src/prog/meminfo.pas (moved from inline kernel.pas)
- Displays multiboot memory, PMM block stats, LMM heap stats
- Register via progmanager.pas following standard prog pattern
Files changed:
- src/pmemorymanager.pas
- src/vmemorymanager.pas
- src/lmemorymanager.pas
- src/kernel.pas
- src/progmanager.pas
- src/prog/meminfo.pas
This commit is contained in:
@@ -74,23 +74,6 @@ procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: uint32); stdcall;
|
||||
|
||||
implementation
|
||||
|
||||
procedure terminal_command_meminfo(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
|
||||
begin
|
||||
push_trace('kernel.terminal_command_meminfo');
|
||||
|
||||
stdio.bufWriteStr(stdout_buf, 'Lower Memory = ');
|
||||
stdio.bufWriteInt(stdout_buf, multibootinfo^.mem_lower);
|
||||
stdio.bufWriteStrLn(stdout_buf, 'KB');
|
||||
stdio.bufWriteStr(stdout_buf, 'Higher Memory = ');
|
||||
stdio.bufWriteInt(stdout_buf, multibootinfo^.mem_upper);
|
||||
stdio.bufWriteStrLn(stdout_buf, 'KB');
|
||||
stdio.bufWriteStr(stdout_buf, 'Total Memory = ');
|
||||
stdio.bufWriteInt(stdout_buf, ((multibootinfo^.mem_upper + 1000) div 1024) + 1);
|
||||
stdio.bufWriteStrLn(stdout_buf, 'MB');
|
||||
|
||||
pop_trace;
|
||||
end;
|
||||
|
||||
procedure terminal_command_bsod(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
|
||||
begin
|
||||
push_trace('kernel.terminal_command_bsod');
|
||||
@@ -183,7 +166,6 @@ begin
|
||||
|
||||
{ Stdio Init }
|
||||
stdio.init();
|
||||
stdio.registerCommand('MEMINFO', @terminal_command_meminfo, 'Print Simple Memory Information.');
|
||||
stdio.registerCommand('BSOD', @terminal_command_bsod, 'Force a Panic Screen.');
|
||||
|
||||
tss.init();
|
||||
|
||||
+520
-165
File diff suppressed because it is too large
Load Diff
+251
-111
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
||||
// Copyright 2021 Kieron Morris
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
{
|
||||
Prog->meminfo - Print memory statistics (PMM / LMM).
|
||||
|
||||
@author(Kieron Morris <[email protected]>)
|
||||
}
|
||||
unit meminfo;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
stdio, multiboot, pmemorymanager, lmemorymanager, tracer;
|
||||
|
||||
procedure init();
|
||||
|
||||
implementation
|
||||
|
||||
procedure run(Params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
|
||||
var
|
||||
pmm_total, pmm_free, pmm_used : uint32;
|
||||
lmm_pages, lmm_free : uint32;
|
||||
begin
|
||||
{ Multiboot memory info }
|
||||
stdio.bufWriteStrLn(stdout_buf, '--- Multiboot ---');
|
||||
stdio.bufWriteStr(stdout_buf, 'Lower Memory : ');
|
||||
stdio.bufWriteInt(stdout_buf, multibootinfo^.mem_lower);
|
||||
stdio.bufWriteStrLn(stdout_buf, ' KB');
|
||||
stdio.bufWriteStr(stdout_buf, 'Upper Memory : ');
|
||||
stdio.bufWriteInt(stdout_buf, multibootinfo^.mem_upper);
|
||||
stdio.bufWriteStrLn(stdout_buf, ' KB');
|
||||
stdio.bufWriteStr(stdout_buf, 'Total Memory : ');
|
||||
stdio.bufWriteInt(stdout_buf, ((multibootinfo^.mem_upper + 1000) div 1024) + 1);
|
||||
stdio.bufWriteStrLn(stdout_buf, ' MB');
|
||||
|
||||
{ PMM stats }
|
||||
stdio.bufWriteStrLn(stdout_buf, '--- PMM (4 MB blocks) ---');
|
||||
pmm_total := pmemorymanager.pmm_total_blocks;
|
||||
pmm_free := pmemorymanager.pmm_free_blocks;
|
||||
pmm_used := pmm_total - pmm_free;
|
||||
stdio.bufWriteStr(stdout_buf, 'Total Blocks : ');
|
||||
stdio.bufWriteIntLn(stdout_buf, pmm_total);
|
||||
stdio.bufWriteStr(stdout_buf, 'Free Blocks : ');
|
||||
stdio.bufWriteIntLn(stdout_buf, pmm_free);
|
||||
stdio.bufWriteStr(stdout_buf, 'Used Blocks : ');
|
||||
stdio.bufWriteIntLn(stdout_buf, pmm_used);
|
||||
stdio.bufWriteStr(stdout_buf, 'Free Physical : ');
|
||||
stdio.bufWriteInt(stdout_buf, pmm_free * 4);
|
||||
stdio.bufWriteStrLn(stdout_buf, ' MB');
|
||||
|
||||
{ LMM heap stats }
|
||||
stdio.bufWriteStrLn(stdout_buf, '--- LMM (Heap) ---');
|
||||
lmm_pages := lmemorymanager.lmm_page_count;
|
||||
lmm_free := lmemorymanager.lmm_total_free;
|
||||
stdio.bufWriteStr(stdout_buf, 'Heap Pages : ');
|
||||
stdio.bufWriteIntLn(stdout_buf, lmm_pages);
|
||||
stdio.bufWriteStr(stdout_buf, 'Heap Size : ');
|
||||
stdio.bufWriteInt(stdout_buf, lmm_pages * 4);
|
||||
stdio.bufWriteStrLn(stdout_buf, ' MB');
|
||||
stdio.bufWriteStr(stdout_buf, 'Heap Free : ');
|
||||
stdio.bufWriteInt(stdout_buf, lmm_free div 1024);
|
||||
stdio.bufWriteStrLn(stdout_buf, ' KB');
|
||||
end;
|
||||
|
||||
procedure init();
|
||||
begin
|
||||
tracer.push_trace('meminfo.init');
|
||||
stdio.registerCommand('MEMINFO', @Run, 'Print memory statistics (PMM/LMM).');
|
||||
end;
|
||||
|
||||
end.
|
||||
+2
-1
@@ -24,7 +24,7 @@ interface
|
||||
uses
|
||||
tracer,
|
||||
//progs
|
||||
base64_prog, md5sum, dhclient, vbeinfo;
|
||||
base64_prog, md5sum, dhclient, vbeinfo, meminfo;
|
||||
|
||||
{ Initialize all baked-in programs }
|
||||
procedure init();
|
||||
@@ -40,6 +40,7 @@ begin
|
||||
tracer.push_trace('progmanager.dhclient.init');
|
||||
dhclient.init();
|
||||
vbeinfo.init();
|
||||
meminfo.init();
|
||||
end;
|
||||
|
||||
end.
|
||||
+26
-22
@@ -159,20 +159,14 @@ end;
|
||||
|
||||
function map_page(page_number : uint16; block : uint16) : boolean;
|
||||
var
|
||||
addr : ubit20;
|
||||
page : uint16;
|
||||
rldpd : uint32;
|
||||
|
||||
begin
|
||||
push_trace('vmemorymanager.map_page');
|
||||
map_page:= false;
|
||||
PageDirectory^[page_number].Present:= true;
|
||||
addr:= block;
|
||||
PageDirectory^[page_number].Address:= addr SHL 10;
|
||||
PageDirectory^[page_number].PageSize:= true;
|
||||
PageDirectory^[page_number].Writable:= true;
|
||||
map_page:= map_page_ex(page_number, block, PageDirectory);
|
||||
rldpd:= uint32(PageDirectory) - KERNEL_VIRTUAL_BASE;
|
||||
{ Delegate to map_page_ex — no redundant direct PDE writes }
|
||||
map_page := map_page_ex(page_number, block, PageDirectory);
|
||||
{ Reload CR3 to flush TLB }
|
||||
rldpd := uint32(PageDirectory) - KERNEL_VIRTUAL_BASE;
|
||||
asm
|
||||
mov eax, rldpd
|
||||
mov CR3, eax
|
||||
@@ -201,14 +195,15 @@ var
|
||||
|
||||
begin
|
||||
push_trace('vmemorymanager.new_page');
|
||||
new_page:= false;
|
||||
new_page := false;
|
||||
if not PageDirectory^[page_number].Present then begin
|
||||
if not PageDirectory^[page_number].Reserved then begin
|
||||
block:= pmemorymanager.new_block(uint32(PageDirectory));
|
||||
if block < 2 then begin
|
||||
GPF;
|
||||
block := pmemorymanager.new_block(uint32(PageDirectory));
|
||||
if block = 0 then begin
|
||||
{ OOM — PMM has no free blocks. Return false gracefully. }
|
||||
syslog.logln('VMM', 'ERROR: new_page failed — PMM OOM.');
|
||||
end else begin
|
||||
new_page:= map_page(page_number, block);
|
||||
new_page := map_page(page_number, block);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@@ -216,17 +211,15 @@ begin
|
||||
end;
|
||||
|
||||
function page_mappable(page_number : uint16) : boolean;
|
||||
var
|
||||
block : uint16;
|
||||
|
||||
begin
|
||||
push_trace('vmemorymanager.page_mappable');
|
||||
page_mappable:= false;
|
||||
page_mappable := false;
|
||||
if not PageDirectory^[page_number].Present then begin
|
||||
if not PageDirectory^[page_number].Reserved then begin
|
||||
page_mappable:= true;
|
||||
page_mappable := true;
|
||||
end;
|
||||
end;
|
||||
pop_trace;
|
||||
end;
|
||||
|
||||
function new_page_at_address(address : uint32) : boolean;
|
||||
@@ -243,14 +236,25 @@ end;
|
||||
procedure free_page(page_number : uint16);
|
||||
var
|
||||
block : uint16;
|
||||
vaddr : uint32;
|
||||
|
||||
begin
|
||||
push_trace('vmemorymanager.free_page');
|
||||
if PageDirectory^[page_number].Present then begin
|
||||
block:= PageDirectory^[page_number].Address;
|
||||
{ Recover block index: Address stores (block SHL 10), so SHR 10 gives block }
|
||||
block := PageDirectory^[page_number].Address SHR 10;
|
||||
{ Clear the PDE before invalidating }
|
||||
PageDirectory^[page_number].Present := false;
|
||||
PageDirectory^[page_number].Writable := false;
|
||||
PageDirectory^[page_number].PageSize := false;
|
||||
PageDirectory^[page_number].Address := 0;
|
||||
{ Invalidate TLB for this virtual address (invlpg needs a virtual addr) }
|
||||
vaddr := uint32(page_number) SHL 22;
|
||||
asm
|
||||
invlpg [page_number]
|
||||
mov eax, vaddr
|
||||
invlpg [eax]
|
||||
end;
|
||||
{ Return physical block to PMM }
|
||||
pmemorymanager.free_block(block, uint32(PageDirectory));
|
||||
end else begin
|
||||
GPF;
|
||||
|
||||
Reference in New Issue
Block a user