From a27d9cb39db3304b64f427c2976ba7c802ad9a31 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sun, 8 Nov 2026 09:02:33 +0000 Subject: [PATCH] Port shared core and platform services to native pointer widths --- doc/planning/memoryallocator.md | 4 +- doc/src/memory/memory.heap.md | 4 +- src/arch/common/arch.common.legacyirq.pas | 39 +- src/arch/common/platform.cpu.inc | 10 + ...interrupts.pas => platform.interrupts.inc} | 18 +- src/arch/common/platform.memory.inc | 5 +- src/arch/common/system.init.inc | 33 ++ src/arch/x86/arch.x86.console.pas | 3 + src/arch/x86/arch.x86.irq.pas | 2 +- src/arch/x86/arch.x86.multiboot.pas | 2 +- src/arch/x86/arch.x86.util.pas | 24 -- src/arch/x86/platform/arch.platform.cpu.pas | 79 +++- .../platform/arch.platform.diagnostics.pas | 29 +- .../x86/platform/arch.platform.interrupts.pas | 47 +++ .../x86/platform/arch.platform.memory.pas | 65 ++- .../x86/platform/arch.platform.process.pas | 12 +- src/arch/x86/proc/arch.x86.proc.context.pas | 7 +- src/arch/x86/proc/arch.x86.proc.sched.pas | 2 +- src/arch/x86/system.pas | 49 +-- src/arch/x86_64/arch.x86_64.context.pas | 75 +++- src/arch/x86_64/arch.x86_64.debug.pas | 2 + src/arch/x86_64/arch.x86_64.irq.pas | 140 +++++- src/arch/x86_64/arch.x86_64.isr.pas | 4 + src/arch/x86_64/arch.x86_64.util.pas | 9 + src/arch/x86_64/boot/stub.asm | 5 +- .../memory/arch.x86_64.memory.physical.pas | 93 +++- .../memory/arch.x86_64.memory.virtual.pas | 179 ++++++-- .../x86_64/platform/arch.platform.cpu.pas | 78 +++- .../platform/arch.platform.diagnostics.pas | 23 +- .../platform/arch.platform.interrupts.pas | 54 +++ .../x86_64/platform/arch.platform.memory.pas | 36 +- .../x86_64/platform/arch.platform.process.pas | 12 +- .../x86_64/proc/arch.x86_64.proc.sched.pas | 82 +++- src/arch/x86_64/system.pas | 20 + src/boot/boot.multiboot.pas | 3 + src/core/core.strings.helpers.pas | 272 ------------ src/core/core.util.pas | 23 +- src/core/ds/core.ds.cfifo.pas | 118 +++--- src/core/ds/core.ds.dynamic.pas | 81 +++- src/core/ds/core.ds.hashmap.pas | 217 +++++----- src/core/ds/core.ds.lists.pas | 398 ++++++------------ src/core/ds/core.ds.types.pas | 2 +- src/core/enc/core.enc.base64.pas | 6 +- src/core/enc/core.enc.fnv1a.pas | 44 +- src/core/enc/core.enc.md5.pas | 54 +-- src/core/strings/core.strings.helpers.pas | 24 +- src/core/strings/core.strings.pas | 161 +++++-- src/debug/debug.early.pas | 6 + src/debug/debug.tracer.pas | 10 +- src/driver/bus/driver.bus.pci.pas | 11 +- src/driver/driver.mgr.pas | 8 +- src/driver/io/driver.io.serial.pas | 35 +- src/driver/timer/driver.timer.rtc.pas | 69 +-- src/io/io.directory.pas | 31 -- src/io/io.stdio.pas | 105 +++-- src/io/io.syslog.pas | 5 - src/io/io.types.pas | 1 + src/memory/memory.bitmap.pas | 3 + src/memory/memory.heap.pas | 102 +++-- src/memory/memory.raw.pas | 34 +- src/proc/proc.mgr.pas | 186 ++++---- src/proc/proc.scheduler.pas | 14 +- 62 files changed, 1933 insertions(+), 1336 deletions(-) rename src/arch/common/{arch.common.interrupts.pas => platform.interrupts.inc} (66%) create mode 100644 src/arch/common/system.init.inc create mode 100644 src/arch/x86/platform/arch.platform.interrupts.pas create mode 100644 src/arch/x86_64/platform/arch.platform.interrupts.pas delete mode 100644 src/core/core.strings.helpers.pas delete mode 100644 src/io/io.directory.pas diff --git a/doc/planning/memoryallocator.md b/doc/planning/memoryallocator.md index 84cbd4dc..7463733f 100644 --- a/doc/planning/memoryallocator.md +++ b/doc/planning/memoryallocator.md @@ -205,7 +205,7 @@ Key constants: - `DATA_OFFSET = $10000` — data area starts at 64 KB into each page - `LARGE_ALLOC_MAGIC = $FFFFFFFE` — sentinel for klalloc'd multi-page allocations -Interrupt safety: `pushfd; cli; pop saved_flags` (stack-balanced) + `restore_if(saved_flags)` +Interrupt safety: `pushfd; cli; pop saved_flags` (stack-balanced) + `arch.platform.cpu.unlock(saved_flags)` inline procedure that only restores IF via `sti`, avoiding dangerous EFLAGS restoration (IOPL, NT, VM, AC bits that caused Bad-TSS on IRET). @@ -256,7 +256,7 @@ list, fixes Search_Page, calls `free_page_at_address`). Skips Root_Page. | Bug | Root Cause | Fix | |-----|-----------|-----| -| Bad TSS exception during network processing | `pushfd`/`popfd` across separate asm blocks: FPC doesn't track ESP changes in asm, causing EFLAGS corruption (NT=1, IOPL wrong) → Bad TSS on IRET | Stack-balanced `pushfd; cli; pop local_var` + `restore_if()` that only does `sti` if IF was set | +| Bad TSS exception during network processing | `pushfd`/`popfd` across separate asm blocks: FPC doesn't track ESP changes in asm, causing EFLAGS corruption (NT=1, IOPL wrong) → Bad TSS on IRET | Stack-balanced `pushfd; cli; pop local_var` + `arch.platform.cpu.unlock()` that only does `sti` if IF was set | | GPF from SSE MOVAPS in doublebuffer flush | `SIZE_PREFIX=4`→8 gave 8-byte alignment; `MOVAPS` needs 16-byte. `klalloc` returned `page+8` (not 16-aligned) | `SIZE_PREFIX=16` (4B size + 12B pad), klalloc header expanded to 16 bytes | | Pages never freed (historic) | `free_page` extracted wrong block index from PDE Address field | Fixed extraction to `Address SHR 10` — page freeing works for the first time | diff --git a/doc/src/memory/memory.heap.md b/doc/src/memory/memory.heap.md index 95cda1f6..0bf8c1ee 100644 --- a/doc/src/memory/memory.heap.md +++ b/doc/src/memory/memory.heap.md @@ -19,7 +19,7 @@ Every allocation is preceded by a 16-byte prefix: 4 bytes for the unit count, 12 Allocations larger than a single heap page's capacity are handled by `klalloc`, which maps contiguous virtual pages and stores a magic sentinel (`$FFFFFFFE`) in the header so `kfree` can identify them and call `klfree` instead of the normal bitmap path. -`kalloc` and `kfree` disable interrupts for the duration of their critical sections using a stack-balanced `pushfd`/`cli`/`pop` sequence and restore the interrupt flag via `restore_if` on all exit paths. +`kalloc` and `kfree` disable interrupts for the duration of their critical sections using a stack-balanced `pushfd`/`cli`/`pop` sequence and restore the interrupt flag via `arch.platform.cpu.unlock` on all exit paths. ## Dependencies @@ -132,7 +132,7 @@ Returns the number of heap pages currently in the linked list. ## Notes -The `restore_if` helper checks bit 9 (IF) of the saved EFLAGS word and issues `STI` only if interrupts were enabled on entry. It uses `STI` rather than `POPFD` to avoid restoring potentially dangerous EFLAGS bits (IOPL, NT, VM, AC) that could trigger a General Protection Fault or Bad-TSS on the next `IRET`. +The `arch.platform.cpu.unlock` helper checks bit 9 (IF) of the saved EFLAGS word and issues `STI` only if interrupts were enabled on entry. It uses `STI` rather than `POPFD` to avoid restoring potentially dangerous EFLAGS bits (IOPL, NT, VM, AC) that could trigger a General Protection Fault or Bad-TSS on the next `IRET`. When `DEBUG_LMM` is defined, `kalloc` allocates one additional unit and writes `GUARD_MAGIC` at the end of the allocation. `kfree` verifies this sentinel before freeing; a mismatch triggers a GPF with a syslog error, catching buffer overruns at free time. diff --git a/src/arch/common/arch.common.legacyirq.pas b/src/arch/common/arch.common.legacyirq.pas index 86fcfd5d..59a5942b 100644 --- a/src/arch/common/arch.common.legacyirq.pas +++ b/src/arch/common/arch.common.legacyirq.pas @@ -12,34 +12,61 @@ { Shared PC interrupt-controller programming. Call with interrupts disabled. } unit arch.common.legacyirq; + interface const PIT_INPUT_HZ = 1193182; PIT_DIVISOR = 149; procedure remap(masterMask, slaveMask: uint8); procedure masks(masterMask, slaveMask: uint8); +procedure enableLine(line: uint8); procedure timerStart; function realIRQ(line: uint8): boolean; procedure acknowledge(line: uint8); + implementation uses arch.platform.cpu; + procedure writeSlow(port: uint16; value: uint8); begin outb(port, value); outb($80, 0); end; + procedure masks(masterMask, slaveMask: uint8); begin - writeSlow($21, masterMask); writeSlow($A1, slaveMask); + writeSlow($21, masterMask); + writeSlow($A1, slaveMask); end; + procedure remap(masterMask, slaveMask: uint8); begin - writeSlow($20, $11); writeSlow($A0, $11); - writeSlow($21, $20); writeSlow($A1, $28); - writeSlow($21, $04); writeSlow($A1, $02); - writeSlow($21, $01); writeSlow($A1, $01); + writeSlow($20, $11); + writeSlow($A0, $11); + writeSlow($21, $20); + writeSlow($A1, $28); + writeSlow($21, $04); + writeSlow($A1, $02); + writeSlow($21, $01); + writeSlow($A1, $01); masks(masterMask, slaveMask); end; + +procedure enableLine(line: uint8); +var + flags: PtrUInt; +begin + if line >= 16 then exit; + flags := arch.platform.cpu.lock; + if line < 8 then + outb($21, inb($21) and not uint8(1 shl line)) + else begin + outb($A1, inb($A1) and not uint8(1 shl (line - 8))); + outb($21, inb($21) and not uint8(4)); + end; + arch.platform.cpu.unlock(flags); +end; + procedure timerStart; begin { Channel 0, low/high byte, mode 2, binary. } @@ -47,6 +74,7 @@ begin outb($40, PIT_DIVISOR and $FF); outb($40, PIT_DIVISOR shr 8); end; + function realIRQ(line: uint8): boolean; begin realIRQ := line < 16; @@ -60,6 +88,7 @@ begin if not realIRQ then outb($20, $20); end; end; + procedure acknowledge(line: uint8); begin if line >= 16 then exit; diff --git a/src/arch/common/platform.cpu.inc b/src/arch/common/platform.cpu.inc index e712aa7d..6c941146 100644 --- a/src/arch/common/platform.cpu.inc +++ b/src/arch/common/platform.cpu.inc @@ -15,6 +15,16 @@ function lock: PtrUInt; procedure unlock(flags: PtrUInt); procedure outb(port: uint16; value: uint8); function inb(port: uint16): uint8; +procedure outw(port: uint16; value: uint16); +function inw(port: uint16): uint16; procedure copyBytes(source, dest: PtrUInt; bytes: SizeUInt); procedure zero(dest: pointer; bytes: SizeUInt); procedure idle; +procedure outl(port: uint16; value: uint32); +function inl(port: uint16): uint32; + +type + TCPURegisters = record + eax, ebx, ecx, edx: uint32; + end; +procedure queryCPU(leaf, subleaf: uint32; var registers: TCPURegisters); diff --git a/src/arch/common/arch.common.interrupts.pas b/src/arch/common/platform.interrupts.inc similarity index 66% rename from src/arch/common/arch.common.interrupts.pas rename to src/arch/common/platform.interrupts.inc index 71b634e1..01cc2a40 100644 --- a/src/arch/common/arch.common.interrupts.pas +++ b/src/arch/common/platform.interrupts.inc @@ -10,14 +10,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -unit arch.common.interrupts; -interface -function lock: PtrUInt; -procedure unlock(flags: PtrUInt); -implementation -uses arch.platform.cpu; -function lock: PtrUInt; -begin lock := arch.platform.cpu.lock; end; -procedure unlock(flags: PtrUInt); -begin arch.platform.cpu.unlock(flags); end; -end. +type + TIRQHandler = procedure; + TTimerHandler = procedure(data: void); +procedure registerIRQ(vector: uint8; handler: TIRQHandler); +procedure registerTimer(handler: TTimerHandler); +procedure routePCI(line, vector: uint8); +function timerTicks: uint64; diff --git a/src/arch/common/platform.memory.inc b/src/arch/common/platform.memory.inc index 9523a52a..bc16d98d 100644 --- a/src/arch/common/platform.memory.inc +++ b/src/arch/common/platform.memory.inc @@ -15,4 +15,7 @@ function allocateHeapPage: PtrUInt; function allocateLarge(pages: uint32): PtrUInt; procedure release(address: PtrUInt; pages: uint32); function identityReserve(address: PtrUInt): pointer; -function freeBacking: uint64; +function freePhysicalBytes: uint64; +function totalPhysicalBytes: uint64; +function physicalAddress(address: PtrUInt): uint64; +function mapDevice(address: uint64; bytes: SizeUInt): pointer; diff --git a/src/arch/common/system.init.inc b/src/arch/common/system.init.inc new file mode 100644 index 00000000..e0a1ed0a --- /dev/null +++ b/src/arch/common/system.init.inc @@ -0,0 +1,33 @@ +// Copyright 2021 Kieron Morris & Aaron Hance +// Copyright 2026 Aaron Hance +// +// 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. + +{ Shared dispatch for the table emitted by compile_initcalls.sh. + Initializers only register boot work or set globals; they cannot depend on + heap, hardware setup, or the alphabetical order of this generated table. } +var + unitsInitialized: boolean; + +procedure initializeUnits; +var + i: longint; + entry: CodePointer; +begin + if unitsInitialized then exit; + unitsInitialized := true; + + for i := 1 to InitFinalTable.TableCount do begin + entry := InitFinalTable.Procs[i].InitProc; + if entry <> nil then + TProcedure(entry)(); + end; +end; diff --git a/src/arch/x86/arch.x86.console.pas b/src/arch/x86/arch.x86.console.pas index bb8b1658..42c3d030 100644 --- a/src/arch/x86/arch.x86.console.pas +++ b/src/arch/x86/arch.x86.console.pas @@ -12,10 +12,13 @@ { Commands extracted from io.stdio (Kieron Morris). } unit arch.x86.console; + interface procedure init; + implementation uses io.stdio, core.version, driver.timer.rtc, arch.x86.util; + procedure cmd_version(params: PParamList; stdin_buf, stdout_buf, stderr_buf: POutBuf); begin bufWriteStr(stdout_buf, ' Asuro Version: '); diff --git a/src/arch/x86/arch.x86.irq.pas b/src/arch/x86/arch.x86.irq.pas index dc2dcab4..abe8342f 100644 --- a/src/arch/x86/arch.x86.irq.pas +++ b/src/arch/x86/arch.x86.irq.pas @@ -40,4 +40,4 @@ end; initialization boot.mgr.registerBoot('arch.x86.irq', @init, 'Interrupt Request Line', 'arch.x86.idt'); -end. \ No newline at end of file +end. diff --git a/src/arch/x86/arch.x86.multiboot.pas b/src/arch/x86/arch.x86.multiboot.pas index e5fe9e48..bcdd53de 100644 --- a/src/arch/x86/arch.x86.multiboot.pas +++ b/src/arch/x86/arch.x86.multiboot.pas @@ -38,7 +38,7 @@ type Pmemory_map_t = boot.multiboot.Pmemory_map_t; var - multibootinfo : Pmultiboot_info_t = nil; + multibootinfo : Pmultiboot_info_t absolute boot.multiboot.multibootinfo; multibootmagic : uint32; implementation diff --git a/src/arch/x86/arch.x86.util.pas b/src/arch/x86/arch.x86.util.pas index e42fa8bc..b7d7b389 100644 --- a/src/arch/x86/arch.x86.util.pas +++ b/src/arch/x86/arch.x86.util.pas @@ -62,7 +62,6 @@ procedure resetSystem(); function getESP : uint32; -function RolDWord(AValue : uint32; Dist : uint8) : uint32; function RorDWord(AValue : uint32; Dist : uint8) : uint32; function MsSinceSystemBoot : uint64; @@ -380,29 +379,6 @@ begin getTSC:= (hi SHL 32) OR lo; end; -function RolDWord(AValue : uint32; Dist : uint8) : uint32; -var - result : uint32; - i : uint8; - -begin - result:= AValue; - asm - PUSH EAX - end; - for i:=0 to Dist-1 do begin - asm - MOV EAX, result - ROL EAX, 1 - MOV result, EAX - end; - end; - asm - POP EAX - end; - RolDWord:= result; -end; - function RorDWord(AValue : uint32; Dist : uint8) : uint32; var result : uint32; diff --git a/src/arch/x86/platform/arch.platform.cpu.pas b/src/arch/x86/platform/arch.platform.cpu.pas index eef76238..0167cff3 100644 --- a/src/arch/x86/platform/arch.platform.cpu.pas +++ b/src/arch/x86/platform/arch.platform.cpu.pas @@ -12,29 +12,67 @@ { Build-selected x86 adapter. } unit arch.platform.cpu; + interface {$i ../../common/platform.cpu.inc} + implementation uses arch.x86.util, memory.raw; + function lock: PtrUInt; assembler; nostackframe; -asm pushfd; cli; pop eax end; +asm + pushfd; + cli; + pop eax +end; + procedure unlock(flags: PtrUInt); -begin if (flags and $200) <> 0 then asm sti end; end; +begin + if (flags and $200) <> 0 then asm + sti + end; +end; + procedure idle; -begin asm hlt end; end; +begin + asm + hlt + end; +end; + procedure outb(port: uint16; value: uint8); -begin arch.x86.util.outb(port, value); end; +begin + arch.x86.util.outb(port, value); +end; + function inb(port: uint16): uint8; -begin inb := arch.x86.util.inb(port); end; +begin + inb := arch.x86.util.inb(port); +end; + procedure copyBytes(source, dest: PtrUInt; bytes: SizeUInt); begin if bytes >= 16 then arch.x86.util.__REP_MOVSB_memcpy(source, dest, bytes) else memory.raw.move(pointer(source), pointer(dest), bytes); end; -procedure zero(dest: pointer; bytes: SizeUInt); -var d: pointer; b: SizeUInt; + +procedure outw(port: uint16; value: uint16); begin - d := dest; b := bytes; + arch.x86.util.outw(port, value); +end; + +function inw(port: uint16): uint16; +begin + inw := arch.x86.util.inw(port); +end; + +procedure zero(dest: pointer; bytes: SizeUInt); +var + d: pointer; + b: SizeUInt; +begin + d := dest; + b := bytes; if b = 0 then exit; asm push edi @@ -50,4 +88,29 @@ begin pop edi end; end; +procedure outl(port: uint16; value: uint32); +begin + arch.x86.util.outl(port, value); +end; + +function inl(port: uint16): uint32; +begin + inl := arch.x86.util.inl(port); +end; + +procedure queryCPU(leaf, subleaf: uint32; var registers: TCPURegisters); assembler; nostackframe; +asm + push ebx + push edi + mov edi, ecx + mov ecx, edx + cpuid + mov [edi], eax + mov [edi + 4], ebx + mov [edi + 8], ecx + mov [edi + 12], edx + pop edi + pop ebx +end; + end. diff --git a/src/arch/x86/platform/arch.platform.diagnostics.pas b/src/arch/x86/platform/arch.platform.diagnostics.pas index 17f664ff..0d15ea8f 100644 --- a/src/arch/x86/platform/arch.platform.diagnostics.pas +++ b/src/arch/x86/platform/arch.platform.diagnostics.pas @@ -12,22 +12,39 @@ { Build-selected x86 adapter. } unit arch.platform.diagnostics; + interface {$i ../../common/platform.diagnostics.inc} + implementation uses io.syslog, debug.tracer, core.panic; + procedure log(tag, message: pchar); -begin io.syslog.logln(tag, message); end; +begin + io.syslog.logln(tag, message); +end; + procedure push_trace(name: pchar); -begin debug.tracer.push_trace(name); end; +begin + debug.tracer.push_trace(name); +end; + procedure pop_trace; -begin debug.tracer.pop_trace; end; +begin + debug.tracer.pop_trace; +end; + procedure fatal(tag, message: pchar); -begin core.panic.panic(tag, message, nil); end; +begin + core.panic.panic(tag, message, nil); +end; + procedure testSummary(tag, passedKey, failedKey: pchar; passed, failed: uint32); begin io.syslog.log(tag, ''); - io.syslog.writeint(passed); io.syslog.writestring(' passed, '); - io.syslog.writeint(failed); io.syslog.writestringln(' failed.'); + io.syslog.writeint(passed); + io.syslog.writestring(' passed, '); + io.syslog.writeint(failed); + io.syslog.writestringln(' failed.'); end; end. diff --git a/src/arch/x86/platform/arch.platform.interrupts.pas b/src/arch/x86/platform/arch.platform.interrupts.pas new file mode 100644 index 00000000..47397e20 --- /dev/null +++ b/src/arch/x86/platform/arch.platform.interrupts.pas @@ -0,0 +1,47 @@ +// Copyright 2026 Aaron Hance +// +// 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. + +unit arch.platform.interrupts; + +interface +{$i ../../common/platform.interrupts.inc} + +implementation +uses arch.x86.isr.mgr, arch.x86.isr.tmr0, arch.x86.isr.ioapic, + arch.x86.bda, arch.platform.cpu; + +function timerTicks: uint64; +var + flags: PtrUInt; +begin + flags := arch.platform.cpu.lock; + timerTicks := Counters.c64; + arch.platform.cpu.unlock(flags); +end; + +procedure registerIRQ(vector: uint8; handler: TIRQHandler); +begin + arch.x86.isr.mgr.registerISR(vector, handler); +end; + +procedure registerTimer(handler: TTimerHandler); +begin + arch.x86.isr.tmr0.hook(PtrUInt(handler)); +end; + +procedure routePCI(line, vector: uint8); +begin + arch.x86.isr.ioapic.init; + arch.x86.isr.ioapic.route_pci_irq(line, vector); +end; + +end. diff --git a/src/arch/x86/platform/arch.platform.memory.pas b/src/arch/x86/platform/arch.platform.memory.pas index 74b6812b..302aa876 100644 --- a/src/arch/x86/platform/arch.platform.memory.pas +++ b/src/arch/x86/platform/arch.platform.memory.pas @@ -12,25 +12,33 @@ { Mapping policy extracted from memory.heap (Kieron Morris). } unit arch.platform.memory; + interface const MAX_HEAP_REGIONS = 256; {$i ../../common/platform.memory.inc} + implementation uses arch.x86.memory.virtual, arch.x86.memory.physical, arch.platform.diagnostics; + function allocateHeapPage: PtrUInt; -var i: uint32; +var + i: uint32; begin allocateHeapPage := 0; i := KERNEL_PAGE_NUMBER + 4; while i < 1024 do begin if arch.x86.memory.virtual.new_page(i) then begin - allocateHeapPage := i shl 22; exit; + allocateHeapPage := i shl 22; + exit; end; inc(i); end; end; + function allocateLarge(pages: uint32): PtrUInt; -var curr_page, i: uint16; miss: boolean; +var + curr_page, i: uint16; + miss: boolean; begin allocateLarge := 0; if (pages = 0) or (pages > MAX_HEAP_REGIONS) then exit; @@ -39,7 +47,9 @@ begin miss := false; for i := 0 to pages - 1 do begin if not page_mappable(curr_page + i) then begin - miss := true; curr_page := curr_page + i; break; + miss := true; + curr_page := curr_page + i; + break; end; end; if not miss then begin @@ -47,7 +57,8 @@ begin while i < pages do begin if not arch.x86.memory.virtual.new_page(curr_page + i) then begin while i > 0 do begin - dec(i); arch.x86.memory.virtual.free_page(curr_page + i); + dec(i); + arch.x86.memory.virtual.free_page(curr_page + i); end; exit; end; @@ -59,23 +70,57 @@ begin inc(curr_page); end; end; + procedure release(address: PtrUInt; pages: uint32); -var i: uint32; page_num: uint16; +var + i: uint32; + page_num: uint16; begin if (pages = 0) or (pages > MAX_HEAP_REGIONS) then begin - fatal('LMM', 'Invalid heap region release'); exit; + fatal('LMM', 'Invalid heap region release'); + exit; end; page_num := address shr 22; for i := 0 to pages - 1 do arch.x86.memory.virtual.free_page(page_num + uint16(i)); end; + function identityReserve(address: PtrUInt): pointer; -var block: uint16; +var + block: uint16; begin block := address shr 22; force_alloc_block(block, 0); map_page(block, block); identityReserve := pointer(uint32(block) shl 22); end; -function freeBacking: uint64; -begin freeBacking := pmm_free_blocks; end; + +function freePhysicalBytes: uint64; +begin + freePhysicalBytes := uint64(pmm_free_blocks) * $400000; +end; + +function totalPhysicalBytes: uint64; +begin + totalPhysicalBytes := uint64(pmm_total_blocks) * $400000; +end; +function physicalAddress(address: PtrUInt): uint64; +begin + physicalAddress := arch.x86.memory.virtual.vtop(address); +end; + +function mapDevice(address: uint64; bytes: SizeUInt): pointer; +var + page, last: uint32; +begin + mapDevice := nil; + if (bytes = 0) or (address > $FFFFFFFF) or (bytes - 1 > $FFFFFFFF - address) then exit; + page := uint32(address) shr 22; + last := uint32(address + bytes - 1) shr 22; + while page <= last do begin + identityReserve(page shl 22); + inc(page); + end; + mapDevice := pointer(PtrUInt(address)); +end; + end. diff --git a/src/arch/x86/platform/arch.platform.process.pas b/src/arch/x86/platform/arch.platform.process.pas index 1bf9910a..75ae5f20 100644 --- a/src/arch/x86/platform/arch.platform.process.pas +++ b/src/arch/x86/platform/arch.platform.process.pas @@ -12,12 +12,20 @@ { Build-selected x86 adapter. } unit arch.platform.process; + interface {$i ../../common/platform.process.inc} + implementation uses arch.x86.proc.context, arch.x86.bda; + function prepare(stack: pointer; bytes: SizeUInt; entry: pointer): PtrUInt; -begin prepare := arch.x86.proc.context.prepare(stack, bytes, entry); end; +begin + prepare := arch.x86.proc.context.prepare(stack, bytes, entry); +end; + function timerTicks: uint32; -begin timerTicks := PUInt32(@Counters.c32)^; end; +begin + timerTicks := PUInt32(@Counters.c32)^; +end; end. diff --git a/src/arch/x86/proc/arch.x86.proc.context.pas b/src/arch/x86/proc/arch.x86.proc.context.pas index a2f4429a..40bcb326 100644 --- a/src/arch/x86/proc/arch.x86.proc.context.pas +++ b/src/arch/x86/proc/arch.x86.proc.context.pas @@ -12,12 +12,17 @@ { i386 initial frame, extracted from proc.mgr (Kieron Morris). } unit arch.x86.proc.context; + interface function prepare(stack: pointer; bytes: SizeUInt; entry: pointer): PtrUInt; + implementation uses memory.raw; + function prepare(stack: pointer; bytes: SizeUInt; entry: pointer): PtrUInt; -var top, frame: PtrUInt; i: uint32; +var + top, frame: PtrUInt; + i: uint32; begin prepare := 0; if (stack = nil) or (entry = nil) or (bytes < 64) or diff --git a/src/arch/x86/proc/arch.x86.proc.sched.pas b/src/arch/x86/proc/arch.x86.proc.sched.pas index c356e0dc..c1e5fd9f 100644 --- a/src/arch/x86/proc/arch.x86.proc.sched.pas +++ b/src/arch/x86/proc/arch.x86.proc.sched.pas @@ -130,4 +130,4 @@ begin debug.tracer.push_trace('arch.x86.proc.sched.init'); end; -end. \ No newline at end of file +end. diff --git a/src/arch/x86/system.pas b/src/arch/x86/system.pas index 8695c89c..ea974ee3 100644 --- a/src/arch/x86/system.pas +++ b/src/arch/x86/system.pas @@ -119,7 +119,6 @@ function Round(d: Double): int64; implementation { ---- Raw driver.io.serial debug helpers (COM1 $3F8, no dependencies) ---- } - procedure serial_putch(ch: char); assembler; asm push edx @@ -154,7 +153,8 @@ begin end; procedure serial_putstr(s: pchar); -var i: uint32; +var + i: uint32; begin i := 0; while s[i] <> #0 do begin @@ -196,20 +196,11 @@ begin end; end; +{$i ../common/system.init.inc} + procedure fpc_initializeunits; [public, alias: 'FPC_INITIALIZEUNITS']; compilerproc; -var - i : longint; - proc : CodePointer; begin - { Walk the compiler-generated INITFINAL table and call each unit's - initialization procedure (if non-nil). This runs before kmain, so - initialization sections must only do trivial work — write to static - globals, register boot entries, etc. No I/O or heap allocation. } - for i := 1 to InitFinalTable.TableCount do begin - proc := InitFinalTable.Procs[i].InitProc; - if proc <> nil then - TProcedure(proc)(); - end; + initializeUnits; end; procedure fpc_do_exit; [public, alias: 'FPC_DO_EXIT']; compilerproc; @@ -348,7 +339,8 @@ begin end; function fpc_mul_dword_to_qword(f1, f2: uint32): qword; [public, alias: 'FPC_MUL_DWORD_TO_QWORD']; compilerproc; -var res: qword; +var + res: qword; begin asm MOV EAX, f1 @@ -368,8 +360,10 @@ procedure udiv64(dividendLo, dividendHi, divisorLo, divisorHi: uint32; var bit: longint; begin - quotLo := 0; quotHi := 0; - remLo := 0; remHi := 0; + quotLo := 0; + quotHi := 0; + remLo := 0; + remHi := 0; for bit := 63 downto 0 do begin { rem := rem shl 1 } @@ -567,7 +561,6 @@ begin end; { ---------- Float-to-integer intrinsics ---------- } - function Trunc(d: Double): int64; [public, alias: 'FPC_TRUNC']; var oldcw, newcw: word; @@ -599,8 +592,8 @@ begin end; { ---------- Memory allocation compilerprocs ---------- } - function kernel_kalloc(size: uint32): Pointer; external name 'kernel_kalloc'; + procedure kernel_kfree(p: Pointer); external name 'kernel_kfree'; function fpc_getmem(size: PtrUInt): Pointer; @@ -616,7 +609,6 @@ begin end; { ---------- Exception handling compilerprocs ---------- } - procedure fpc_reraise; [public, alias: 'FPC_RERAISE']; compilerproc; begin { In a bare-metal core.version re-raise is a fatal condition } @@ -641,30 +633,17 @@ begin end; procedure init(); -var - i : longint; - proc : CodePointer; begin ASURO_KERNEL_START := uint32(@AK_START); ASURO_KERNEL_END := uint32(@AK_END); - ASURO_KERNEL_SIZE:= ASURO_KERNEL_END - ASURO_KERNEL_START; + ASURO_KERNEL_SIZE := ASURO_KERNEL_END - ASURO_KERNEL_START; ExceptAddrStack := nil; ErrorAddr := nil; ErrorCode := 0; ExitCode := 0; ExitProc := nil; - { Walk the INITFINAL table generated by compile_initcalls.sh and call - each unit's initialization section. FPC with -n does not insert this - call automatically, so we do it explicitly here — after basic system - globals are set up but before kmain proceeds. - Note: we inline the walk rather than calling fpc_initializeunits - because compilerproc procedures cannot be called from Pascal code. } - for i := 1 to InitFinalTable.TableCount do begin - proc := InitFinalTable.Procs[i].InitProc; - if proc <> nil then - TProcedure(proc)(); - end; + initializeUnits; end; end. diff --git a/src/arch/x86_64/arch.x86_64.context.pas b/src/arch/x86_64/arch.x86_64.context.pas index 309d0986..8b293c90 100644 --- a/src/arch/x86_64/arch.x86_64.context.pas +++ b/src/arch/x86_64/arch.x86_64.context.pas @@ -14,6 +14,7 @@ A saved context points at an aligned 512-byte FX image followed by the normalized interrupt-frame pointer. Only kernel contexts are supported. } unit arch.x86_64.context; + interface type PNative = ^PtrUInt; @@ -31,6 +32,7 @@ type procedure setSelector(selector: TSelectContext); function prepare(stack: pointer; bytes: SizeUInt; entry: pointer; argument: PtrUInt): PtrUInt; procedure UnitTest; + implementation uses proc.types, proc.scheduler, arch.x86_64.util, arch.x86_64.irq, arch.common.legacyirq, memory.heap, debug.early; var @@ -41,21 +43,26 @@ var testDone: array[0..1] of uint32; testFailures: uint32; testSwitches: uint32; + procedure setSelector(selector: TSelectContext); begin { Caller owns IRQ exclusion and context lifetimes. } choose := selector; end; + function selectContext(saved: PtrUInt): PtrUInt; cdecl; [public, alias: 'x64_select_context']; -var frame: PFrame; +var + frame: PFrame; begin selectContext := saved; frame := PFrame(PNative(saved + 512)^); if (frame^.vector = 32) and assigned(choose) then selectContext := choose(saved); end; + function prepare(stack: pointer; bytes: SizeUInt; entry: pointer; argument: PtrUInt): PtrUInt; -var top, frameAddress, fx: PtrUInt; +var + top, frameAddress, fx: PtrUInt; frame: PFrame; begin prepare := 0; @@ -71,17 +78,21 @@ begin frame := PFrame(frameAddress); frame^.rdi := argument; frame^.rip := PtrUInt(entry); - frame^.cs := $18; frame^.ss := $10; - frame^.flags := $202; frame^.rsp := top; + frame^.cs := $18; + frame^.ss := $10; + frame^.flags := $202; + frame^.rsp := top; PUInt16(fx)^ := $037F; { initial x87 control word; empty tag word } PUInt32(fx + 24)^ := $1F80; { initial MXCSR } PNative(fx + 512)^ := frameAddress; prepare := fx; end; + function testGetProcess(index: uint32): PProcessContext; begin if index < 3 then testGetProcess := testProcesses[index] else testGetProcess := nil; end; + function testSelect(saved: PtrUInt): PtrUInt; begin testCurrent^.SavedStack := saved; @@ -90,9 +101,12 @@ begin inc(testSwitches); testSelect := testCurrent^.SavedStack; end; + function testRegisters(counter: PUInt32; pattern: uint64): uint32; cdecl; external name 'x64_test_context_registers'; + procedure worker(index: PtrUInt); cdecl; -var value: PtrUInt; +var + value: PtrUInt; allocation: void; begin value := PtrUInt($1234567800000000) + index; @@ -102,21 +116,30 @@ begin if (PUInt64(allocation)^ <> value) or (value <> PtrUInt($1234567800000000) + index) then inc(testFailures); memory.heap.kfree(allocation); - asm cli end; + asm + cli + end; testProcesses[index + 1]^.State := psFinished; PUInt32(@testDone[index])^ := 1; - asm sti end; + asm + sti + end; { The next timer moves away permanently; stack is freed by bootstrap. } - while true do asm pause end; + while true do asm + pause + end; end; + procedure UnitTest; -var stackA, stackB: void; +var + stackA, stackB: void; beforeFree: SizeUInt; attempts, i: uint32; saved: PtrUInt; begin beforeFree := lmm_total_free; - stackA := kalloc(16384); stackB := kalloc(16384); + stackA := kalloc(16384); + stackB := kalloc(16384); for i := 0 to 2 do begin testProcesses[i] := PProcessContext(kalloc(sizeof(TProcessContext))); testProcesses[i]^.ProcessID := i; @@ -126,30 +149,46 @@ begin testProcesses[1]^.SavedStack := prepare(pointer(stackA), 16384, @worker, 0); testProcesses[2]^.SavedStack := prepare(pointer(stackB), 16384, @worker, 1); if (testProcesses[1]^.SavedStack = 0) or (testProcesses[2]^.SavedStack = 0) then begin - puts('FAIL: prepare kernel contexts'#13#10); bootstrap_halt; + puts('FAIL: prepare kernel contexts'#13#10); + bootstrap_halt; end; - testCurrent := testProcesses[0]; testCurrent^.State := psRunning; testCursor := 0; testDone[0] := 0; testDone[1] := 0; - testFailures := 0; testSwitches := 0; + testCurrent := testProcesses[0]; + testCurrent^.State := psRunning; + testCursor := 0; + testDone[0] := 0; + testDone[1] := 0; + testFailures := 0; + testSwitches := 0; setSelector(@testSelect); masks($FE, $FF); - asm sti end; + asm + sti + end; attempts := 0; while ((PUInt32(@testDone[0])^ = 0) or (PUInt32(@testDone[1])^ = 0)) and (attempts < 10000000) do begin - inc(attempts); asm pause end; + inc(attempts); + asm + pause + end; end; saved := lockIRQ; masks($FF, $FF); setSelector(nil); if (testDone[0] = 0) or (testDone[1] = 0) or (testSwitches < 6) or ((saved and $200) = 0) then inc(testFailures); - kfree(stackA); kfree(stackB); - for i := 0 to 2 do begin kfree(void(testProcesses[i])); testProcesses[i] := nil; end; + kfree(stackA); + kfree(stackB); + for i := 0 to 2 do begin + kfree(void(testProcesses[i])); + testProcesses[i] := nil; + end; if lmm_total_free <> beforeFree then inc(testFailures); field('CONTEXT_SWITCHES', testSwitches); field('CONTEXT_TESTS_FAILED', testFailures); if testFailures <> 0 then begin - puts('FAIL: preemptive kernel contexts'#13#10); bootstrap_halt; + puts('FAIL: preemptive kernel contexts'#13#10); + bootstrap_halt; end; puts('PASS: two kernel contexts preserve registers/x87/SSE, allocate and release stacks'#13#10); end; diff --git a/src/arch/x86_64/arch.x86_64.debug.pas b/src/arch/x86_64/arch.x86_64.debug.pas index 6edfd396..2179a588 100644 --- a/src/arch/x86_64/arch.x86_64.debug.pas +++ b/src/arch/x86_64/arch.x86_64.debug.pas @@ -11,8 +11,10 @@ // limitations under the License. unit arch.x86_64.debug; + interface procedure init; + implementation uses debug.early; { The stub initializes COM1. Bounded polling also allows boot without a UART. } diff --git a/src/arch/x86_64/arch.x86_64.irq.pas b/src/arch/x86_64/arch.x86_64.irq.pas index 68d504f8..5e9c1371 100644 --- a/src/arch/x86_64/arch.x86_64.irq.pas +++ b/src/arch/x86_64/arch.x86_64.irq.pas @@ -11,68 +11,184 @@ // limitations under the License. unit arch.x86_64.irq; + interface +type + TIRQHandler = procedure; + TTimerHandler = procedure(data: void); +function registerIRQ(vector: uint8; handler: TIRQHandler): boolean; +function registerTimer(handler: TTimerHandler): boolean; procedure init; procedure dispatch(vector: uint32); procedure UnitTest; function ticks: uint64; + implementation uses arch.common.legacyirq, arch.x86_64.util, debug.early, memory.heap; +type + PIRQHook = ^TIRQHook; + TIRQHook = record + Next: PIRQHook; + Handler: TIRQHandler; + end; + PTimerHook = ^TTimerHook; + TTimerHook = record + Next: PTimerHook; + Handler: TTimerHandler; + end; var + hooks: array[0..15] of PIRQHook; + timerHooks: PTimerHook; timerTicks, unexpected, spurious: uint64; ready: boolean; + +function registerIRQ(vector: uint8; handler: TIRQHandler): boolean; +var + node, current: PIRQHook; + flags: PtrUInt; +begin + registerIRQ := false; + if (vector < 32) or (vector > 47) or (handler = nil) then exit; + flags := lockIRQ; + current := hooks[vector - 32]; + while current <> nil do begin + if current^.Handler = handler then begin + unlockIRQ(flags); + registerIRQ := true; + exit; + end; + current := current^.Next; + end; + node := PIRQHook(kalloc(sizeof(TIRQHook))); + if node <> nil then begin + node^.Handler := handler; + node^.Next := hooks[vector - 32]; + hooks[vector - 32] := node; + registerIRQ := true; + end; + unlockIRQ(flags); +end; + +function registerTimer(handler: TTimerHandler): boolean; +var + node, current: PTimerHook; + flags: PtrUInt; +begin + registerTimer := false; + if handler = nil then exit; + flags := lockIRQ; + current := timerHooks; + while current <> nil do begin + if current^.Handler = handler then begin + unlockIRQ(flags); + registerTimer := true; + exit; + end; + current := current^.Next; + end; + node := PTimerHook(kalloc(sizeof(TTimerHook))); + if node <> nil then begin + node^.Handler := handler; + node^.Next := timerHooks; + timerHooks := node; + registerTimer := true; + end; + unlockIRQ(flags); +end; + procedure init; begin - asm cli end; + asm + cli + end; remap($FF, $FF); - timerTicks := 0; unexpected := 0; spurious := 0; + timerTicks := 0; + unexpected := 0; + spurious := 0; timerStart; ready := true; end; + function ticks: uint64; begin ticks := PUInt64(@timerTicks)^; end; + procedure dispatch(vector: uint32); -var line: uint8; +var + line: uint8; + hook: PIRQHook; + timer: PTimerHook; begin if (not ready) or (vector < 32) or (vector > 47) then begin - puts('FAIL: IRQ dispatched before controller setup'#13#10); bootstrap_halt; + puts('FAIL: IRQ dispatched before controller setup'#13#10); + bootstrap_halt; end; line := vector - 32; - if not realIRQ(line) then begin inc(spurious); exit; end; - if line = 0 then inc(timerTicks) else inc(unexpected); + if not realIRQ(line) then begin + inc(spurious); + exit; + end; + if line = 0 then begin + inc(timerTicks); + timer := timerHooks; + while timer <> nil do begin + timer^.Handler(nil); + timer := timer^.Next; + end; + end else if hooks[line] = nil then + inc(unexpected); + hook := hooks[line]; + while hook <> nil do begin + hook^.Handler; + hook := hook^.Next; + end; acknowledge(line); end; + procedure UnitTest; var attempts, failed: uint32; saved, beforeTicks: uint64; allocation: void; + procedure Assert(condition: boolean; name: pchar); begin - if condition then puts('PASS: ') else begin inc(failed); puts('FAIL: '); end; - puts(name); puts(#13#10); + if condition then puts('PASS: ') else begin + inc(failed); + puts('FAIL: '); + end; + puts(name); + puts(#13#10); end; begin failed := 0; { Synthetic spurious vectors must return without delivering device work. } - asm int $27; int $2F end; + asm + int $27; + int $2F + end; Assert((spurious = 2) and (unexpected = 0), 'PIC spurious IRQ7/IRQ15 filtered'); masks($FE, $FF); { only IRQ0; no storage or keyboard IRQs yet } beforeTicks := ticks; - asm sti end; + asm + sti + end; attempts := 0; { Bounded polling, not HLT: a missing timer must report failure. } while (ticks - beforeTicks < 16) and (attempts < 10000000) do begin - asm pause end; + asm + pause + end; inc(attempts); end; saved := lockIRQ; Assert((saved and $200) <> 0, 'IRQ return restores interrupt enable'); Assert(ticks - beforeTicks >= 16, 'PIT delivers repeated hardware interrupts'); beforeTicks := ticks; - for attempts := 1 to 10000 do asm pause end; + for attempts := 1 to 10000 do asm + pause + end; Assert(ticks = beforeTicks, 'CLI excludes timer interrupts'); unlockIRQ(saved); allocation := memory.heap.kalloc(17); diff --git a/src/arch/x86_64/arch.x86_64.isr.pas b/src/arch/x86_64/arch.x86_64.isr.pas index 1bd40d91..c9df54ae 100644 --- a/src/arch/x86_64/arch.x86_64.isr.pas +++ b/src/arch/x86_64/arch.x86_64.isr.pas @@ -67,8 +67,11 @@ var breakpointValid, pageFaultValid, fatalActive: boolean; function testBreakpoint: uint32; cdecl; external name 'x64_test_breakpoint'; + procedure testPageFault; cdecl; external name 'x64_test_page_fault'; + procedure testInvalidOpcode; cdecl; external name 'x64_test_invalid_opcode'; + procedure testDoubleFault; cdecl; external name 'x64_test_double_fault'; procedure loadTables(descriptor: pointer); assembler; nostackframe; @@ -199,6 +202,7 @@ procedure UnitTest; var passed, failed: uint32; result: uint32; + procedure Assert(condition: boolean; name: pchar); begin if condition then begin diff --git a/src/arch/x86_64/arch.x86_64.util.pas b/src/arch/x86_64/arch.x86_64.util.pas index 13a35328..5de3b246 100644 --- a/src/arch/x86_64/arch.x86_64.util.pas +++ b/src/arch/x86_64/arch.x86_64.util.pas @@ -11,6 +11,7 @@ // limitations under the License. unit arch.x86_64.util; + interface function lockIRQ: PtrUInt; procedure unlockIRQ(flags: PtrUInt); @@ -19,13 +20,16 @@ procedure invalidate(address: PtrUInt); function pageRoot: uint64; procedure outb(port: uint16; value: uint8); function inb(port: uint16): uint8; + implementation + function lockIRQ: PtrUInt; assembler; nostackframe; asm pushfq cli pop rax end; + procedure unlockIRQ(flags: PtrUInt); assembler; nostackframe; asm test rdi, $200 @@ -33,6 +37,7 @@ asm sti @done: end; + procedure zero(dest: pointer; bytes: SizeUInt); assembler; nostackframe; asm mov rcx, rsi @@ -44,20 +49,24 @@ asm and rcx, 7 rep stosb end; + procedure invalidate(address: PtrUInt); assembler; nostackframe; asm invlpg [rdi] end; + function pageRoot: uint64; assembler; nostackframe; asm mov rax, cr3 end; + procedure outb(port: uint16; value: uint8); assembler; nostackframe; asm mov edx, edi mov eax, esi out dx, al end; + function inb(port: uint16): uint8; assembler; nostackframe; asm mov edx, edi diff --git a/src/arch/x86_64/boot/stub.asm b/src/arch/x86_64/boot/stub.asm index c7789395..a617984c 100644 --- a/src/arch/x86_64/boot/stub.asm +++ b/src/arch/x86_64/boot/stub.asm @@ -25,13 +25,14 @@ extern kernel_end section .multiboot align=4 header: dd 0x1BADB002 - dd 0x00010003 - dd -(0x1BADB002 + 0x00010003) + dd 0x00010007 + dd -(0x1BADB002 + 0x00010007) dd header dd header dd load_end dd kernel_end dd loader + dd 0, 1024, 768, 32 section .text loader: diff --git a/src/arch/x86_64/memory/arch.x86_64.memory.physical.pas b/src/arch/x86_64/memory/arch.x86_64.memory.physical.pas index ec656970..925bb82e 100644 --- a/src/arch/x86_64/memory/arch.x86_64.memory.physical.pas +++ b/src/arch/x86_64/memory/arch.x86_64.memory.physical.pas @@ -14,6 +14,7 @@ the heap exists. Non-available memory-map entries always override available entries. Only the BSP is active; IRQ exclusion is not an SMP lock. } unit arch.x86_64.memory.physical; + interface uses boot.multiboot; const @@ -25,41 +26,53 @@ function alloc_high: uint64; function free_page(address: uint64): boolean; function reserve_range(address, bytes: uint64): boolean; function free_count: uint64; +function total_count: uint64; procedure UnitTest; + implementation uses arch.x86_64.util, debug.early; var eligible, used: PUInt32; frameCount, wordCount, hint: uint32; - freeFrames: uint64; + freeFrames, totalFrames: uint64; reservedEnd: uint64; kernelEnd: uint8; external name 'kernel_end'; mapStart, mapEnd: PtrUInt; procedure fail(message: pchar); begin - puts('FAIL: PMM '); puts(message); puts(#13#10); bootstrap_halt; + puts('FAIL: PMM '); + puts(message); + puts(#13#10); + bootstrap_halt; end; + function regionEnd(base, bytes: uint64): uint64; begin if (base >= MAX_MANAGED) or (bytes > MAX_MANAGED - base) then regionEnd := MAX_MANAGED else regionEnd := base + bytes; end; + function nextEntry(entry: Pmemory_map_t): Pmemory_map_t; begin nextEntry := Pmemory_map_t(PtrUInt(entry) + entry^.size + 4); end; + procedure preserve(address, bytes: uint64); -var finish: uint64; +var + finish: uint64; begin if (address >= $100000000) or (bytes > $100000000 - address) then fail('boot data outside initial mapping'); finish := address + bytes; if finish > reservedEnd then reservedEnd := finish; end; + procedure setAvailable(base, bytes: uint64; available: boolean); -var first, last, i, mask: uint32; finish: uint64; +var + first, last, i, mask: uint32; + finish: uint64; begin finish := regionEnd(base, bytes); if (base >= finish) or (base >= uint64(frameCount) * FRAME_SIZE) then exit; @@ -85,8 +98,11 @@ begin end; end; end; + function metadataAddress(bytes: uint64): uint64; -var e, other: Pmemory_map_t; candidate, finish, nextCandidate: uint64; +var + e, other: Pmemory_map_t; + candidate, finish, nextCandidate: uint64; begin metadataAddress := 0; e := Pmemory_map_t(mapStart); @@ -117,9 +133,15 @@ begin e := nextEntry(e); end; end; + procedure init(info: Pmultiboot_info_t); -var e: Pmemory_map_t; limit, bytes, storage: uint64; i, entries: uint32; - modules: Pmodule_t; sections: PtrUInt; sectionAddress, sectionBytes: uint64; +var + e: Pmemory_map_t; + limit, bytes, storage: uint64; + i, entries: uint32; + modules: Pmodule_t; + sections: PtrUInt; + sectionAddress, sectionBytes: uint64; begin if (info = nil) or ((info^.flags and (1 shl 6)) = 0) then fail('memory map missing'); if (info^.mmap_length = 0) or (info^.mmap_length > $100000) or @@ -194,6 +216,7 @@ begin if e^.mtype <> 1 then setAvailable(e^.base_addr, e^.length, false); e := nextEntry(e); end; + totalFrames := freeFrames; setAvailable(0, reservedEnd, false); setAvailable(storage, bytes, false); hint := 0; @@ -201,8 +224,11 @@ begin field('PMM_MANAGED_LIMIT', uint64(frameCount) * FRAME_SIZE); field('PMM_METADATA', storage); end; + function allocateBetween(first, last: uint32): uint64; -var saved: PtrUInt; pass, start, finish, idx, bits, mask, bit: uint32; +var + saved: PtrUInt; + pass, start, finish, idx, bits, mask, bit: uint32; begin allocateBetween := 0; saved := lockIRQ; @@ -237,17 +263,22 @@ begin end; unlockIRQ(saved); end; + function alloc_page(lowOnly: boolean): uint64; begin if lowOnly then alloc_page := allocateBetween(0, $100000) else alloc_page := allocateBetween(0, frameCount); end; + function alloc_high: uint64; begin alloc_high := allocateBetween($100000, frameCount); end; + function free_page(address: uint64): boolean; -var idx, mask: uint32; saved: PtrUInt; +var + idx, mask: uint32; + saved: PtrUInt; begin free_page := false; if (address and (FRAME_SIZE - 1) <> 0) or (address div FRAME_SIZE >= frameCount) then exit; @@ -262,8 +293,11 @@ begin end; unlockIRQ(saved); end; + function reserve_range(address, bytes: uint64): boolean; -var first, last, i: uint32; saved: PtrUInt; +var + first, last, i: uint32; + saved: PtrUInt; begin reserve_range := false; if (bytes = 0) or (address >= MAX_MANAGED) or (bytes > MAX_MANAGED - address) then exit; @@ -273,25 +307,48 @@ begin saved := lockIRQ; if first < last then for i := first to last - 1 do if (used[i shr 5] and (uint32(1) shl (i and 31))) <> 0 then begin - unlockIRQ(saved); exit; + unlockIRQ(saved); + exit; end; setAvailable(address, bytes, false); reserve_range := true; unlockIRQ(saved); end; + function free_count: uint64; -begin free_count := freeFrames; end; +begin + free_count := freeFrames; +end; + +function total_count: uint64; +begin + total_count := totalFrames; +end; + procedure UnitTest; -var before, a, b: uint64; passed, failed: uint32; +var + before, a, b: uint64; + passed, failed: uint32; + procedure Assert(condition: boolean; name: pchar); begin - if condition then begin inc(passed); puts('PASS: '); end - else begin inc(failed); puts('FAIL: '); end; - puts(name); puts(#13#10); + if condition then begin + inc(passed); + puts('PASS: '); + end + else begin + inc(failed); + puts('FAIL: '); + end; + puts(name); + puts(#13#10); end; begin - passed := 0; failed := 0; before := free_count; - a := alloc_page(true); b := alloc_page(true); + passed := 0; + failed := 0; + before := free_count; + a := alloc_page(true); + b := alloc_page(true); Assert((a <> 0) and (b <> 0) and (a <> b) and (a >= reservedEnd) and (b >= reservedEnd), 'PMM unique usable frames exclude boot reservations'); Assert(not free_page(0) and not free_page(a + 1), 'PMM rejects reserved/unaligned frees'); diff --git a/src/arch/x86_64/memory/arch.x86_64.memory.virtual.pas b/src/arch/x86_64/memory/arch.x86_64.memory.virtual.pas index 7f1b199e..4a8cfa9e 100644 --- a/src/arch/x86_64/memory/arch.x86_64.memory.virtual.pas +++ b/src/arch/x86_64/memory/arch.x86_64.memory.virtual.pas @@ -13,6 +13,7 @@ { BSP kernel mappings. Page-table frames stay below 4 GiB so the bootstrap identity map can access them; data frames may live above 4 GiB. } unit arch.x86_64.memory.virtual; + interface const ARENA_SIZE = $400000; @@ -23,8 +24,10 @@ function release_region(address: PtrUInt; arenas: uint32): boolean; function map_page(address: PtrUInt; physical: uint64; owned: boolean): boolean; function unmap_page(address: PtrUInt; var physical: uint64): boolean; function translate(address: PtrUInt): uint64; +function mapDevice(address: uint64; bytes: SizeUInt): pointer; procedure init; procedure UnitTest; + implementation uses arch.x86_64.memory.physical, arch.x86_64.util, memory.bitmap, debug.early; const @@ -36,7 +39,6 @@ var tablePages: uint64; leafFlags: uint64; failAfter: sint32; { deterministic OOM injection, disabled outside UnitTest } - function acquire(lowOnly: boolean): uint64; begin acquire := 0; @@ -44,12 +46,20 @@ begin if failAfter > 0 then dec(failAfter); acquire := arch.x86_64.memory.physical.alloc_page(lowOnly); end; + procedure fail(message: pchar); -begin puts('FAIL: VMM '); puts(message); puts(#13#10); bootstrap_halt; end; +begin + puts('FAIL: VMM '); + puts(message); + puts(#13#10); + bootstrap_halt; +end; + function managed(address: PtrUInt): boolean; begin managed := (address >= HEAP_BASE) and (address - HEAP_BASE < uint64(ARENA_COUNT) * ARENA_SIZE); end; + function enableNX: boolean; assembler; nostackframe; asm push rbx @@ -66,8 +76,12 @@ asm @done: pop rbx end; + function map_page(address: PtrUInt; physical: uint64; owned: boolean): boolean; -var table, slot: PUInt64; shift, created: sint32; frame, saved: uint64; +var + table, slot: PUInt64; + shift, created: sint32; + frame, saved: uint64; parents: array[0..2] of PUInt64; frames: array[0..2] of uint64; begin @@ -75,7 +89,9 @@ begin if not managed(address) or ((address and $FFF) <> 0) or ((physical and not ADDRESS_MASK) <> 0) then exit; saved := lockIRQ; - table := root; created := 0; shift := 39; + table := root; + created := 0; + shift := 39; while shift > 12 do begin slot := @table[(address shr shift) and $1FF]; if (slot^ and 1) = 0 then begin @@ -84,7 +100,8 @@ begin zero(pointer(PtrUInt(frame)), 4096); parents[created] := slot; frames[created] := frame; - inc(created); inc(tablePages); + inc(created); + inc(tablePages); slot^ := frame or 3; end; if (slot^ and $80) <> 0 then break; @@ -108,12 +125,17 @@ begin end; unlockIRQ(saved); end; + function leaf(address: PtrUInt): PUInt64; -var table: PUInt64; shift: sint32; entry: uint64; +var + table: PUInt64; + shift: sint32; + entry: uint64; begin leaf := nil; if not managed(address) then exit; - table := root; shift := 39; + table := root; + shift := 39; while shift > 12 do begin entry := table[(address shr shift) and $1FF]; if ((entry and 1) = 0) or ((entry and $80) <> 0) then exit; @@ -122,12 +144,17 @@ begin end; leaf := @table[(address shr 12) and $1FF]; end; + function translate(address: PtrUInt): uint64; -var table: PUInt64; shift: sint32; entry, span: uint64; +var + table: PUInt64; + shift: sint32; + entry, span: uint64; begin translate := 0; if (address > $00007FFFFFFFFFFF) and (address < HEAP_BASE) then exit; - table := root; shift := 39; + table := root; + shift := 39; while shift >= 12 do begin entry := table[(address shr shift) and $1FF]; if (entry and 1) = 0 then exit; @@ -140,34 +167,78 @@ begin dec(shift, 9); end; end; -procedure prune(address: PtrUInt); -var parents: array[0..2] of PUInt64; tables: array[0..2] of PUInt64; - table, slot: PUInt64; shift, level: sint32; i: uint32; empty: boolean; + +function mapDevice(address: uint64; bytes: SizeUInt): pointer; +var + first, last, page: PtrUInt; + table, slot: PUInt64; + flags: PtrUInt; begin - table := root; shift := 39; level := 0; + mapDevice := nil; + if (bytes = 0) or (address >= $100000000) or (bytes > $100000000 - address) then exit; + first := PtrUInt(address) and not PtrUInt($1FFFFF); + last := PtrUInt(address + bytes - 1) and not PtrUInt($1FFFFF); + flags := lockIRQ; + page := first; + repeat + table := PUInt64(PtrUInt(root[0] and ADDRESS_MASK)); + table := PUInt64(PtrUInt(table[(page shr 30) and $1FF] and ADDRESS_MASK)); + slot := @table[(page shr 21) and $1FF]; + { Bootstrap low identity leaves are 2 MiB. Disable caching for MMIO. } + slot^ := slot^ or $18; + invalidate(page); + if page = last then break; + inc(page, $200000); + until false; + unlockIRQ(flags); + mapDevice := pointer(PtrUInt(address)); +end; + +procedure prune(address: PtrUInt); +var + parents: array[0..2] of PUInt64; + tables: array[0..2] of PUInt64; + table, slot: PUInt64; + shift, level: sint32; + i: uint32; + empty: boolean; +begin + table := root; + shift := 39; + level := 0; while shift > 12 do begin slot := @table[(address shr shift) and $1FF]; if ((slot^ and 1) = 0) or ((slot^ and $80) <> 0) then exit; parents[level] := slot; table := PUInt64(PtrUInt(slot^ and ADDRESS_MASK)); tables[level] := table; - inc(level); dec(shift, 9); + inc(level); + dec(shift, 9); end; while level > 0 do begin - dec(level); empty := true; - for i := 0 to 511 do if (tables[level][i] and 1) <> 0 then begin empty := false; break; end; + dec(level); + empty := true; + for i := 0 to 511 do if (tables[level][i] and 1) <> 0 then begin + empty := false; + break; + end; if not empty then exit; parents[level]^ := 0; if not arch.x86_64.memory.physical.free_page(PtrUInt(tables[level])) then fail('table release'); dec(tablePages); end; end; + function unmap_page(address: PtrUInt; var physical: uint64): boolean; -var slot: PUInt64; saved: PtrUInt; +var + slot: PUInt64; + saved: PtrUInt; begin - unmap_page := false; physical := 0; + unmap_page := false; + physical := 0; if not managed(address) or ((address and $FFF) <> 0) then exit; - saved := lockIRQ; slot := leaf(address); + saved := lockIRQ; + slot := leaf(address); if slot <> nil then if (slot^ and 1) <> 0 then begin physical := slot^ and ADDRESS_MASK; slot^ := 0; @@ -177,8 +248,12 @@ begin end; unlockIRQ(saved); end; + procedure releaseMapped(address: PtrUInt; pages: uint32); -var i: uint32; slot: PUInt64; physical: uint64; +var + i: uint32; + slot: PUInt64; + physical: uint64; begin if pages = 0 then exit; for i := 0 to pages - 1 do begin @@ -196,14 +271,22 @@ begin inc(i, 512); end; end; + function allocate_region(arenas: uint32): PtrUInt; -var idx: sint32; i, pages: uint32; base, saved: PtrUInt; physical: uint64; +var + idx: sint32; + i, pages: uint32; + base, saved: PtrUInt; + physical: uint64; begin allocate_region := 0; if (arenas = 0) or (arenas > ARENA_COUNT) or (arenaBitmap = nil) then exit; saved := lockIRQ; idx := find_free_run(arenaBitmap, 0, arenas, ARENA_COUNT); - if idx < 0 then begin unlockIRQ(saved); exit; end; + if idx < 0 then begin + unlockIRQ(saved); + exit; + end; bitmap_set_range(arenaBitmap, idx, arenas); base := HEAP_BASE + uint64(idx) * ARENA_SIZE; pages := arenas * (ARENA_SIZE div 4096); @@ -223,8 +306,12 @@ begin end else allocate_region := base; unlockIRQ(saved); end; + function release_region(address: PtrUInt; arenas: uint32): boolean; -var idx, i, pages: uint32; saved: PtrUInt; slot: PUInt64; +var + idx, i, pages: uint32; + saved: PtrUInt; + slot: PUInt64; begin release_region := false; if not managed(address) or ((address and (ARENA_SIZE - 1)) <> 0) or @@ -232,43 +319,69 @@ begin idx := (address - HEAP_BASE) div ARENA_SIZE; if arenas > ARENA_COUNT - idx then exit; saved := lockIRQ; - if not bitmap_all_set(arenaBitmap, idx, arenas) then begin unlockIRQ(saved); exit; end; + if not bitmap_all_set(arenaBitmap, idx, arenas) then begin + unlockIRQ(saved); + exit; + end; pages := arenas * (ARENA_SIZE div 4096); for i := 0 to pages - 1 do begin slot := leaf(address + uint64(i) * 4096); - if slot = nil then begin unlockIRQ(saved); exit; end; - if (slot^ and (PTE_OWNED or 1)) <> (PTE_OWNED or 1) then begin unlockIRQ(saved); exit; end; + if slot = nil then begin + unlockIRQ(saved); + exit; + end; + if (slot^ and (PTE_OWNED or 1)) <> (PTE_OWNED or 1) then begin + unlockIRQ(saved); + exit; + end; end; releaseMapped(address, pages); bitmap_clear_range(arenaBitmap, idx, arenas); release_region := true; unlockIRQ(saved); end; + procedure init; -var storage: uint64; +var + storage: uint64; begin root := PUInt64(PtrUInt(pageRoot and ADDRESS_MASK)); leafFlags := 3; if enableNX then leafFlags := leafFlags or (uint64(1) shl 63); - tablePages := 0; failAfter := -1; + tablePages := 0; + failAfter := -1; storage := arch.x86_64.memory.physical.alloc_page(true); if storage = 0 then fail('arena metadata allocation'); arenaBitmap := PUInt32(PtrUInt(storage)); zero(arenaBitmap, 4096); end; + procedure UnitTest; const TEST_ADDRESS = HEAP_BASE + $800000; TEST_PATTERN = uint64($FEDCBA9876543210); -var before, frame, removed, highFrame: uint64; region: PtrUInt; passed, failed: uint32; +var + before, frame, removed, highFrame: uint64; + region: PtrUInt; + passed, failed: uint32; + procedure Assert(condition: boolean; name: pchar); begin - if condition then begin inc(passed); puts('PASS: '); end - else begin inc(failed); puts('FAIL: '); end; - puts(name); puts(#13#10); + if condition then begin + inc(passed); + puts('PASS: '); + end + else begin + inc(failed); + puts('FAIL: '); + end; + puts(name); + puts(#13#10); if not condition then bootstrap_halt; end; begin - passed := 0; failed := 0; before := free_count; + passed := 0; + failed := 0; + before := free_count; frame := arch.x86_64.memory.physical.alloc_page(true); Assert(frame <> 0, 'VMM test frame allocation'); Assert(map_page(TEST_ADDRESS, frame, false), 'VMM high virtual mapping'); diff --git a/src/arch/x86_64/platform/arch.platform.cpu.pas b/src/arch/x86_64/platform/arch.platform.cpu.pas index 849be299..ee1ce9c5 100644 --- a/src/arch/x86_64/platform/arch.platform.cpu.pas +++ b/src/arch/x86_64/platform/arch.platform.cpu.pas @@ -12,24 +12,90 @@ { Build-selected x86_64 adapter. } unit arch.platform.cpu; + interface {$i ../../common/platform.cpu.inc} + implementation uses arch.x86_64.util, memory.raw; + function lock: PtrUInt; -begin lock := arch.x86_64.util.lockIRQ; end; +begin + lock := arch.x86_64.util.lockIRQ; +end; + procedure unlock(flags: PtrUInt); -begin if (flags and $200) <> 0 then asm sti end; end; +begin + if (flags and $200) <> 0 then asm + sti + end; +end; + procedure idle; -begin asm hlt end; end; +begin + asm + hlt + end; +end; + procedure outb(port: uint16; value: uint8); -begin arch.x86_64.util.outb(port, value); end; +begin + arch.x86_64.util.outb(port, value); +end; + function inb(port: uint16): uint8; -begin inb := arch.x86_64.util.inb(port); end; +begin + inb := arch.x86_64.util.inb(port); +end; + procedure copyBytes(source, dest: PtrUInt; bytes: SizeUInt); begin memory.raw.move(pointer(source), pointer(dest), bytes); end; + procedure zero(dest: pointer; bytes: SizeUInt); -begin arch.x86_64.util.zero(dest, bytes); end; +begin + arch.x86_64.util.zero(dest, bytes); +end; +procedure outl(port: uint16; value: uint32); assembler; nostackframe; +asm + mov edx, edi + mov eax, esi + out dx, eax +end; + +function inl(port: uint16): uint32; assembler; nostackframe; +asm + mov edx, edi + in eax, dx +end; + +procedure queryCPU(leaf, subleaf: uint32; var registers: TCPURegisters); assembler; nostackframe; +asm + push rbx + mov r8, rdx + mov eax, edi + mov ecx, esi + cpuid + mov [r8], eax + mov [r8 + 4], ebx + mov [r8 + 8], ecx + mov [r8 + 12], edx + pop rbx +end; + +procedure outw(port: uint16; value: uint16); assembler; nostackframe; +asm + mov edx, edi + mov eax, esi + out dx, ax +end; + +function inw(port: uint16): uint16; assembler; nostackframe; +asm + mov edx, edi + xor eax, eax + in ax, dx +end; + end. diff --git a/src/arch/x86_64/platform/arch.platform.diagnostics.pas b/src/arch/x86_64/platform/arch.platform.diagnostics.pas index 1b9176b3..91495edd 100644 --- a/src/arch/x86_64/platform/arch.platform.diagnostics.pas +++ b/src/arch/x86_64/platform/arch.platform.diagnostics.pas @@ -12,18 +12,33 @@ { Build-selected x86_64 adapter. } unit arch.platform.diagnostics; + interface {$i ../../common/platform.diagnostics.inc} + implementation uses debug.early; + procedure log(tag, message: pchar); -begin debug.early.puts(message); debug.early.puts(#13#10); end; +begin + debug.early.puts(message); + debug.early.puts(#13#10); +end; + procedure push_trace(name: pchar); -begin end; +begin +end; + procedure pop_trace; -begin end; +begin +end; + procedure fatal(tag, message: pchar); -begin log(tag, message); bootstrap_halt; end; +begin + log(tag, message); + bootstrap_halt; +end; + procedure testSummary(tag, passedKey, failedKey: pchar; passed, failed: uint32); begin debug.early.field(passedKey, passed); diff --git a/src/arch/x86_64/platform/arch.platform.interrupts.pas b/src/arch/x86_64/platform/arch.platform.interrupts.pas new file mode 100644 index 00000000..4de01089 --- /dev/null +++ b/src/arch/x86_64/platform/arch.platform.interrupts.pas @@ -0,0 +1,54 @@ +// Copyright 2026 Aaron Hance +// +// 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. + +unit arch.platform.interrupts; + +interface +{$i ../../common/platform.interrupts.inc} + +implementation +uses arch.x86_64.irq, arch.x86_64.util, arch.platform.diagnostics; + +function timerTicks: uint64; +begin + timerTicks := arch.x86_64.irq.ticks; +end; + +procedure registerIRQ(vector: uint8; handler: TIRQHandler); +begin + if not arch.x86_64.irq.registerIRQ(vector, handler) then + fatal('IRQ', 'IRQ handler registration failed'); +end; + +procedure registerTimer(handler: TTimerHandler); +begin + if not arch.x86_64.irq.registerTimer(handler) then + fatal('IRQ', 'Timer handler registration failed'); +end; + +procedure routePCI(line, vector: uint8); +var + flags: PtrUInt; +begin + if (line >= 16) or (line = 2) or (vector <> line + 32) then + fatal('IRQ', 'Unsupported PCI legacy IRQ route'); + flags := lockIRQ; + if line < 8 then + outb($21, inb($21) and not uint8(1 shl line)) + else begin + outb($A1, inb($A1) and not uint8(1 shl (line - 8))); + outb($21, inb($21) and not uint8(4)); + end; + unlockIRQ(flags); +end; + +end. diff --git a/src/arch/x86_64/platform/arch.platform.memory.pas b/src/arch/x86_64/platform/arch.platform.memory.pas index 253ed8b7..d909a9fd 100644 --- a/src/arch/x86_64/platform/arch.platform.memory.pas +++ b/src/arch/x86_64/platform/arch.platform.memory.pas @@ -11,20 +11,30 @@ // limitations under the License. unit arch.platform.memory; + interface uses arch.x86_64.memory.virtual; const MAX_HEAP_REGIONS = arch.x86_64.memory.virtual.ARENA_COUNT; {$i ../../common/platform.memory.inc} + implementation uses arch.x86_64.memory.physical, arch.platform.diagnostics; + function allocateHeapPage: PtrUInt; -begin allocateHeapPage := allocate_region(1); end; +begin + allocateHeapPage := allocate_region(1); +end; + function allocateLarge(pages: uint32): PtrUInt; -begin allocateLarge := allocate_region(pages); end; +begin + allocateLarge := allocate_region(pages); +end; + procedure release(address: PtrUInt; pages: uint32); begin if not release_region(address, pages) then fatal('LMM', 'Invalid heap region release'); end; + function identityReserve(address: PtrUInt): pointer; begin identityReserve := nil; @@ -33,6 +43,24 @@ begin if reserve_range(address, $400000) then identityReserve := pointer(address); end; end; -function freeBacking: uint64; -begin freeBacking := free_count; end; + +function freePhysicalBytes: uint64; +begin + freePhysicalBytes := free_count * FRAME_SIZE; +end; + +function totalPhysicalBytes: uint64; +begin + totalPhysicalBytes := total_count * FRAME_SIZE; +end; +function physicalAddress(address: PtrUInt): uint64; +begin + physicalAddress := arch.x86_64.memory.virtual.translate(address); +end; + +function mapDevice(address: uint64; bytes: SizeUInt): pointer; +begin + mapDevice := arch.x86_64.memory.virtual.mapDevice(address, bytes); +end; + end. diff --git a/src/arch/x86_64/platform/arch.platform.process.pas b/src/arch/x86_64/platform/arch.platform.process.pas index 48e2b2b1..b5ceb67a 100644 --- a/src/arch/x86_64/platform/arch.platform.process.pas +++ b/src/arch/x86_64/platform/arch.platform.process.pas @@ -12,12 +12,20 @@ { Build-selected x86_64 adapter. } unit arch.platform.process; + interface {$i ../../common/platform.process.inc} + implementation uses arch.x86_64.context, arch.x86_64.irq; + function prepare(stack: pointer; bytes: SizeUInt; entry: pointer): PtrUInt; -begin prepare := arch.x86_64.context.prepare(stack, bytes, entry, 0); end; +begin + prepare := arch.x86_64.context.prepare(stack, bytes, entry, 0); +end; + function timerTicks: uint32; -begin timerTicks := uint32(arch.x86_64.irq.ticks); end; +begin + timerTicks := uint32(arch.x86_64.irq.ticks); +end; end. diff --git a/src/arch/x86_64/proc/arch.x86_64.proc.sched.pas b/src/arch/x86_64/proc/arch.x86_64.proc.sched.pas index 87bdb690..66970f6d 100644 --- a/src/arch/x86_64/proc/arch.x86_64.proc.sched.pas +++ b/src/arch/x86_64/proc/arch.x86_64.proc.sched.pas @@ -12,9 +12,11 @@ { Adapter between saved CPU contexts and the shared process manager. } unit arch.x86_64.proc.sched; + interface procedure init; procedure UnitTest; + implementation uses proc.mgr, proc.types, io.stdio, core.strings, driver.storage.fdtable, arch.x86_64.context, arch.x86_64.util, arch.x86_64.irq, arch.common.legacyirq, memory.heap, memory.raw, @@ -28,6 +30,7 @@ var commandValid, sleepValid, asyncRetained: boolean; asyncEntry: PFileDescriptor; fdReleased: uint32; + function selectProcess(saved: PtrUInt): PtrUInt; begin CurrentProcess^.SavedStack := saved; @@ -35,11 +38,13 @@ begin inc(switches); selectProcess := CurrentProcess^.SavedStack; end; + procedure init; begin { Caller owns IRQ exclusion; proc.mgr.init must have installed idle. } setSelector(@selectProcess); end; + procedure cleanupResource(handle: void); begin inc(cleaned); @@ -47,8 +52,10 @@ begin unbindResource(cleanupCtx, handle); kfree(handle); end; + procedure parentWorker(ctx: PProcessContext); -var flags: PtrUInt; +var + flags: PtrUInt; begin flags := lockIRQ; if ctx^.PendingMsg <> smChildExited then ctx^.State := psAwaiting; @@ -57,19 +64,30 @@ begin if (ctx^.PendingMsg = smChildExited) and (PtrUInt(ctx^.MsgData) = childID) then inc(woke); inc(returned); end; + procedure childWorker(ctx: PProcessContext); begin inc(exited); proc_exit(7); inc(forbidden); end; + procedure forbiddenWorker(ctx: PProcessContext); -begin inc(forbidden); end; +begin + inc(forbidden); +end; + procedure closeChildFD(data: pointer); -begin inc(fdReleased); kfree(void(data)); end; +begin + inc(fdReleased); + kfree(void(data)); +end; + procedure commandWorker(params: PParamList; stdin_buf, stdout_buf, stderr_buf: POutBuf); type PState = ^TProcessState; -var start: uint64; held: PProcessContext; +var + start: uint64; + held: PProcessContext; begin commandValid := (params = commandParams) and (stdin_buf = commandBuffer) and (stdout_buf = commandBuffer) and (stderr_buf = commandBuffer); @@ -83,6 +101,7 @@ begin fd_end_async(asyncEntry); end; end; + procedure UnitTest; type PCommandBuffer = ^TCommandBuffer; var @@ -94,37 +113,55 @@ var list: PDList; slot: ^PtrUInt; i, failed, passed, baseCount: uint32; + procedure Assert(condition: boolean; name: pchar); begin - if condition then begin inc(passed); puts('PASS: '); end - else begin inc(failed); puts('FAIL: '); end; - puts(name); puts(#13#10); + if condition then begin + inc(passed); + puts('PASS: '); + end + else begin + inc(failed); + puts('FAIL: '); + end; + puts(name); + puts(#13#10); end; begin - failed := 0; passed := 0; + failed := 0; + passed := 0; beforeFree := lmm_total_free; baseCount := processCount; list := DL_New(sizeof(PtrUInt)); for i := 0 to 79 do begin - slot := pointer(DL_Add(list)); slot^ := PtrUInt($1234567800000000) + i; + slot := pointer(DL_Add(list)); + slot^ := PtrUInt($1234567800000000) + i; end; - slot := pointer(DL_Insert(list, 1)); slot^ := 777; + slot := pointer(DL_Insert(list, 1)); + slot^ := 777; Assert(PUInt64(DL_Get(list, 2))^ = uint64($1234567800000001), 'native dynamic list overlapping insertion'); DL_Delete(list, 1); Assert(PUInt64(DL_Get(list, 79))^ = uint64($123456780000004F), 'native dynamic list growth and deletion'); - DL_Clear(list); DL_ShrinkToFit(list); + DL_Clear(list); + DL_ShrinkToFit(list); slot := pointer(DL_Add(list)); Assert((slot <> nil) and (slot^ = 0), 'empty dynamic list regrows with zeroed slots'); DL_Free(list); Assert(lmm_total_free = beforeFree, 'dynamic list releases allocation ownership'); - cleaned := 0; returned := 0; woke := 0; exited := 0; forbidden := 0; + cleaned := 0; + returned := 0; + woke := 0; + exited := 0; + forbidden := 0; parent := createSuspended('lifecycle parent', @parentWorker, nil, 1); child := createSuspended('lifecycle child', @childWorker, nil, 3); killed := createSuspended('never run', @forbiddenWorker, nil, 1); - parentID := parent^.ProcessID; childID := child^.ProcessID; + parentID := parent^.ProcessID; + childID := child^.ProcessID; child^.ParentID := parentID; - asyncRetained := false; fdReleased := 0; + asyncRetained := false; + fdReleased := 0; asyncEntry := @PFDTable(child^.FDTable)^.Entries[0]; asyncEntry^.InUse := true; asyncEntry^.FSPrivate := pointer(kalloc(11)); @@ -141,17 +178,23 @@ begin commandParams := getParams(input^); kfree(void(input)); commandBuffer := createOutBuf(1); - commandValid := false; sleepValid := false; + commandValid := false; + sleepValid := false; runCommand('scheduled ECHO', findCommand('echo')^.method, commandParams, nil, commandBuffer, nil); runCommand('lifecycle command', @commandWorker, commandParams, commandBuffer, commandBuffer, commandBuffer); - parent^.State := psReady; child^.State := psReady; + parent^.State := psReady; + child^.State := psReady; started := ticks; masks($FE, $FF); - asm sti end; + asm + sti + end; repeat - asm pause end; + asm + pause + end; until (processCount = baseCount) or (ticks - started > 16000); flags := lockIRQ; masks($FF, $FF); @@ -164,7 +207,8 @@ begin Assert(sleepValid, 'process sleep uses the programmed PIT rate'); Assert(asyncRetained and (fdReleased = 1), 'reaper waits for async FD completion then closes exactly once'); Assert(stringEquals(commandBuffer^.buf, 'scheduled '#10), 'shared ECHO executes through scheduler'); - freeParams(commandParams); freeOutBuf(commandBuffer); + freeParams(commandParams); + freeOutBuf(commandBuffer); Assert(lmm_total_free = beforeFree, 'process contexts, stacks, CWD and resources freed'); field('PROCESS_TESTS_PASSED', passed); field('PROCESS_TESTS_FAILED', failed); diff --git a/src/arch/x86_64/system.pas b/src/arch/x86_64/system.pas index 8b7538eb..b9f59377 100644 --- a/src/arch/x86_64/system.pas +++ b/src/arch/x86_64/system.pas @@ -16,6 +16,8 @@ unit system; interface +const TRACER_ENABLE = true; + type SizeInt = int64; SizeUInt = qword; @@ -35,11 +37,29 @@ type frametype: longint; end; +procedure init; +procedure fpc_initializeunits; compilerproc; + procedure bootstrap_halt; external name 'bootstrap_halt'; function Trunc(d: Double): int64; implementation +var + InitFinalTable: TInitFinalTable; external name 'INITFINAL'; + +{$i ../common/system.init.inc} + +procedure init; +begin + initializeUnits; +end; + +procedure fpc_initializeunits; [public, alias: 'FPC_INITIALIZEUNITS']; compilerproc; +begin + initializeUnits; +end; + { SysV AMD64 passes Double in XMM0. CVTTSD2SI rounds toward zero without changing the caller's x87 control word or MXCSR rounding mode. } function Trunc(d: Double): int64; assembler; nostackframe; diff --git a/src/boot/boot.multiboot.pas b/src/boot/boot.multiboot.pas index f9aa36e5..0d856c4a 100644 --- a/src/boot/boot.multiboot.pas +++ b/src/boot/boot.multiboot.pas @@ -14,6 +14,7 @@ { Multiboot v1 wire layout: fixed-width fields on every architecture. } unit boot.multiboot; + interface const MULTIBOOT_BOOTLOADER_MAGIC = $2BADB002; type @@ -71,5 +72,7 @@ type mtype: uint32; end; +var multibootinfo: Pmultiboot_info_t = nil; + implementation end. diff --git a/src/core/core.strings.helpers.pas b/src/core/core.strings.helpers.pas deleted file mode 100644 index bc4a917b..00000000 --- a/src/core/core.strings.helpers.pas +++ /dev/null @@ -1,272 +0,0 @@ -{ - Core->StringHelpers - Reusable file-oriented search, sort, and format utilities. - - Provides: - - getFileExtension: zero-copy extension extraction - - fmtFileSize: human-readable file size formatting (kalloc'd result) - - sortStringArray: in-place insertion sort on pchar arrays - - sortStringArrayWithData: parallel insertion sort (pchar + uint32 arrays) - - @author(Aaron Hance ) -} -unit core.strings.helpers; - -interface - -type - PPChar = ^pchar; - -{ Return a pointer into name at the last '.' (including the dot). - Returns nil if there is no extension. Zero-copy: does NOT allocate. } -function getFileExtension(name : pchar) : pchar; - -{ Return a kalloc'd human-readable size string (e.g. '1.2 MB'). - Caller must kfree the result. } -function fmtFileSize(sz : uint32) : pchar; - -{ In-place case-insensitive insertion sort on an array of n pchar pointers. } -procedure sortStringArray(arr : PPChar; n : uint32); - -{ In-place case-insensitive insertion sort on an array of n pchar pointers, - swapping a parallel uint32 data array in lockstep. } -procedure sortStringArrayWithData(arr : PPChar; data : puint32; n : uint32); - -procedure UnitTest; - -implementation - -uses - core.strings, - memory.heap, - io.syslog; - -{ ============================================================ - getFileExtension - Walk backwards from end of name to find the last '.'. - Returns pointer into original string at the dot, or nil. - ============================================================ } -function getFileExtension(name : pchar) : pchar; -var - len : uint32; - i : uint32; -begin - getFileExtension := nil; - if name = nil then exit; - len := stringSize(name); - if len = 0 then exit; - i := len; - while i > 0 do begin - i := i - 1; - if name[i] = '.' then begin - getFileExtension := @name[i]; - exit; - end; - end; -end; - -{ ============================================================ - fmtFileSize - Returns a kalloc'd human-readable size string. - Caller must kfree the result. - ============================================================ } -function fmtFileSize(sz : uint32) : pchar; -var - whole, frac : uint32; - s1, s2, s3, res : pchar; -begin - if sz < 1024 then begin - s1 := intToString(sz); - res := stringConcat(s1, ' B'); - kfree(void(s1)); - end else if sz < uint32(1024 * 1024) then begin - whole := sz div 1024; - frac := (sz mod 1024) * 10 div 1024; - s1 := intToString(whole); - s2 := stringConcat(s1, '.'); kfree(void(s1)); - s3 := intToString(frac); - s1 := stringConcat(s2, s3); kfree(void(s2)); kfree(void(s3)); - res := stringConcat(s1, ' KB'); kfree(void(s1)); - end else begin - whole := sz div (1024 * 1024); - frac := (sz mod (1024 * 1024)) * 10 div (1024 * 1024); - s1 := intToString(whole); - s2 := stringConcat(s1, '.'); kfree(void(s1)); - s3 := intToString(frac); - s1 := stringConcat(s2, s3); kfree(void(s2)); kfree(void(s3)); - res := stringConcat(s1, ' MB'); kfree(void(s1)); - end; - fmtFileSize := res; -end; - -{ ============================================================ - sortStringArray - In-place insertion sort using stringCompareCI. - Guarded for n=0 (Lesson #13). - ============================================================ } -procedure sortStringArray(arr : PPChar; n : uint32); -var - i, j : uint32; - tmp : pchar; - p : PPChar; -begin - if (arr = nil) or (n < 2) then exit; - if n > 1 then - for i := 1 to n - 1 do begin - p := PPChar(pointer(uint32(arr) + i * sizeof(pchar))); - tmp := p^; - j := i; - while j > 0 do begin - p := PPChar(pointer(uint32(arr) + (j - 1) * sizeof(pchar))); - if stringCompareCI(tmp, p^) >= 0 then break; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := p^; - j := j - 1; - end; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := tmp; - end; -end; - -{ ============================================================ - sortStringArrayWithData - In-place insertion sort on pchar array, swapping a parallel - uint32 data array in lockstep. Uses stringCompareCI. - Guarded for n=0 (Lesson #13). - ============================================================ } -procedure sortStringArrayWithData(arr : PPChar; data : puint32; n : uint32); -var - i, j : uint32; - tmpN : pchar; - tmpD : uint32; - p : PPChar; - dp : puint32; -begin - if (arr = nil) or (n < 2) then exit; - if n > 1 then - for i := 1 to n - 1 do begin - p := PPChar(pointer(uint32(arr) + i * sizeof(pchar))); - dp := puint32(pointer(uint32(data) + i * sizeof(uint32))); - tmpN := p^; - tmpD := dp^; - j := i; - while j > 0 do begin - p := PPChar(pointer(uint32(arr) + (j - 1) * sizeof(pchar))); - if stringCompareCI(tmpN, p^) >= 0 then break; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := p^; - puint32(pointer(uint32(data) + j * sizeof(uint32)))^ := - puint32(pointer(uint32(data) + (j - 1) * sizeof(uint32)))^; - j := j - 1; - end; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := tmpN; - puint32(pointer(uint32(data) + j * sizeof(uint32)))^ := tmpD; - end; -end; - -{ ============================================================ - UnitTest - ============================================================ } -procedure UnitTest; -var - passed, failed : uint32; - s : pchar; - ext : pchar; - names : array[0..4] of pchar; - sizes : array[0..4] of uint32; - - procedure Assert(condition : boolean; testName : pchar); - var - msg : pchar; - begin - if condition then begin - inc(passed); - end else begin - inc(failed); - msg := stringConcat('FAIL: ', testName); - io.syslog.logln('SEARCH', msg); - kfree(void(msg)); - end; - end; - - procedure PrintSummary; - var - pStr, fStr, msg, tmp : pchar; - begin - pStr := intToString(passed); - fStr := intToString(failed); - msg := stringConcat(pStr, ' passed, '); - tmp := stringConcat(msg, fStr); - kfree(void(msg)); - msg := stringConcat(tmp, ' failed.'); - kfree(void(tmp)); - io.syslog.logln('SEARCH', msg); - kfree(void(msg)); - kfree(void(pStr)); - kfree(void(fStr)); - end; - -begin - passed := 0; - failed := 0; - io.syslog.logln('SEARCH', 'Unit tests starting...'); - - { === getFileExtension === } - ext := getFileExtension('readme.txt'); - Assert(ext <> nil, 'getFileExt txt not nil'); - Assert(stringEquals(ext, '.txt'), 'getFileExt txt'); - ext := getFileExtension('archive.tar.gz'); - Assert(stringEquals(ext, '.gz'), 'getFileExt tar.gz'); - ext := getFileExtension('noext'); - Assert(ext = nil, 'getFileExt none'); - ext := getFileExtension(''); - Assert(ext = nil, 'getFileExt empty'); - ext := getFileExtension(nil); - Assert(ext = nil, 'getFileExt nil'); - ext := getFileExtension('.hidden'); - Assert(stringEquals(ext, '.hidden'), 'getFileExt dotfile'); - - { === fmtFileSize === } - s := fmtFileSize(0); - Assert(stringEquals(s, '0 B'), 'fmtFileSize 0'); - kfree(void(s)); - s := fmtFileSize(512); - Assert(stringEquals(s, '512 B'), 'fmtFileSize 512'); - kfree(void(s)); - s := fmtFileSize(1024); - Assert(stringEquals(s, '1.0 KB'), 'fmtFileSize 1KB'); - kfree(void(s)); - s := fmtFileSize(1024 * 1024); - Assert(stringEquals(s, '1.0 MB'), 'fmtFileSize 1MB'); - kfree(void(s)); - - { === sortStringArray === } - names[0] := 'delta'; - names[1] := 'alpha'; - names[2] := 'Charlie'; - names[3] := 'bravo'; - names[4] := 'Echo'; - sortStringArray(@names[0], 5); - Assert(stringEqualsCI(names[0], 'alpha'), 'sort[0]=alpha'); - Assert(stringEqualsCI(names[1], 'bravo'), 'sort[1]=bravo'); - Assert(stringEqualsCI(names[2], 'Charlie'), 'sort[2]=charlie'); - Assert(stringEqualsCI(names[3], 'delta'), 'sort[3]=delta'); - Assert(stringEqualsCI(names[4], 'Echo'), 'sort[4]=echo'); - - { === sortStringArray edge cases === } - sortStringArray(@names[0], 0); { should not crash } - sortStringArray(@names[0], 1); { should not crash } - sortStringArray(nil, 5); { should not crash } - - { === sortStringArrayWithData === } - names[0] := 'delta'; sizes[0] := 4; - names[1] := 'alpha'; sizes[1] := 1; - names[2] := 'Charlie'; sizes[2] := 3; - names[3] := 'bravo'; sizes[3] := 2; - names[4] := 'Echo'; sizes[4] := 5; - sortStringArrayWithData(@names[0], @sizes[0], 5); - Assert(stringEqualsCI(names[0], 'alpha'), 'sortData[0]=alpha'); - Assert(sizes[0] = 1, 'sortData[0] data=1'); - Assert(stringEqualsCI(names[4], 'Echo'), 'sortData[4]=echo'); - Assert(sizes[4] = 5, 'sortData[4] data=5'); - - PrintSummary; -end; - -end. diff --git a/src/core/core.util.pas b/src/core/core.util.pas index f6c233fd..8ec8c73e 100644 --- a/src/core/core.util.pas +++ b/src/core/core.util.pas @@ -33,6 +33,7 @@ function switchendian16(b : uint16) : uint16; function switchendian32(b : uint32) : uint32; function getWord(i : uint32; hi : boolean) : uint16; function getByte(i : uint32; index : uint8) : uint8; +function RolDWord(value: uint32; distance: uint8): uint32; procedure memset(location : PtrUInt; value : uint8; size : SizeUInt); procedure memcpy(source : PtrUInt; dest : PtrUInt; size : SizeUInt); @@ -42,8 +43,19 @@ function HexCharToDecimal(hex : char) : uint8; function abs(x : sint32) : uint32; +procedure waitTicks(count: uint32); + implementation -uses memory.raw, arch.platform.cpu; +uses memory.raw, arch.platform.cpu, arch.platform.process; + +function RolDWord(value: uint32; distance: uint8): uint32; +begin + distance := distance and 31; + if distance = 0 then + RolDWord := value + else + RolDWord := (value shl distance) or (value shr (32 - distance)); +end; function abs(x : sint32) : uint32; var @@ -145,4 +157,13 @@ begin BCDToUint8:= ((bcd SHR 4) * 10) + (bcd AND $0F); end; +procedure waitTicks(count: uint32); +var + started: uint32; +begin + started := arch.platform.process.timerTicks; + while arch.platform.process.timerTicks - started < count do + arch.platform.cpu.idle; +end; + end. diff --git a/src/core/ds/core.ds.cfifo.pas b/src/core/ds/core.ds.cfifo.pas index 52c07cea..56e7d2b5 100644 --- a/src/core/ds/core.ds.cfifo.pas +++ b/src/core/ds/core.ds.cfifo.pas @@ -24,7 +24,7 @@ interface uses core.ds.types, memory.heap, - core.util, arch.x86.util; + core.util; { ============================================================================ } { Contiguous FIFO Queue — CFIFO_* API } @@ -93,7 +93,7 @@ uses implementation uses - io.syslog, core.strings; + arch.platform.diagnostics, core.strings; { ---------------------------------------------------------------------------- } { Internal helpers } @@ -102,38 +102,43 @@ uses {** Shifts elements back to index 0, called when Head >= Capacity div 2. **} procedure CFIFO_Compact(Queue : PCFIFOQueue); var - src : uint32; - len : uint32; + src : PtrUInt; + len : SizeUInt; begin if (Queue^.Head = 0) or (Queue^.Count = 0) then exit; - src := uint32(Queue^.Data) + (Queue^.Head * Queue^.ElementSize); - len := Queue^.Count * Queue^.ElementSize; + src := PtrUInt(Queue^.Data) + (SizeUInt(Queue^.Head) * Queue^.ElementSize); + len := SizeUInt(Queue^.Count) * Queue^.ElementSize; { memcpy is safe here because dst < src (Head > 0), so no overlap issue } - memcpy(src, uint32(Queue^.Data), len); + memcpy(src, PtrUInt(Queue^.Data), len); Queue^.Head := 0; end; {** Doubles the buffer capacity, compacting first. **} -procedure CFIFO_Grow(Queue : PCFIFOQueue); +function CFIFO_Grow(Queue : PCFIFOQueue): boolean; var newCap : uint32; newBuf : void; begin + CFIFO_Grow := false; + if Queue^.Capacity > High(uint32) div 2 then exit; + { Compact so elements start at index 0 before copying } CFIFO_Compact(Queue); newCap := Queue^.Capacity * 2; if newCap = 0 then newCap := 8; - newBuf := kalloc(newCap * Queue^.ElementSize); - memset(uint32(newBuf), 0, newCap * Queue^.ElementSize); + if SizeUInt(newCap) > High(SizeUInt) div Queue^.ElementSize then exit; + newBuf := kalloc(SizeUInt(newCap) * Queue^.ElementSize); + if newBuf = nil then exit; if Queue^.Count > 0 then - memcpy(uint32(Queue^.Data), uint32(newBuf), Queue^.Count * Queue^.ElementSize); + memcpy(PtrUInt(Queue^.Data), PtrUInt(newBuf), SizeUInt(Queue^.Count) * Queue^.ElementSize); kfree(Queue^.Data); Queue^.Data := newBuf; Queue^.Capacity := newCap; + CFIFO_Grow := true; end; { ---------------------------------------------------------------------------- } @@ -141,24 +146,36 @@ end; { ---------------------------------------------------------------------------- } function CFIFO_New(ElementSize : uint32; InitialCapacity : uint32) : PCFIFOQueue; +var + queue: PCFIFOQueue; begin - CFIFO_New := PCFIFOQueue(kalloc(sizeof(TCFIFOQueue))); - CFIFO_New^.ElementSize := ElementSize; - CFIFO_New^.Capacity := InitialCapacity; - CFIFO_New^.Count := 0; - CFIFO_New^.Head := 0; - CFIFO_New^.Data := kalloc(InitialCapacity * ElementSize); - memset(uint32(CFIFO_New^.Data), 0, InitialCapacity * ElementSize); + CFIFO_New := nil; + if ElementSize = 0 then exit; + if InitialCapacity = 0 then InitialCapacity := 8; + if SizeUInt(InitialCapacity) > High(SizeUInt) div ElementSize then exit; + + queue := PCFIFOQueue(kalloc(sizeof(TCFIFOQueue))); + if queue = nil then exit; + queue^.Data := kalloc(SizeUInt(InitialCapacity) * ElementSize); + if queue^.Data = nil then begin + kfree(void(queue)); + exit; + end; + queue^.ElementSize := ElementSize; + queue^.Capacity := InitialCapacity; + CFIFO_New := queue; end; procedure CFIFO_Enqueue(Queue : PCFIFOQueue; Data : void); var tail : uint32; - dst : uint32; + dst : PtrUInt; begin + if (Queue = nil) or (Data = nil) then exit; + { Grow if all slots are consumed } if Queue^.Count >= Queue^.Capacity then - CFIFO_Grow(Queue); + if not CFIFO_Grow(Queue) then exit; { Compact if there's no room at the back even though Count < Capacity, which happens when Head has advanced past zero } @@ -169,20 +186,20 @@ begin tail := Queue^.Count; { Head is now 0 } end; - dst := uint32(Queue^.Data) + (tail * Queue^.ElementSize); - memcpy(uint32(Data), dst, Queue^.ElementSize); + dst := PtrUInt(Queue^.Data) + (SizeUInt(tail) * Queue^.ElementSize); + memcpy(PtrUInt(Data), dst, Queue^.ElementSize); Queue^.Count := Queue^.Count + 1; end; function CFIFO_Dequeue(Queue : PCFIFOQueue; Data : void) : boolean; var - src : uint32; + src : PtrUInt; begin CFIFO_Dequeue := false; if Queue^.Count = 0 then exit; - src := uint32(Queue^.Data) + (Queue^.Head * Queue^.ElementSize); - memcpy(src, uint32(Data), Queue^.ElementSize); + src := PtrUInt(Queue^.Data) + (SizeUInt(Queue^.Head) * Queue^.ElementSize); + memcpy(src, PtrUInt(Data), Queue^.ElementSize); Queue^.Head := Queue^.Head + 1; Queue^.Count := Queue^.Count - 1; @@ -203,7 +220,7 @@ begin if Queue^.Count = 0 then CFIFO_Peek := nil else - CFIFO_Peek := void(uint32(Queue^.Data) + (Queue^.Head * Queue^.ElementSize)); + CFIFO_Peek := void(PtrUInt(Queue^.Data) + (SizeUInt(Queue^.Head) * Queue^.ElementSize)); end; function CFIFO_Size(Queue : PCFIFOQueue) : uint32; @@ -232,6 +249,7 @@ procedure UnitTest; var q : PCFIFOQueue; v, r : uint32; + nativeValue, nativeResult: PtrUInt; ok : boolean; p : void; i : uint32; @@ -246,32 +264,16 @@ var end else begin inc(failed); msg := stringConcat('FAIL: ', testName); - io.syslog.logln('CFIFO', msg); + arch.platform.diagnostics.log('CFIFO', msg); kfree(void(msg)); end; end; - procedure PrintSummary; - var - pStr, fStr, msg, tmp : pchar; - begin - pStr := intToString(passed); - fStr := intToString(failed); - msg := stringConcat(pStr, ' passed, '); - tmp := stringConcat(msg, fStr); - kfree(void(msg)); - msg := stringConcat(tmp, ' failed.'); - kfree(void(tmp)); - io.syslog.logln('CFIFO', msg); - kfree(void(msg)); - kfree(void(pStr)); - kfree(void(fStr)); - end; begin passed := 0; failed := 0; - io.syslog.logln('CFIFO', 'Unit tests starting...'); + arch.platform.diagnostics.log('CFIFO', 'Unit tests starting...'); { === New / Empty / Size / Capacity === } q := CFIFO_New(sizeof(uint32), 4); @@ -306,12 +308,18 @@ begin Assert(CFIFO_Size(q) = 6, 'Size after grow'); { === Verify FIFO order: 30,40,50,60,70,80 === } - ok := CFIFO_Dequeue(q, @r); Assert(r = 30, 'Order 30'); - ok := CFIFO_Dequeue(q, @r); Assert(r = 40, 'Order 40'); - ok := CFIFO_Dequeue(q, @r); Assert(r = 50, 'Order 50'); - ok := CFIFO_Dequeue(q, @r); Assert(r = 60, 'Order 60'); - ok := CFIFO_Dequeue(q, @r); Assert(r = 70, 'Order 70'); - ok := CFIFO_Dequeue(q, @r); Assert(r = 80, 'Order 80'); + ok := CFIFO_Dequeue(q, @r); + Assert(r = 30, 'Order 30'); + ok := CFIFO_Dequeue(q, @r); + Assert(r = 40, 'Order 40'); + ok := CFIFO_Dequeue(q, @r); + Assert(r = 50, 'Order 50'); + ok := CFIFO_Dequeue(q, @r); + Assert(r = 60, 'Order 60'); + ok := CFIFO_Dequeue(q, @r); + Assert(r = 70, 'Order 70'); + ok := CFIFO_Dequeue(q, @r); + Assert(r = 80, 'Order 80'); Assert(CFIFO_IsEmpty(q), 'Empty after drain'); { === Edge: peek/dequeue on empty === } @@ -322,7 +330,15 @@ begin CFIFO_Free(q); - PrintSummary; + q := CFIFO_New(sizeof(PtrUInt), 0); + nativeValue := PtrUInt(q); + CFIFO_Enqueue(q, void(@nativeValue)); + Assert(CFIFO_Dequeue(q, void(@nativeResult)) and (nativeResult = nativeValue), + 'Native pointer survives queue storage'); + CFIFO_Free(q); + Assert(CFIFO_New(0, 4) = nil, 'Zero-sized elements rejected'); + + testSummary('CFIFO', 'CFIFO_TESTS_PASSED', 'CFIFO_TESTS_FAILED', passed, failed); end; end. diff --git a/src/core/ds/core.ds.dynamic.pas b/src/core/ds/core.ds.dynamic.pas index adee400e..80de780e 100644 --- a/src/core/ds/core.ds.dynamic.pas +++ b/src/core/ds/core.ds.dynamic.pas @@ -14,6 +14,7 @@ Original interface: Kieron Morris. Capacity remains bounded to uint32 bytes; addresses are native-width. Operations require caller-owned synchronization. } unit core.ds.dynamic; + interface type PDList = ^TDList; @@ -22,6 +23,7 @@ type Data: void; ElementSize, DataSize: uint32; end; + function DL_New(ElementSize : uint32) : PDList; function DL_Add(DList : PDList) : Void; function DL_Delete(DList : PDList; idx : uint32) : boolean; @@ -37,14 +39,20 @@ function DL_Insert(DList : PDList; idx : uint32) : Void; function DL_Capacity(DList : PDList) : uint32; procedure DL_Reserve(DList : PDList; NewCapacity : uint32); procedure DL_ShrinkToFit(DList : PDList); + implementation uses memory.heap, memory.raw; + function resize(list: PDList; bytes: uint32): boolean; -var data: void; +var + data: void; begin resize := false; if bytes < list^.Count * list^.ElementSize then exit; - if bytes = list^.DataSize then begin resize := true; exit; end; + if bytes = list^.DataSize then begin + resize := true; + exit; + end; data := nil; if bytes <> 0 then begin data := kalloc(bytes); @@ -52,24 +60,33 @@ begin memory.raw.move(pointer(list^.Data), pointer(data), list^.Count * list^.ElementSize); end; kfree(list^.Data); - list^.Data := data; list^.DataSize := bytes; + list^.Data := data; + list^.DataSize := bytes; resize := true; end; + function ensure(list: PDList; count: uint32): boolean; -var bytes, capacity: uint32; +var + bytes, capacity: uint32; begin ensure := false; if (list = nil) or (list^.ElementSize = 0) then exit; if count > High(uint32) div list^.ElementSize then exit; bytes := count * list^.ElementSize; - if bytes <= list^.DataSize then begin ensure := true; exit; end; + if bytes <= list^.DataSize then begin + ensure := true; + exit; + end; capacity := list^.DataSize; if capacity <= High(uint32) div 2 then capacity := capacity * 2; if capacity < bytes then capacity := bytes; ensure := resize(list, capacity); end; + function DL_New(ElementSize: uint32): PDList; -var list: PDList; count: uint32; +var + list: PDList; + count: uint32; begin DL_New := nil; if (ElementSize = 0) or (ElementSize > High(uint32) div 4) then exit; @@ -80,11 +97,16 @@ begin if ElementSize > 64 then count := 8 else if ElementSize > 32 then count := 16 else if ElementSize > 16 then count := 32 else count := 64; - if not ensure(list, count) then begin kfree(void(list)); exit; end; + if not ensure(list, count) then begin + kfree(void(list)); + exit; + end; DL_New := list; end; + function DL_Insert(DList: PDList; idx: uint32): void; -var address: PtrUInt; +var + address: PtrUInt; begin DL_Insert := nil; if DList = nil then exit; @@ -97,13 +119,16 @@ begin inc(DList^.Count); DL_Insert := void(address); end; + function DL_Add(DList: PDList): void; begin DL_Add := nil; if DList <> nil then DL_Add := DL_Insert(DList, DList^.Count); end; + function DL_Delete(DList: PDList; idx: uint32): boolean; -var address: PtrUInt; +var + address: PtrUInt; begin DL_Delete := false; if DList = nil then exit; @@ -116,6 +141,7 @@ begin Explicit DL_ShrinkToFit remains available at a suitable safe point. } DL_Delete := true; end; + function DL_Get(DList: PDList; idx: uint32): void; begin DL_Get := nil; @@ -123,25 +149,32 @@ begin if idx < DList^.Count then DL_Get := void(PtrUInt(DList^.Data) + SizeUInt(idx) * DList^.ElementSize); end; + function DL_Set(DList: PDList; idx: uint32; elm: PUInt32): boolean; -var target: void; +var + target: void; begin target := DL_Get(DList, idx); DL_Set := (target <> nil) and (elm <> nil); if DL_Set then memory.raw.move(pointer(elm), pointer(target), DList^.ElementSize); end; + function DL_Size(DList: PDList): uint32; begin DL_Size := 0; if DList <> nil then DL_Size := DList^.Count; end; + procedure DL_Free(DList: PDList); begin if DList = nil then exit; - kfree(DList^.Data); kfree(void(DList)); + kfree(DList^.Data); + kfree(void(DList)); end; + function DL_IndexOf(DList: PDList; elm: PUInt32): uint32; -var offset: PtrUInt; +var + offset: PtrUInt; begin DL_IndexOf := High(uint32); if (DList = nil) or (elm = nil) then exit; @@ -150,15 +183,21 @@ begin if (offset < SizeUInt(DList^.Count) * DList^.ElementSize) and (offset mod DList^.ElementSize = 0) then DL_IndexOf := offset div DList^.ElementSize; end; + function DL_Contains(DList: PDList; elm: PUInt32): boolean; -begin DL_Contains := DL_IndexOf(DList, elm) <> High(uint32); end; +begin + DL_Contains := DL_IndexOf(DList, elm) <> High(uint32); +end; + function DL_Concat(DList1, DList2: PDList): PDList; -var count, oldCount: uint32; +var + count, oldCount: uint32; begin DL_Concat := nil; if (DList1 = nil) or (DList2 = nil) then exit; if DList1^.ElementSize <> DList2^.ElementSize then exit; - count := DList2^.Count; oldCount := DList1^.Count; + count := DList2^.Count; + oldCount := DList1^.Count; if count > High(uint32) - oldCount then exit; if not ensure(DList1, oldCount + count) then exit; memory.raw.move(pointer(DList2^.Data), @@ -167,15 +206,23 @@ begin DList1^.Count := oldCount + count; DL_Concat := DList1; end; + procedure DL_Clear(DList: PDList); -begin if DList <> nil then DList^.Count := 0; end; +begin + if DList <> nil then DList^.Count := 0; +end; + function DL_Capacity(DList: PDList): uint32; begin DL_Capacity := 0; if DList <> nil then DL_Capacity := DList^.DataSize div DList^.ElementSize; end; + procedure DL_Reserve(DList: PDList; NewCapacity: uint32); -begin ensure(DList, NewCapacity); end; +begin + ensure(DList, NewCapacity); +end; + procedure DL_ShrinkToFit(DList: PDList); begin if DList <> nil then resize(DList, DList^.Count * DList^.ElementSize); diff --git a/src/core/ds/core.ds.hashmap.pas b/src/core/ds/core.ds.hashmap.pas index 68402dd6..56274a26 100644 --- a/src/core/ds/core.ds.hashmap.pas +++ b/src/core/ds/core.ds.hashmap.pas @@ -1,4 +1,5 @@ // Copyright 2021 Kieron Morris +// Copyright 2026 Aaron Hance // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,9 +27,7 @@ uses memory.heap, core.enc.fnv1a, core.strings, - io.syslog, - debug.tracer, - core.util, arch.x86.util; + arch.platform.diagnostics; type DPHashItem = ^PHashItem; @@ -59,6 +58,7 @@ function newEx(size : uint32; loadFactor : Single) : PHashMap; procedure add(map : PHashMap; key : pchar; value : void); function get(map : PHashMap; key : pchar) : void; procedure delete(map : PHashMap; key : pchar; freeItem : boolean); +procedure UnitTest; procedure printMap(map : PHashMap); procedure forEach(map : PHashMap; cb : THashForEachCb; ud : void); @@ -66,7 +66,7 @@ implementation function KeyHash(key : pchar) : uint32; begin - KeyHash := Hash_FNV1a32(void(uint32(key)), StringSize(key)); + KeyHash := Hash_FNV1a32(void(key), StringSize(key)); end; function hashIndex(size : uint32; hash : uint32) : uint32; @@ -74,31 +74,10 @@ begin hashIndex:= hash mod size; end; -function newItem : PHashItem; -begin - newItem:= PHashItem(kalloc(sizeof(THashItem))); - newItem^.Next:= nil; - newItem^.Key:= nil; - newItem^.Data:= nil; - newItem^.Hash:= 0; -end; - procedure putItem(table : DPHashItem; item : PHashItem; idx : uint32); -var - ExistingItem : PHashItem; - c : uint32; - begin - ExistingItem:= table[idx]; - if ExistingItem = nil then begin - table[idx]:= item; - end else begin - c:=0; - while ExistingItem^.Next <> nil do begin - ExistingItem:= ExistingItem^.Next; - end; - ExistingItem^.Next:= item; - end; + item^.Next := table[idx]; + table[idx] := item; end; procedure checkAndRebase(map : PHashMap); @@ -117,12 +96,11 @@ begin loadFactor:= map^.LoadFactor; max_load:= size * loadFactor; if map^.count > max_load then begin - NewSize:= map^.Size * 2; - NewTable:= DPHashItem(kalloc(sizeof(PHashItem) * NewSize)); + if map^.Size > High(uint32) div 2 then exit; + NewSize := map^.Size * 2; + if SizeUInt(NewSize) > High(SizeUInt) div sizeof(PHashItem) then exit; + NewTable:= DPHashItem(kalloc(SizeUInt(NewSize) * sizeof(PHashItem))); If NewTable <> nil then begin - for i:=0 to NewSize-1 do begin - NewTable[i]:= nil; - end; for i:=0 to map^.size-1 do begin item:= map^.table[i]; while item <> nil do begin @@ -141,40 +119,34 @@ end; procedure add(map : PHashMap; key : pchar; value : void); var - Idx : uint32; - hash : uint32; - Item : PHashItem; - nItem : PHashItem; - + idx, hash: uint32; + item: PHashItem; begin - if (map <> nil) and (key <> nil) then begin - hash:= KeyHash(key); - Idx:= hashIndex(map^.size, hash); - if map^.Table[Idx] = nil then begin - Item:= newItem; - Item^.Next:= nil; - map^.Table[Idx]:= Item; - end else begin - //Collision - Item:= map^.Table[Idx]; - while not StringEquals(Item^.key, key) do begin - if Item^.Next <> nil then begin - Item:= Item^.Next; - end else begin - nItem:= newItem(); - Item^.Next:= nItem; - Item:= nItem; - Item^.Next:= nil; - break; - end; - end; + if (map = nil) or (key = nil) then exit; + + hash := KeyHash(key); + idx := hashIndex(map^.Size, hash); + item := map^.Table[idx]; + while item <> nil do begin + if (item^.Hash = hash) and StringEquals(item^.Key, key) then begin + item^.Data := value; + exit; end; - if Item^.Key = nil then Item^.Key:= stringCopy(key); - Item^.Data:= value; - Item^.Hash:= hash; - inc(map^.count); - checkAndRebase(map); + item := item^.Next; end; + + item := PHashItem(kalloc(sizeof(THashItem))); + if item = nil then exit; + item^.Key := stringCopy(key); + if item^.Key = nil then begin + kfree(void(item)); + exit; + end; + item^.Data := value; + item^.Hash := hash; + putItem(map^.Table, item, idx); + inc(map^.Count); + checkAndRebase(map); end; function get(map : PHashMap; key : pchar) : void; @@ -200,19 +172,22 @@ end; function newEx(size : uint32; loadFactor : Single) : PHashMap; var - Map : PHashMap; - i : uint32; - + map: PHashMap; begin - Map:= PHashMap(kalloc(sizeof(THashMap))); - Map^.size:= size; - Map^.Table:= DPHashItem(Kalloc(sizeof(PHashItem) * Size)); - Map^.LoadFactor:= loadFactor; - Map^.Count:= 0; - for i:=0 to size-1 do begin - Map^.Table[i]:= nil; + newEx := nil; + if (size = 0) or (SizeUInt(size) > High(SizeUInt) div sizeof(PHashItem)) then exit; + if not (loadFactor > 0) then exit; + + map := PHashMap(kalloc(sizeof(THashMap))); + if map = nil then exit; + map^.Table := DPHashItem(kalloc(SizeUInt(size) * sizeof(PHashItem))); + if map^.Table = nil then begin + kfree(void(map)); + exit; end; - newEx:= Map; + map^.Size := size; + map^.LoadFactor := loadFactor; + newEx := map; end; function new : PHashMap; @@ -275,31 +250,79 @@ end; procedure printMap(map : PHashMap); var - i,c : uint32; - item : PHashItem; - + i: uint32; + item: PHashItem; begin - for i:=0 to map^.Size-1 do begin - if map^.table[i] <> nil then begin - io.syslog.writestring('Map['); - io.syslog.writeint(i); - io.syslog.writestring(']->(0)="'); - io.syslog.writestring(map^.table[i]^.key); - io.syslog.writestring('"'); - item:= map^.table[i]^.next; - c:=1; - while item <> nil do begin - io.syslog.writestring('->('); - io.syslog.writeint(c); - io.syslog.writestring(')="'); - io.syslog.writestring(item^.key); - io.syslog.writestring('"'); - inc(c); - item:= item^.next; - end; - io.syslog.writestringln(' '); + if (map = nil) or (map^.Size = 0) then exit; + + for i := 0 to map^.Size - 1 do begin + item := map^.Table[i]; + while item <> nil do begin + log('HASHMAP', item^.Key); + item := item^.Next; end; end; end; -end. \ No newline at end of file +procedure UnitTest; +var + map: PHashMap; + value: void; + key: pchar; + i, passed, failed: uint32; + beforeFree: SizeUInt; + + procedure Assert(condition: boolean; name: pchar); + begin + if condition then + inc(passed) + else begin + inc(failed); + log('HASHMAP', name); + end; + end; +begin + passed := 0; + failed := 0; + beforeFree := lmm_total_free; + value := kalloc(sizeof(PtrUInt)); + map := newEx(1, 100); + add(map, 'a', value); + add(map, 'b', value); + add(map, 'c', value); + Assert((map^.Count = 3) and (get(map, 'a') = value) and + (get(map, 'c') = value), 'FAIL: native hash collision chain'); + add(map, 'b', nil); + Assert((map^.Count = 3) and (get(map, 'b') = nil), 'FAIL: hash replacement count'); + delete(map, 'b', false); + Assert((map^.Count = 2) and (get(map, 'a') = value) and + (get(map, 'c') = value), 'FAIL: hash collision middle deletion'); + delete(map, 'c', false); + delete(map, 'a', false); + Assert(map^.Count = 0, 'FAIL: hash collision head and tail deletion'); + kfree(void(map^.Table)); + kfree(void(map)); + + map := new; + for i := 0 to 39 do begin + key := intToString(i); + add(map, key, value); + kfree(void(key)); + end; + Assert((map^.Count = 40) and (map^.Size > HASHMAP_DEFAULT_SIZE), + 'FAIL: hash growth'); + for i := 0 to 39 do begin + key := intToString(i); + Assert(get(map, key) = value, 'FAIL: hash rebase preserves high pointers'); + delete(map, key, false); + kfree(void(key)); + end; + kfree(void(map^.Table)); + kfree(void(map)); + kfree(value); + Assert(newEx(0, 0.75) = nil, 'FAIL: zero-sized hash table rejected'); + Assert(lmm_total_free = beforeFree, 'FAIL: hash allocation accounting'); + testSummary('HASHMAP', 'HASHMAP_TESTS_PASSED', 'HASHMAP_TESTS_FAILED', passed, failed); +end; + +end. diff --git a/src/core/ds/core.ds.lists.pas b/src/core/ds/core.ds.lists.pas index 09518497..17555c5b 100644 --- a/src/core/ds/core.ds.lists.pas +++ b/src/core/ds/core.ds.lists.pas @@ -24,10 +24,9 @@ interface uses core.ds.types, core.ds.dynamic, - io.syslog, memory.heap, - debug.tracer, - core.util, arch.x86.util; + arch.platform.diagnostics, + core.util; type {** @@ -288,6 +287,9 @@ implementation uses core.strings; +type + PStringSlot = ^pchar; + { ---------------------------------------------------------------------------- } { Managed Linked List } { ---------------------------------------------------------------------------- } @@ -300,6 +302,7 @@ uses function LL_New(ElementSize : uint32) : PLinkedListBase; begin LL_New := PLinkedListBase(kalloc(sizeof(TLinkedListBase))); + if LL_New = nil then exit; LL_New^.ElementSize := ElementSize; LL_New^.Count := 0; LL_New^.Head := nil; @@ -311,38 +314,10 @@ end; @returns Pointer to the newly allocated data space. **} function LL_Add(LinkedList : PLinkedListBase) : Void; -var - Element : PLinkedList; - Base : PLinkedList; begin - if LinkedList^.Head = nil then - begin - Element := PLinkedList(kalloc(sizeof(TLinkedList))); - Element^.Previous := nil; - Element^.Next := nil; - Element^.Data := kalloc(LinkedList^.ElementSize); - memset(uint32(Element^.Data), 0, LinkedList^.ElementSize); - - LinkedList^.Head := Element; - LinkedList^.Count := 1; - LL_Add := Element^.Data; - end - else - begin - Base := LinkedList^.Head; - while Base^.Next <> nil do - Base := Base^.Next; - - Element := PLinkedList(kalloc(sizeof(TLinkedList))); - Element^.Previous := Base; - Element^.Next := nil; - Element^.Data := kalloc(LinkedList^.ElementSize); - memset(uint32(Element^.Data), 0, LinkedList^.ElementSize); - - Base^.Next := Element; - LinkedList^.Count := LinkedList^.Count + 1; - LL_Add := Element^.Data; - end; + LL_Add := nil; + if LinkedList <> nil then + LL_Add := LL_Insert(LinkedList, LinkedList^.Count); end; {** @@ -406,63 +381,37 @@ end; **} function LL_Insert(LinkedList : PLinkedListBase; idx : uint32) : void; var - i : uint32; - Base : PLinkedList; - Prev, Next : PLinkedList; - Element : PLinkedList; + i: uint32; + next, prev, element: PLinkedList; begin - LL_Insert := nil; - if idx > LinkedList^.Count then - Exit; + LL_Insert := nil; + if LinkedList = nil then exit; + if (idx > LinkedList^.Count) or (LinkedList^.Count = High(uint32)) then exit; - Base := LinkedList^.Head; - i := 0; - while (i < idx) and (Base <> nil) do - begin - Base := Base^.Next; - Inc(i); - end; + prev := nil; + next := LinkedList^.Head; + for i := 1 to idx do begin + prev := next; + next := next^.Next; + end; - // Insert at head - if i = 0 then - begin - Element := PLinkedList(kalloc(sizeof(TLinkedList))); - Element^.Data := kalloc(LinkedList^.ElementSize); - memset(uint32(Element^.Data), 0, LinkedList^.ElementSize); - Element^.Next := LinkedList^.Head; - Element^.Previous := nil; - if LinkedList^.Head <> nil then - LinkedList^.Head^.Previous := Element; - LinkedList^.Head := Element; - LinkedList^.Count := LinkedList^.Count + 1; - LL_Insert := Element^.Data; - end - else - begin - // Insert in the middle or end - if Base = nil then - Exit; - Prev := Base^.Previous; - Next := Base; - - Element := PLinkedList(kalloc(sizeof(TLinkedList))); - Element^.Data := kalloc(LinkedList^.ElementSize); - memset(uint32(Element^.Data), 0, LinkedList^.ElementSize); - - Element^.Previous := Prev; - Element^.Next := Next; - - if Prev = nil then - LinkedList^.Head := Element + element := PLinkedList(kalloc(sizeof(TLinkedList))); + if element = nil then exit; + element^.Data := kalloc(LinkedList^.ElementSize); + if element^.Data = nil then begin + kfree(void(element)); + exit; + end; + element^.Previous := prev; + element^.Next := next; + if prev = nil then + LinkedList^.Head := element else - Prev^.Next := Element; - - if Next <> nil then - Next^.Previous := Element; - - LinkedList^.Count := LinkedList^.Count + 1; - LL_Insert := Element^.Data; - end; + prev^.Next := element; + if next <> nil then + next^.Previous := element; + inc(LinkedList^.Count); + LL_Insert := element^.Data; end; {** @@ -511,40 +460,8 @@ end; @returns Pointer to the resulting linked list. **} function LL_FromString(str : pchar; delimter : char) : PLinkedListBase; -var - list : PLinkedListBase; - head, tail : pchar; - size : uint32; - null_delim : boolean; - elm : puint32; - out_str : pchar; begin - list := LL_New(sizeof(uint32)); - LL_FromString := list; - - head := str; - tail := head; - null_delim := false; - - while not null_delim do - begin - if (head^ = delimter) or (head^ = char(0)) then - begin - if head^ = char(0) then - null_delim := true; - size := uint32(head) - uint32(tail); - if size > 0 then - begin - elm := puint32(LL_Add(list)); - out_str := stringNew(size + 1); - // Copy from tail -> out_str - memcpy(uint32(tail), uint32(out_str), size); - elm^ := uint32(out_str); - end; - tail := head + 1; - end; - Inc(head); - end; + LL_FromString := STRLL_FromString(str, delimter); end; { ---------------------------------------------------------------------------- } @@ -558,10 +475,10 @@ end; **} procedure STRLL_Add(LinkedList : PLinkedListBase; str : pchar); var - ptr : puint32; + ptr : PStringSlot; begin - ptr := puint32(LL_Add(LinkedList)); - ptr^ := uint32(str); + ptr := PStringSlot(LL_Add(LinkedList)); + if ptr <> nil then ptr^ := str; end; {** @@ -572,9 +489,9 @@ end; **} function STRLL_Get(LinkedList : PLinkedListBase; idx : uint32) : pchar; var - ptr : puint32; + ptr : PStringSlot; begin - ptr := puint32(LL_Get(LinkedList, idx)); + ptr := PStringSlot(LL_Get(LinkedList, idx)); STRLL_Get := nil; if ptr <> nil then STRLL_Get := pchar(ptr^); @@ -586,7 +503,7 @@ end; **} function STRLL_New : PLinkedListBase; begin - STRLL_New := LL_New(sizeof(uint32)); + STRLL_New := LL_New(sizeof(pchar)); end; {** @@ -609,11 +526,12 @@ procedure STRLL_Delete(LinkedList : PLinkedListBase; idx : uint32); var ptr : pchar; begin - debug.tracer.push_trace('core.ds.lists.STRLL_Delete (Don''t add static core.strings)'); + arch.platform.diagnostics.push_trace('core.ds.lists.STRLL_Delete (Don''t add static core.strings)'); ptr := STRLL_Get(LinkedList, idx); if ptr <> nil then kfree(void(ptr)); LL_Delete(LinkedList, idx); + pop_trace; end; {** @@ -646,38 +564,46 @@ end; **} function STRLL_FromString(str : pchar; delimter : char) : PLinkedListBase; var - list : PLinkedListBase; - head, tail : pchar; - size : uint32; - null_delim : boolean; - elm : puint32; - out_str : pchar; + list: PLinkedListBase; + head, tail, text: pchar; + size: SizeUInt; + slot: PStringSlot; begin - list := LL_New(sizeof(uint32)); - STRLL_FromString := list; + STRLL_FromString := nil; + if str = nil then exit; + list := STRLL_New; + if list = nil then exit; - head := str; - tail := head; - null_delim := false; - - while not null_delim do - begin - if (head^ = delimter) or (head^ = char(0)) then - begin - if head^ = char(0) then - null_delim := true; - size := uint32(head) - uint32(tail); - if size > 0 then - begin - elm := puint32(LL_Add(list)); - out_str := stringNew(size + 1); - memcpy(uint32(tail), uint32(out_str), size); - elm^ := uint32(out_str); - end; - tail := head + 1; - end; - Inc(head); - end; + head := str; + tail := str; + repeat + if (head^ = delimter) or (head^ = #0) then begin + size := PtrUInt(head) - PtrUInt(tail); + if size > 0 then begin + if size >= High(uint32) then begin + STRLL_Free(list); + exit; + end; + text := stringNew(uint32(size)); + if text = nil then begin + STRLL_Free(list); + exit; + end; + slot := PStringSlot(LL_Add(list)); + if slot = nil then begin + kfree(void(text)); + STRLL_Free(list); + exit; + end; + memcpy(PtrUInt(tail), PtrUInt(text), size); + slot^ := text; + end; + tail := head + 1; + end; + if head^ = #0 then break; + inc(head); + until false; + STRLL_FromString := list; end; { ---------------------------------------------------------------------------- } @@ -766,118 +692,56 @@ end; procedure TestAllLists; var - i : uint32; - p : pointer; - ll : PLinkedListBase; // Managed linked list - strList: PLinkedListBase; // String linked list - dlist : PDList; // Dynamic list - intPtr : puint32; // For dynamic list integer values - testStr: pchar; - testStr2: pchar; + list: PLinkedListBase; + slot: puint32; + source: pchar; + beforeFree: SizeUInt; + passed, failed: uint32; + + procedure Assert(condition: boolean; name: pchar); + begin + if condition then + inc(passed) + else begin + inc(failed); + log('LISTS', name); + end; + end; begin - { --- Test Managed Linked List --- } - io.syslog.writeString('--- Testing Managed Linked List ---'); - ll := LL_New(sizeof(uint32)); - for i := 1 to 3 do - begin - p := LL_Add(ll); - puint32(p)^ := i; // Store the value (1, 2, 3) - end; - io.syslog.writeString('Managed Linked List Size: '); - io.syslog.writeintln(LL_Size(ll)); - for i := 0 to LL_Size(ll) - 1 do - begin - p := LL_Get(ll, i); - io.syslog.writeString('Element '); - io.syslog.writeint(i); - io.syslog.writeString(': '); - io.syslog.writeintln(puint32(p)^); - end; - LL_Free(ll); + passed := 0; + failed := 0; + beforeFree := lmm_total_free; + list := LL_New(sizeof(uint32)); + slot := puint32(LL_Add(list)); + slot^ := 10; + slot := puint32(LL_Insert(list, 0)); + slot^ := 5; + slot := puint32(LL_Insert(list, 2)); + slot^ := 20; + Assert((LL_Size(list) = 3) and (puint32(LL_Get(list, 0))^ = 5) and + (puint32(LL_Get(list, 2))^ = 20), 'FAIL: list head and tail insertion'); + Assert(LL_Delete(list, 1) and (puint32(LL_Get(list, 1))^ = 20), + 'FAIL: list middle deletion'); + Assert((LL_Get(list, 2) = nil) and (LL_Insert(list, 3) = nil), + 'FAIL: list bounds'); + LL_Free(list); - { --- Test String Linked List --- } - io.syslog.writeString(''); - io.syslog.writeString('--- Testing String Linked List ---'); - testStr := stringNew(5); - PuInt8(testStr)^ := ord('H'); - PuInt8(testStr + 1)^ := ord('e'); - PuInt8(testStr + 2)^ := ord('l'); - PuInt8(testStr + 3)^ := ord('l'); - PuInt8(testStr + 4)^ := ord('o'); - - testStr2 := stringNew(5); - puint8(testStr2)^ := ord('W'); - puint8(testStr2 + 1)^ := ord('o'); - puint8(testStr2 + 2)^ := ord('r'); - puint8(testStr2 + 3)^ := ord('l'); - puint8(testStr2 + 4)^ := ord('d'); - - - strList := STRLL_New; - STRLL_Add(strList, testStr); - STRLL_Add(strList, testStr2); - io.syslog.writeString('String Linked List Size: '); - io.syslog.writeintln(STRLL_Size(strList)); - for i := 0 to STRLL_Size(strList) - 1 do - begin - io.syslog.writeString('String '); - io.syslog.writeint(i); - io.syslog.writeString(': '); - io.syslog.writeString(STRLL_Get(strList, i)); - - end; - STRLL_Free(strList); - - { --- Test Dynamic List --- } - io.syslog.writeString(''); - io.syslog.writeString('--- Testing Dynamic List ---'); - dlist := DL_New(sizeof(uint32)); - for i := 1 to 5 do - begin - intPtr := DL_Add(dlist); - intPtr^ := i * 10; // Store values: 10, 20, 30, 40, 50 - end; - io.syslog.writeString('Dynamic List Size: '); - io.syslog.writeintln(DL_Size(dlist)); - for i := 0 to DL_Size(dlist) - 1 do - begin - intPtr := DL_Get(dlist, i); - io.syslog.writeString('Element '); - io.syslog.writeint(i); - io.syslog.writeString(': '); - io.syslog.writeintln(intPtr^); - end; - - { Insert a new element at index 2 } - intPtr := DL_Insert(dlist, 2); - if intPtr <> nil then - intPtr^ := 999; // Inserted value 999 at index 2 - io.syslog.writeString('After insertion at index 2, size: '); - io.syslog.writeintln(DL_Size(dlist)); - for i := 0 to DL_Size(dlist) - 1 do - begin - intPtr := DL_Get(dlist, i); - io.syslog.writeString('Element '); - io.syslog.writeint(i); - io.syslog.writeString(': '); - io.syslog.writeintln(intPtr^); - end; - - { Delete element at index 3 } - if DL_Delete(dlist, 3) then - io.syslog.writeString('Deleted element at index 3.'); - io.syslog.writeString('After deletion, size: '); - io.syslog.writeintln(DL_Size(dlist)); - for i := 0 to DL_Size(dlist) - 1 do - begin - intPtr := DL_Get(dlist, i); - io.syslog.writeString('Element '); - io.syslog.writeint(i); - io.syslog.writeString(': '); - io.syslog.writeintln(intPtr^); - end; - DL_Free(dlist); + source := stringCopy('/alpha//beta/'); + list := STRLL_FromString(source, '/'); + kfree(void(source)); + Assert((STRLL_Size(list) = 2) and StringEquals(STRLL_Get(list, 0), 'alpha') and + StringEquals(STRLL_Get(list, 1), 'beta'), 'FAIL: native string list split'); + STRLL_Delete(list, 0); + Assert(StringEquals(STRLL_Get(list, 0), 'beta'), 'FAIL: string list delete'); + STRLL_Free(list); + list := LL_FromString('one:two', ':'); + Assert(StringEquals(STRLL_Get(list, 1), 'two'), 'FAIL: shared string splitter'); + STRLL_Free(list); + list := STRLL_FromString('', '/'); + Assert(STRLL_Size(list) = 0, 'FAIL: empty string list'); + STRLL_Free(list); + Assert(lmm_total_free = beforeFree, 'FAIL: list allocation accounting'); + testSummary('LISTS', 'LISTS_TESTS_PASSED', 'LISTS_TESTS_FAILED', passed, failed); end; - end. diff --git a/src/core/ds/core.ds.types.pas b/src/core/ds/core.ds.types.pas index 23f9ae16..d61f99b6 100644 --- a/src/core/ds/core.ds.types.pas +++ b/src/core/ds/core.ds.types.pas @@ -256,4 +256,4 @@ type implementation -end. \ No newline at end of file +end. diff --git a/src/core/enc/core.enc.base64.pas b/src/core/enc/core.enc.base64.pas index 002b1556..09780adc 100644 --- a/src/core/enc/core.enc.base64.pas +++ b/src/core/enc/core.enc.base64.pas @@ -26,7 +26,7 @@ uses memory.heap, core.strings, debug.tracer, - core.util, arch.x86.util; + core.util, arch.platform.cpu; const base64_enc_map : array[0..63] of char = ('A','B','C','D','E', @@ -64,7 +64,7 @@ begin // integer overflow end else begin output := PuInt8(kalloc(olen)); - b64end := PuInt8(uInt32(src) + uInt32(len)); + b64end := PuInt8(PtrUInt(src) + uInt32(len)); b64in := src; b64pos := output; line_len := 0; @@ -130,7 +130,7 @@ var begin pad := 0; - memset(uInt32(@dtable[0]), $80, 256); + memset(PtrUInt(@dtable[0]), $80, 256); for i := 0 to (sizeof(base64_enc_map) - 2) do begin dtable[uInt8(base64_enc_map[i])] := uInt8(i); diff --git a/src/core/enc/core.enc.fnv1a.pas b/src/core/enc/core.enc.fnv1a.pas index 34ac856b..22701e79 100644 --- a/src/core/enc/core.enc.fnv1a.pas +++ b/src/core/enc/core.enc.fnv1a.pas @@ -47,7 +47,7 @@ interface implementation uses - io.syslog, + arch.platform.diagnostics, memory.heap, core.strings; @@ -58,13 +58,13 @@ var h : uint32; begin h := $811C9DC5; - p := puint8(uint32(Data)); + p := puint8(Data); if DataLen > 0 then for i := 0 to DataLen - 1 do begin h := h xor uint32(p^); h := h * $01000193; - p := puint8(uint32(p) + 1); + inc(p); end; Hash_FNV1a32 := h; end; @@ -76,13 +76,13 @@ var h : uint64; begin h := uint64($CBF29CE484222325); - p := puint8(uint32(Data)); + p := puint8(Data); if DataLen > 0 then for i := 0 to DataLen - 1 do begin h := h xor uint64(p^); h := h * uint64($00000100000001B3); - p := puint8(uint32(p) + 1); + inc(p); end; Hash_FNV1a64 := h; end; @@ -103,36 +103,28 @@ var else begin inc(failed); msg := stringConcat('FAIL: ', testName); - io.syslog.logln('FNV1A', msg); + arch.platform.diagnostics.log('FNV1A', msg); kfree(void(msg)); end; end; - procedure PrintSummary; - var - pStr, fStr, msg, tmp : pchar; - begin - pStr := intToString(passed); - fStr := intToString(failed); - msg := stringConcat(pStr, ' passed, '); - tmp := stringConcat(msg, fStr); - kfree(void(msg)); - msg := stringConcat(tmp, ' failed.'); - kfree(void(tmp)); - io.syslog.logln('FNV1A', msg); - kfree(void(msg)); - kfree(void(pStr)); - kfree(void(fStr)); - end; begin passed := 0; failed := 0; - io.syslog.logln('FNV1A', 'Unit tests starting...'); + arch.platform.diagnostics.log('FNV1A', 'Unit tests starting...'); { Two distinct 5-byte inputs } - buf1[0] := ord('h'); buf1[1] := ord('e'); buf1[2] := ord('l'); buf1[3] := ord('l'); buf1[4] := ord('o'); - buf2[0] := ord('w'); buf2[1] := ord('o'); buf2[2] := ord('r'); buf2[3] := ord('l'); buf2[4] := ord('d'); + buf1[0] := ord('h'); + buf1[1] := ord('e'); + buf1[2] := ord('l'); + buf1[3] := ord('l'); + buf1[4] := ord('o'); + buf2[0] := ord('w'); + buf2[1] := ord('o'); + buf2[2] := ord('r'); + buf2[3] := ord('l'); + buf2[4] := ord('d'); { === Known basis value: zero-length input returns initial h === } Assert(Hash_FNV1a32(void(@buf1[0]), 0) = $811C9DC5, 'FNV1a32 empty = basis $811C9DC5'); @@ -155,7 +147,7 @@ begin h64a := Hash_FNV1a64(void(@buf1[0]), 5); Assert(uint32(h64a) <> h32a, 'FNV1a32 != low32(FNV1a64)'); - PrintSummary; + testSummary('FNV1A', 'FNV1A_TESTS_PASSED', 'FNV1A_TESTS_FAILED', passed, failed); end; end. diff --git a/src/core/enc/core.enc.md5.pas b/src/core/enc/core.enc.md5.pas index 1d4c28c0..6c1f052c 100644 --- a/src/core/enc/core.enc.md5.pas +++ b/src/core/enc/core.enc.md5.pas @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -{ +{ Include->MD5 - MD5 checksum. - + @author(Angus C ) @author(Kieron Morris ) } @@ -24,7 +24,7 @@ unit core.enc.md5; interface uses - core.util, arch.x86.util, + core.util, arch.platform.cpu, memory.heap, debug.tracer; @@ -70,7 +70,7 @@ begin T^ := S[0] or (S[1] shl 8) or (S[2] shl 16) or (S[3] shl 24); inc(S,4); inc(T); - end; + end; end; procedure MD5Transform(Context: PMD5Context; Buffer: Pointer); @@ -166,14 +166,14 @@ end; procedure MD5Init(context : PMD5Context); begin debug.tracer.push_trace('core.enc.md5.MD5Init'); - memset(uInt32(context), 0, SizeOf(TMD5Context)); + memset(PtrUInt(context), 0, SizeOf(TMD5Context)); context^.Align := 64; context^.State[0] := $67452301; context^.State[1] := $efcdab89; context^.State[2] := $98badcfe; context^.State[3] := $10325476; context^.Length := 0; - context^.BufferCount := 0; + context^.BufferCount := 0; end; procedure MD5Update(context : PMD5Context; buffer : PuInt8; bufferLen : uInt32); @@ -183,33 +183,33 @@ var Num : uInt32; i : uint32; -begin +begin debug.tracer.push_trace('core.enc.md5.MD5Update'); if bufferLen = 0 then Exit; Align := context^.Align; Src := buffer; Num := 0; - if context^.BufferCount > 0 then begin - Num := Align - context^.BufferCount; + if context^.BufferCount > 0 then begin + Num := Align - context^.BufferCount; if Num > bufferLen then num := bufferLen; - memcpy(uInt32(Src), uInt32(@context^.Buffer[Context^.BufferCount]), Num); - context^.BufferCount := context^.BufferCount + Num; - Src := PuInt8(uInt32(Src) + uint32(Num)); - if context^.BufferCount = Align then begin - MD5Transform(context, @context^.buffer[0]); + memcpy(PtrUInt(Src), PtrUInt(@context^.Buffer[Context^.BufferCount]), Num); + context^.BufferCount := context^.BufferCount + Num; + Src := PuInt8(PtrUInt(Src) + uint32(Num)); + if context^.BufferCount = Align then begin + MD5Transform(context, @context^.buffer[0]); context^.BufferCount := 0; - end; + end; end; Num := bufferLen - Num; While Num >= Align do begin MD5Transform(context, Src); - Src := PuInt8(uInt32(Src) + Align); + Src := PuInt8(PtrUInt(Src) + Align); Num := Num - Align; end; - if Num > 0 then begin - context^.BufferCount := Num; - memcpy(uInt32(Src), uInt32(@context^.Buffer[0]), Num); - end; + if Num > 0 then begin + context^.BufferCount := Num; + memcpy(PtrUInt(Src), PtrUInt(@context^.Buffer[0]), Num); + end; end; function MD5Final(context : PMD5Context) : PMD5Digest; @@ -229,12 +229,12 @@ begin else Pads := 56 - context^.BufferCount; MD5Update(context, PuInt8(@MD5_Padding[0]), Pads); - MD5Update(context, PuInt8(@Length), 8); - Digest := PMD5Digest(kalloc(SizeOf(TMD5Digest))); - memset(uInt32(Digest), 0, SizeOf(TMD5Digest)); - Invert(PuInt8(@context^.State), PuInt32(Digest), 16); - memset(uInt32(context), 0, SizeOf(TMD5Context)); - MD5Final := Digest; + MD5Update(context, PuInt8(@Length), 8); + Digest := PMD5Digest(kalloc(SizeOf(TMD5Digest))); + memset(PtrUInt(Digest), 0, SizeOf(TMD5Digest)); + Invert(PuInt8(@context^.State), PuInt32(Digest), 16); + memset(PtrUInt(context), 0, SizeOf(TMD5Context)); + MD5Final := Digest; end; function MD5Buffer(buffer : PuInt8; bufferLen : uInt32) : PMD5Digest; @@ -258,4 +258,4 @@ begin MD5To32:= Input^.DWords[0] xor Input^.DWords[1] xor Input^.DWords[2] xor Input^.DWords[3]; end; -end. \ No newline at end of file +end. diff --git a/src/core/strings/core.strings.helpers.pas b/src/core/strings/core.strings.helpers.pas index bc4a917b..e18c0b64 100644 --- a/src/core/strings/core.strings.helpers.pas +++ b/src/core/strings/core.strings.helpers.pas @@ -112,16 +112,16 @@ begin if (arr = nil) or (n < 2) then exit; if n > 1 then for i := 1 to n - 1 do begin - p := PPChar(pointer(uint32(arr) + i * sizeof(pchar))); + p := PPChar(pointer(PtrUInt(arr) + i * sizeof(pchar))); tmp := p^; j := i; while j > 0 do begin - p := PPChar(pointer(uint32(arr) + (j - 1) * sizeof(pchar))); + p := PPChar(pointer(PtrUInt(arr) + (j - 1) * sizeof(pchar))); if stringCompareCI(tmp, p^) >= 0 then break; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := p^; + PPChar(pointer(PtrUInt(arr) + j * sizeof(pchar)))^ := p^; j := j - 1; end; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := tmp; + PPChar(pointer(PtrUInt(arr) + j * sizeof(pchar)))^ := tmp; end; end; @@ -142,21 +142,21 @@ begin if (arr = nil) or (n < 2) then exit; if n > 1 then for i := 1 to n - 1 do begin - p := PPChar(pointer(uint32(arr) + i * sizeof(pchar))); - dp := puint32(pointer(uint32(data) + i * sizeof(uint32))); + p := PPChar(pointer(PtrUInt(arr) + i * sizeof(pchar))); + dp := puint32(pointer(PtrUInt(data) + i * sizeof(uint32))); tmpN := p^; tmpD := dp^; j := i; while j > 0 do begin - p := PPChar(pointer(uint32(arr) + (j - 1) * sizeof(pchar))); + p := PPChar(pointer(PtrUInt(arr) + (j - 1) * sizeof(pchar))); if stringCompareCI(tmpN, p^) >= 0 then break; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := p^; - puint32(pointer(uint32(data) + j * sizeof(uint32)))^ := - puint32(pointer(uint32(data) + (j - 1) * sizeof(uint32)))^; + PPChar(pointer(PtrUInt(arr) + j * sizeof(pchar)))^ := p^; + puint32(pointer(PtrUInt(data) + j * sizeof(uint32)))^ := + puint32(pointer(PtrUInt(data) + (j - 1) * sizeof(uint32)))^; j := j - 1; end; - PPChar(pointer(uint32(arr) + j * sizeof(pchar)))^ := tmpN; - puint32(pointer(uint32(data) + j * sizeof(uint32)))^ := tmpD; + PPChar(pointer(PtrUInt(arr) + j * sizeof(pchar)))^ := tmpN; + puint32(pointer(PtrUInt(data) + j * sizeof(uint32)))^ := tmpD; end; end; diff --git a/src/core/strings/core.strings.pas b/src/core/strings/core.strings.pas index 565d4aac..8536ab11 100644 --- a/src/core/strings/core.strings.pas +++ b/src/core/strings/core.strings.pas @@ -159,24 +159,20 @@ var begin size:= stringSize(str); result:= stringNew(size); - if result = nil then begin stringCopy:= nil; exit; end; + if result = nil then begin + stringCopy:= nil; + exit; + end; memcpy(PtrUInt(str), PtrUInt(result), size); stringCopy:= result; end; function stringNew(size : uint32) : pchar; -var - result : pchar; - newSize : uint32; - begin stringNew:= nil; if size = high(uint32) then exit; - newSize:= size + 1; - result:= pchar(kalloc(newSize)); - if result = nil then exit; - memset(PtrUInt(result), 0, newSize); - stringNew:= result; + { kalloc zeroes the buffer and its terminating byte. } + stringNew:= pchar(kalloc(size + 1)); end; function stringSize(str : pchar) : uint32; @@ -203,15 +199,22 @@ var begin size1:= stringSize(str1); size2:= stringSize(str2); - if size2 >= high(uint32) - size1 then begin stringConcat:= nil; exit; end; + if size2 >= high(uint32) - size1 then begin + stringConcat:= nil; + exit; + end; result:= stringNew(size1 + size2); - if result = nil then begin stringConcat:= nil; exit; end; + if result = nil then begin + stringConcat:= nil; + exit; + end; memcpy(PtrUInt(str1), PtrUInt(result), size1); memcpy(PtrUInt(str2), PtrUInt(result + size1), size2); stringConcat:= result; end; // Trim the string to the specified length. + function stringTrim(str : pchar; length : uInt32) : pchar; var result : pchar; @@ -220,12 +223,16 @@ begin strLen:= stringSize(str); if length > strLen then length:= strLen; result:= stringNew(length); - if result = nil then begin stringTrim:= nil; exit; end; + if result = nil then begin + stringTrim:= nil; + exit; + end; memcpy(PtrUInt(str), PtrUInt(result), length); stringTrim:= result; end; // Return a substring of the string. + function stringSub(str : pchar; start, size : uint32) : pchar; var result : pchar; @@ -238,12 +245,16 @@ begin end; if size > strLen - start then size:= strLen - start; result:= stringNew(size); - if result = nil then begin stringSub:= nil; exit; end; + if result = nil then begin + stringSub:= nil; + exit; + end; memcpy(PtrUInt(str)+start, PtrUInt(result), size); stringSub:= result; end; // Compare find against str starting at position pos, without requiring full string equality. + function stringMatchAt(str : pchar; pos : uint32; find : pchar) : boolean; var i, findLen, strLen : uint32; @@ -264,6 +275,7 @@ begin end; // Replace first instance of a string with another. + function stringReplace(str, find, replace : pchar) : pchar; var result : pchar; @@ -287,9 +299,15 @@ begin // If we found the find string, replace it. if found then begin - if replLen >= high(uint32) - (strLen - findLen) then begin stringReplace:= nil; exit; end; + if replLen >= high(uint32) - (strLen - findLen) then begin + stringReplace:= nil; + exit; + end; result:= stringNew(strLen - findLen + replLen); - if result = nil then begin stringReplace:= nil; exit; end; + if result = nil then begin + stringReplace:= nil; + exit; + end; // Copy prefix before match if pos > 0 then memcpy(PtrUInt(str), PtrUInt(result), pos); @@ -308,6 +326,7 @@ end; // Find the index of the first instance of a string. + function stringIndexOf(str, find : pchar) : sint32; var i, strLen, findLen : uint32; @@ -343,13 +362,28 @@ function stringCompare(a, b : pchar) : sint32; var i : uint32; begin - if (a = nil) and (b = nil) then begin stringCompare := 0; exit; end; - if a = nil then begin stringCompare := -1; exit; end; - if b = nil then begin stringCompare := 1; exit; end; + if (a = nil) and (b = nil) then begin + stringCompare := 0; + exit; + end; + if a = nil then begin + stringCompare := -1; + exit; + end; + if b = nil then begin + stringCompare := 1; + exit; + end; i := 0; while (a[i] <> #0) and (b[i] <> #0) do begin - if ord(a[i]) < ord(b[i]) then begin stringCompare := -1; exit; end; - if ord(a[i]) > ord(b[i]) then begin stringCompare := 1; exit; end; + if ord(a[i]) < ord(b[i]) then begin + stringCompare := -1; + exit; + end; + if ord(a[i]) > ord(b[i]) then begin + stringCompare := 1; + exit; + end; i := i + 1; end; if a[i] = b[i] then @@ -368,15 +402,30 @@ var i : uint32; ca, cb : char; begin - if (a = nil) and (b = nil) then begin stringCompareCI := 0; exit; end; - if a = nil then begin stringCompareCI := -1; exit; end; - if b = nil then begin stringCompareCI := 1; exit; end; + if (a = nil) and (b = nil) then begin + stringCompareCI := 0; + exit; + end; + if a = nil then begin + stringCompareCI := -1; + exit; + end; + if b = nil then begin + stringCompareCI := 1; + exit; + end; i := 0; while (a[i] <> #0) and (b[i] <> #0) do begin ca := charToLower(a[i]); cb := charToLower(b[i]); - if ord(ca) < ord(cb) then begin stringCompareCI := -1; exit; end; - if ord(ca) > ord(cb) then begin stringCompareCI := 1; exit; end; + if ord(ca) < ord(cb) then begin + stringCompareCI := -1; + exit; + end; + if ord(ca) > ord(cb) then begin + stringCompareCI := 1; + exit; + end; i := i + 1; end; if a[i] = b[i] then @@ -405,8 +454,14 @@ var begin strLen := stringSize(str); findLen := stringSize(find); - if findLen = 0 then begin stringIndexOfCI := 0; exit; end; - if findLen > strLen then begin stringIndexOfCI := -1; exit; end; + if findLen = 0 then begin + stringIndexOfCI := 0; + exit; + end; + if findLen > strLen then begin + stringIndexOfCI := -1; + exit; + end; for i := 0 to strLen - findLen do begin found := true; if findLen > 0 then @@ -416,7 +471,10 @@ begin break; end; end; - if found then begin stringIndexOfCI := i; exit; end; + if found then begin + stringIndexOfCI := i; + exit; + end; end; stringIndexOfCI := -1; end; @@ -437,10 +495,16 @@ var i, prefLen : uint32; begin stringStartsWith := false; - if prefix = nil then begin stringStartsWith := true; exit; end; + if prefix = nil then begin + stringStartsWith := true; + exit; + end; if str = nil then exit; prefLen := stringSize(prefix); - if prefLen = 0 then begin stringStartsWith := true; exit; end; + if prefLen = 0 then begin + stringStartsWith := true; + exit; + end; if stringSize(str) < prefLen then exit; if prefLen > 0 then for i := 0 to prefLen - 1 do @@ -456,10 +520,16 @@ var i, prefLen : uint32; begin stringStartsWithCI := false; - if prefix = nil then begin stringStartsWithCI := true; exit; end; + if prefix = nil then begin + stringStartsWithCI := true; + exit; + end; if str = nil then exit; prefLen := stringSize(prefix); - if prefLen = 0 then begin stringStartsWithCI := true; exit; end; + if prefLen = 0 then begin + stringStartsWithCI := true; + exit; + end; if stringSize(str) < prefLen then exit; if prefLen > 0 then for i := 0 to prefLen - 1 do @@ -475,11 +545,17 @@ var i, strLen, sufLen : uint32; begin stringEndsWith := false; - if suffix = nil then begin stringEndsWith := true; exit; end; + if suffix = nil then begin + stringEndsWith := true; + exit; + end; if str = nil then exit; strLen := stringSize(str); sufLen := stringSize(suffix); - if sufLen = 0 then begin stringEndsWith := true; exit; end; + if sufLen = 0 then begin + stringEndsWith := true; + exit; + end; if strLen < sufLen then exit; if sufLen > 0 then for i := 0 to sufLen - 1 do @@ -495,11 +571,17 @@ var i, strLen, sufLen : uint32; begin stringEndsWithCI := false; - if suffix = nil then begin stringEndsWithCI := true; exit; end; + if suffix = nil then begin + stringEndsWithCI := true; + exit; + end; if str = nil then exit; strLen := stringSize(str); sufLen := stringSize(suffix); - if sufLen = 0 then begin stringEndsWithCI := true; exit; end; + if sufLen = 0 then begin + stringEndsWithCI := true; + exit; + end; if strLen < sufLen then exit; if sufLen > 0 then for i := 0 to sufLen - 1 do @@ -758,8 +840,7 @@ var passed, failed : uint32; procedure Assert(condition : boolean; testName : pchar); - var - msg : pchar; + var msg : pchar; begin if condition then begin inc(passed); @@ -1123,4 +1204,4 @@ begin arch.platform.diagnostics.testSummary('STRINGS', 'STRINGS_TESTS_PASSED', 'STRINGS_TESTS_FAILED', passed, failed); end; -end. \ No newline at end of file +end. diff --git a/src/debug/debug.early.pas b/src/debug/debug.early.pas index 8f64f664..fb1ee081 100644 --- a/src/debug/debug.early.pas +++ b/src/debug/debug.early.pas @@ -13,6 +13,7 @@ { Allocation-free diagnostics. The architecture supplies the character sink; formatting remains independent of UARTs, graphics, heaps, and interrupts. } unit debug.early; + interface type TCharWriter = procedure(ch: char); @@ -20,13 +21,16 @@ procedure setWriter(writer: TCharWriter); procedure puts(message: pchar); procedure hex(value: uint64); procedure field(name: pchar; value: uint64); + implementation var sink: TCharWriter; + procedure setWriter(writer: TCharWriter); begin sink := writer; end; + procedure puts(message: pchar); begin if sink = nil then exit; @@ -35,6 +39,7 @@ begin inc(message); end; end; + procedure hex(value: uint64); const digits: array[0..15] of char = '0123456789ABCDEF'; @@ -45,6 +50,7 @@ begin for shift := 15 downto 0 do sink(digits[(value shr (shift * 4)) and $F]); end; + procedure field(name: pchar; value: uint64); begin puts(name); diff --git a/src/debug/debug.tracer.pas b/src/debug/debug.tracer.pas index 70e53248..37d8fa44 100644 --- a/src/debug/debug.tracer.pas +++ b/src/debug/debug.tracer.pas @@ -37,8 +37,7 @@ procedure print_traces; implementation uses - boot.mgr, - core.util, arch.x86.util, core.strings, io.stdio, io.syslog; + core.util, core.strings, io.stdio, io.syslog; const MAX_TRACE = 40; @@ -186,9 +185,4 @@ begin end; end; -initialization - //procedure registerBoot(Name: PChar; InitProc: TBootProc; Status: PChar; DependsOn: PChar); - boot.mgr.registerBoot('debug.tracer.freeze', @freeze, 'Debug Tracer Freeze', 'io.syslog' ); - boot.mgr.registerBoot('debug.tracer', @init, 'Debug Tracer Initialization', 'io.stdio' ); - -end. \ No newline at end of file +end. diff --git a/src/driver/bus/driver.bus.pci.pas b/src/driver/bus/driver.bus.pci.pas index 23b12808..efc2daa1 100644 --- a/src/driver/bus/driver.bus.pci.pas +++ b/src/driver/bus/driver.bus.pci.pas @@ -23,13 +23,11 @@ unit driver.bus.pci; interface uses - boot.mgr, debug.tracer, - core.util, arch.x86.util, + core.util, arch.platform.cpu, io.syslog, driver.types, memory.heap, - arch.x86.memory.virtual, driver.mgr; const @@ -148,7 +146,7 @@ begin result := loadDeviceConfig(bus, slot, 0); if result = true then begin if devices[device_count - 1].header_type and $80 <> 0 then begin - for func := 1 to 8 do begin + for func := 1 to 7 do begin loadDeviceConfig(bus, slot, func); end; end; @@ -404,7 +402,7 @@ begin devices[device_count] := device; device_count := device_count + 1; - driver.mgr.register_device('driver.bus.pci Device', DevID, @device); + driver.mgr.register_device('driver.bus.pci Device', DevID, @devices[device_count - 1]); kfree(void(DevID)); end else begin loadBusConfig(bus, slot, func, device); @@ -474,7 +472,4 @@ begin pop_trace; end; -Initialization - boot.mgr.registerBoot('driver.bus.pci', @Init, 'PCI Bus Driver', BOOT_MGR_BARRIER_BUS_LATE); - end. diff --git a/src/driver/driver.mgr.pas b/src/driver/driver.mgr.pas index 323a42f9..68f75ebe 100644 --- a/src/driver/driver.mgr.pas +++ b/src/driver/driver.mgr.pas @@ -22,8 +22,7 @@ unit driver.mgr; interface uses - boot.mgr, - io.syslog, io.stdio, core.util, arch.x86.util, core.strings, memory.heap, debug.tracer; + io.syslog, io.stdio, core.util, arch.platform.cpu, core.strings, memory.heap, debug.tracer; const idANY = $FFFFFFFF; @@ -424,7 +423,4 @@ begin pop_trace; end; -initialization - boot.mgr.registerBoot('driver.mgr', @init, 'Driver Manager Initialization', BOOT_MGR_BARRIER_MID); - -end. \ No newline at end of file +end. diff --git a/src/driver/io/driver.io.serial.pas b/src/driver/io/driver.io.serial.pas index fa67c44e..4bff1fa7 100644 --- a/src/driver/io/driver.io.serial.pas +++ b/src/driver/io/driver.io.serial.pas @@ -22,8 +22,7 @@ unit driver.io.serial; interface uses - boot.mgr, - core.util, arch.x86.util, arch.x86.isr.mgr, core.strings; + arch.platform.cpu, core.strings; const COM1 = $3F8; @@ -45,33 +44,14 @@ implementation procedure soutb(port : uint16; val : uint8); begin - asm - PUSH EAX - PUSH EDX - MOV DX, port - MOV AL, val - OUT DX, AL - POP EDX - POP EAX - end; - io_wait; + arch.platform.cpu.outb(port, val); + arch.platform.cpu.outb($80, 0); end; function sinb(port : uint16) : uint8; -var - tmp : uint8; begin - asm - PUSH EAX - PUSH EDX - MOV DX, port - IN AL, DX - MOV tmp, AL - POP EDX - POP EAX - end; - sinb := tmp; - io_wait; + sinb := arch.platform.cpu.inb(port); + arch.platform.cpu.outb($80, 0); end; procedure IRQ_Hook(); @@ -202,7 +182,4 @@ begin sendHex:= true; end; -initialization - boot.mgr.registerBoot('driver.io.serial', @init, 'Serial Interface', 'immediate'); - -end. \ No newline at end of file +end. diff --git a/src/driver/timer/driver.timer.rtc.pas b/src/driver/timer/driver.timer.rtc.pas index e6549120..1630bb16 100644 --- a/src/driver/timer/driver.timer.rtc.pas +++ b/src/driver/timer/driver.timer.rtc.pas @@ -22,8 +22,7 @@ unit driver.timer.rtc; interface uses - boot.mgr, - arch.x86.isr.mgr, core.util, arch.x86.util, arch.x86.isr.tmr0; + core.util, arch.platform.cpu; type TDateTime = record @@ -66,7 +65,7 @@ var begin outb($70, $0A); - io_wait(); + arch.platform.cpu.outb($80, 0); bin:= inb($71); is_update_in_progress:= (bin AND ($1 SHL 7)) <> 0; end; @@ -74,7 +73,7 @@ end; procedure update(); begin //outb($70, $0C); // select register C - //io_wait(); + //arch.platform.cpu.outb($80, 0); //inb($71); //console.writestringln('driver.timer.rtc Update'); //while not is_update_in_progress do begin @@ -82,37 +81,37 @@ begin //while is_update_in_progress do begin //end; outb($70, $00); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Seconds:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); outb($70, $02); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Minutes:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); outb($70, $04); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Hours:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); outb($70, $06); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Weekday:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); outb($70, $07); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Day:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); outb($70, $08); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Month:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); outb($70, $09); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Year:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); outb($70, $32); - io_wait(); + arch.platform.cpu.outb($80, 0); DateTime.Century:= BCDToUint8(inb($71)); - io_wait(); + arch.platform.cpu.outb($80, 0); end; function getDateTime : TDateTime; @@ -122,34 +121,8 @@ begin end; procedure init; -var - prev : uint8; - begin - CLI; - //setup driver.timer.rtc - outb($70, $8A); - io_wait(); - outb($71, $20); - io_wait(); - - //enable ints - outb($70, $8B); - io_wait(); - prev:= inb($71); - io_wait(); - outb($70, $8B); - io_wait(); - outb($71, prev OR $40); - STI; - outb($70, $00); - inb($71); - - //arch.x86.isr.mgr.registerISR(32 + 8, @update); - //arch.x86.isr.tmr0.hook(uint32(@update)); + update; end; -initialization - boot.mgr.registerBoot('driver.timer.rtc', @init, 'Real Time Clock Driver', 'arch.x86.fault'); - -end. \ No newline at end of file +end. diff --git a/src/io/io.directory.pas b/src/io/io.directory.pas deleted file mode 100644 index 2e7dc454..00000000 --- a/src/io/io.directory.pas +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2026 Aaron Hance -// -// 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. - -{ VFS directory helpers extracted from io.stdio (Kieron Morris). } -unit io.directory; -interface -function getWorkingDirectory: pchar; -procedure setWorkingDirectory(str: pchar); -implementation -uses driver.storage.vfs; -function getWorkingDirectory: pchar; -begin - getWorkingDirectory := driver.storage.vfs.GetWorkingDirectory; -end; - -procedure setWorkingDirectory(str: pchar); -begin - if str <> nil then - driver.storage.vfs.ChangeDirectory(str); -end; - -end. diff --git a/src/io/io.stdio.pas b/src/io/io.stdio.pas index 64090597..f7e8d133 100644 --- a/src/io/io.stdio.pas +++ b/src/io/io.stdio.pas @@ -79,7 +79,6 @@ procedure freeParams(params: PParamList); function paramCount(params: PParamList): uint32; function getParam(index: uint32; params: PParamList): pchar; -{ Working-directory helpers live in io.directory, which requires VFS. } { Halt mechanism for async commands } function halt(id: uint32; cb: THaltCallback): boolean; @@ -97,7 +96,7 @@ procedure UnitTest; implementation uses - arch.platform.diagnostics, arch.common.interrupts; + arch.platform.diagnostics, arch.platform.cpu; const INITIAL_CMD_CAP = 64; @@ -108,7 +107,6 @@ var CmdCap : uint32 = 0; { ---- Output buffer ---- } - function createOutBuf(initial_cap: uint32): POutBuf; var ob: POutBuf; @@ -119,10 +117,12 @@ begin createOutBuf := nil; if ob = nil then exit; ob^.buf := pchar(kalloc(cap)); - if ob^.buf = nil then begin kfree(void(ob)); exit; end; + if ob^.buf = nil then begin + kfree(void(ob)); + exit; + end; ob^.len := 0; ob^.cap := cap; - memset(PtrUInt(ob^.buf), 0, cap); createOutBuf := ob; end; @@ -142,16 +142,24 @@ begin end; function bufGrow(buf: POutBuf; needed: uint32): boolean; -var newCap, required: uint32; newBuf: pchar; +var + newCap, required: uint32; + newBuf: pchar; begin bufGrow := false; if needed >= high(uint32) - buf^.len then exit; required := buf^.len + needed + 1; - if required <= buf^.cap then begin bufGrow := true; exit; end; + if required <= buf^.cap then begin + bufGrow := true; + exit; + end; newCap := buf^.cap; if newCap = 0 then newCap := 64; while newCap < required do begin - if newCap > high(uint32) div 2 then begin newCap := required; break; end; + if newCap > high(uint32) div 2 then begin + newCap := required; + break; + end; newCap := newCap * 2; end; newBuf := pchar(kalloc(newCap)); @@ -174,7 +182,9 @@ begin end; procedure bufWriteStr(buf: POutBuf; s: pchar); -var len, offset: uint32; aliases: boolean; +var + len, offset: uint32; + aliases: boolean; begin if (buf = nil) or (s = nil) then exit; len := stringSize(s); @@ -305,7 +315,6 @@ begin end; { ---- ANSI core.gfx.color helpers ---- } - procedure bufSetColor(buf: POutBuf; fg: uint8); begin { ESC[38;5;m } @@ -330,9 +339,10 @@ begin end; { ---- Dynamic command registry ---- } - function growCommands: boolean; -var newCap: uint32; newArr: PCommand; +var + newCap: uint32; + newArr: PCommand; begin growCommands := false; if CmdCap > high(uint32) div sizeof(TCommand) div 2 then exit; @@ -385,7 +395,9 @@ begin end; function findCommand(name: pchar): PCommand; -var i: uint32; cmd: PCommand; +var + i: uint32; + cmd: PCommand; begin findCommand := nil; if (name = nil) or (CmdCount = 0) then exit; @@ -399,7 +411,6 @@ begin end; { ---- Parameter parsing ---- } - function getParams(var buf: TCommandBuffer): PParamList; var start, finish, size: uint32; @@ -421,10 +432,16 @@ begin (buf[finish] <> 0) do inc(finish); size := finish - start; current^.Param := stringNew(size); - if current^.Param = nil then begin freeParams(root); exit; end; + if current^.Param = nil then begin + freeParams(root); + exit; + end; memcpy(PtrUInt(@buf[start]), PtrUInt(current^.Param), size); current^.next := PParamList(kalloc(SizeOf(TParamList))); - if current^.next = nil then begin freeParams(root); exit; end; + if current^.next = nil then begin + freeParams(root); + exit; + end; current := current^.next; current^.next := nil; current^.Param := nil; @@ -484,24 +501,26 @@ end; { ---- Halt mechanism ---- } - function halt(id: uint32; cb: THaltCallback): boolean; -var flags: PtrUInt; +var + flags: PtrUInt; begin - flags := arch.common.interrupts.lock; + flags := arch.platform.cpu.lock; halt := not Halted; if not Halted then begin HaltID := id; HaltCB := cb; Halted := true; end; - arch.common.interrupts.unlock(flags); + arch.platform.cpu.unlock(flags); end; function done(id: uint32): boolean; -var flags: PtrUInt; callback: THaltCallback; +var + flags: PtrUInt; + callback: THaltCallback; begin - flags := arch.common.interrupts.lock; + flags := arch.platform.cpu.lock; done := Halted and (id = HaltID); callback := nil; if done then begin @@ -510,7 +529,7 @@ begin Halted := false; HaltID := 0; end; - arch.common.interrupts.unlock(flags); + arch.platform.cpu.unlock(flags); if callback <> nil then callback(); end; @@ -572,8 +591,10 @@ begin end; -var testCallbackCount: uint32; +var + testCallbackCount: uint32; testCallbackReentered: boolean; + procedure testHaltCallback; begin inc(testCallbackCount); @@ -593,6 +614,7 @@ var oldHaltCB: THaltCallback; oldCount, oldCap, i, passed, failed: uint32; beforeFree: SizeUInt; + procedure Assert(condition: boolean; name: pchar); begin if condition then inc(passed) else begin @@ -601,18 +623,31 @@ var end; end; begin - passed := 0; failed := 0; beforeFree := lmm_total_free; - oldHalted := Halted; oldHaltID := HaltID; oldHaltCB := HaltCB; - Halted := false; HaltID := 0; HaltCB := nil; - testCallbackCount := 0; testCallbackReentered := false; + passed := 0; + failed := 0; + beforeFree := lmm_total_free; + oldHalted := Halted; + oldHaltID := HaltID; + oldHaltCB := HaltCB; + Halted := false; + HaltID := 0; + HaltCB := nil; + testCallbackCount := 0; + testCallbackReentered := false; Assert(halt(41, @testHaltCallback) and not halt(42, nil), 'halt reserves one owner'); Assert(not done(42) and Halted, 'wrong completion preserves halt owner'); Assert(done(41) and (testCallbackCount = 1) and testCallbackReentered and Halted and (HaltID = 42), 'completion detaches before callback reentry'); Assert(done(42) and not done(42), 'completion releases exactly once'); - Halted := oldHalted; HaltID := oldHaltID; HaltCB := oldHaltCB; - oldCommands := Commands; oldCount := CmdCount; oldCap := CmdCap; - Commands := nil; CmdCount := 0; CmdCap := 0; + Halted := oldHalted; + HaltID := oldHaltID; + HaltCB := oldHaltCB; + oldCommands := Commands; + oldCount := CmdCount; + oldCap := CmdCap; + Commands := nil; + CmdCount := 0; + CmdCap := 0; Assert(findCommand('missing') = nil, 'empty registry lookup'); for i := 0 to 69 do registerCommand('ECHO', @cmd_echo, 'test'); Assert((getCommandCount = 70) and (getCommand(69) <> nil) and @@ -655,15 +690,17 @@ begin freeParams(params); Assert(getParam(0, nil) = nil, 'nil parameter lookup'); end else Assert(false, 'stdio test allocation'); - freeOutBuf(output); kfree(void(input)); + freeOutBuf(output); + kfree(void(input)); kfree(void(Commands)); - Commands := oldCommands; CmdCount := oldCount; CmdCap := oldCap; + Commands := oldCommands; + CmdCount := oldCount; + CmdCap := oldCap; Assert(lmm_total_free = beforeFree, 'stdio allocation ownership restored'); arch.platform.diagnostics.testSummary('STDIO', 'STDIO_TESTS_PASSED', 'STDIO_TESTS_FAILED', passed, failed); end; { ---- Initialization ---- } - procedure init; begin if Commands <> nil then exit; diff --git a/src/io/io.syslog.pas b/src/io/io.syslog.pas index a19a0f00..35e6ed6e 100644 --- a/src/io/io.syslog.pas +++ b/src/io/io.syslog.pas @@ -11,8 +11,6 @@ unit io.syslog; interface -uses - boot.mgr; type TLogHook = procedure(msg: pchar); @@ -297,7 +295,4 @@ begin If we registered serialHook, driver.io.serial would get double output. } end; -initialization - boot.mgr.registerBoot('io.syslog', @init, 'Syslog Interface', 'driver.io.serial'); - end. diff --git a/src/io/io.types.pas b/src/io/io.types.pas index deb6a5a7..0b4895dd 100644 --- a/src/io/io.types.pas +++ b/src/io/io.types.pas @@ -13,6 +13,7 @@ { Shared I/O data contracts without console, driver or runtime dependencies. Types extracted from io.stdio by Kieron Morris. } unit io.types; + interface type { Output buffer — kalloc'd, growable } diff --git a/src/memory/memory.bitmap.pas b/src/memory/memory.bitmap.pas index 657225d6..1889490b 100644 --- a/src/memory/memory.bitmap.pas +++ b/src/memory/memory.bitmap.pas @@ -12,12 +12,15 @@ { Shared bitmap operations extracted from memory.heap. } unit memory.bitmap; + interface function find_free_run(bmp: PuInt32; start, count, total: uint32): sint32; procedure bitmap_set_range(bmp: PuInt32; start, count: uint32); procedure bitmap_clear_range(bmp: PuInt32; start, count: uint32); function bitmap_all_set(bmp: PuInt32; start, count: uint32): boolean; + implementation + function find_free_run(bmp: PuInt32; start, count, total: uint32): sInt32; var idx, run_len, dw_idx: uint32; diff --git a/src/memory/memory.heap.pas b/src/memory/memory.heap.pas index fea5db6a..3ef56552 100644 --- a/src/memory/memory.heap.pas +++ b/src/memory/memory.heap.pas @@ -72,6 +72,7 @@ var procedure init; procedure UnitTest; +{ Returned allocations are zero-filled, including reused storage. } function kalloc(size : SizeUInt) : void; function klalloc(size : SizeUInt) : void; procedure klfree(address : PtrUInt); @@ -85,15 +86,23 @@ function lmm_page_count : uint32; implementation uses arch.platform.memory, arch.platform.cpu, arch.platform.diagnostics; + procedure GPF; -begin fatal('LMM', 'FAIL: heap invariant'); end; +begin + fatal('LMM', 'FAIL: heap invariant'); +end; + procedure heap_log(message: pchar); -begin arch.platform.diagnostics.log('LMM', message); end; +begin + arch.platform.diagnostics.log('LMM', message); +end; + procedure heap_oom(message: pchar); -begin fatal('OOM', message); end; +begin + fatal('OOM', message); +end; { ==== Internal helpers ==== } - function get_bitmap(hp: PHeapPageHeader): PuInt32; inline; begin get_bitmap := PuInt32(PtrUInt(hp) + uint32(sizeof(THeapPageHeader))); @@ -104,16 +113,7 @@ begin get_data_base := PtrUInt(hp) + DATA_OFFSET; end; -procedure zero_mem(dest: Pointer; bytes: SizeUInt); -begin arch.platform.cpu.zero(dest, bytes); end; -procedure restore_if(saved: PtrUInt); inline; -begin arch.platform.cpu.unlock(saved); end; - { ==== Page management ==== } - -function new_lmm_page: PtrUInt; -begin new_lmm_page := arch.platform.memory.allocateHeapPage; end; - function new_heap_page(current: PHeapPageHeader): PHeapPageHeader; var addr: PtrUInt; @@ -121,7 +121,7 @@ var i: uint32; begin push_trace('memory.heap.new_heap_page'); - addr := new_lmm_page; + addr := arch.platform.memory.allocateHeapPage; if addr = 0 then begin new_heap_page := nil; pop_trace; @@ -175,7 +175,6 @@ begin end; { ==== Public API ==== } - procedure init; begin push_trace('memory.heap.init'); @@ -191,7 +190,9 @@ begin end; function kpalloc(address: PtrUInt): void; -begin kpalloc := void(arch.platform.memory.identityReserve(address)); end; +begin + kpalloc := void(arch.platform.memory.identityReserve(address)); +end; procedure klfree(address: PtrUInt); var @@ -220,22 +221,25 @@ begin { Need extra 16 bytes for header: [page_count:4][magic:4][pad:8] } if (size = 0) or (size > High(SizeUInt) - PAGE_SIZE_LMM - SIZE_PREFIX) then begin - pop_trace; exit; + pop_trace; + exit; end; total_size := size + 16; if ((total_size + PAGE_SIZE_LMM - 1) div PAGE_SIZE_LMM) > MAX_HEAP_REGIONS then begin - pop_trace; exit; + pop_trace; + exit; end; pages := (total_size + PAGE_SIZE_LMM - 1) div PAGE_SIZE_LMM; if pages = 0 then pages := 1; base_addr := arch.platform.memory.allocateLarge(pages); if base_addr <> 0 then begin - zero_mem(Pointer(base_addr), SIZE_PREFIX); + arch.platform.cpu.zero(Pointer(base_addr), SIZE_PREFIX); PuInt32(base_addr)^ := pages; PuInt32(base_addr + 4)^ := LARGE_ALLOC_MAGIC; klalloc := void(base_addr + SIZE_PREFIX); - pop_trace; exit; + pop_trace; + exit; end; heap_log('ERROR: klalloc no contiguous pages.'); @@ -260,7 +264,7 @@ begin kalloc := nil; if (size = 0) or (size > High(SizeUInt) - PAGE_SIZE_LMM - SIZE_PREFIX) then begin - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); exit; end; @@ -268,12 +272,12 @@ begin if size > TOTAL_UNITS * ALLOC_UNIT - SIZE_PREFIX - 16 then begin kalloc := klalloc(size); if kalloc = nil then begin - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); heap_oom('kalloc: large allocation failed'); exit; end; - zero_mem(Pointer(kalloc), size); - restore_if(saved_flags); + arch.platform.cpu.zero(Pointer(kalloc), size); + arch.platform.cpu.unlock(saved_flags); exit; end; @@ -308,7 +312,7 @@ begin kalloc := void(alloc_base + SIZE_PREFIX); { Zero the user-visible area } - zero_mem(Pointer(kalloc), size); + arch.platform.cpu.zero(Pointer(kalloc), size); {$IFDEF DEBUG_LMM} { Write guard sentinel at end of allocated region } @@ -316,7 +320,7 @@ begin {$ENDIF} Search_Page := hp; - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); exit; end; end; @@ -325,7 +329,7 @@ begin if hp^.NextPage = nil then begin hp := new_heap_page(hp); if hp = nil then begin - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); heap_oom('kalloc: out of memory'); exit; end; @@ -335,7 +339,7 @@ begin Search_Page := hp; end; - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); end; procedure kfree(area: void); [public, alias: 'kernel_kfree']; @@ -358,7 +362,7 @@ begin if PuInt32(PtrUInt(area) - 12)^ = LARGE_ALLOC_MAGIC then begin base := PtrUInt(area) - 16; klfree(base); - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); exit; end; @@ -378,7 +382,7 @@ begin (prefix_val > hp^.TotalUnits - unit_idx) then begin heap_log('ERROR: kfree invalid range.'); GPF; - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); exit; end; @@ -387,7 +391,7 @@ begin if PuInt32(alloc_base + prefix_val * ALLOC_UNIT - 4)^ <> GUARD_MAGIC then begin heap_log('ERROR: kfree guard corrupted - buffer overrun!'); GPF; - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); exit; end; {$ENDIF} @@ -396,7 +400,7 @@ begin if not bitmap_all_set(bmp, unit_idx, prefix_val) then begin heap_log('ERROR: kfree double free!'); GPF; - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); exit; end; @@ -411,11 +415,10 @@ begin { If page is completely free, return it to VMM/PMM } try_release_page(hp); - restore_if(saved_flags); + arch.platform.cpu.unlock(saved_flags); end; { ==== Statistics ==== } - function lmm_total_free: SizeUInt; var hp: PHeapPageHeader; @@ -446,21 +449,34 @@ var beforeFree: SizeUInt; beforeFrames: uint64; passed, failed: uint32; + procedure Assert(condition: boolean; name: pchar); begin - if condition then begin inc(passed); heap_log(name); end - else begin inc(failed); heap_log('FAIL: heap test'); heap_log(name); end; + if condition then begin + inc(passed); + heap_log(name); + end + else begin + inc(failed); + heap_log('FAIL: heap test'); + heap_log(name); + end; end; begin - passed := 0; failed := 0; + passed := 0; + failed := 0; beforeFree := lmm_total_free; - beforeFrames := arch.platform.memory.freeBacking; - a := kalloc(1); b := kalloc(17); + beforeFrames := arch.platform.memory.freePhysicalBytes; + a := kalloc(1); + b := kalloc(17); Assert((a <> nil) and (b <> nil) and ((PtrUInt(a) and 15) = 0) and ((PtrUInt(b) and 15) = 0), 'PASS: heap odd sizes remain 16-byte aligned'); Assert((PUInt8(a)^ = 0) and (PUInt8(PtrUInt(b) + 16)^ = 0), 'PASS: heap allocation zeroed'); - PUInt8(a)^ := 123; PUInt8(PtrUInt(b) + 16)^ := 45; - kfree(a); kfree(b); kfree(nil); + PUInt8(a)^ := 123; + PUInt8(PtrUInt(b) + 16)^ := 45; + kfree(a); + kfree(b); + kfree(nil); a := kalloc(1); Assert(PUInt8(a)^ = 0, 'PASS: heap reused storage zeroed'); kfree(a); @@ -473,11 +489,11 @@ begin kfree(large); Assert((kalloc(0) = nil) and (kalloc(High(SizeUInt)) = nil), 'PASS: heap rejects zero/overflow sizes'); Assert((lmm_total_free = beforeFree) and (lmm_page_count = 1), 'PASS: heap free accounting restored'); - Assert(arch.platform.memory.freeBacking = beforeFrames, 'PASS: heap backing frames restored'); + Assert(arch.platform.memory.freePhysicalBytes = beforeFrames, 'PASS: heap backing frames restored'); arch.platform.diagnostics.testSummary('LMM', 'HEAP_TESTS_PASSED', 'HEAP_TESTS_FAILED', passed, failed); if failed <> 0 then GPF; end; -end. \ No newline at end of file +end. diff --git a/src/memory/memory.raw.pas b/src/memory/memory.raw.pas index 7e564efd..51e45c6f 100644 --- a/src/memory/memory.raw.pas +++ b/src/memory/memory.raw.pas @@ -12,38 +12,56 @@ { Allocation-free scalar memory operations with native addresses. } unit memory.raw; + interface procedure fill(dest: pointer; value: uint8; bytes: SizeUInt); procedure move(source, dest: pointer; bytes: SizeUInt); + implementation type PNative = ^PtrUInt; + procedure fill(dest: pointer; value: uint8; bytes: SizeUInt); -var address, pattern: PtrUInt; +var + address, pattern: PtrUInt; begin address := PtrUInt(dest); pattern := (High(PtrUInt) div 255) * value; while bytes >= sizeof(PtrUInt) do begin PNative(address)^ := pattern; - inc(address, sizeof(PtrUInt)); dec(bytes, sizeof(PtrUInt)); + inc(address, sizeof(PtrUInt)); + dec(bytes, sizeof(PtrUInt)); + end; + while bytes <> 0 do begin + PUInt8(address)^ := value; + inc(address); + dec(bytes); end; - while bytes <> 0 do begin PUInt8(address)^ := value; inc(address); dec(bytes); end; end; + procedure move(source, dest: pointer; bytes: SizeUInt); -var src, dst: PtrUInt; +var + src, dst: PtrUInt; begin - src := PtrUInt(source); dst := PtrUInt(dest); + src := PtrUInt(source); + dst := PtrUInt(dest); if (src = dst) or (bytes = 0) then exit; if (dst > src) and (dst - src < bytes) then begin while bytes <> 0 do begin - dec(bytes); PUInt8(dst + bytes)^ := PUInt8(src + bytes)^; + dec(bytes); + PUInt8(dst + bytes)^ := PUInt8(src + bytes)^; end; end else begin while bytes >= sizeof(PtrUInt) do begin PNative(dst)^ := PNative(src)^; - inc(dst, sizeof(PtrUInt)); inc(src, sizeof(PtrUInt)); dec(bytes, sizeof(PtrUInt)); + inc(dst, sizeof(PtrUInt)); + inc(src, sizeof(PtrUInt)); + dec(bytes, sizeof(PtrUInt)); end; while bytes <> 0 do begin - PUInt8(dst)^ := PUInt8(src)^; inc(dst); inc(src); dec(bytes); + PUInt8(dst)^ := PUInt8(src)^; + inc(dst); + inc(src); + dec(bytes); end; end; end; diff --git a/src/proc/proc.mgr.pas b/src/proc/proc.mgr.pas index 59bb2eca..b3cb6368 100644 --- a/src/proc/proc.mgr.pas +++ b/src/proc/proc.mgr.pas @@ -28,9 +28,9 @@ unit proc.mgr; interface uses - core.ds.dynamic, memory.heap, memory.raw, proc.types, proc.scheduler, - io.types, driver.storage.fdtable, arch.common.interrupts, arch.common.legacyirq, - arch.platform.process, arch.platform.diagnostics, arch.platform.cpu, + core.ds.dynamic, memory.heap, proc.types, proc.scheduler, + io.types, driver.storage.fdtable, arch.platform.cpu, arch.common.legacyirq, + arch.platform.process, arch.platform.diagnostics, io.stdio, core.strings; { Core API } @@ -40,7 +40,8 @@ function create(name : pchar; priority : uint8) : PProcessContext; function createSuspended(name: pchar; entry: TProcessEntryPoint; - localData: void; priority: uint8): PProcessContext; + localData: void; + priority: uint8): PProcessContext; function findByID(pid : uint32) : PProcessContext; function findByName(name : pchar) : PProcessContext; procedure sendMessage(pid : uint32; msg : TProcessSysMsg; data : void); @@ -101,32 +102,46 @@ type PProcessSlot = ^PtrUInt; PProcessState = ^TProcessState; procedure log(message: pchar); -begin arch.platform.diagnostics.log('PROCMGR', message); end; +begin + arch.platform.diagnostics.log('PROCMGR', message); +end; + procedure releaseFileTable(ctx: PProcessContext); begin if ctx^.FDTable <> nil then fd_table_free(PFDTable(ctx^.FDTable)); ctx^.FDTable := nil; end; + function rootDirectory: pchar; begin rootDirectory := pchar(kalloc(2)); - if rootDirectory <> nil then begin rootDirectory[0] := '/'; rootDirectory[1] := #0; end; + if rootDirectory <> nil then begin + rootDirectory[0] := '/'; + rootDirectory[1] := #0; + end; end; + function nameEquals(left, right: pchar): boolean; -var i: uint32; +var + i: uint32; begin nameEquals := false; if (left = nil) or (right = nil) then exit; i := 0; while i < 32 do begin if left[i] <> right[i] then exit; - if left[i] = #0 then begin nameEquals := true; exit; end; + if left[i] = #0 then begin + nameEquals := true; + exit; + end; inc(i); end; end; -var nextProcessID: uint32; +var + nextProcessID: uint32; reaperProcess: PProcessContext; reapPending: boolean; + procedure requestReap; begin { Called with IRQs excluded. Coalesces completed tasks into one worker wake. } @@ -134,32 +149,35 @@ begin if (reaperProcess <> nil) and (reaperProcess^.State = psAwaiting) then reaperProcess^.State := psReady; end; + procedure reaperEntry(ctx: PProcessContext); -var flags: PtrUInt; +var + flags: PtrUInt; begin while true do begin - flags := arch.common.interrupts.lock; + flags := arch.platform.cpu.lock; reapPending := false; - arch.common.interrupts.unlock(flags); + arch.platform.cpu.unlock(flags); reapFinished; - flags := arch.common.interrupts.lock; + flags := arch.platform.cpu.lock; if not reapPending then ctx^.State := psAwaiting; - arch.common.interrupts.unlock(flags); + arch.platform.cpu.unlock(flags); proc_await; end; end; + function newProcessID: uint32; -var flags: PtrUInt; +var + flags: PtrUInt; begin - flags := arch.common.interrupts.lock; + flags := arch.platform.cpu.lock; { IDs are never recycled; reject exhaustion rather than aliasing live IDs. } newProcessID := nextProcessID; if nextProcessID <> 0 then inc(nextProcessID); - arch.common.interrupts.unlock(flags); + arch.platform.cpu.unlock(flags); end; { ---- Internal helpers ---- } - procedure copyName(dest : pchar; src : pchar; maxLen : uint32); var i : uint32; @@ -176,11 +194,11 @@ end; procedure process_trampoline; forward; { ---- Process creation ---- } - function createInternal(name : pchar; entry : TProcessEntryPoint; localData : void; - priority : uint8; runnable : boolean) : PProcessContext; + priority : uint8; + runnable : boolean) : PProcessContext; var ctx : PProcessContext; slotPtr : PProcessSlot; @@ -191,7 +209,10 @@ begin push_trace('proc.mgr.create'); createInternal := nil; - if (entry = nil) or (name = nil) or (Processes = nil) then begin pop_trace; exit; end; + if (entry = nil) or (name = nil) or (Processes = nil) then begin + pop_trace; + exit; + end; if priority = 0 then prio := 1 else prio := priority; { Allocate process context } @@ -201,11 +222,14 @@ begin pop_trace; exit; end; - memory.raw.fill(pointer(ctx), 0, SizeOf(TProcessContext)); { Identity } ctx^.ProcessID := newProcessID; - if ctx^.ProcessID = 0 then begin kfree(void(ctx)); pop_trace; exit; end; + if ctx^.ProcessID = 0 then begin + kfree(void(ctx)); + pop_trace; + exit; + end; copyName(@ctx^.Name[0], name, 32); ctx^.ParentID := 0; if CurrentProcess <> nil then @@ -278,10 +302,10 @@ begin @process_trampoline); { Add to process table } - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; slotPtr := PProcessSlot(DL_Add(Processes)); if slotPtr = nil then begin - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); log('Failed to allocate process table slot.'); if ctx^.StackBase <> nil then kfree(ctx^.StackBase); @@ -300,7 +324,7 @@ begin { Mark as ready for scheduling } if runnable then ctx^.State := psReady; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); log(name); @@ -311,13 +335,15 @@ end; { Public creation retains its ready-on-return behavior. Commands need their arguments and streams installed before the scheduler may enter them. } function create(name : pchar; entry : TProcessEntryPoint; - localData : void; priority : uint8) : PProcessContext; + localData : void; + priority : uint8) : PProcessContext; begin create := createInternal(name, entry, localData, priority, true); end; function createSuspended(name: pchar; entry: TProcessEntryPoint; - localData: void; priority: uint8): PProcessContext; + localData: void; + priority: uint8): PProcessContext; begin createSuspended := createInternal(name, entry, localData, priority, false); end; @@ -341,7 +367,6 @@ begin end; { ---- Find ---- } - function findByID(pid : uint32) : PProcessContext; var i : uint32; @@ -383,11 +408,12 @@ begin end; { ---- Messaging ---- } - procedure sendMessage(pid: uint32; msg: TProcessSysMsg; data: void); -var ctx: PProcessContext; savedFlags: PtrUInt; +var + ctx: PProcessContext; + savedFlags: PtrUInt; begin - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; ctx := findByID(pid); if ctx <> nil then begin ctx^.MsgData := data; @@ -395,7 +421,7 @@ begin if (ctx^.State = psAwaiting) or ((ctx^.State = psSuspended) and (msg = smResume)) then ctx^.State := psReady; end; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); end; procedure terminate(pid : uint32); @@ -406,33 +432,38 @@ begin end; procedure kill(pid: uint32); -var ctx: PProcessContext; savedFlags: PtrUInt; +var + ctx: PProcessContext; + savedFlags: PtrUInt; begin if pid = 0 then exit; - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; ctx := findByID(pid); if (ctx <> nil) and (ctx <> reaperProcess) then begin - ctx^.State := psFinished; ctx^.ExitCode := High(uint32); requestReap; + ctx^.State := psFinished; + ctx^.ExitCode := High(uint32); + requestReap; end; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); end; { ---- Process self-management ---- } - procedure proc_yield; -var savedFlags: PtrUInt; +var + savedFlags: PtrUInt; begin if CurrentProcess <> nil then begin - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; CurrentProcess^.TicksUsed := CurrentProcess^.Quantum; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); { Next timer tick will preempt us } arch.platform.cpu.idle; end; end; procedure proc_sleep_ms(ms: uint32); -var remaining: uint64; +var + remaining: uint64; previous, now, delta: uint32; begin remaining := (uint64(ms) * PIT_INPUT_HZ + uint64(PIT_DIVISOR) * 1000 - 1) div (PIT_DIVISOR * 1000); @@ -461,12 +492,13 @@ begin end; procedure proc_suspend; -var savedFlags: PtrUInt; +var + savedFlags: PtrUInt; begin if CurrentProcess <> nil then begin - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; CurrentProcess^.State := psSuspended; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); arch.platform.cpu.idle; end; end; @@ -483,27 +515,34 @@ begin end; { ---- Resource binding ---- } - procedure bindResource(ctx: PProcessContext; kind: TResourceKind; handle: void; cleanup: TResourceCleanup); -var binding: PResourceBinding; savedFlags: PtrUInt; +var + binding: PResourceBinding; + savedFlags: PtrUInt; begin if ctx = nil then exit; - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; if ctx^.Resources <> nil then begin binding := PResourceBinding(DL_Add(PDList(ctx^.Resources))); if binding <> nil then begin - binding^.Kind := kind; binding^.Handle := handle; binding^.Cleanup := cleanup; + binding^.Kind := kind; + binding^.Handle := handle; + binding^.Cleanup := cleanup; end; end; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); end; procedure unbindResource(ctx: PProcessContext; handle: void); -var resList: PDList; binding: PResourceBinding; i: uint32; - cleanup: TResourceCleanup; savedFlags: PtrUInt; +var + resList: PDList; + binding: PResourceBinding; + i: uint32; + cleanup: TResourceCleanup; + savedFlags: PtrUInt; begin if ctx = nil then exit; - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; resList := PDList(ctx^.Resources); cleanup := nil; if DL_Size(resList) > 0 then for i := 0 to DL_Size(resList) - 1 do begin @@ -514,21 +553,28 @@ begin break; end; end; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); if assigned(cleanup) then cleanup(handle); end; procedure unbindResourceNoCleanup(ctx: PProcessContext; handle: void); -var resList: PDList; binding: PResourceBinding; i: uint32; savedFlags: PtrUInt; +var + resList: PDList; + binding: PResourceBinding; + i: uint32; + savedFlags: PtrUInt; begin if ctx = nil then exit; - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; resList := PDList(ctx^.Resources); if DL_Size(resList) > 0 then for i := 0 to DL_Size(resList) - 1 do begin binding := PResourceBinding(DL_Get(resList, i)); - if binding^.Handle = handle then begin DL_Delete(resList, i); break; end; + if binding^.Handle = handle then begin + DL_Delete(resList, i); + break; + end; end; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); end; procedure unbindAllResources(ctx : PProcessContext); @@ -539,12 +585,15 @@ var savedFlags: PtrUInt; begin if ctx = nil then exit; - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; resList := PDList(ctx^.Resources); - if resList = nil then begin arch.common.interrupts.unlock(savedFlags); exit; end; + if resList = nil then begin + arch.platform.cpu.unlock(savedFlags); + exit; + end; { Detach early so cleanup callbacks that re-enter find nothing } ctx^.Resources := nil; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); for i := sint32(DL_Size(resList)) - 1 downto 0 do begin binding := PResourceBinding(DL_Get(resList, uint32(i))); if binding <> nil then begin @@ -583,15 +632,14 @@ begin ctx^.StdErr := stderr_buf; { Publish only after all fields read by legacy_entry are initialized. psCreated is also runnable, so creation must use psSuspended. } - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; ctx^.State := psReady; - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); end; runCommand := ctx; end; { ---- Query ---- } - function processCount : uint32; begin if Processes = nil then @@ -613,7 +661,6 @@ begin end; { ---- Reaping ---- } - procedure reapFinished; var i: sint32; @@ -624,7 +671,7 @@ begin Detach under IRQ exclusion before callbacks can re-enter the registry. } i := sint32(DL_Size(Processes)) - 1; while i >= 0 do begin - savedFlags := arch.common.interrupts.lock; + savedFlags := arch.platform.cpu.lock; ctx := getProcessByIndex(uint32(i)); if (ctx <> nil) and (ctx <> CurrentProcess) and (ctx^.ProcessID <> 0) and ((ctx^.State = psFinished) or (ctx^.State = psError)) then @@ -634,7 +681,7 @@ begin if (ctx <> nil) and (ctx <> CurrentProcess) and (ctx^.ProcessID <> 0) and ((ctx^.State = psFinished) or (ctx^.State = psError)) then begin DL_Delete(Processes, uint32(i)); - arch.common.interrupts.unlock(savedFlags); + arch.platform.cpu.unlock(savedFlags); releaseFileTable(ctx); if ctx^.Cwd <> nil then kfree(void(ctx^.Cwd)); unbindAllResources(ctx); @@ -642,13 +689,12 @@ begin if ctx^.ParentID <> 0 then sendMessage(ctx^.ParentID, smChildExited, void(PtrUInt(ctx^.ProcessID))); kfree(void(ctx)); - end else arch.common.interrupts.unlock(savedFlags); + end else arch.platform.cpu.unlock(savedFlags); dec(i); end; end; { ---- Scheduler ---- } - function scheduler_pick_next : PProcessContext; begin if (CurrentProcess^.State = psFinished) or (CurrentProcess^.State = psError) then requestReap; @@ -775,7 +821,6 @@ end; { ---- Init ---- } - procedure init; var slotPtr : PProcessSlot; @@ -791,7 +836,6 @@ begin { Create idle process — PID 0, unkillable, uses core.version main stack } IdleProcess := PProcessContext(kalloc(SizeOf(TProcessContext))); - memory.raw.fill(pointer(IdleProcess), 0, SizeOf(TProcessContext)); IdleProcess^.ProcessID := 0; copyName(@IdleProcess^.Name[0], 'Idle', 32); IdleProcess^.State := psRunning; diff --git a/src/proc/proc.scheduler.pas b/src/proc/proc.scheduler.pas index deb1241a..7899069f 100644 --- a/src/proc/proc.scheduler.pas +++ b/src/proc/proc.scheduler.pas @@ -14,14 +14,19 @@ { Shared round-robin/quantum policy extracted from proc.mgr (Kieron Morris). Caller excludes interrupts and owns process lifetime/reaping. } unit proc.scheduler; + interface uses proc.types; type TGetProcess = function(index: uint32): PProcessContext; function pickNext(current, idle: PProcessContext; count: uint32; - var cursor: uint32; getProcess: TGetProcess): PProcessContext; + var cursor: uint32; + getProcess: TGetProcess): PProcessContext; + implementation + function pickNext(current, idle: PProcessContext; count: uint32; - var cursor: uint32; getProcess: TGetProcess): PProcessContext; + var cursor: uint32; + getProcess: TGetProcess): PProcessContext; var i : uint32; idx : uint32; @@ -58,7 +63,10 @@ begin end; { No runnable process found — fall back to idle } - if idle = nil then begin pickNext := nil; exit; end; + if idle = nil then begin + pickNext := nil; + exit; + end; idle^.State := psRunning; idle^.TicksUsed := 0; pickNext := idle;