Compare commits

...
Author SHA1 Message Date
t3hn3rd f427b7bcc7 refactor(memory): complete PMM/VMM/LMM rewrite - bitmap allocators, bug fixes, hardening
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
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
2026-03-04 19:23:46 +00:00
Aaron 4ba3c5b524 Merge pull request 'feature/Queues' (#37) from feature/Queues into develop
continuous-integration/drone/push Build is passing
Reviewed-on: #37
2026-03-04 00:06:10 +00:00
t3hn3rd 7180ad39d8 Merge pull request 'feature: Pipeline - LVGL Build Caching' (#38) from feature/lvgl-build-caching into develop
continuous-integration/drone/push Build is passing
Reviewed-on: #38
Reviewed-by: Aaron Hance <[email protected]>
2026-03-04 00:05:01 +00:00
Aaron 6c7bcc1911 better testing
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2026-03-04 00:00:56 +00:00
Aaron e09c9b5595 refactor 2026-03-04 00:00:56 +00:00
Aaron 9476bb0c8a compile 2026-03-04 00:00:56 +00:00
Aaron 8f7db8507b Queues 2026-03-04 00:00:56 +00:00
17 changed files with 3494 additions and 319 deletions
+1 -1
View File
@@ -4,4 +4,4 @@ echo "======================="
echo " "
echo "Compiling FPC Sources..."
echo " "
fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas
fpc -Aelf -gw -g -gl -n -v0ew -O3 -OpPENTIUM3 -Si -Sc -Sg -Xd -CX -XXs -CfSSE -CfSSE2 -Rintel -Pi386 -Tlinux -FElib/ -Fusrc/* -Fusrc/include/* -Fusrc/driver/* -Fusrc/driver/net/* -Fusrc/driver/bus/* -Fusrc/driver/bus/usb/* -Fusrc/driver/hid/* src/kernel.pas
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+10 -19
View File
@@ -67,29 +67,13 @@ uses
hashmap, vfs,
video, vesa, doublebuffer, color, lvgl, desktop, uidebug,
vterminal,
graphicsrefresh, usbhotplug;
graphicsrefresh, usbhotplug,
fifo, cfifo, cfifols, lifo, circ, minh, maxh, prio;
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');
@@ -182,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();
@@ -271,6 +254,14 @@ begin
usbhub.UnitTest;
usb_keyboard.UnitTest;
usb_mouse.UnitTest;
fifo.UnitTest;
cfifo.UnitTest;
cfifols.UnitTest;
lifo.UnitTest;
circ.UnitTest;
minh.UnitTest;
maxh.UnitTest;
prio.UnitTest;
{ Register timer-driven tasks }
graphicsrefresh.init;
+520 -165
View File
File diff suppressed because it is too large Load Diff
+251 -111
View File
File diff suppressed because it is too large Load Diff
+83
View File
@@ -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
View File
@@ -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
View File
@@ -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;