Compare commits

..
10 Commits
Author SHA1 Message Date
t3hn3rd 8b4335768b 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 10:36:09 +00:00
t3hn3rd 452203b7b4 Merge pull request 'bug: vtop incorrect bitmask' (#36) from feature/vtop-fix into develop
continuous-integration/drone/push Build is passing
Reviewed-on: #36
Reviewed-by: Aaron Hance <[email protected]>
2026-03-03 20:03:47 +00:00
t3hn3rd 76c419336e bug: vtop incorrect bitmask
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
- Fix for issue #32 in which the incorrect bitmask ($FFFFFF) is used where
  $3FFFFF should be used instead. (24 vs 22 bits).
2026-03-03 19:10:08 +00:00
admin 2ad0ae8420 Merge pull request 'feature/gitattributes' (#30) from feature/gitattributes into develop
continuous-integration/drone/push Build is passing
Reviewed-on: #30
2026-03-02 21:56:56 +00:00
t3hn3rd c0c199bfe6 .gitattributes update
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
- Updated .gitattributes linguist excludes for compile-time directories.
2026-03-02 21:48:10 +00:00
t3hn3rd 7ec3bcb9b9 .gitattributes & doc directory
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
- Added gitattributes for linguist exclusions
- Created empty doc folder for local working docs
2026-03-02 21:41:14 +00:00
admin 4bd0a2dfbf Merge pull request 'Merge feature/graphics-interrupts into develop' (#14) from feature/graphics-interrupts into develop
continuous-integration/drone/push Build is passing
Reviewed-on: #14
2026-03-02 19:19:46 +00:00
admin 431e5bc8f4 Merge pull request 'Merge feature/USB into develop' (#13) from feature/USB into develop
continuous-integration/drone/push Build is passing
Reviewed-on: #13
2026-03-02 19:14:05 +00:00
t3hn3rd 8169d93a9c refactor: move render loop & USB hotplug to timer-driven units
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Replace the blocking main render loop in kernel.pas with two new
timer-hooked units that are driven by the 1024Hz PIT interrupt:

- graphicsrefresh: calls desktop.update, uidebug.update, lvgl_handler
  and video.Flush every 4 ticks (~256 FPS target).
- usbhotplug: calls usbcore.usb_check_hotplug every 1024 ticks (~1Hz).

The kernel main procedure now registers both timer hooks and enters an
idle STI/HLT loop (kernel.yield), allowing the CPU to sleep between
interrupts instead of busy-spinning.

Also removes the unused myUserLandFunction and reorders early syslog
init calls for clarity.
2026-03-02 19:04:42 +00:00
admin 983d9a428d Merge pull request 'feature: Tracer O(1) ring buffer refactor' (#12) from feature/tracer-optimizations into develop
continuous-integration/drone/push Build is passing
Reviewed-on: #12
2026-03-01 22:29:53 +00:00
11 changed files with 999 additions and 335 deletions

No files matched your search

+7
View File
@@ -1 +1,8 @@
* text=auto eol=lf * text=auto eol=lf
lvglh/** linguist-vendored
lvgl/** linguist-vendored
.vscode/* linguist-vendored
lib/* linguist-generated
release/* linguist-generated
iso/* linguist-generated
*.asm linguist-detectable
+1
View File
@@ -15,3 +15,4 @@ localenv.json
dockerout.txt dockerout.txt
AGENTS.md AGENTS.md
*.log *.log
/doc/*.md
View File
Whitespace-only changes.
+52
View File
@@ -0,0 +1,52 @@
{
Driver->Timer->GraphicsRefresh - Timer-driven graphics refresh at ~120FPS.
Hooks into the 1024Hz timer (TMR_0_ISR) and calls the desktop, uidebug,
LVGL and video flush routines every ~9 ticks (1024/9 ≈ 113.8 FPS).
@author(Kieron Morris <[email protected]>)
}
unit graphicsrefresh;
interface
procedure init;
implementation
uses
util,
TMR_0_ISR,
desktop,
uidebug,
lvgl,
video,
syslog;
const
{ 1024 / 4 ≈ 256 FPS }
TICKS_PER_FRAME = 4;
var
TickCounter : uint32;
procedure on_tick(data : void);
begin
TickCounter := TickCounter + 1;
if TickCounter >= TICKS_PER_FRAME then begin
TickCounter := 0;
desktop.update;
uidebug.update;
lvgl_handler;
video.Flush;
end;
end;
procedure init;
begin
TickCounter := 0;
TMR_0_ISR.hook(uint32(@on_tick));
syslog.logln('GFXREFRESH', 'Hooked into 1024Hz timer (~120 FPS).');
end;
end.
+46
View File
@@ -0,0 +1,46 @@
{
Driver->Timer->USBHotplug - Timer-driven USB hotplug polling at ~1Hz.
Hooks into the 1024Hz timer (TMR_0_ISR) and calls
usbcore.usb_check_hotplug every 1024 ticks (approximately once per second).
@author(Kieron Morris <[email protected]>)
}
unit usbhotplug;
interface
procedure init;
implementation
uses
util,
TMR_0_ISR,
usbcore,
syslog;
const
{ 1024 ticks at 1024Hz ≈ 1 second }
TICKS_PER_POLL = 1024;
var
TickCounter : uint32;
procedure on_tick(data : void);
begin
TickCounter := TickCounter + 1;
if TickCounter >= TICKS_PER_POLL then begin
TickCounter := 0;
usbcore.usb_check_hotplug;
end;
end;
procedure init;
begin
TickCounter := 0;
TMR_0_ISR.hook(uint32(@on_tick));
syslog.logln('USBHOTPLUG', 'Hooked into 1024Hz timer (~1Hz polling).');
end;
end.
+15 -40
View File
@@ -66,29 +66,13 @@ uses
rand, rand,
hashmap, vfs, hashmap, vfs,
video, vesa, doublebuffer, color, lvgl, desktop, uidebug, video, vesa, doublebuffer, color, lvgl, desktop, uidebug,
vterminal; vterminal,
graphicsrefresh, usbhotplug;
procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: uint32); stdcall; procedure kmain(mbinfo: Pmultiboot_info_t; mbmagic: uint32); stdcall;
implementation 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); procedure terminal_command_bsod(params : PParamList; stdin_buf, stdout_buf, stderr_buf : POutBuf);
begin begin
push_trace('kernel.terminal_command_bsod'); push_trace('kernel.terminal_command_bsod');
@@ -103,17 +87,13 @@ begin
pop_trace; pop_trace;
end; end;
procedure myUserLandFunction; procedure yield();
var
i : uint32;
begin begin
i:=0;
while true do begin
i:=i+1;
asm asm
MOV EAX, i sti
end; @idle:
hlt
jmp @idle
end; end;
end; end;
@@ -132,6 +112,7 @@ begin
{ Syslog Init — right after serial so log hooks work } { Syslog Init — right after serial so log hooks work }
syslog.init(); syslog.init();
syslog.writestringln('Booting Asuro...');
{ Store Multiboot info } { Store Multiboot info }
multibootinfo:= mbinfo; multibootinfo:= mbinfo;
@@ -140,10 +121,8 @@ begin
{ Ensure tracer is frozen } { Ensure tracer is frozen }
tracer.freeze(); tracer.freeze();
syslog.writestringln('Booting Asuro...');
syslog.writestringln('Checking for Multiboot Compliance');
{ Check for Multiboot } { Check for Multiboot }
syslog.writestringln('Checking for Multiboot Compliance');
if (multibootmagic <> MULTIBOOT_BOOTLOADER_MAGIC) then begin if (multibootmagic <> MULTIBOOT_BOOTLOADER_MAGIC) then begin
syslog.logln('KERNEL', 'Multiboot Compliant Boot-Loader Needed!'); syslog.logln('KERNEL', 'Multiboot Compliant Boot-Loader Needed!');
syslog.logln('KERNEL', 'HALTING.'); syslog.logln('KERNEL', 'HALTING.');
@@ -186,7 +165,6 @@ begin
{ Stdio Init } { Stdio Init }
stdio.init(); stdio.init();
stdio.registerCommand('MEMINFO', @terminal_command_meminfo, 'Print Simple Memory Information.');
stdio.registerCommand('BSOD', @terminal_command_bsod, 'Force a Panic Screen.'); stdio.registerCommand('BSOD', @terminal_command_bsod, 'Force a Panic Screen.');
tss.init(); tss.init();
@@ -276,16 +254,13 @@ begin
usb_keyboard.UnitTest; usb_keyboard.UnitTest;
usb_mouse.UnitTest; usb_mouse.UnitTest;
{ Main render loop — USB completions are now interrupt-driven } { Register timer-driven tasks }
syslog.logln('KERNEL', 'Entering main render loop.'); graphicsrefresh.init;
while true do begin usbhotplug.init;
desktop.update;
uidebug.update;
lvgl_handler;
usbcore.usb_check_hotplug;
video.Flush();
end;
{ All work is now driven by timer interrupts — idle the CPU }
syslog.logln('KERNEL', 'All tasks registered. Halting into idle.');
kernel.yield();
end; end;
end. end.
+520 -165
View File
File diff suppressed because it is too large. Load diff
+246 -106
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 uses
tracer, tracer,
//progs //progs
base64_prog, md5sum, dhclient, vbeinfo; base64_prog, md5sum, dhclient, vbeinfo, meminfo;
{ Initialize all baked-in programs } { Initialize all baked-in programs }
procedure init(); procedure init();
@@ -40,6 +40,7 @@ begin
tracer.push_trace('progmanager.dhclient.init'); tracer.push_trace('progmanager.dhclient.init');
dhclient.init(); dhclient.init();
vbeinfo.init(); vbeinfo.init();
meminfo.init();
end; end;
end. end.
+27 -23
View File
@@ -159,20 +159,14 @@ end;
function map_page(page_number : uint16; block : uint16) : boolean; function map_page(page_number : uint16; block : uint16) : boolean;
var var
addr : ubit20;
page : uint16;
rldpd : uint32; rldpd : uint32;
begin begin
push_trace('vmemorymanager.map_page'); push_trace('vmemorymanager.map_page');
map_page:= false; { Delegate to map_page_ex — no redundant direct PDE writes }
PageDirectory^[page_number].Present:= true; map_page := map_page_ex(page_number, block, PageDirectory);
addr:= block; { Reload CR3 to flush TLB }
PageDirectory^[page_number].Address:= addr SHL 10; rldpd := uint32(PageDirectory) - KERNEL_VIRTUAL_BASE;
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;
asm asm
mov eax, rldpd mov eax, rldpd
mov CR3, eax mov CR3, eax
@@ -190,7 +184,7 @@ begin
push_trace('vmemorymanager.vtop'); push_trace('vmemorymanager.vtop');
idx:= address SHR 22; idx:= address SHR 22;
paddress:= uint32(KERNEL_PAGE_DIRECTORY^[idx].address) SHL 12; paddress:= uint32(KERNEL_PAGE_DIRECTORY^[idx].address) SHL 12;
loadd:= address AND $FFFFFF; loadd:= address AND $3FFFFF;
vtop:= paddress + loadd; vtop:= paddress + loadd;
pop_trace; pop_trace;
end; end;
@@ -201,14 +195,15 @@ var
begin begin
push_trace('vmemorymanager.new_page'); push_trace('vmemorymanager.new_page');
new_page:= false; new_page := false;
if not PageDirectory^[page_number].Present then begin if not PageDirectory^[page_number].Present then begin
if not PageDirectory^[page_number].Reserved then begin if not PageDirectory^[page_number].Reserved then begin
block:= pmemorymanager.new_block(uint32(PageDirectory)); block := pmemorymanager.new_block(uint32(PageDirectory));
if block < 2 then begin if block = 0 then begin
GPF; { OOM — PMM has no free blocks. Return false gracefully. }
syslog.logln('VMM', 'ERROR: new_page failed — PMM OOM.');
end else begin end else begin
new_page:= map_page(page_number, block); new_page := map_page(page_number, block);
end; end;
end; end;
end; end;
@@ -216,17 +211,15 @@ begin
end; end;
function page_mappable(page_number : uint16) : boolean; function page_mappable(page_number : uint16) : boolean;
var
block : uint16;
begin begin
push_trace('vmemorymanager.page_mappable'); push_trace('vmemorymanager.page_mappable');
page_mappable:= false; page_mappable := false;
if not PageDirectory^[page_number].Present then begin if not PageDirectory^[page_number].Present then begin
if not PageDirectory^[page_number].Reserved then begin if not PageDirectory^[page_number].Reserved then begin
page_mappable:= true; page_mappable := true;
end; end;
end; end;
pop_trace;
end; end;
function new_page_at_address(address : uint32) : boolean; function new_page_at_address(address : uint32) : boolean;
@@ -243,14 +236,25 @@ end;
procedure free_page(page_number : uint16); procedure free_page(page_number : uint16);
var var
block : uint16; block : uint16;
vaddr : uint32;
begin begin
push_trace('vmemorymanager.free_page'); push_trace('vmemorymanager.free_page');
if PageDirectory^[page_number].Present then begin 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 asm
invlpg [page_number] mov eax, vaddr
invlpg [eax]
end; end;
{ Return physical block to PMM }
pmemorymanager.free_block(block, uint32(PageDirectory)); pmemorymanager.free_block(block, uint32(PageDirectory));
end else begin end else begin
GPF; GPF;