aboutsummaryrefslogtreecommitdiff
path: root/mirai.old
diff options
context:
space:
mode:
Diffstat (limited to 'mirai.old')
-rw-r--r--mirai.old/asm/cmos.zig56
-rw-r--r--mirai.old/asm/context.zig66
-rw-r--r--mirai.old/asm/cpu.zig68
-rw-r--r--mirai.old/asm/cpuid.zig58
-rw-r--r--mirai.old/asm/entry.zig128
-rw-r--r--mirai.old/asm/io.zig46
-rw-r--r--mirai.old/asm/isr.zig234
-rw-r--r--mirai.old/asm/memory.zig32
-rw-r--r--mirai.old/asm/msr.zig26
-rw-r--r--mirai.old/asm/syscall.zig27
-rw-r--r--mirai.old/boot/gdt/gdt.zig108
-rw-r--r--mirai.old/boot/gdt/types.zig26
-rw-r--r--mirai.old/boot/multiboot/framebuffer.zig48
-rw-r--r--mirai.old/boot/multiboot/memory.zig51
-rw-r--r--mirai.old/boot/multiboot/multiboot.zig13
-rw-r--r--mirai.old/boot/multiboot/types.zig16
-rw-r--r--mirai.old/boot/sequence/message.zig74
-rw-r--r--mirai.old/boot/sequence/sequence.zig174
-rw-r--r--mirai.old/boot/tss/tss.zig54
-rw-r--r--mirai.old/common/constants/ahci.zig46
-rw-r--r--mirai.old/common/constants/akiba.zig9
-rw-r--r--mirai.old/common/constants/ascii.zig10
-rw-r--r--mirai.old/common/constants/ata.zig33
-rw-r--r--mirai.old/common/constants/attachment.zig8
-rw-r--r--mirai.old/common/constants/colors.zig14
-rw-r--r--mirai.old/common/constants/elf.zig18
-rw-r--r--mirai.old/common/constants/fs.zig46
-rw-r--r--mirai.old/common/constants/gdt.zig7
-rw-r--r--mirai.old/common/constants/heap.zig6
-rw-r--r--mirai.old/common/constants/idt.zig10
-rw-r--r--mirai.old/common/constants/interrupts.zig39
-rw-r--r--mirai.old/common/constants/invocations.zig25
-rw-r--r--mirai.old/common/constants/kata.zig27
-rw-r--r--mirai.old/common/constants/keyboard.zig11
-rw-r--r--mirai.old/common/constants/memory.zig90
-rw-r--r--mirai.old/common/constants/msr.zig20
-rw-r--r--mirai.old/common/constants/paging.zig11
-rw-r--r--mirai.old/common/constants/pci.zig42
-rw-r--r--mirai.old/common/constants/pic.zig20
-rw-r--r--mirai.old/common/constants/pit.zig20
-rw-r--r--mirai.old/common/constants/pmm.zig14
-rw-r--r--mirai.old/common/constants/ports.zig32
-rw-r--r--mirai.old/common/constants/psf.zig7
-rw-r--r--mirai.old/common/constants/serial.zig3
-rw-r--r--mirai.old/common/constants/time.zig9
-rw-r--r--mirai.old/common/constants/vga.zig3
-rw-r--r--mirai.old/common/constants/video.zig6
-rw-r--r--mirai.old/common/errors/fs.zig15
-rw-r--r--mirai.old/common/errors/io.zig15
-rw-r--r--mirai.old/common/errors/kata.zig9
-rw-r--r--mirai.old/common/errors/memory.zig10
-rw-r--r--mirai.old/common/limits/ahci.zig8
-rw-r--r--mirai.old/common/limits/ata.zig4
-rw-r--r--mirai.old/common/limits/attachment.zig3
-rw-r--r--mirai.old/common/limits/boot.zig7
-rw-r--r--mirai.old/common/limits/crimson.zig6
-rw-r--r--mirai.old/common/limits/fs.zig6
-rw-r--r--mirai.old/common/limits/io.zig6
-rw-r--r--mirai.old/common/limits/kata.zig8
-rw-r--r--mirai.old/common/limits/keyboard.zig3
-rw-r--r--mirai.old/common/limits/memory.zig27
-rw-r--r--mirai.old/common/limits/pci.zig6
-rw-r--r--mirai.old/common/limits/terminal.zig6
-rw-r--r--mirai.old/common/limits/vga.zig4
-rw-r--r--mirai.old/crimson/crimson.zig15
-rw-r--r--mirai.old/crimson/exception.zig136
-rw-r--r--mirai.old/crimson/format.zig132
-rw-r--r--mirai.old/crimson/panic.zig66
-rw-r--r--mirai.old/crimson/render.zig155
-rw-r--r--mirai.old/crimson/types.zig50
-rw-r--r--mirai.old/drivers/ahci/ahci.zig232
-rw-r--r--mirai.old/drivers/ahci/fis.zig40
-rw-r--r--mirai.old/drivers/ahci/port.zig64
-rw-r--r--mirai.old/drivers/ahci/types.zig78
-rw-r--r--mirai.old/drivers/ata/ata.zig63
-rw-r--r--mirai.old/drivers/keyboard/keyboard.zig93
-rw-r--r--mirai.old/drivers/keyboard/scancode.zig39
-rw-r--r--mirai.old/drivers/pci/pci.zig120
-rw-r--r--mirai.old/drivers/pci/types.zig21
-rw-r--r--mirai.old/drivers/pit/pit.zig12
-rw-r--r--mirai.old/drivers/serial/serial.zig175
-rw-r--r--mirai.old/drivers/vga/vga.zig42
-rw-r--r--mirai.old/fs/afs/afs.zig125
-rw-r--r--mirai.old/fs/afs/cache.zig32
-rw-r--r--mirai.old/fs/afs/cluster.zig70
-rw-r--r--mirai.old/fs/afs/info.zig15
-rw-r--r--mirai.old/fs/afs/location.zig30
-rw-r--r--mirai.old/fs/afs/read.zig188
-rw-r--r--mirai.old/fs/afs/types.zig81
-rw-r--r--mirai.old/fs/afs/write.zig197
-rw-r--r--mirai.old/fs/gpt/gpt.zig41
-rw-r--r--mirai.old/fs/gpt/types.zig6
-rw-r--r--mirai.old/graphics/fonts/psf.zig137
-rw-r--r--mirai.old/graphics/terminal/terminal.zig184
-rw-r--r--mirai.old/graphics/video/video.zig42
-rw-r--r--mirai.old/hikari/elf/elf.zig10
-rw-r--r--mirai.old/hikari/elf/parser.zig73
-rw-r--r--mirai.old/hikari/elf/types.zig40
-rw-r--r--mirai.old/hikari/format/format.zig11
-rw-r--r--mirai.old/hikari/format/parser.zig64
-rw-r--r--mirai.old/hikari/format/types.zig28
-rw-r--r--mirai.old/hikari/hikari.zig9
-rw-r--r--mirai.old/hikari/loader.zig181
-rw-r--r--mirai.old/interrupts/handlers.zig9
-rw-r--r--mirai.old/interrupts/idt.zig72
-rw-r--r--mirai.old/interrupts/pic.zig30
-rw-r--r--mirai.old/interrupts/types.zig16
-rw-r--r--mirai.old/invocations/fs/getlocation.zig35
-rw-r--r--mirai.old/invocations/fs/setlocation.zig64
-rw-r--r--mirai.old/invocations/fs/viewstack.zig87
-rw-r--r--mirai.old/invocations/handler.zig92
-rw-r--r--mirai.old/invocations/io/attach.zig112
-rw-r--r--mirai.old/invocations/io/getkeychar.zig13
-rw-r--r--mirai.old/invocations/io/mark.zig62
-rw-r--r--mirai.old/invocations/io/seal.zig28
-rw-r--r--mirai.old/invocations/io/view.zig74
-rw-r--r--mirai.old/invocations/io/wipe.zig10
-rw-r--r--mirai.old/invocations/kata/exit.zig33
-rw-r--r--mirai.old/invocations/kata/postman.zig80
-rw-r--r--mirai.old/invocations/kata/reap.zig62
-rw-r--r--mirai.old/invocations/kata/spawn.zig163
-rw-r--r--mirai.old/invocations/kata/wait.zig46
-rw-r--r--mirai.old/invocations/kata/yield.zig42
-rw-r--r--mirai.old/invocations/os/cpuinfo.zig42
-rw-r--r--mirai.old/invocations/os/diskinfo.zig26
-rw-r--r--mirai.old/invocations/os/gettime.zig72
-rw-r--r--mirai.old/invocations/os/meminfo.zig20
-rw-r--r--mirai.old/invocations/os/uptime.zig12
-rw-r--r--mirai.old/invocations/syscall.zig85
-rw-r--r--mirai.old/kata/attachment.zig59
-rw-r--r--mirai.old/kata/kata.zig29
-rw-r--r--mirai.old/kata/memory.zig288
-rw-r--r--mirai.old/kata/pool.zig145
-rw-r--r--mirai.old/kata/sensei/queue.zig70
-rw-r--r--mirai.old/kata/sensei/sensei.zig140
-rw-r--r--mirai.old/kata/sensei/waker.zig74
-rw-r--r--mirai.old/kata/shift.zig53
-rw-r--r--mirai.old/kata/types.zig128
-rw-r--r--mirai.old/memory/heap.zig215
-rw-r--r--mirai.old/memory/paging.zig415
-rw-r--r--mirai.old/memory/pmm.zig229
-rw-r--r--mirai.old/mirai.zig25
-rw-r--r--mirai.old/utils/bytes/endian.zig49
-rw-r--r--mirai.old/utils/fs/location.zig162
-rw-r--r--mirai.old/utils/graphics/color.zig33
-rw-r--r--mirai.old/utils/graphics/pixel.zig197
-rw-r--r--mirai.old/utils/kata/attachment.zig37
-rw-r--r--mirai.old/utils/mem/copy.zig27
-rw-r--r--mirai.old/utils/mem/slice.zig29
-rw-r--r--mirai.old/utils/random/xorshift.zig26
-rw-r--r--mirai.old/utils/string/compare.zig22
-rw-r--r--mirai.old/utils/string/copy.zig23
-rw-r--r--mirai.old/utils/types/int.zig41
-rw-r--r--mirai.old/utils/types/ptr.zig13
-rw-r--r--mirai.old/utils/types/result.zig27
155 files changed, 9074 insertions, 0 deletions
diff --git a/mirai.old/asm/cmos.zig b/mirai.old/asm/cmos.zig
new file mode 100644
index 0000000..ba4005b
--- /dev/null
+++ b/mirai.old/asm/cmos.zig
@@ -0,0 +1,56 @@
+//! CMOS/RTC Operations
+
+const io = @import("io.zig");
+const ports = @import("../common/constants/ports.zig");
+
+/// Read a CMOS register
+pub inline fn read_register(reg: u8) u8 {
+ io.out_byte(ports.CMOS_ADDRESS, reg);
+ return io.in_byte(ports.CMOS_DATA);
+}
+
+/// Write to a CMOS register
+pub inline fn write_register(reg: u8, value: u8) void {
+ io.out_byte(ports.CMOS_ADDRESS, reg);
+ io.out_byte(ports.CMOS_DATA, value);
+}
+
+/// Read RTC seconds (BCD)
+pub inline fn read_seconds() u8 {
+ return read_register(0x00);
+}
+
+/// Read RTC minutes (BCD)
+pub inline fn read_minutes() u8 {
+ return read_register(0x02);
+}
+
+/// Read RTC hours (BCD)
+pub inline fn read_hours() u8 {
+ return read_register(0x04);
+}
+
+/// Read RTC day of month (BCD)
+pub inline fn read_day() u8 {
+ return read_register(0x07);
+}
+
+/// Read RTC month (BCD)
+pub inline fn read_month() u8 {
+ return read_register(0x08);
+}
+
+/// Read RTC year (BCD, 00-99)
+pub inline fn read_year() u8 {
+ return read_register(0x09);
+}
+
+/// Read RTC century (BCD)
+pub inline fn read_century() u8 {
+ return read_register(0x32);
+}
+
+/// Convert BCD to binary
+pub inline fn bcd_to_bin(bcd: u8) u8 {
+ return (bcd & 0x0F) + ((bcd >> 4) * 10);
+}
diff --git a/mirai.old/asm/context.zig b/mirai.old/asm/context.zig
new file mode 100644
index 0000000..ada6585
--- /dev/null
+++ b/mirai.old/asm/context.zig
@@ -0,0 +1,66 @@
+//! Context Switching Operations
+//! Low-level assembly for switching between Kata execution contexts
+
+const kata_mod = @import("../kata/kata.zig");
+
+/// Switch to a Kata's execution context
+/// This function never returns - it jumps to userspace via iretq
+pub fn switch_to_context(ctx: *const kata_mod.Context, page_table: u64, kernel_stack: u64) noreturn {
+ const ctx_addr = @intFromPtr(ctx);
+
+ asm volatile (
+ // Load context address and page table into registers
+ \\mov %[ctx_addr], %%rdi
+ \\mov %[pt], %%rsi
+ \\
+ // Switch to kata's higher-half kernel stack
+ \\mov %[kstack], %%rsp
+ \\
+ // Read iretq frame values from context struct
+ // Offsets based on Context struct layout:
+ // rsp=56, rip=128, rflags=136, cs=144, ss=152
+ \\mov 152(%%rdi), %%r12
+ \\mov 56(%%rdi), %%r13
+ \\mov 136(%%rdi), %%r14
+ \\mov 144(%%rdi), %%r15
+ \\mov 128(%%rdi), %%rax
+ \\
+ // Build iretq frame (ss, rsp, rflags, cs, rip)
+ \\pushq %%r12
+ \\pushq %%r13
+ \\pushq %%r14
+ \\pushq %%r15
+ \\pushq %%rax
+ \\
+ // Switch to user page table
+ \\mov %%rsi, %%cr3
+ \\
+ // Restore registers from context (offsets from Context struct)
+ // rax=0, rbx=8, rcx=16, rdx=24, rsi=32, rdi=40, rbp=48
+ // r8=64, r9=72, r10=80, r11=88, r12=96, r13=104, r14=112, r15=120
+ \\mov 8(%%rdi), %%rbx
+ \\mov 16(%%rdi), %%rcx
+ \\mov 24(%%rdi), %%rdx
+ \\mov 32(%%rdi), %%rsi
+ \\mov 48(%%rdi), %%rbp
+ \\mov 64(%%rdi), %%r8
+ \\mov 72(%%rdi), %%r9
+ \\mov 80(%%rdi), %%r10
+ \\mov 88(%%rdi), %%r11
+ \\mov 96(%%rdi), %%r12
+ \\mov 104(%%rdi), %%r13
+ \\mov 112(%%rdi), %%r14
+ \\mov 120(%%rdi), %%r15
+ \\mov 0(%%rdi), %%rax
+ \\mov 40(%%rdi), %%rdi
+ \\
+ // Jump to userspace
+ \\iretq
+ :
+ : [ctx_addr] "r" (ctx_addr),
+ [pt] "r" (page_table),
+ [kstack] "r" (kernel_stack),
+ : .{ .memory = true }
+ );
+ unreachable;
+}
diff --git a/mirai.old/asm/cpu.zig b/mirai.old/asm/cpu.zig
new file mode 100644
index 0000000..86324a0
--- /dev/null
+++ b/mirai.old/asm/cpu.zig
@@ -0,0 +1,68 @@
+//! CPU Control Operations
+//! Processor state management and descriptor table operations
+
+/// Halt processor until next interrupt
+pub inline fn halt_processor() void {
+ asm volatile ("hlt");
+}
+
+/// Disable hardware interrupts
+pub inline fn disable_interrupts() void {
+ asm volatile ("cli");
+}
+
+/// Enable hardware interrupts
+pub inline fn enable_interrupts() void {
+ asm volatile ("sti");
+}
+
+/// Load Global Descriptor Table
+pub inline fn load_global_descriptor_table(gdtr_address: u64) void {
+ asm volatile ("lgdt (%[gdtr])"
+ :
+ : [gdtr] "r" (gdtr_address),
+ );
+}
+
+/// Load Interrupt Descriptor Table
+pub inline fn load_interrupt_descriptor_table(idtr_address: u64) void {
+ asm volatile ("lidt (%[idtr])"
+ :
+ : [idtr] "r" (idtr_address),
+ );
+}
+
+/// Load Task Register
+pub inline fn load_task_register(selector: u16) void {
+ asm volatile ("ltr %[sel]"
+ :
+ : [sel] "r" (selector),
+ );
+}
+
+/// Reload all data segment registers with specified selector
+pub inline fn reload_segment_registers(selector: u16) void {
+ asm volatile (
+ \\mov %[sel], %%ds
+ \\mov %[sel], %%es
+ \\mov %[sel], %%fs
+ \\mov %[sel], %%gs
+ \\mov %[sel], %%ss
+ :
+ : [sel] "r" (selector),
+ );
+}
+
+/// Reload code segment via far return (required after GDT change)
+pub inline fn reload_code_segment(selector: u16) void {
+ asm volatile (
+ \\pushq %[sel]
+ \\leaq 1f(%%rip), %%rax
+ \\pushq %%rax
+ \\lretq
+ \\1:
+ :
+ : [sel] "r" (@as(u64, selector)),
+ : .{ .rax = true }
+ );
+}
diff --git a/mirai.old/asm/cpuid.zig b/mirai.old/asm/cpuid.zig
new file mode 100644
index 0000000..6367b42
--- /dev/null
+++ b/mirai.old/asm/cpuid.zig
@@ -0,0 +1,58 @@
+//! CPUID Operations
+
+pub const CpuidResult = struct {
+ eax: u32,
+ ebx: u32,
+ ecx: u32,
+ edx: u32,
+};
+
+/// Execute CPUID instruction with given leaf
+pub inline fn cpuid(leaf: u32) CpuidResult {
+ var eax: u32 = undefined;
+ var ebx: u32 = undefined;
+ var ecx: u32 = undefined;
+ var edx: u32 = undefined;
+
+ asm volatile ("cpuid"
+ : [eax] "={eax}" (eax),
+ [ebx] "={ebx}" (ebx),
+ [ecx] "={ecx}" (ecx),
+ [edx] "={edx}" (edx),
+ : [leaf] "{eax}" (leaf),
+ );
+
+ return CpuidResult{
+ .eax = eax,
+ .ebx = ebx,
+ .ecx = ecx,
+ .edx = edx,
+ };
+}
+
+/// Get maximum extended CPUID leaf
+pub inline fn get_max_extended() u32 {
+ return cpuid(0x80000000).eax;
+}
+
+/// Check if extended brand string is supported
+pub inline fn has_brand_string() bool {
+ return get_max_extended() >= 0x80000004;
+}
+
+/// Get CPU brand string (48 bytes)
+pub fn get_brand_string(buffer: *[48]u8) void {
+ var pos: usize = 0;
+
+ inline for ([_]u32{ 0x80000002, 0x80000003, 0x80000004 }) |leaf| {
+ const result = cpuid(leaf);
+
+ inline for ([_]u32{ result.eax, result.ebx, result.ecx, result.edx }) |reg| {
+ buffer[pos] = @truncate(reg);
+ buffer[pos + 1] = @truncate(reg >> 8);
+ buffer[pos + 2] = @truncate(reg >> 16);
+ buffer[pos + 3] = @truncate(reg >> 24);
+ pos += 4;
+ }
+ }
+}
diff --git a/mirai.old/asm/entry.zig b/mirai.old/asm/entry.zig
new file mode 100644
index 0000000..4f9adf6
--- /dev/null
+++ b/mirai.old/asm/entry.zig
@@ -0,0 +1,128 @@
+//! System Call Entry Point
+//! Assembly stub for handling SYSCALL instruction from userspace
+
+// External handlers
+extern fn handle_syscall(ctx: u64) void;
+extern fn get_kernel_stack() u64;
+
+/// Address of syscall entry point for LSTAR MSR
+pub fn get_entry_address() u64 {
+ return @intFromPtr(&syscall_entry_asm);
+}
+
+extern fn syscall_entry_asm() void;
+
+comptime {
+ asm (
+ \\.global syscall_entry_asm
+ \\syscall_entry_asm:
+ \\ # SYSCALL has already:
+ \\ # - Saved RIP to RCX
+ \\ # - Saved RFLAGS to R11
+ \\ # - Loaded CS/SS from STAR
+ \\ # - Jumped here (LSTAR)
+ \\ # - Masked RFLAGS per FMASK
+ \\ #
+ \\ # We need to:
+ \\ # - Save user RSP
+ \\ # - Switch to kernel stack (from TSS)
+ \\ # - Save all registers
+ \\ # - Call handler
+ \\ # - Restore registers
+ \\ # - Use sysret to return
+ \\
+ \\ # At entry: RCX = user RIP, R11 = user RFLAGS, RSP = user RSP, RAX = syscall#
+ \\
+ \\ # Strategy: Use SWAPGS-like approach but with stack
+ \\ # Save everything on user stack, switch to kernel stack, copy over
+ \\
+ \\ # First, save user RSP by pushing all regs then calculating
+ \\ push %rax
+ \\ push %rbx
+ \\ push %rcx
+ \\ push %rdx
+ \\ push %rsi
+ \\ push %rdi
+ \\ push %rbp
+ \\ push %r8
+ \\ push %r9
+ \\ push %r10
+ \\ push %r11
+ \\ push %r12
+ \\ push %r13
+ \\ push %r14
+ \\ push %r15
+ \\
+ \\ # Get kernel stack (returns in RAX, RSP unchanged after ret)
+ \\ call get_kernel_stack
+ \\
+ \\ # RAX now has kernel stack pointer
+ \\ # RSP points to saved R15 (call/ret balanced)
+ \\ mov %rsp, %r15 # R15 = pointer to saved registers
+ \\ lea 120(%r15), %r14 # R14 = original user RSP (before 15 pushes)
+ \\ mov %rax, %rsp # Switch to kernel stack
+ \\
+ \\ # Build struct on kernel stack by reading from user stack (via R15)
+ \\ # User stack has (from low to high addr): [rax][rbx][rcx][rdx][rsi][rdi][rbp][r8-r15]
+ \\ # We push in REVERSE order so struct layout matches (stack grows down)
+ \\ # NOTE: RCX and R11 already have user_rip and user_rflags from SYSCALL!
+ \\ push %r14 # user_rsp (last in struct, push first)
+ \\ push 32(%r15) # user_rflags (R11 value before syscall)
+ \\ push $0x1B # user_ss
+ \\ push 96(%r15) # user_rip (RCX value before syscall)
+ \\ push $0x23 # user_cs
+ \\ push 0(%r15) # r15
+ \\ push 8(%r15) # r14
+ \\ push 16(%r15) # r13
+ \\ push 24(%r15) # r12
+ \\ push 32(%r15) # r11 (value before syscall)
+ \\ push 40(%r15) # r10
+ \\ push 48(%r15) # r9
+ \\ push 56(%r15) # r8
+ \\ push 64(%r15) # rbp
+ \\ push 72(%r15) # rdi
+ \\ push 80(%r15) # rsi
+ \\ push 88(%r15) # rdx
+ \\ push 96(%r15) # rcx (value before syscall)
+ \\ push 104(%r15) # rbx
+ \\ push 112(%r15) # rax (first in struct, push last)
+ \\
+ \\ # Re-enable interrupts (we're on kernel stack now)
+ \\ sti
+ \\
+ \\ # Call invocation handler
+ \\ mov %rsp, %rdi # Pass context pointer
+ \\ call handle_syscall
+ \\
+ \\ # Disable interrupts before returning to user
+ \\ cli
+ \\
+ \\ # Restore registers (RAX might be modified with return value)
+ \\ # NOTE: Don't restore RCX and R11 - they're needed for sysret!
+ \\ pop %rax
+ \\ pop %rbx
+ \\ add $8, %rsp # Skip RCX (will be loaded from saved RIP)
+ \\ pop %rdx
+ \\ pop %rsi
+ \\ pop %rdi
+ \\ pop %rbp
+ \\ pop %r8
+ \\ pop %r9
+ \\ pop %r10
+ \\ add $8, %rsp # Skip R11 (will be loaded from saved RFLAGS)
+ \\ pop %r12
+ \\ pop %r13
+ \\ pop %r14
+ \\ pop %r15
+ \\
+ \\ # Pop saved context
+ \\ add $8, %rsp # Skip user CS (sysret will set it)
+ \\ pop %rcx # User RIP for sysret
+ \\ add $8, %rsp # Skip user SS (sysret will set it)
+ \\ pop %r11 # User RFLAGS for sysret
+ \\ pop %rsp # Restore user RSP
+ \\
+ \\ # Return to userspace
+ \\ sysretq
+ );
+}
diff --git a/mirai.old/asm/io.zig b/mirai.old/asm/io.zig
new file mode 100644
index 0000000..2f969d2
--- /dev/null
+++ b/mirai.old/asm/io.zig
@@ -0,0 +1,46 @@
+//! I/O Port Operations
+
+pub inline fn out_byte(port: u16, value: u8) void {
+ asm volatile ("outb %[value], %[port]"
+ :
+ : [value] "{al}" (value),
+ [port] "N{dx}" (port),
+ );
+}
+
+pub inline fn in_byte(port: u16) u8 {
+ return asm volatile ("inb %[port], %[result]"
+ : [result] "={al}" (-> u8),
+ : [port] "N{dx}" (port),
+ );
+}
+
+pub inline fn out_word(port: u16, value: u16) void {
+ asm volatile ("outw %[value], %[port]"
+ :
+ : [value] "{ax}" (value),
+ [port] "N{dx}" (port),
+ );
+}
+
+pub inline fn in_word(port: u16) u16 {
+ return asm volatile ("inw %[port], %[result]"
+ : [result] "={ax}" (-> u16),
+ : [port] "N{dx}" (port),
+ );
+}
+
+pub inline fn out_long(port: u16, value: u32) void {
+ asm volatile ("outl %[value], %[port]"
+ :
+ : [value] "{eax}" (value),
+ [port] "N{dx}" (port),
+ );
+}
+
+pub inline fn in_long(port: u16) u32 {
+ return asm volatile ("inl %[port], %[result]"
+ : [result] "={eax}" (-> u32),
+ : [port] "N{dx}" (port),
+ );
+}
diff --git a/mirai.old/asm/isr.zig b/mirai.old/asm/isr.zig
new file mode 100644
index 0000000..1fe5a9e
--- /dev/null
+++ b/mirai.old/asm/isr.zig
@@ -0,0 +1,234 @@
+//! Interrupt Service Routine Stubs
+//! Assembly entry points for exceptions and hardware interrupts
+
+const std = @import("std");
+
+// External handlers (implemented in Zig)
+extern fn exception_handler(ctx: u64) void;
+extern fn timer_handler() void;
+extern fn keyboard_handler() void;
+
+// ISR function type for IDT registration
+pub const ISRHandler = *const fn () callconv(.c) void;
+
+/// Get ISR handler address for exception vector
+pub fn get_exception_handler(vector: u8) ISRHandler {
+ return switch (vector) {
+ 0 => &isr0,
+ 1 => &isr1,
+ 2 => &isr2,
+ 3 => &isr3,
+ 4 => &isr4,
+ 5 => &isr5,
+ 6 => &isr6,
+ 7 => &isr7,
+ 8 => &isr8,
+ 9 => &isr9,
+ 10 => &isr10,
+ 11 => &isr11,
+ 12 => &isr12,
+ 13 => &isr13,
+ 14 => &isr14,
+ 15 => &isr15,
+ 16 => &isr16,
+ 17 => &isr17,
+ 18 => &isr18,
+ 19 => &isr19,
+ 20 => &isr20,
+ 21 => &isr21,
+ 22 => &isr22,
+ 23 => &isr23,
+ 24 => &isr24,
+ 25 => &isr25,
+ 26 => &isr26,
+ 27 => &isr27,
+ 28 => &isr28,
+ 29 => &isr29,
+ 30 => &isr30,
+ 31 => &isr31,
+ else => &isr0,
+ };
+}
+
+/// Get IRQ handler address
+pub fn get_irq_handler(irq: u8) ISRHandler {
+ return switch (irq) {
+ 0 => &irq0_handler,
+ 1 => &irq1_handler,
+ else => &irq0_handler,
+ };
+}
+
+// Exception ISR stubs (0-31)
+comptime {
+ // Generate ISR stubs for all 32 exceptions
+ var i: u32 = 0;
+ while (i < 32) : (i += 1) {
+ if (i == 8 or (i >= 10 and i <= 14) or i == 17 or i == 21) {
+ // Exceptions with error code pushed by CPU
+ asm (std.fmt.comptimePrint(
+ \\.global isr{d}
+ \\isr{d}:
+ \\ push ${d}
+ \\ jmp common_exception_handler
+ , .{ i, i, i }));
+ } else {
+ // Exceptions without error code - push dummy 0
+ asm (std.fmt.comptimePrint(
+ \\.global isr{d}
+ \\isr{d}:
+ \\ push $0
+ \\ push ${d}
+ \\ jmp common_exception_handler
+ , .{ i, i, i }));
+ }
+ }
+
+ // Common exception handler - saves all registers and calls Zig handler
+ asm (
+ \\common_exception_handler:
+ \\ push %rax
+ \\ push %rbx
+ \\ push %rcx
+ \\ push %rdx
+ \\ push %rsi
+ \\ push %rdi
+ \\ push %rbp
+ \\ push %r8
+ \\ push %r9
+ \\ push %r10
+ \\ push %r11
+ \\ push %r12
+ \\ push %r13
+ \\ push %r14
+ \\ push %r15
+ \\ mov %rsp, %rdi
+ \\ call exception_handler
+ \\ pop %r15
+ \\ pop %r14
+ \\ pop %r13
+ \\ pop %r12
+ \\ pop %r11
+ \\ pop %r10
+ \\ pop %r9
+ \\ pop %r8
+ \\ pop %rbp
+ \\ pop %rdi
+ \\ pop %rsi
+ \\ pop %rdx
+ \\ pop %rcx
+ \\ pop %rbx
+ \\ pop %rax
+ \\ add $16, %rsp
+ \\ iretq
+ );
+
+ // IRQ handlers
+ asm (
+ \\.global irq0_handler
+ \\irq0_handler:
+ \\ push %rax
+ \\ push %rbx
+ \\ push %rcx
+ \\ push %rdx
+ \\ push %rsi
+ \\ push %rdi
+ \\ push %rbp
+ \\ push %r8
+ \\ push %r9
+ \\ push %r10
+ \\ push %r11
+ \\ push %r12
+ \\ push %r13
+ \\ push %r14
+ \\ push %r15
+ \\ call timer_handler
+ \\ pop %r15
+ \\ pop %r14
+ \\ pop %r13
+ \\ pop %r12
+ \\ pop %r11
+ \\ pop %r10
+ \\ pop %r9
+ \\ pop %r8
+ \\ pop %rbp
+ \\ pop %rdi
+ \\ pop %rsi
+ \\ pop %rdx
+ \\ pop %rcx
+ \\ pop %rbx
+ \\ pop %rax
+ \\ iretq
+ \\
+ \\.global irq1_handler
+ \\irq1_handler:
+ \\ push %rax
+ \\ push %rbx
+ \\ push %rcx
+ \\ push %rdx
+ \\ push %rsi
+ \\ push %rdi
+ \\ push %rbp
+ \\ push %r8
+ \\ push %r9
+ \\ push %r10
+ \\ push %r11
+ \\ push %r12
+ \\ push %r13
+ \\ push %r14
+ \\ push %r15
+ \\ call keyboard_handler
+ \\ pop %r15
+ \\ pop %r14
+ \\ pop %r13
+ \\ pop %r12
+ \\ pop %r11
+ \\ pop %r10
+ \\ pop %r9
+ \\ pop %r8
+ \\ pop %rbp
+ \\ pop %rdi
+ \\ pop %rsi
+ \\ pop %rdx
+ \\ pop %rcx
+ \\ pop %rbx
+ \\ pop %rax
+ \\ iretq
+ );
+}
+
+// Extern declarations for ISR addresses
+extern fn isr0() callconv(.c) void;
+extern fn isr1() callconv(.c) void;
+extern fn isr2() callconv(.c) void;
+extern fn isr3() callconv(.c) void;
+extern fn isr4() callconv(.c) void;
+extern fn isr5() callconv(.c) void;
+extern fn isr6() callconv(.c) void;
+extern fn isr7() callconv(.c) void;
+extern fn isr8() callconv(.c) void;
+extern fn isr9() callconv(.c) void;
+extern fn isr10() callconv(.c) void;
+extern fn isr11() callconv(.c) void;
+extern fn isr12() callconv(.c) void;
+extern fn isr13() callconv(.c) void;
+extern fn isr14() callconv(.c) void;
+extern fn isr15() callconv(.c) void;
+extern fn isr16() callconv(.c) void;
+extern fn isr17() callconv(.c) void;
+extern fn isr18() callconv(.c) void;
+extern fn isr19() callconv(.c) void;
+extern fn isr20() callconv(.c) void;
+extern fn isr21() callconv(.c) void;
+extern fn isr22() callconv(.c) void;
+extern fn isr23() callconv(.c) void;
+extern fn isr24() callconv(.c) void;
+extern fn isr25() callconv(.c) void;
+extern fn isr26() callconv(.c) void;
+extern fn isr27() callconv(.c) void;
+extern fn isr28() callconv(.c) void;
+extern fn isr29() callconv(.c) void;
+extern fn isr30() callconv(.c) void;
+extern fn isr31() callconv(.c) void;
+extern fn irq0_handler() callconv(.c) void;
+extern fn irq1_handler() callconv(.c) void;
diff --git a/mirai.old/asm/memory.zig b/mirai.old/asm/memory.zig
new file mode 100644
index 0000000..1704e28
--- /dev/null
+++ b/mirai.old/asm/memory.zig
@@ -0,0 +1,32 @@
+//! Memory Management Operations
+//! Wrappers for paging and memory control
+
+/// Read page table base register
+pub inline fn read_page_table_base() u64 {
+ return asm volatile ("mov %%cr3, %[result]"
+ : [result] "=r" (-> u64),
+ );
+}
+
+/// Write page table base register
+pub inline fn write_page_table_base(value: u64) void {
+ asm volatile ("mov %[value], %%cr3"
+ :
+ : [value] "r" (value),
+ : .{ .memory = true });
+}
+
+/// Read page fault address register
+pub inline fn read_page_fault_address() u64 {
+ return asm volatile ("mov %%cr2, %[result]"
+ : [result] "=r" (-> u64),
+ );
+}
+
+/// Invalidate translation lookaside buffer entry for given address
+pub inline fn invalidate_page(address: u64) void {
+ asm volatile ("invlpg (%[addr])"
+ :
+ : [addr] "r" (address),
+ : .{ .memory = true });
+}
diff --git a/mirai.old/asm/msr.zig b/mirai.old/asm/msr.zig
new file mode 100644
index 0000000..c8ac37f
--- /dev/null
+++ b/mirai.old/asm/msr.zig
@@ -0,0 +1,26 @@
+//! Model Specific Register Operations
+//! Wrappers for reading and writing CPU-specific registers
+
+/// Read model specific register
+pub inline fn read(register: u32) u64 {
+ var low: u32 = undefined;
+ var high: u32 = undefined;
+ asm volatile ("rdmsr"
+ : [low] "={eax}" (low),
+ [high] "={edx}" (high),
+ : [msr] "{ecx}" (register),
+ );
+ return (@as(u64, high) << 32) | @as(u64, low);
+}
+
+/// Write model specific register
+pub inline fn write(register: u32, value: u64) void {
+ const low: u32 = @truncate(value);
+ const high: u32 = @truncate(value >> 32);
+ asm volatile ("wrmsr"
+ :
+ : [msr] "{ecx}" (register),
+ [low] "{eax}" (low),
+ [high] "{edx}" (high),
+ );
+}
diff --git a/mirai.old/asm/syscall.zig b/mirai.old/asm/syscall.zig
new file mode 100644
index 0000000..7c0ee19
--- /dev/null
+++ b/mirai.old/asm/syscall.zig
@@ -0,0 +1,27 @@
+//! System Call Operations
+//! Wrappers for userspace-kernel communication
+
+/// Invoke system call with up to 6 parameters
+/// Always sets all 6 parameter registers (unused ones are zero)
+pub inline fn invoke_syscall(
+ number: u64,
+ param0: u64,
+ param1: u64,
+ param2: u64,
+ param3: u64,
+ param4: u64,
+ param5: u64,
+) u64 {
+ var result: u64 = undefined;
+ asm volatile ("syscall"
+ : [ret] "={rax}" (result),
+ : [num] "{rax}" (number),
+ [p0] "{rdi}" (param0),
+ [p1] "{rsi}" (param1),
+ [p2] "{rdx}" (param2),
+ [p3] "{r10}" (param3),
+ [p4] "{r8}" (param4),
+ [p5] "{r9}" (param5),
+ : .{ .rcx = true, .r11 = true, .memory = true });
+ return result;
+}
diff --git a/mirai.old/boot/gdt/gdt.zig b/mirai.old/boot/gdt/gdt.zig
new file mode 100644
index 0000000..7b24c3f
--- /dev/null
+++ b/mirai.old/boot/gdt/gdt.zig
@@ -0,0 +1,108 @@
+//! Global Descriptor Table
+
+const cpu = @import("../../asm/cpu.zig");
+const gdt_const = @import("../../common/constants/gdt.zig");
+const io = @import("../../asm/io.zig");
+const ports = @import("../../common/constants/ports.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+const tss = @import("../tss/tss.zig");
+const types = @import("types.zig");
+
+pub const KERNEL_CODE = gdt_const.KERNEL_CODE;
+pub const KERNEL_DATA = gdt_const.KERNEL_DATA;
+pub const USER_DATA = gdt_const.USER_DATA;
+pub const USER_CODE = gdt_const.USER_CODE;
+pub const TSS_SEGMENT = gdt_const.TSS_SEGMENT;
+
+var gdt: [8]u64 align(16) = undefined;
+var gdt_ptr: types.Pointer = undefined;
+
+pub fn init() void {
+ gdt[0] = 0;
+ gdt[1] = create_code_descriptor(0, true);
+ gdt[2] = create_data_descriptor(0);
+ gdt[3] = create_data_descriptor(3);
+ gdt[4] = create_code_descriptor(3, true);
+
+ create_tss_descriptor(tss.get_address(), tss.get_size());
+
+ gdt_ptr = types.Pointer{
+ .limit = @sizeOf(@TypeOf(gdt)) - 1,
+ .base = @intFromPtr(&gdt),
+ };
+
+ cpu.load_global_descriptor_table(@intFromPtr(&gdt_ptr));
+ cpu.reload_segment_registers(KERNEL_DATA);
+ cpu.reload_code_segment(KERNEL_CODE);
+ cpu.load_task_register(TSS_SEGMENT);
+
+ io.out_byte(ports.PIC1_DATA, 0xFF);
+ io.out_byte(ports.PIC2_DATA, 0xFF);
+
+ serial.printf("GDT: code={x} data={x} ucode={x} udata={x} tss={x}\n", .{
+ KERNEL_CODE, KERNEL_DATA, USER_CODE, USER_DATA, TSS_SEGMENT,
+ });
+}
+
+fn create_code_descriptor(dpl: u8, long_mode: bool) u64 {
+ var desc: u64 = 0;
+
+ desc |= 0xFFFF;
+ desc |= (@as(u64, 0xF) << 48);
+
+ var access: u8 = 0;
+ access |= (1 << 0);
+ access |= (1 << 1);
+ access |= (1 << 3);
+ access |= (1 << 4);
+ access |= (@as(u8, dpl & 0x3) << 5);
+ access |= (1 << 7);
+ desc |= (@as(u64, access) << 40);
+
+ var flags: u8 = 0;
+ flags |= (1 << 3);
+ if (long_mode) {
+ flags |= (1 << 1);
+ } else {
+ flags |= (1 << 2);
+ }
+ desc |= (@as(u64, flags) << 52);
+
+ return desc;
+}
+
+fn create_data_descriptor(dpl: u8) u64 {
+ var desc: u64 = 0;
+
+ desc |= 0xFFFF;
+ desc |= (@as(u64, 0xF) << 48);
+
+ var access: u8 = 0;
+ access |= (1 << 0);
+ access |= (1 << 1);
+ access |= (1 << 4);
+ access |= (@as(u8, dpl & 0x3) << 5);
+ access |= (1 << 7);
+ desc |= (@as(u64, access) << 40);
+
+ var flags: u8 = 0;
+ flags |= (1 << 3);
+ flags |= (1 << 2);
+ desc |= (@as(u64, flags) << 52);
+
+ return desc;
+}
+
+fn create_tss_descriptor(base: u64, limit: u64) void {
+ var desc_low: u64 = 0;
+
+ desc_low |= (limit & 0xFFFF);
+ desc_low |= ((base & 0xFFFF) << 16);
+ desc_low |= ((base & 0xFF0000) >> 16) << 32;
+ desc_low |= (@as(u64, 0x89) << 40);
+ desc_low |= (((limit >> 16) & 0xF) << 48);
+ desc_low |= (((base & 0xFF000000) >> 24) << 56);
+
+ gdt[5] = desc_low;
+ gdt[6] = (base >> 32) & 0xFFFFFFFF;
+}
diff --git a/mirai.old/boot/gdt/types.zig b/mirai.old/boot/gdt/types.zig
new file mode 100644
index 0000000..41c891a
--- /dev/null
+++ b/mirai.old/boot/gdt/types.zig
@@ -0,0 +1,26 @@
+//! GDT type definitions
+
+pub const Entry = packed struct {
+ limit_low: u16,
+ base_low: u16,
+ base_middle: u8,
+ access: u8,
+ granularity: u8,
+ base_high: u8,
+};
+
+pub const TSSDescriptor = packed struct {
+ length: u16,
+ base_low: u16,
+ base_middle: u8,
+ flags1: u8,
+ flags2: u8,
+ base_high: u8,
+ base_upper: u32,
+ reserved: u32,
+};
+
+pub const Pointer = packed struct {
+ limit: u16,
+ base: u64,
+};
diff --git a/mirai.old/boot/multiboot/framebuffer.zig b/mirai.old/boot/multiboot/framebuffer.zig
new file mode 100644
index 0000000..f5526d5
--- /dev/null
+++ b/mirai.old/boot/multiboot/framebuffer.zig
@@ -0,0 +1,48 @@
+//! Multiboot framebuffer parser
+
+const serial = @import("../../drivers/serial/serial.zig");
+const types = @import("types.zig");
+
+var saved: ?types.FramebufferInfo = null;
+
+pub fn parse(addr: u64) ?types.FramebufferInfo {
+ const total_size = @as(*u32, @ptrFromInt(addr)).*;
+ var offset: u64 = 8;
+
+ while (offset < total_size) {
+ const tag_addr = addr + offset;
+ const tag_type = @as(*u32, @ptrFromInt(tag_addr)).*;
+ const tag_size = @as(*u32, @ptrFromInt(tag_addr + 4)).*;
+
+ if (tag_type == 0) break;
+
+ if (tag_type == 8) {
+ const info = types.FramebufferInfo{
+ .addr = @as(*u64, @ptrFromInt(tag_addr + 8)).*,
+ .pitch = @as(*u32, @ptrFromInt(tag_addr + 16)).*,
+ .width = @as(*u32, @ptrFromInt(tag_addr + 20)).*,
+ .height = @as(*u32, @ptrFromInt(tag_addr + 24)).*,
+ .bpp = @as(*u8, @ptrFromInt(tag_addr + 28)).*,
+ .framebuffer_type = @as(*u8, @ptrFromInt(tag_addr + 29)).*,
+ };
+
+ serial.printf("Framebuffer: {x} {}x{} pitch={} bpp={}\n", .{
+ info.addr, info.width, info.height, info.pitch, info.bpp,
+ });
+
+ return info;
+ }
+
+ offset += (tag_size + 7) & ~@as(u64, 7);
+ }
+
+ return null;
+}
+
+pub fn set(fb: types.FramebufferInfo) void {
+ saved = fb;
+}
+
+pub fn get() ?types.FramebufferInfo {
+ return saved;
+}
diff --git a/mirai.old/boot/multiboot/memory.zig b/mirai.old/boot/multiboot/memory.zig
new file mode 100644
index 0000000..1fc23da
--- /dev/null
+++ b/mirai.old/boot/multiboot/memory.zig
@@ -0,0 +1,51 @@
+//! Multiboot memory map parser
+
+const boot_limits = @import("../../common/limits/boot.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+const types = @import("types.zig");
+
+var entries: [boot_limits.MAX_MEMORY_ENTRIES]types.MemoryEntry = undefined;
+
+pub fn parse(addr: u64) []types.MemoryEntry {
+ const total_size = @as(*u32, @ptrFromInt(addr)).*;
+ var offset: u64 = 8;
+ var count: usize = 0;
+
+ while (offset < total_size) {
+ const tag_addr = addr + offset;
+ const tag_type = @as(*u32, @ptrFromInt(tag_addr)).*;
+ const tag_size = @as(*u32, @ptrFromInt(tag_addr + 4)).*;
+
+ if (tag_type == 0) break;
+
+ if (tag_type == 6) {
+ const entry_size = @as(*u32, @ptrFromInt(tag_addr + 8)).*;
+ const entry_count = (tag_size - 16) / entry_size;
+
+ for (0..entry_count) |i| {
+ if (count >= boot_limits.MAX_MEMORY_ENTRIES) break;
+
+ const entry_addr = tag_addr + 16 + (i * entry_size);
+ const base = @as(*u64, @ptrFromInt(entry_addr)).*;
+ const length = @as(*u64, @ptrFromInt(entry_addr + 8)).*;
+ const entry_type = @as(*u32, @ptrFromInt(entry_addr + 16)).*;
+
+ entries[count] = types.MemoryEntry{
+ .base = base,
+ .length = length,
+ .entry_type = entry_type,
+ };
+ count += 1;
+
+ const status = if (entry_type == 1) "Available" else "Reserved";
+ serial.printf(" {x}-{x} ({} MB) [{s}]\n", .{
+ base, base + length - 1, length / (1024 * 1024), status,
+ });
+ }
+ }
+
+ offset += (tag_size + 7) & ~@as(u64, 7);
+ }
+
+ return entries[0..count];
+}
diff --git a/mirai.old/boot/multiboot/multiboot.zig b/mirai.old/boot/multiboot/multiboot.zig
new file mode 100644
index 0000000..46ba8a7
--- /dev/null
+++ b/mirai.old/boot/multiboot/multiboot.zig
@@ -0,0 +1,13 @@
+//! Multiboot2 protocol
+
+pub const types = @import("types.zig");
+pub const memory = @import("memory.zig");
+pub const framebuffer = @import("framebuffer.zig");
+
+pub const MemoryEntry = types.MemoryEntry;
+pub const FramebufferInfo = types.FramebufferInfo;
+
+pub const parse_memory_map = memory.parse;
+pub const parse_framebuffer = framebuffer.parse;
+pub const init_framebuffer = framebuffer.set;
+pub const get_framebuffer = framebuffer.get;
diff --git a/mirai.old/boot/multiboot/types.zig b/mirai.old/boot/multiboot/types.zig
new file mode 100644
index 0000000..9da2650
--- /dev/null
+++ b/mirai.old/boot/multiboot/types.zig
@@ -0,0 +1,16 @@
+//! Multiboot type definitions
+
+pub const MemoryEntry = struct {
+ base: u64,
+ length: u64,
+ entry_type: u32,
+};
+
+pub const FramebufferInfo = struct {
+ addr: u64,
+ pitch: u32,
+ width: u32,
+ height: u32,
+ bpp: u8,
+ framebuffer_type: u8,
+};
diff --git a/mirai.old/boot/sequence/message.zig b/mirai.old/boot/sequence/message.zig
new file mode 100644
index 0000000..d6ca08e
--- /dev/null
+++ b/mirai.old/boot/sequence/message.zig
@@ -0,0 +1,74 @@
+//! Boot message buffering
+
+const boot_limits = @import("../../common/limits/boot.zig");
+const colors = @import("../../common/constants/colors.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+
+var terminal_print_fn: ?*const fn ([]const u8) void = null;
+var terminal_print_color_fn: ?*const fn ([]const u8, u32) void = null;
+
+const Message = struct {
+ text: [boot_limits.MAX_MESSAGE_LENGTH]u8,
+ len: usize,
+ color: u32,
+ is_colored: bool,
+};
+
+var buffer: [boot_limits.MAX_BOOT_MESSAGES]Message = undefined;
+var count: usize = 0;
+var ready: bool = false;
+
+pub fn set_terminal(
+ print_fn: *const fn ([]const u8) void,
+ print_color_fn: *const fn ([]const u8, u32) void,
+) void {
+ terminal_print_fn = print_fn;
+ terminal_print_color_fn = print_color_fn;
+ ready = true;
+ replay();
+}
+
+pub fn is_ready() bool {
+ return ready;
+}
+
+pub fn print(text: []const u8) void {
+ write(text, colors.WHITE, false);
+}
+
+pub fn print_color(text: []const u8, color: u32) void {
+ write(text, color, true);
+}
+
+fn write(text: []const u8, color: u32, is_colored: bool) void {
+ serial.print(text);
+
+ if (ready) {
+ if (is_colored) {
+ if (terminal_print_color_fn) |f| f(text, color);
+ } else {
+ if (terminal_print_fn) |f| f(text);
+ }
+ } else if (count < buffer.len) {
+ var msg = &buffer[count];
+ const len = @min(text.len, boot_limits.MAX_MESSAGE_LENGTH - 1);
+ for (text[0..len], 0..) |c, i| {
+ msg.text[i] = c;
+ }
+ msg.len = len;
+ msg.color = color;
+ msg.is_colored = is_colored;
+ count += 1;
+ }
+}
+
+fn replay() void {
+ for (buffer[0..count]) |msg| {
+ if (msg.is_colored) {
+ if (terminal_print_color_fn) |f| f(msg.text[0..msg.len], msg.color);
+ } else {
+ if (terminal_print_fn) |f| f(msg.text[0..msg.len]);
+ }
+ }
+ count = 0;
+}
diff --git a/mirai.old/boot/sequence/sequence.zig b/mirai.old/boot/sequence/sequence.zig
new file mode 100644
index 0000000..685b8f1
--- /dev/null
+++ b/mirai.old/boot/sequence/sequence.zig
@@ -0,0 +1,174 @@
+//! Boot sequence
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const boot_limits = @import("../../common/limits/boot.zig");
+const colors = @import("../../common/constants/colors.zig");
+const cpu = @import("../../asm/cpu.zig");
+const crimson = @import("../../crimson/crimson.zig");
+const font = @import("../../graphics/fonts/psf.zig");
+const gdt = @import("../gdt/gdt.zig");
+const gpt = @import("../../fs/gpt/gpt.zig");
+const heap = @import("../../memory/heap.zig");
+const hikari = @import("../../hikari/loader.zig");
+const idt = @import("../../interrupts/idt.zig");
+const invocations = @import("../../invocations/handler.zig");
+const kata = @import("../../kata/kata.zig");
+const keyboard = @import("../../drivers/keyboard/keyboard.zig");
+const message = @import("message.zig");
+const multiboot = @import("../multiboot/multiboot.zig");
+const pci = @import("../../drivers/pci/pci.zig");
+const pit = @import("../../drivers/pit/pit.zig");
+const pmm = @import("../../memory/pmm.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+const memory_const = @import("../../common/constants/memory.zig");
+const terminal = @import("../../graphics/terminal/terminal.zig");
+const tss = @import("../tss/tss.zig");
+
+var filesystem: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn get_filesystem() *afs.AFS(ahci.BlockDevice) {
+ return filesystem.?;
+}
+
+pub fn run(multiboot_addr: u64) void {
+ const fb_info = multiboot.parse_framebuffer(multiboot_addr);
+
+ step("Parsing memory map");
+ const memory_map = multiboot.parse_memory_map(multiboot_addr);
+ ok();
+
+ step("Initializing physical memory");
+ pmm.init(memory_const.MIRAI_END(), memory_map);
+ ok();
+
+ step("Initializing heap");
+ heap.init();
+ ok();
+
+ step("Initializing kata management");
+ kata.init();
+ ok();
+
+ step("Setting up TSS");
+ tss.init();
+ ok();
+
+ step("Setting up GDT");
+ gdt.init();
+ ok();
+
+ step("Setting up IDT");
+ idt.init();
+ ok();
+
+ step("Setting up PIT");
+ pit.init();
+ ok();
+
+ step("Scanning PCI bus");
+ pci.scan_bus();
+ ok();
+
+ step("Detecting storage controller");
+ ahci.find_and_init() catch |err| {
+ fail();
+ print_error(err);
+ halt();
+ };
+ ok();
+
+ step("Mounting AFS");
+ var device = ahci.BlockDevice.init();
+ const partition = gpt.find_afs_partition(&device) orelse {
+ fail();
+ message.print("AFS partition not found\n");
+ halt();
+ };
+
+ var fs = afs.AFS(ahci.BlockDevice).init(&device, partition.start_lba) catch |err| {
+ fail();
+ print_error(err);
+ halt();
+ };
+ filesystem = &fs;
+ ok();
+
+ step("Initializing invocations");
+ invocations.init(&fs);
+ ok();
+
+ step("Loading font");
+ var font_buffer: [boot_limits.FONT_BUFFER_SIZE]u8 = undefined;
+ const bytes_read = fs.view_unit_at("/system/fonts/akiba.psf", &font_buffer) catch |err| {
+ fail();
+ print_error(err);
+ halt();
+ };
+
+ font.init(font_buffer[0..bytes_read]) catch |err| {
+ fail();
+ print_error(err);
+ halt();
+ };
+ ok();
+
+ if (fb_info) |fb| {
+ step("Initializing framebuffer");
+ terminal.init(fb);
+ crimson.init(fb);
+ multiboot.init_framebuffer(fb);
+ message.set_terminal(terminal.print, terminal.print_color);
+ ok();
+ }
+
+ step("Initializing keyboard");
+ keyboard.init();
+ ok();
+
+ message.print_color("Boot complete!\n", colors.GREEN);
+
+ step("Loading Pulse");
+ _ = hikari.init(&fs) catch |err| {
+ serial.printf("Failed to load Pulse: {s}\n", .{@errorName(err)});
+ crimson.collapse("Akiba's Pulse is missing. System is dying.", null);
+ };
+ ok();
+
+ if (message.is_ready()) {
+ terminal.clear_screen();
+ message.print_color("Akiba OS\n", colors.AKIBA_PINK);
+ message.print("Drifting from abyss towards the infinity!\n\n");
+ }
+
+ sensei.schedule();
+
+ serial.print("ERROR: Returned from schedule!\n");
+ halt();
+}
+
+fn step(name: []const u8) void {
+ message.print(name);
+ message.print("... ");
+}
+
+fn ok() void {
+ message.print_color("[ OK ]\n", colors.GREEN);
+}
+
+fn fail() void {
+ message.print_color("[ FAIL ]\n", colors.RED);
+}
+
+fn print_error(err: anyerror) void {
+ message.print("ERROR: ");
+ message.print(@errorName(err));
+ message.print("\n");
+}
+
+fn halt() noreturn {
+ while (true) {
+ cpu.halt_processor();
+ }
+}
diff --git a/mirai.old/boot/tss/tss.zig b/mirai.old/boot/tss/tss.zig
new file mode 100644
index 0000000..395ef06
--- /dev/null
+++ b/mirai.old/boot/tss/tss.zig
@@ -0,0 +1,54 @@
+//! Task State Segment
+
+const boot_limits = @import("../../common/limits/boot.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+
+const TSS = packed struct {
+ reserved1: u32,
+ rsp0: u64,
+ rsp1: u64,
+ rsp2: u64,
+ reserved2: u64,
+ ist1: u64,
+ ist2: u64,
+ ist3: u64,
+ ist4: u64,
+ ist5: u64,
+ ist6: u64,
+ ist7: u64,
+ reserved3: u64,
+ reserved4: u16,
+ iomap_base: u16,
+};
+
+var tss: TSS align(16) = undefined;
+var kernel_stack: [boot_limits.KERNEL_STACK_SIZE]u8 align(16) = undefined;
+var ist1_stack: [65536]u8 align(16) = undefined; // 64KB for double fault/page fault
+
+pub fn init() void {
+ const tss_bytes = @as([*]u8, @ptrCast(&tss));
+ for (0..@sizeOf(TSS)) |i| {
+ tss_bytes[i] = 0;
+ }
+
+ tss.rsp0 = @intFromPtr(&kernel_stack) + kernel_stack.len;
+ tss.ist1 = @intFromPtr(&ist1_stack) + ist1_stack.len;
+
+ serial.printf("TSS: addr={x} rsp0={x} ist1={x}\n", .{ @intFromPtr(&tss), tss.rsp0, tss.ist1 });
+}
+
+pub fn get_address() u64 {
+ return @intFromPtr(&tss);
+}
+
+pub fn get_size() u64 {
+ return @sizeOf(TSS) - 1;
+}
+
+pub fn set_kernel_stack(stack_pointer: u64) void {
+ tss.rsp0 = stack_pointer;
+}
+
+pub fn get_kernel_stack() u64 {
+ return tss.rsp0;
+}
diff --git a/mirai.old/common/constants/ahci.zig b/mirai.old/common/constants/ahci.zig
new file mode 100644
index 0000000..c37c668
--- /dev/null
+++ b/mirai.old/common/constants/ahci.zig
@@ -0,0 +1,46 @@
+//! AHCI constants
+
+// HBA port command bits
+pub const PORT_CMD_ST: u32 = 0x0001;
+pub const PORT_CMD_FRE: u32 = 0x0010;
+pub const PORT_CMD_FR: u32 = 0x4000;
+pub const PORT_CMD_CR: u32 = 0x8000;
+
+// Global HBA control
+pub const GHC_AE: u32 = 1 << 31;
+
+// Port interrupt status
+pub const PxIS_TFES: u32 = 1 << 30;
+
+// FIS types
+pub const FIS_TYPE_REG_H2D: u8 = 0x27;
+pub const FIS_TYPE_REG_D2H: u8 = 0x34;
+pub const FIS_TYPE_DMA_ACT: u8 = 0x39;
+pub const FIS_TYPE_DMA_SETUP: u8 = 0x41;
+pub const FIS_TYPE_DATA: u8 = 0x46;
+pub const FIS_TYPE_BIST: u8 = 0x58;
+pub const FIS_TYPE_PIO_SETUP: u8 = 0x5F;
+pub const FIS_TYPE_DEV_BITS: u8 = 0xA1;
+
+// SSTS detection
+pub const SSTS_DET_PRESENT: u8 = 3;
+pub const SSTS_IPM_ACTIVE: u8 = 1;
+
+// Port types
+pub const PORT_TYPE_NONE: u32 = 0;
+pub const PORT_TYPE_SATA: u32 = 1;
+pub const PORT_TYPE_ATAPI: u32 = 2;
+pub const PORT_TYPE_SEMB: u32 = 3;
+pub const PORT_TYPE_PM: u32 = 4;
+
+// Port offsets
+pub const PORT_OFFSET_BASE: usize = 0x100;
+
+// BAR mask
+pub const BAR_MASK: u32 = 0xFFFFFFF0;
+
+// PRDT interrupt bit
+pub const PRDT_INTERRUPT: u32 = 1 << 31;
+
+// PIC end of interrupt
+pub const PIC_EOI: u8 = 0x20;
diff --git a/mirai.old/common/constants/akiba.zig b/mirai.old/common/constants/akiba.zig
new file mode 100644
index 0000000..b1ac359
--- /dev/null
+++ b/mirai.old/common/constants/akiba.zig
@@ -0,0 +1,9 @@
+//! Akiba executable format constants
+
+pub const MAGIC = [8]u8{ 'A', 'K', 'I', 'B', 'A', 'E', 'L', 'F' };
+pub const VERSION: u32 = 1;
+
+pub const TYPE_CLI: u32 = 0;
+pub const TYPE_GUI: u32 = 1;
+pub const TYPE_SERVICE: u32 = 2;
+pub const TYPE_LIBRARY: u32 = 3;
diff --git a/mirai.old/common/constants/ascii.zig b/mirai.old/common/constants/ascii.zig
new file mode 100644
index 0000000..73ece6b
--- /dev/null
+++ b/mirai.old/common/constants/ascii.zig
@@ -0,0 +1,10 @@
+//! ASCII character constants
+
+pub const NEWLINE: u8 = '\n';
+pub const BACKSPACE: u8 = 0x08;
+pub const TAB: u8 = '\t';
+pub const SPACE: u8 = ' ';
+pub const CARRIAGE_RETURN: u8 = '\r';
+
+pub const PRINTABLE_START: u8 = 0x20;
+pub const PRINTABLE_END: u8 = 0x7E;
diff --git a/mirai.old/common/constants/ata.zig b/mirai.old/common/constants/ata.zig
new file mode 100644
index 0000000..2b948ff
--- /dev/null
+++ b/mirai.old/common/constants/ata.zig
@@ -0,0 +1,33 @@
+//! ATA constants
+
+// Commands
+pub const CMD_READ_SECTORS: u8 = 0x20;
+pub const CMD_WRITE_SECTORS: u8 = 0x30;
+pub const CMD_READ_DMA_EX: u8 = 0x25;
+pub const CMD_WRITE_DMA_EX: u8 = 0x35;
+pub const CMD_IDENTIFY: u8 = 0xEC;
+
+// Status register bits
+pub const STATUS_ERR: u8 = 0x01;
+pub const STATUS_DRQ: u8 = 0x08;
+pub const STATUS_SRV: u8 = 0x10;
+pub const STATUS_DF: u8 = 0x20;
+pub const STATUS_RDY: u8 = 0x40;
+pub const STATUS_BSY: u8 = 0x80;
+
+// Device signatures
+pub const SIG_ATA: u32 = 0x00000101;
+pub const SIG_ATAPI: u32 = 0xEB140101;
+pub const SIG_SEMB: u32 = 0xC33C0101;
+pub const SIG_PM: u32 = 0x96690101;
+
+// Port registers (primary)
+pub const PRIMARY_DATA: u16 = 0x1F0;
+pub const PRIMARY_ERROR: u16 = 0x1F1;
+pub const PRIMARY_SECTOR_COUNT: u16 = 0x1F2;
+pub const PRIMARY_LBA_LOW: u16 = 0x1F3;
+pub const PRIMARY_LBA_MID: u16 = 0x1F4;
+pub const PRIMARY_LBA_HIGH: u16 = 0x1F5;
+pub const PRIMARY_DRIVE_SELECT: u16 = 0x1F6;
+pub const PRIMARY_STATUS: u16 = 0x1F7;
+pub const PRIMARY_COMMAND: u16 = 0x1F7;
diff --git a/mirai.old/common/constants/attachment.zig b/mirai.old/common/constants/attachment.zig
new file mode 100644
index 0000000..c3f4377
--- /dev/null
+++ b/mirai.old/common/constants/attachment.zig
@@ -0,0 +1,8 @@
+//! Attachment flags
+
+pub const VIEW_ONLY: u32 = 0x01;
+pub const MARK_ONLY: u32 = 0x02;
+pub const BOTH: u32 = 0x03;
+pub const CREATE: u32 = 0x0100;
+pub const CLEAR: u32 = 0x0200;
+pub const EXTEND: u32 = 0x0400;
diff --git a/mirai.old/common/constants/colors.zig b/mirai.old/common/constants/colors.zig
new file mode 100644
index 0000000..a959663
--- /dev/null
+++ b/mirai.old/common/constants/colors.zig
@@ -0,0 +1,14 @@
+//! Display colors
+
+pub const AKIBA_PINK: u32 = 0x00FF6B9D;
+
+pub const WHITE: u32 = 0x00FFFFFF;
+pub const BLACK: u32 = 0x00000000;
+pub const RED: u32 = 0x00FF0000;
+pub const GREEN: u32 = 0x0000FF00;
+pub const BLUE: u32 = 0x000000FF;
+pub const YELLOW: u32 = 0x00FFFF00;
+pub const CYAN: u32 = 0x0000FFFF;
+pub const MAGENTA: u32 = 0x00FF00FF;
+pub const GRAY: u32 = 0x00808080;
+pub const CRIMSON: u32 = 0x00DC143C;
diff --git a/mirai.old/common/constants/elf.zig b/mirai.old/common/constants/elf.zig
new file mode 100644
index 0000000..877d896
--- /dev/null
+++ b/mirai.old/common/constants/elf.zig
@@ -0,0 +1,18 @@
+//! ELF format constants
+
+pub const MAGIC = [4]u8{ 0x7F, 'E', 'L', 'F' };
+
+pub const CLASS_64: u8 = 2;
+pub const DATA_LSB: u8 = 1;
+
+pub const TYPE_EXEC: u16 = 2;
+pub const TYPE_DYN: u16 = 3;
+
+pub const PT_NULL: u32 = 0;
+pub const PT_LOAD: u32 = 1;
+pub const PT_DYNAMIC: u32 = 2;
+pub const PT_INTERP: u32 = 3;
+
+pub const PF_X: u32 = 1;
+pub const PF_W: u32 = 2;
+pub const PF_R: u32 = 4;
diff --git a/mirai.old/common/constants/fs.zig b/mirai.old/common/constants/fs.zig
new file mode 100644
index 0000000..7c68e45
--- /dev/null
+++ b/mirai.old/common/constants/fs.zig
@@ -0,0 +1,46 @@
+//! Filesystem constants
+
+// Sector and cluster
+pub const SECTOR_SIZE: usize = 512;
+pub const SECTOR_ALIGN: u8 = 16;
+
+// AFS signatures
+pub const AFS_SIGNATURE = "AKIBAFS!";
+pub const AFS_BOOT_SIG: u16 = 0xAA55;
+
+// AFS entry types
+pub const ENTRY_TYPE_END: u8 = 0x00;
+pub const ENTRY_TYPE_UNIT: u8 = 0x01;
+pub const ENTRY_TYPE_STACK: u8 = 0x02;
+
+// AFS permissions
+pub const PERM_OWNER: u8 = 1;
+pub const PERM_WORLD: u8 = 2;
+pub const PERM_READ_ONLY: u8 = 3;
+
+// AFS cluster markers
+pub const CLUSTER_FREE: u32 = 0x00000000;
+pub const CLUSTER_END: u32 = 0xFFFFFFFF;
+pub const CLUSTER_MIN: u32 = 2;
+
+// AFS limits
+pub const MAX_IDENTITY_LEN: usize = 255;
+pub const MAX_OWNER_NAME_LEN: usize = 64;
+pub const MAX_LOCATION_LENGTH: usize = 256;
+pub const PARENT_CACHE_SIZE: usize = 256;
+
+// GPT
+pub const GPT_SIGNATURE = "EFI PART";
+pub const GPT_HEADER_SECTOR: u64 = 1;
+pub const GPT_PARTITION_ENTRIES_MAX: usize = 4;
+pub const GPT_ENTRY_SIZE: usize = 128;
+pub const GPT_HEADER_PARTITION_LBA_OFFSET: usize = 72;
+pub const GPT_ENTRY_TYPE_GUID_SIZE: usize = 16;
+pub const GPT_ENTRY_START_LBA_OFFSET: usize = 32;
+pub const GPT_ENTRY_END_LBA_OFFSET: usize = 40;
+
+// AFS partition GUID
+pub const AFS_PARTITION_GUID = [_]u8{
+ 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18,
+ 0x29, 0x3A, 0x4B, 0x5C, 0x6D, 0x7E, 0x8F, 0x90,
+};
diff --git a/mirai.old/common/constants/gdt.zig b/mirai.old/common/constants/gdt.zig
new file mode 100644
index 0000000..ca852c8
--- /dev/null
+++ b/mirai.old/common/constants/gdt.zig
@@ -0,0 +1,7 @@
+//! GDT segment selectors
+
+pub const KERNEL_CODE: u16 = 0x08;
+pub const KERNEL_DATA: u16 = 0x10;
+pub const USER_DATA: u16 = 0x18;
+pub const USER_CODE: u16 = 0x20;
+pub const TSS_SEGMENT: u16 = 0x28;
diff --git a/mirai.old/common/constants/heap.zig b/mirai.old/common/constants/heap.zig
new file mode 100644
index 0000000..8bd3f26
--- /dev/null
+++ b/mirai.old/common/constants/heap.zig
@@ -0,0 +1,6 @@
+//! Heap allocator constants
+
+pub const SIZE_CLASSES = [_]usize{ 16, 32, 64, 128, 256, 512, 1024, 2048 };
+pub const NUM_CACHES: usize = SIZE_CLASSES.len;
+pub const LARGE_ALLOC_THRESHOLD: usize = 2048;
+pub const MAX_LARGE_PAGES: usize = 64;
diff --git a/mirai.old/common/constants/idt.zig b/mirai.old/common/constants/idt.zig
new file mode 100644
index 0000000..d7c7bac
--- /dev/null
+++ b/mirai.old/common/constants/idt.zig
@@ -0,0 +1,10 @@
+//! IDT constants
+
+pub const NUM_ENTRIES: usize = 256;
+pub const NUM_EXCEPTIONS: u8 = 32;
+
+pub const GATE_INTERRUPT: u8 = 0x8E;
+pub const GATE_TRAP: u8 = 0x8F;
+
+pub const VECTOR_TIMER: u8 = 32;
+pub const VECTOR_KEYBOARD: u8 = 33;
diff --git a/mirai.old/common/constants/interrupts.zig b/mirai.old/common/constants/interrupts.zig
new file mode 100644
index 0000000..6628cee
--- /dev/null
+++ b/mirai.old/common/constants/interrupts.zig
@@ -0,0 +1,39 @@
+//! Interrupt vectors
+
+pub const EXCEPTION_DIVIDE_ERROR: u8 = 0;
+pub const EXCEPTION_DEBUG: u8 = 1;
+pub const EXCEPTION_NMI: u8 = 2;
+pub const EXCEPTION_BREAKPOINT: u8 = 3;
+pub const EXCEPTION_OVERFLOW: u8 = 4;
+pub const EXCEPTION_BOUND_RANGE: u8 = 5;
+pub const EXCEPTION_INVALID_OPCODE: u8 = 6;
+pub const EXCEPTION_DEVICE_NOT_AVAILABLE: u8 = 7;
+pub const EXCEPTION_DOUBLE_FAULT: u8 = 8;
+pub const EXCEPTION_INVALID_TSS: u8 = 10;
+pub const EXCEPTION_SEGMENT_NOT_PRESENT: u8 = 11;
+pub const EXCEPTION_STACK_FAULT: u8 = 12;
+pub const EXCEPTION_GENERAL_PROTECTION: u8 = 13;
+pub const EXCEPTION_PAGE_FAULT: u8 = 14;
+pub const EXCEPTION_FPU_ERROR: u8 = 16;
+pub const EXCEPTION_ALIGNMENT_CHECK: u8 = 17;
+pub const EXCEPTION_MACHINE_CHECK: u8 = 18;
+pub const EXCEPTION_SIMD_EXCEPTION: u8 = 19;
+
+pub const IRQ_TIMER: u8 = 32;
+pub const IRQ_KEYBOARD: u8 = 33;
+pub const IRQ_CASCADE: u8 = 34;
+pub const IRQ_COM2: u8 = 35;
+pub const IRQ_COM1: u8 = 36;
+pub const IRQ_LPT2: u8 = 37;
+pub const IRQ_FLOPPY: u8 = 38;
+pub const IRQ_LPT1: u8 = 39;
+pub const IRQ_RTC: u8 = 40;
+pub const IRQ_ACPI: u8 = 41;
+pub const IRQ_AVAILABLE_10: u8 = 42;
+pub const IRQ_AVAILABLE_11: u8 = 43;
+pub const IRQ_MOUSE: u8 = 44;
+pub const IRQ_FPU: u8 = 45;
+pub const IRQ_PRIMARY_ATA: u8 = 46;
+pub const IRQ_SECONDARY_ATA: u8 = 47;
+
+pub const INTERRUPT_INVOCATION: u8 = 0x80;
diff --git a/mirai.old/common/constants/invocations.zig b/mirai.old/common/constants/invocations.zig
new file mode 100644
index 0000000..afeb9d4
--- /dev/null
+++ b/mirai.old/common/constants/invocations.zig
@@ -0,0 +1,25 @@
+//! Invocation numbers
+
+pub const EXIT: u64 = 0x01;
+pub const ATTACH: u64 = 0x02;
+pub const SEAL: u64 = 0x03;
+pub const VIEW: u64 = 0x04;
+pub const MARK: u64 = 0x05;
+pub const SPAWN: u64 = 0x06;
+pub const WAIT: u64 = 0x07;
+pub const YIELD: u64 = 0x08;
+pub const GETKEYCHAR: u64 = 0x09;
+pub const VIEWSTACK: u64 = 0x0A;
+pub const GETLOCATION: u64 = 0x0B;
+pub const SETLOCATION: u64 = 0x0C;
+pub const POSTMAN: u64 = 0x0D;
+pub const WIPE: u64 = 0x0E;
+pub const CPUINFO: u64 = 0x0F;
+pub const MEMINFO: u64 = 0x10;
+pub const UPTIME: u64 = 0x11;
+pub const GETTIME: u64 = 0x12;
+pub const DISKINFO: u64 = 0x13;
+pub const REAP: u64 = 0x14;
+
+pub const ERROR: u64 = @as(u64, @bitCast(@as(i64, -1)));
+pub const NO_DATA: u64 = @as(u64, @bitCast(@as(i64, -2)));
diff --git a/mirai.old/common/constants/kata.zig b/mirai.old/common/constants/kata.zig
new file mode 100644
index 0000000..c790a6b
--- /dev/null
+++ b/mirai.old/common/constants/kata.zig
@@ -0,0 +1,27 @@
+//! Kata constants
+
+const memory = @import("memory.zig");
+
+pub const KATA_STACK_TOP: u64 = 0x00007FFFFFF00000;
+pub const KATA_STACK_PAGES: u64 = 64;
+pub const KATA_STACK_SIZE: u64 = KATA_STACK_PAGES * memory.PAGE_SIZE;
+pub const MIRAI_STACK_SIZE: u64 = memory.PAGE_SIZE;
+
+pub const TIMER_TICK_NS: u64 = 1_000_000;
+pub const SENSEI_TIME_SLICE: u64 = 10;
+
+pub const STATE_READY: u8 = 0;
+pub const STATE_RUNNING: u8 = 1;
+pub const STATE_BLOCKED: u8 = 2;
+pub const STATE_WAITING: u8 = 3;
+pub const STATE_DISSOLVED: u8 = 4;
+
+pub const LETTER_NONE: u8 = 0;
+pub const LETTER_NAVIGATE: u8 = 1;
+
+pub const POSTMAN_SEND: u64 = 0;
+pub const POSTMAN_READ: u64 = 1;
+
+pub const DEFAULT_WEIGHT: u32 = 1024;
+pub const KERNEL_RFLAGS: u64 = 0x202;
+pub const USER_RFLAGS: u64 = 0x3202;
diff --git a/mirai.old/common/constants/keyboard.zig b/mirai.old/common/constants/keyboard.zig
new file mode 100644
index 0000000..2c60e2d
--- /dev/null
+++ b/mirai.old/common/constants/keyboard.zig
@@ -0,0 +1,11 @@
+//! Keyboard constants
+
+pub const SCANCODE_RELEASED: u8 = 0x80;
+
+pub const SCANCODE_LSHIFT: u8 = 0x2A;
+pub const SCANCODE_RSHIFT: u8 = 0x36;
+pub const SCANCODE_LCTRL: u8 = 0x1D;
+pub const SCANCODE_LALT: u8 = 0x38;
+pub const SCANCODE_CAPS: u8 = 0x3A;
+
+pub const LOWERCASE_TO_UPPERCASE: u8 = 32;
diff --git a/mirai.old/common/constants/memory.zig b/mirai.old/common/constants/memory.zig
new file mode 100644
index 0000000..08b7409
--- /dev/null
+++ b/mirai.old/common/constants/memory.zig
@@ -0,0 +1,90 @@
+//! Memory constants
+
+pub const PAGE_SIZE: u64 = 4096;
+pub const PAGE_OFFSET_MASK: u64 = 0xFFF;
+pub const PAGE_FRAME_MASK: u64 = ~PAGE_OFFSET_MASK;
+
+pub const HIGHER_HALF_START: u64 = 0xFFFF800000000000;
+pub const MIRAI_PHYSICAL_START: u64 = 0x100000;
+pub const MIRAI_PHYSICAL_END: u64 = 0x500000;
+pub const MIRAI_VIRTUAL_START: u64 = HIGHER_HALF_START + MIRAI_PHYSICAL_START;
+
+extern const _kernel_end: u8;
+
+pub fn MIRAI_END() u64 {
+ const link_addr = @intFromPtr(&_kernel_end);
+ if (link_addr >= HIGHER_HALF_START) {
+ return link_addr - HIGHER_HALF_START;
+ } else {
+ return link_addr;
+ }
+}
+
+pub const KATA_SPACE_START: u64 = 0x1000;
+pub const KATA_SPACE_END: u64 = 0x0000800000000000;
+
+// Stack configuration
+pub const USER_STACK_TOP: u64 = 0x00007FFFFFF00000;
+pub const USER_STACK_MAX_PAGES: u64 = 512; // 2MB max
+pub const USER_STACK_INITIAL_PAGES: u64 = 16; // 64KB initial, grows on demand
+pub const USER_STACK_SIZE: u64 = USER_STACK_MAX_PAGES * PAGE_SIZE;
+pub const KERNEL_STACK_PAGES: u64 = 4; // 16KB
+pub const KERNEL_STACK_SIZE: u64 = KERNEL_STACK_PAGES * PAGE_SIZE;
+
+pub const PTE_PRESENT: u64 = 1 << 0;
+pub const PTE_WRITABLE: u64 = 1 << 1;
+pub const PTE_USER: u64 = 1 << 2;
+pub const PTE_WRITE_THROUGH: u64 = 1 << 3;
+pub const PTE_CACHE_DISABLE: u64 = 1 << 4;
+pub const PTE_ACCESSED: u64 = 1 << 5;
+pub const PTE_DIRTY: u64 = 1 << 6;
+pub const PTE_HUGE_PAGE: u64 = 1 << 7;
+pub const PTE_GLOBAL: u64 = 1 << 8;
+pub const PTE_NO_EXECUTE: u64 = 1 << 63;
+
+pub const PAGE_TABLE_ENTRIES: usize = 512;
+pub const PML4_SHIFT: u6 = 39;
+pub const PDPT_SHIFT: u6 = 30;
+pub const PD_SHIFT: u6 = 21;
+pub const PT_SHIFT: u6 = 12;
+pub const TABLE_INDEX_MASK: u64 = 0x1FF;
+
+pub inline fn align_down(addr: u64) u64 {
+ return addr & PAGE_FRAME_MASK;
+}
+
+pub inline fn align_up(addr: u64) u64 {
+ return (addr + PAGE_SIZE - 1) & PAGE_FRAME_MASK;
+}
+
+pub inline fn pages_for_size(size: u64) u64 {
+ return (size + PAGE_SIZE - 1) / PAGE_SIZE;
+}
+
+pub inline fn is_page_aligned(addr: u64) bool {
+ return (addr & PAGE_OFFSET_MASK) == 0;
+}
+
+pub inline fn pml4_index(vaddr: u64) u64 {
+ return (vaddr >> PML4_SHIFT) & TABLE_INDEX_MASK;
+}
+
+pub inline fn pdpt_index(vaddr: u64) u64 {
+ return (vaddr >> PDPT_SHIFT) & TABLE_INDEX_MASK;
+}
+
+pub inline fn pd_index(vaddr: u64) u64 {
+ return (vaddr >> PD_SHIFT) & TABLE_INDEX_MASK;
+}
+
+pub inline fn pt_index(vaddr: u64) u64 {
+ return (vaddr >> PT_SHIFT) & TABLE_INDEX_MASK;
+}
+
+pub inline fn virt_to_phys(vaddr: u64) u64 {
+ return vaddr - HIGHER_HALF_START;
+}
+
+pub inline fn phys_to_virt(paddr: u64) u64 {
+ return paddr + HIGHER_HALF_START;
+}
diff --git a/mirai.old/common/constants/msr.zig b/mirai.old/common/constants/msr.zig
new file mode 100644
index 0000000..89a8367
--- /dev/null
+++ b/mirai.old/common/constants/msr.zig
@@ -0,0 +1,20 @@
+//! Model Specific Register addresses and values
+
+pub const IA32_EFER: u32 = 0xC0000080;
+pub const IA32_STAR: u32 = 0xC0000081;
+pub const IA32_LSTAR: u32 = 0xC0000082;
+pub const IA32_CSTAR: u32 = 0xC0000083;
+pub const IA32_FMASK: u32 = 0xC0000084;
+pub const IA32_FS_BASE: u32 = 0xC0000100;
+pub const IA32_GS_BASE: u32 = 0xC0000101;
+pub const IA32_KERNEL_GS_BASE: u32 = 0xC0000102;
+
+pub const EFER_SCE: u64 = 1 << 0;
+pub const EFER_LME: u64 = 1 << 8;
+pub const EFER_LMA: u64 = 1 << 10;
+pub const EFER_NXE: u64 = 1 << 11;
+
+pub const STAR_KERNEL_CS_SHIFT: u6 = 32;
+pub const STAR_USER_BASE_SHIFT: u6 = 48;
+
+pub const FMASK_IF: u64 = 0x200;
diff --git a/mirai.old/common/constants/paging.zig b/mirai.old/common/constants/paging.zig
new file mode 100644
index 0000000..0a9e9bb
--- /dev/null
+++ b/mirai.old/common/constants/paging.zig
@@ -0,0 +1,11 @@
+//! Paging constants
+
+pub const PTE_PRESENT: u64 = 1 << 0;
+pub const PTE_WRITABLE: u64 = 1 << 1;
+pub const PTE_USER: u64 = 1 << 2;
+
+pub const PTE_MASK: u64 = 0x000FFFFFFFFFF000;
+pub const OFFSET_MASK: u64 = 0xFFF;
+
+pub const PML4_ENTRIES: usize = 512;
+pub const KERNEL_PML4_START: usize = 256;
diff --git a/mirai.old/common/constants/pci.zig b/mirai.old/common/constants/pci.zig
new file mode 100644
index 0000000..2da8287
--- /dev/null
+++ b/mirai.old/common/constants/pci.zig
@@ -0,0 +1,42 @@
+//! PCI configuration constants
+
+// Config register offsets
+pub const REG_VENDOR_ID: u8 = 0x00;
+pub const REG_DEVICE_ID: u8 = 0x02;
+pub const REG_COMMAND: u8 = 0x04;
+pub const REG_STATUS: u8 = 0x06;
+pub const REG_REVISION: u8 = 0x08;
+pub const REG_PROG_IF: u8 = 0x09;
+pub const REG_SUBCLASS: u8 = 0x0A;
+pub const REG_CLASS_CODE: u8 = 0x0B;
+pub const REG_HEADER_TYPE: u8 = 0x0E;
+pub const REG_BAR0: u8 = 0x10;
+pub const REG_BAR1: u8 = 0x14;
+pub const REG_BAR2: u8 = 0x18;
+pub const REG_BAR3: u8 = 0x1C;
+pub const REG_BAR4: u8 = 0x20;
+pub const REG_BAR5: u8 = 0x24;
+pub const REG_INTERRUPT_LINE: u8 = 0x3C;
+pub const REG_INTERRUPT_PIN: u8 = 0x3D;
+
+// Command register bits
+pub const CMD_IO_SPACE: u16 = 0x01;
+pub const CMD_MEMORY_SPACE: u16 = 0x02;
+pub const CMD_BUS_MASTER: u16 = 0x04;
+
+// Class codes
+pub const CLASS_MASS_STORAGE: u8 = 0x01;
+pub const SUBCLASS_SATA: u8 = 0x06;
+
+// Header type bits
+pub const HEADER_MULTIFUNCTION: u8 = 0x80;
+
+// Invalid vendor
+pub const VENDOR_INVALID: u16 = 0xFFFF;
+
+// Config address bits
+pub const CONFIG_ENABLE: u32 = 1 << 31;
+pub const CONFIG_BUS_SHIFT: u5 = 16;
+pub const CONFIG_DEVICE_SHIFT: u5 = 11;
+pub const CONFIG_FUNCTION_SHIFT: u5 = 8;
+pub const CONFIG_OFFSET_MASK: u8 = 0xFC;
diff --git a/mirai.old/common/constants/pic.zig b/mirai.old/common/constants/pic.zig
new file mode 100644
index 0000000..656e9a1
--- /dev/null
+++ b/mirai.old/common/constants/pic.zig
@@ -0,0 +1,20 @@
+//! PIC constants
+
+pub const MASTER_CMD: u16 = 0x20;
+pub const MASTER_DATA: u16 = 0x21;
+pub const SLAVE_CMD: u16 = 0xA0;
+pub const SLAVE_DATA: u16 = 0xA1;
+
+pub const ICW1_INIT: u8 = 0x11;
+pub const ICW4_8086: u8 = 0x01;
+
+pub const MASTER_OFFSET: u8 = 0x20;
+pub const SLAVE_OFFSET: u8 = 0x28;
+
+pub const MASTER_CASCADE: u8 = 0x04;
+pub const SLAVE_CASCADE: u8 = 0x02;
+
+pub const MASK_TIMER_KEYBOARD: u8 = 0xFC;
+pub const MASK_ALL: u8 = 0xFF;
+
+pub const EOI: u8 = 0x20;
diff --git a/mirai.old/common/constants/pit.zig b/mirai.old/common/constants/pit.zig
new file mode 100644
index 0000000..423e3f3
--- /dev/null
+++ b/mirai.old/common/constants/pit.zig
@@ -0,0 +1,20 @@
+//! PIT (Programmable Interval Timer) constants
+
+pub const CHANNEL_0: u16 = 0x40;
+pub const CHANNEL_1: u16 = 0x41;
+pub const CHANNEL_2: u16 = 0x42;
+pub const COMMAND: u16 = 0x43;
+
+pub const BASE_FREQUENCY: u32 = 1193182;
+
+// Command byte bits
+pub const SELECT_CHANNEL_0: u8 = 0x00;
+pub const ACCESS_LOHI: u8 = 0x30; // Low byte then high byte
+pub const MODE_RATE_GENERATOR: u8 = 0x04; // Mode 2: rate generator
+pub const MODE_SQUARE_WAVE: u8 = 0x06; // Mode 3: square wave
+
+// For 1000 Hz (1ms ticks): divisor = 1193182 / 1000 = 1193
+pub const DIVISOR_1000HZ: u16 = 1193;
+
+// For 100 Hz (10ms ticks): divisor = 1193182 / 100 = 11932
+pub const DIVISOR_100HZ: u16 = 11932;
diff --git a/mirai.old/common/constants/pmm.zig b/mirai.old/common/constants/pmm.zig
new file mode 100644
index 0000000..75cd032
--- /dev/null
+++ b/mirai.old/common/constants/pmm.zig
@@ -0,0 +1,14 @@
+//! Physical memory manager constants
+
+pub const MEMORY_AVAILABLE: u32 = 1;
+
+pub const FIRST_MB: u64 = 0x100000;
+pub const KERNEL_BASE: u64 = 0x100000;
+pub const KERNEL_MAP_END: u64 = 0x10000000; // 256MB
+
+pub const MMIO_FRAMEBUFFER_BASE: u64 = 0x80000000;
+pub const MMIO_FRAMEBUFFER_SIZE: u64 = 0x10000000;
+pub const MMIO_PCI_BASE: u64 = 0xE0000000;
+pub const MMIO_PCI_SIZE: u64 = 0x10000000;
+
+pub const BITMAP_MARK_USED: u8 = 0xFF;
diff --git a/mirai.old/common/constants/ports.zig b/mirai.old/common/constants/ports.zig
new file mode 100644
index 0000000..57ede2f
--- /dev/null
+++ b/mirai.old/common/constants/ports.zig
@@ -0,0 +1,32 @@
+//! I/O port addresses
+
+// Serial (COM1)
+pub const COM1: u16 = 0x3F8;
+pub const COM1_DATA: u16 = COM1;
+pub const COM1_INT_ENABLE: u16 = COM1 + 1;
+pub const COM1_FIFO_CTRL: u16 = COM1 + 2;
+pub const COM1_LINE_CTRL: u16 = COM1 + 3;
+pub const COM1_MODEM_CTRL: u16 = COM1 + 4;
+pub const COM1_LINE_STATUS: u16 = COM1 + 5;
+
+// PS/2 Keyboard
+pub const KEYBOARD_DATA: u16 = 0x60;
+pub const KEYBOARD_STATUS: u16 = 0x64;
+
+// PIC
+pub const PIC1_CMD: u16 = 0x20;
+pub const PIC1_DATA: u16 = 0x21;
+pub const PIC2_CMD: u16 = 0xA0;
+pub const PIC2_DATA: u16 = 0xA1;
+pub const PIC_EOI: u8 = 0x20;
+
+// PCI
+pub const PCI_CONFIG_ADDRESS: u16 = 0xCF8;
+pub const PCI_CONFIG_DATA: u16 = 0xCFC;
+
+// VGA
+pub const VGA_BUFFER_ADDR: usize = 0xB8000;
+
+// CMOS/RTC
+pub const CMOS_ADDRESS: u16 = 0x70;
+pub const CMOS_DATA: u16 = 0x71;
diff --git a/mirai.old/common/constants/psf.zig b/mirai.old/common/constants/psf.zig
new file mode 100644
index 0000000..445a831
--- /dev/null
+++ b/mirai.old/common/constants/psf.zig
@@ -0,0 +1,7 @@
+//! PSF font constants
+
+pub const PSF1_MAGIC: u16 = 0x0436;
+pub const PSF2_MAGIC: u32 = 0x864ab572;
+pub const PSF1_MODE_512: u8 = 0x01;
+pub const PSF1_GLYPHS_256: u32 = 256;
+pub const PSF1_GLYPHS_512: u32 = 512;
diff --git a/mirai.old/common/constants/serial.zig b/mirai.old/common/constants/serial.zig
new file mode 100644
index 0000000..92f6e34
--- /dev/null
+++ b/mirai.old/common/constants/serial.zig
@@ -0,0 +1,3 @@
+//! Serial port constants
+
+pub const LINE_STATUS_TX_EMPTY: u8 = 0x20;
diff --git a/mirai.old/common/constants/time.zig b/mirai.old/common/constants/time.zig
new file mode 100644
index 0000000..ce50e2a
--- /dev/null
+++ b/mirai.old/common/constants/time.zig
@@ -0,0 +1,9 @@
+//! Time constants
+
+pub const TICKS_PER_SECOND: u64 = 100;
+
+pub const SECONDS_PER_MINUTE: u64 = 60;
+pub const SECONDS_PER_HOUR: u64 = 3600;
+pub const SECONDS_PER_DAY: u64 = 86400;
+
+pub const EPOCH_YEAR: u64 = 1970;
diff --git a/mirai.old/common/constants/vga.zig b/mirai.old/common/constants/vga.zig
new file mode 100644
index 0000000..4cc2f33
--- /dev/null
+++ b/mirai.old/common/constants/vga.zig
@@ -0,0 +1,3 @@
+//! VGA constants
+
+pub const DEFAULT_ATTR: u16 = 0x0F00;
diff --git a/mirai.old/common/constants/video.zig b/mirai.old/common/constants/video.zig
new file mode 100644
index 0000000..adf13f2
--- /dev/null
+++ b/mirai.old/common/constants/video.zig
@@ -0,0 +1,6 @@
+//! Video/framebuffer constants
+
+pub const BPP_24: u32 = 24;
+pub const BPP_32: u32 = 32;
+pub const BYTES_PER_PIXEL_24: u32 = 3;
+pub const BYTES_PER_PIXEL_32: u32 = 4;
diff --git a/mirai.old/common/errors/fs.zig b/mirai.old/common/errors/fs.zig
new file mode 100644
index 0000000..efa9b58
--- /dev/null
+++ b/mirai.old/common/errors/fs.zig
@@ -0,0 +1,15 @@
+//! Filesystem errors
+
+pub const FsError = error{
+ NotFound,
+ InvalidLocation,
+ NotAStack,
+ NotAUnit,
+ StackNotEmpty,
+ AlreadyExists,
+ NoSpace,
+ ReadOnly,
+ DiskError,
+ Corrupted,
+ CannotCreate,
+};
diff --git a/mirai.old/common/errors/io.zig b/mirai.old/common/errors/io.zig
new file mode 100644
index 0000000..c7afdaf
--- /dev/null
+++ b/mirai.old/common/errors/io.zig
@@ -0,0 +1,15 @@
+//! I/O errors
+
+pub const IoError = error{
+ InvalidAttachment,
+ TooManyAttachments,
+ ViewFailed,
+ MarkFailed,
+ InvalidDevice,
+ CannotView,
+ CannotMark,
+ BadPointer,
+ BufferTooSmall,
+ UnknownDevice,
+ SendFailed,
+};
diff --git a/mirai.old/common/errors/kata.zig b/mirai.old/common/errors/kata.zig
new file mode 100644
index 0000000..e997d43
--- /dev/null
+++ b/mirai.old/common/errors/kata.zig
@@ -0,0 +1,9 @@
+//! Kata errors
+
+pub const KataError = error{
+ TooManyKatas,
+ KataNotFound,
+ InvalidState,
+ NotChild,
+ WaitingSelf,
+};
diff --git a/mirai.old/common/errors/memory.zig b/mirai.old/common/errors/memory.zig
new file mode 100644
index 0000000..58d77b8
--- /dev/null
+++ b/mirai.old/common/errors/memory.zig
@@ -0,0 +1,10 @@
+//! Memory errors
+
+pub const MemoryError = error{
+ OutOfMemory,
+ InvalidAddress,
+ PageNotPresent,
+ AllocationFailed,
+ NotAligned,
+ PermissionDenied,
+};
diff --git a/mirai.old/common/limits/ahci.zig b/mirai.old/common/limits/ahci.zig
new file mode 100644
index 0000000..027a992
--- /dev/null
+++ b/mirai.old/common/limits/ahci.zig
@@ -0,0 +1,8 @@
+//! AHCI limits
+
+pub const MAX_PORTS: u8 = 32;
+pub const MAX_CMD_SLOTS: u8 = 32;
+pub const TIMEOUT_SPIN: u32 = 500000;
+pub const TIMEOUT_CMD: u32 = 1000000;
+pub const TIMEOUT_WAIT: u32 = 10000000;
+pub const CMD_TABLE_CLEAR_SIZE: usize = 256;
diff --git a/mirai.old/common/limits/ata.zig b/mirai.old/common/limits/ata.zig
new file mode 100644
index 0000000..917916c
--- /dev/null
+++ b/mirai.old/common/limits/ata.zig
@@ -0,0 +1,4 @@
+//! ATA limits
+
+pub const SECTOR_SIZE: usize = 512;
+pub const TIMEOUT_DATA_READY: u32 = 10000;
diff --git a/mirai.old/common/limits/attachment.zig b/mirai.old/common/limits/attachment.zig
new file mode 100644
index 0000000..511ba9d
--- /dev/null
+++ b/mirai.old/common/limits/attachment.zig
@@ -0,0 +1,3 @@
+//! Attachment limits
+
+pub const MAX_LOCATION_LENGTH: usize = 256;
diff --git a/mirai.old/common/limits/boot.zig b/mirai.old/common/limits/boot.zig
new file mode 100644
index 0000000..76b4a0d
--- /dev/null
+++ b/mirai.old/common/limits/boot.zig
@@ -0,0 +1,7 @@
+//! Boot limits
+
+pub const MAX_MEMORY_ENTRIES: usize = 32;
+pub const MAX_BOOT_MESSAGES: usize = 100;
+pub const MAX_MESSAGE_LENGTH: usize = 256;
+pub const KERNEL_STACK_SIZE: usize = 16384;
+pub const FONT_BUFFER_SIZE: usize = 8192;
diff --git a/mirai.old/common/limits/crimson.zig b/mirai.old/common/limits/crimson.zig
new file mode 100644
index 0000000..9f80985
--- /dev/null
+++ b/mirai.old/common/limits/crimson.zig
@@ -0,0 +1,6 @@
+//! Crimson limits
+
+pub const MAX_STACK_FRAMES: usize = 16;
+pub const REGISTER_BUFFER_SIZE: usize = 100;
+pub const STACK_FRAME_BUFFER_SIZE: usize = 50;
+pub const ASSERT_BUFFER_SIZE: usize = 256;
diff --git a/mirai.old/common/limits/fs.zig b/mirai.old/common/limits/fs.zig
new file mode 100644
index 0000000..ca7103d
--- /dev/null
+++ b/mirai.old/common/limits/fs.zig
@@ -0,0 +1,6 @@
+//! Filesystem limits
+
+pub const MAX_LOCATION_LENGTH: usize = 4096;
+pub const MAX_IDENTITY_LENGTH: usize = 255;
+pub const MAX_UNIT_SIZE: u64 = 64 * 1024 * 1024; // 64MB
+pub const MAX_STACK_ITEMS: usize = 4096;
diff --git a/mirai.old/common/limits/io.zig b/mirai.old/common/limits/io.zig
new file mode 100644
index 0000000..2dc80c4
--- /dev/null
+++ b/mirai.old/common/limits/io.zig
@@ -0,0 +1,6 @@
+//! I/O limits
+
+pub const MAX_MARK_SIZE: u64 = 4 * 1024 * 1024; // 4MB
+pub const MAX_VIEW_SIZE: u64 = 4 * 1024 * 1024; // 4MB
+pub const MIRAI_COPY_BUFFER_SIZE: usize = 4096; // 4KB
+pub const MAX_STRING_LENGTH: usize = 4096;
diff --git a/mirai.old/common/limits/kata.zig b/mirai.old/common/limits/kata.zig
new file mode 100644
index 0000000..6f827f3
--- /dev/null
+++ b/mirai.old/common/limits/kata.zig
@@ -0,0 +1,8 @@
+//! Kata Limits
+
+pub const MAX_KATAS: usize = 64;
+pub const MAX_ATTACHMENTS: usize = 16;
+pub const MAX_PARAMETERS: usize = 64;
+pub const MAX_ENV_VARS: usize = 64;
+pub const MAX_LOCATION_LENGTH: usize = 256;
+pub const MAX_LETTER_LENGTH: usize = 256;
diff --git a/mirai.old/common/limits/keyboard.zig b/mirai.old/common/limits/keyboard.zig
new file mode 100644
index 0000000..b4d5818
--- /dev/null
+++ b/mirai.old/common/limits/keyboard.zig
@@ -0,0 +1,3 @@
+//! Keyboard limits
+
+pub const BUFFER_SIZE: usize = 256;
diff --git a/mirai.old/common/limits/memory.zig b/mirai.old/common/limits/memory.zig
new file mode 100644
index 0000000..dba20e6
--- /dev/null
+++ b/mirai.old/common/limits/memory.zig
@@ -0,0 +1,27 @@
+//! Memory limits
+
+const memory_const = @import("../constants/memory.zig");
+
+pub const KATA_SPACE_MAX: u64 = memory_const.KATA_SPACE_END;
+pub const KATA_SPACE_MIN: u64 = memory_const.KATA_SPACE_START;
+pub const MIRAI_SPACE_START: u64 = memory_const.HIGHER_HALF_START;
+
+pub inline fn is_valid_kata_pointer(ptr: u64) bool {
+ return ptr >= KATA_SPACE_MIN and ptr < KATA_SPACE_MAX;
+}
+
+pub inline fn is_kata_address(addr: u64) bool {
+ return addr < KATA_SPACE_MAX;
+}
+
+pub inline fn is_mirai_address(addr: u64) bool {
+ return addr >= MIRAI_SPACE_START;
+}
+
+pub inline fn is_kata_range(start: u64, size: u64) bool {
+ if (start >= KATA_SPACE_MAX) return false;
+ if (size == 0) return true;
+ const end = start +% size;
+ if (end < start) return false;
+ return end <= KATA_SPACE_MAX;
+}
diff --git a/mirai.old/common/limits/pci.zig b/mirai.old/common/limits/pci.zig
new file mode 100644
index 0000000..b9b069c
--- /dev/null
+++ b/mirai.old/common/limits/pci.zig
@@ -0,0 +1,6 @@
+//! PCI limits
+
+pub const MAX_BUS: u16 = 256;
+pub const MAX_DEVICE: u8 = 32;
+pub const MAX_FUNCTION: u8 = 8;
+pub const MAX_DEVICES: usize = 64;
diff --git a/mirai.old/common/limits/terminal.zig b/mirai.old/common/limits/terminal.zig
new file mode 100644
index 0000000..410631d
--- /dev/null
+++ b/mirai.old/common/limits/terminal.zig
@@ -0,0 +1,6 @@
+//! Terminal limits
+
+pub const MAX_LINES: usize = 256;
+pub const TAB_WIDTH: u32 = 4;
+pub const DEFAULT_CHAR_WIDTH: u32 = 8;
+pub const DEFAULT_CHAR_HEIGHT: u32 = 16;
diff --git a/mirai.old/common/limits/vga.zig b/mirai.old/common/limits/vga.zig
new file mode 100644
index 0000000..556a3cb
--- /dev/null
+++ b/mirai.old/common/limits/vga.zig
@@ -0,0 +1,4 @@
+//! VGA limits
+
+pub const WIDTH: usize = 80;
+pub const HEIGHT: usize = 25;
diff --git a/mirai.old/crimson/crimson.zig b/mirai.old/crimson/crimson.zig
new file mode 100644
index 0000000..37542aa
--- /dev/null
+++ b/mirai.old/crimson/crimson.zig
@@ -0,0 +1,15 @@
+//! Crimson - Kernel panic handler
+
+pub const types = @import("types.zig");
+pub const panic = @import("panic.zig");
+pub const exception = @import("exception.zig");
+pub const render = @import("render.zig");
+pub const format = @import("format.zig");
+
+pub const Context = types.Context;
+pub const ExceptionFrame = types.ExceptionFrame;
+
+pub const init = panic.init;
+pub const collapse = panic.collapse;
+pub const assert_failed = panic.assert_failed;
+pub const exception_handler = exception.exception_handler;
diff --git a/mirai.old/crimson/exception.zig b/mirai.old/crimson/exception.zig
new file mode 100644
index 0000000..9c74569
--- /dev/null
+++ b/mirai.old/crimson/exception.zig
@@ -0,0 +1,136 @@
+//! CPU exception handler
+
+const kata_memory = @import("../kata/memory.zig");
+const memory = @import("../asm/memory.zig");
+const panic = @import("panic.zig");
+const sensei = @import("../kata/sensei/sensei.zig");
+const serial = @import("../drivers/serial/serial.zig");
+const types = @import("types.zig");
+
+const NAMES = [_][]const u8{
+ "Division By Zero",
+ "Debug",
+ "Non-Maskable Interrupt",
+ "Breakpoint",
+ "Overflow",
+ "Bound Range Exceeded",
+ "Invalid Opcode",
+ "Device Not Available",
+ "Double Fault",
+ "Coprocessor Segment Overrun",
+ "Invalid TSS",
+ "Segment Not Present",
+ "Stack-Segment Fault",
+ "General Protection Fault",
+ "Page Fault",
+ "Reserved",
+ "x87 Floating-Point Exception",
+ "Alignment Check",
+ "Machine Check",
+ "SIMD Floating-Point Exception",
+ "Virtualization Exception",
+ "Control Protection Exception",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Hypervisor Injection Exception",
+ "VMM Communication Exception",
+ "Security Exception",
+ "Reserved",
+};
+
+var in_exception: bool = false;
+
+export fn exception_handler(frame_ptr: u64) void {
+ const frame = @as(*types.ExceptionFrame, @ptrFromInt(frame_ptr));
+
+ // Prevent infinite recursion if exception handler itself faults
+ if (in_exception) {
+ serial.print("NESTED EXCEPTION - HALTING\n");
+ while (true) {
+ asm volatile ("cli; hlt");
+ }
+ }
+ in_exception = true;
+
+ // Handle page faults - check for stack growth
+ if (frame.int_num == 14) {
+ const cr2 = memory.read_page_fault_address();
+ const is_not_present = (frame.error_code & 1) == 0;
+ const is_rsvd = (frame.error_code & 8) != 0;
+
+ // Check if fault is in user stack region (demand paging)
+ if (sensei.get_current_kata()) |kata| {
+ if (cr2 >= kata.user_stack_bottom and cr2 < kata.user_stack_top and is_not_present and !is_rsvd) {
+ if (kata_memory.grow_stack(kata, cr2)) {
+ in_exception = false;
+ return;
+ }
+ }
+ }
+ }
+
+ serial.print("\n!!! EXCEPTION: ");
+ if (frame.int_num < NAMES.len) {
+ serial.print(NAMES[frame.int_num]);
+ } else {
+ serial.print("Unknown");
+ }
+ serial.print(" !!!\n");
+
+ serial.printf("Vector: {x}\n", .{frame.int_num});
+ serial.printf("Error Code: {x}\n", .{frame.error_code});
+ serial.printf("RIP: {x}\n", .{frame.rip});
+ serial.printf("CS: {x}\n", .{frame.cs});
+ serial.printf("RFLAGS: {x}\n", .{frame.rflags});
+ serial.printf("RSP: {x}\n", .{frame.rsp});
+ serial.printf("SS: {x}\n", .{frame.ss});
+
+ if (frame.int_num == 14) {
+ const cr2 = memory.read_page_fault_address();
+ serial.printf("CR2 (fault addr): {x}\n", .{cr2});
+
+ if (sensei.get_current_kata()) |kata| {
+ serial.printf("Stack: bottom={x} committed={x} top={x}\n", .{ kata.user_stack_bottom, kata.user_stack_committed, kata.user_stack_top });
+ serial.printf("Kata: id={x} PT={x}\n", .{ kata.id, kata.page_table });
+ }
+
+ serial.print("Fault type: ");
+ if ((frame.error_code & 1) == 0) {
+ serial.print("Page not present");
+ } else {
+ serial.print("Protection violation");
+ }
+ if ((frame.error_code & 2) != 0) {
+ serial.print(", Write");
+ } else {
+ serial.print(", Read");
+ }
+ if ((frame.error_code & 4) != 0) {
+ serial.print(", User mode");
+ } else {
+ serial.print(", Kernel mode");
+ }
+ if ((frame.error_code & 8) != 0) {
+ serial.print(", RESERVED BIT SET");
+ }
+ serial.print("\n");
+
+ serial.printf("CR3 (page table): {x}\n", .{memory.read_page_table_base()});
+ }
+
+ serial.print("\nRegisters:\n");
+ serial.printf("RAX: {x} RBX: {x}\n", .{ frame.rax, frame.rbx });
+ serial.printf("RCX: {x} RDX: {x}\n", .{ frame.rcx, frame.rdx });
+ serial.printf("RSI: {x} RDI: {x}\n", .{ frame.rsi, frame.rdi });
+ serial.printf("RBP: {x}\n", .{frame.rbp});
+ serial.printf("R8: {x} R9: {x}\n", .{ frame.r8, frame.r9 });
+ serial.printf("R10: {x} R11: {x}\n", .{ frame.r10, frame.r11 });
+ serial.printf("R12: {x} R13: {x}\n", .{ frame.r12, frame.r13 });
+ serial.printf("R14: {x} R15: {x}\n", .{ frame.r14, frame.r15 });
+
+ panic.collapse(if (frame.int_num < NAMES.len) NAMES[frame.int_num] else "Unknown Exception", null);
+}
diff --git a/mirai.old/crimson/format.zig b/mirai.old/crimson/format.zig
new file mode 100644
index 0000000..1681ded
--- /dev/null
+++ b/mirai.old/crimson/format.zig
@@ -0,0 +1,132 @@
+//! Crimson formatting utilities
+
+pub fn u32_decimal(value: u32, buffer: []u8) usize {
+ if (buffer.len == 0) return 0;
+
+ var temp: [10]u8 = undefined;
+ var temp_pos: usize = 0;
+ var val = value;
+
+ if (val == 0) {
+ buffer[0] = '0';
+ return 1;
+ }
+
+ while (val > 0 and temp_pos < 10) {
+ temp[temp_pos] = @as(u8, @truncate(val % 10)) + '0';
+ val /= 10;
+ temp_pos += 1;
+ }
+
+ var pos: usize = 0;
+ while (temp_pos > 0 and pos < buffer.len) {
+ temp_pos -= 1;
+ buffer[pos] = temp[temp_pos];
+ pos += 1;
+ }
+
+ return pos;
+}
+
+pub fn u64_hex(value: u64, buffer: []u8) usize {
+ const hex = "0123456789ABCDEF";
+ var shift: u6 = 60;
+ var pos: usize = 0;
+
+ while (pos < 16 and pos < buffer.len) : (pos += 1) {
+ const nibble = @as(u8, @truncate((value >> shift) & 0xF));
+ buffer[pos] = hex[nibble];
+ if (shift >= 4) {
+ shift -= 4;
+ }
+ }
+
+ return pos;
+}
+
+pub fn register(label: []const u8, value: u64, buffer: []u8) []const u8 {
+ var pos: usize = 0;
+
+ for (label) |c| {
+ if (pos < buffer.len) {
+ buffer[pos] = c;
+ pos += 1;
+ }
+ }
+
+ if (pos < buffer.len) {
+ buffer[pos] = ':';
+ pos += 1;
+ }
+ if (pos < buffer.len) {
+ buffer[pos] = ' ';
+ pos += 1;
+ }
+
+ pos += u64_hex(value, buffer[pos..]);
+
+ return buffer[0..pos];
+}
+
+pub fn stack_frame(num: usize, addr: u64, buffer: []u8) []const u8 {
+ var pos: usize = 0;
+
+ if (pos < buffer.len) {
+ buffer[pos] = '#';
+ pos += 1;
+ }
+
+ pos += u32_decimal(@truncate(num), buffer[pos..]);
+
+ if (pos < buffer.len) {
+ buffer[pos] = ' ';
+ pos += 1;
+ }
+
+ pos += u64_hex(addr, buffer[pos..]);
+
+ return buffer[0..pos];
+}
+
+pub fn assert_message(condition: []const u8, file: []const u8, line: u32, buffer: []u8) []const u8 {
+ var pos: usize = 0;
+
+ const prefix = "Assertion failed: ";
+ for (prefix) |c| {
+ if (pos < buffer.len) {
+ buffer[pos] = c;
+ pos += 1;
+ }
+ }
+
+ for (condition) |c| {
+ if (pos < buffer.len) {
+ buffer[pos] = c;
+ pos += 1;
+ }
+ }
+
+ const at_str = " at ";
+ for (at_str) |c| {
+ if (pos < buffer.len) {
+ buffer[pos] = c;
+ pos += 1;
+ }
+ }
+
+ for (file) |c| {
+ if (pos < buffer.len) {
+ buffer[pos] = c;
+ pos += 1;
+ }
+ }
+
+ if (pos < buffer.len) {
+ buffer[pos] = ':';
+ pos += 1;
+ }
+
+ pos += u32_decimal(line, buffer[pos..]);
+
+ return buffer[0..pos];
+}
diff --git a/mirai.old/crimson/panic.zig b/mirai.old/crimson/panic.zig
new file mode 100644
index 0000000..a0e52b1
--- /dev/null
+++ b/mirai.old/crimson/panic.zig
@@ -0,0 +1,66 @@
+//! Crimson panic handler
+
+const cpu = @import("../asm/cpu.zig");
+const crimson_limits = @import("../common/limits/crimson.zig");
+const format = @import("format.zig");
+const multiboot = @import("../boot/multiboot/multiboot.zig");
+const render = @import("render.zig");
+const serial = @import("../drivers/serial/serial.zig");
+const types = @import("types.zig");
+
+var framebuffer: ?multiboot.FramebufferInfo = null;
+
+pub fn init(fb: multiboot.FramebufferInfo) void {
+ framebuffer = fb;
+ serial.print("Crimson panic handler initialized\n");
+}
+
+pub fn collapse(message: []const u8, context: ?*const types.Context) noreturn {
+ cpu.disable_interrupts();
+
+ serial.print("\n╔════════════════════════════════════╗\n");
+ serial.print("║ CRIMSON COLLAPSE ║\n");
+ serial.print("╚════════════════════════════════════╝\n");
+ serial.print(message);
+ serial.print("\n");
+
+ if (context) |ctx| {
+ dump_registers(ctx);
+ }
+
+ if (framebuffer) |fb| {
+ render.screen(fb, message, context);
+ }
+
+ halt();
+}
+
+pub fn assert_failed(condition: []const u8, file: []const u8, line: u32) noreturn {
+ var buffer: [crimson_limits.ASSERT_BUFFER_SIZE]u8 = undefined;
+ const msg = format.assert_message(condition, file, line, &buffer);
+ collapse(msg, null);
+}
+
+fn dump_registers(ctx: *const types.Context) void {
+ serial.print("\nRegister Dump:\n");
+ serial.printf("RAX: {x} RBX: {x}\n", .{ ctx.rax, ctx.rbx });
+ serial.printf("RCX: {x} RDX: {x}\n", .{ ctx.rcx, ctx.rdx });
+ serial.printf("RSI: {x} RDI: {x}\n", .{ ctx.rsi, ctx.rdi });
+ serial.printf("RBP: {x} RSP: {x}\n", .{ ctx.rbp, ctx.rsp });
+ serial.printf("RIP: {x} CR2: {x}\n", .{ ctx.rip, ctx.cr2 });
+ serial.printf("CR3: {x} ERR: {x}\n", .{ ctx.cr3, ctx.error_code });
+
+ serial.print("\nFaulting instruction bytes at RIP:\n ");
+ const rip_ptr = @as([*]const u8, @ptrFromInt(ctx.rip));
+ for (0..16) |i| {
+ serial.printf("{x} ", .{rip_ptr[i]});
+ }
+ serial.print("\n");
+}
+
+fn halt() noreturn {
+ while (true) {
+ cpu.disable_interrupts();
+ cpu.halt_processor();
+ }
+}
diff --git a/mirai.old/crimson/render.zig b/mirai.old/crimson/render.zig
new file mode 100644
index 0000000..5877aa5
--- /dev/null
+++ b/mirai.old/crimson/render.zig
@@ -0,0 +1,155 @@
+//! Crimson screen rendering
+
+const colors = @import("../common/constants/colors.zig");
+const crimson_limits = @import("../common/limits/crimson.zig");
+const font = @import("../graphics/fonts/psf.zig");
+const format = @import("format.zig");
+const multiboot = @import("../boot/multiboot/multiboot.zig");
+const types = @import("types.zig");
+const video_const = @import("../common/constants/video.zig");
+
+pub fn screen(fb: multiboot.FramebufferInfo, message: []const u8, context: ?*const types.Context) void {
+ fill_crimson(fb);
+
+ const char_height = font.get_height();
+ var y: u32 = 60;
+
+ const heading = "Hey! You finally met Crimson!";
+ centered_text(heading, y, fb, colors.WHITE);
+ y += char_height + 20;
+
+ const desc1 = "Mirai Kernel has encountered an error and the system";
+ const desc2 = "will need to be restarted. Please reboot your machine.";
+ centered_text(desc1, y, fb, colors.WHITE);
+ y += char_height + 4;
+ centered_text(desc2, y, fb, colors.WHITE);
+ y += char_height + 30;
+
+ centered_text("Error:", y, fb, colors.WHITE);
+ y += char_height + 4;
+ centered_text(message, y, fb, colors.WHITE);
+ y += char_height + 30;
+
+ if (context) |ctx| {
+ registers(ctx, y, fb);
+ y += (char_height + 4) * 10;
+
+ centered_text("Stack Trace:", y, fb, colors.WHITE);
+ y += char_height + 4;
+ stack_trace(ctx.rbp, ctx.rip, y, fb);
+ }
+}
+
+fn fill_crimson(fb: multiboot.FramebufferInfo) void {
+ if (fb.bpp == video_const.BPP_32) {
+ const pixels = @as([*]volatile u32, @ptrFromInt(fb.addr));
+ const total = fb.height * (fb.pitch / 4);
+
+ for (0..total) |i| {
+ pixels[i] = colors.CRIMSON;
+ }
+ } else if (fb.bpp == video_const.BPP_24) {
+ const pixels = @as([*]volatile u8, @ptrFromInt(fb.addr));
+
+ const r: u8 = @truncate((colors.CRIMSON >> 16) & 0xFF);
+ const g: u8 = @truncate((colors.CRIMSON >> 8) & 0xFF);
+ const b: u8 = @truncate(colors.CRIMSON & 0xFF);
+
+ for (0..fb.height) |y| {
+ for (0..fb.width) |x| {
+ const offset = y * fb.pitch + x * 3;
+ pixels[offset] = b;
+ pixels[offset + 1] = g;
+ pixels[offset + 2] = r;
+ }
+ }
+ }
+}
+
+pub fn centered_text(text: []const u8, y: u32, fb: multiboot.FramebufferInfo, color: u32) void {
+ const char_width: u32 = font.get_width();
+ const text_width: u32 = @intCast(text.len * char_width);
+
+ if (text_width > fb.width) {
+ font.render_text(text, 20, y, fb, color);
+ } else {
+ const x: u32 = (fb.width - text_width) / 2;
+ font.render_text(text, x, y, fb, color);
+ }
+}
+
+fn registers(ctx: *const types.Context, start_y: u32, fb: multiboot.FramebufferInfo) void {
+ const char_height = font.get_height();
+ var y = start_y;
+ var buffer: [crimson_limits.REGISTER_BUFFER_SIZE]u8 = undefined;
+
+ register_pair("RAX", ctx.rax, "RBX", ctx.rbx, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("RCX", ctx.rcx, "RDX", ctx.rdx, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("RSI", ctx.rsi, "RDI", ctx.rdi, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("RBP", ctx.rbp, "RSP", ctx.rsp, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("R8 ", ctx.r8, "R9 ", ctx.r9, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("R10", ctx.r10, "R11", ctx.r11, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("R12", ctx.r12, "R13", ctx.r13, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("R14", ctx.r14, "R15", ctx.r15, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("RIP", ctx.rip, "CR2", ctx.cr2, y, &buffer, fb);
+ y += char_height + 4;
+
+ register_pair("CR3", ctx.cr3, "ERR", ctx.error_code, y, &buffer, fb);
+}
+
+fn register_pair(label1: []const u8, val1: u64, label2: []const u8, val2: u64, y: u32, buffer: []u8, fb: multiboot.FramebufferInfo) void {
+ const left_x: u32 = 100;
+ const right_x: u32 = 500;
+
+ const text1 = format.register(label1, val1, buffer[0..49]);
+ font.render_text(text1, left_x, y, fb, colors.WHITE);
+
+ const text2 = format.register(label2, val2, buffer[50..99]);
+ font.render_text(text2, right_x, y, fb, colors.WHITE);
+}
+
+fn stack_trace(rbp: u64, rip: u64, start_y: u32, fb: multiboot.FramebufferInfo) void {
+ const char_height = font.get_height();
+ var y = start_y;
+ var buffer: [crimson_limits.STACK_FRAME_BUFFER_SIZE]u8 = undefined;
+
+ const text0 = format.stack_frame(0, rip, &buffer);
+ centered_text(text0, y, fb, colors.WHITE);
+ y += char_height + 2;
+
+ var frame_rbp = rbp;
+ var frame_num: usize = 1;
+
+ while (frame_num < crimson_limits.MAX_STACK_FRAMES) : (frame_num += 1) {
+ if (frame_rbp < 0xFFFF800000000000 or frame_rbp == 0) break;
+
+ const ret_addr = @as(*const u64, @ptrFromInt(frame_rbp + 8)).*;
+
+ if (ret_addr < 0xFFFF800000000000) break;
+
+ const text = format.stack_frame(frame_num, ret_addr, &buffer);
+ centered_text(text, y, fb, colors.WHITE);
+ y += char_height + 2;
+
+ const next_rbp = @as(*const u64, @ptrFromInt(frame_rbp)).*;
+
+ if (next_rbp == 0 or next_rbp == frame_rbp) break;
+ frame_rbp = next_rbp;
+ }
+}
diff --git a/mirai.old/crimson/types.zig b/mirai.old/crimson/types.zig
new file mode 100644
index 0000000..6383c71
--- /dev/null
+++ b/mirai.old/crimson/types.zig
@@ -0,0 +1,50 @@
+//! Crimson type definitions
+
+pub const Context = struct {
+ rax: u64,
+ rbx: u64,
+ rcx: u64,
+ rdx: u64,
+ rsi: u64,
+ rdi: u64,
+ rbp: u64,
+ rsp: u64,
+ r8: u64,
+ r9: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ rip: u64,
+ rflags: u64,
+ error_code: u64,
+ cr2: u64,
+ cr3: u64,
+};
+
+pub const ExceptionFrame = packed struct {
+ r15: u64,
+ r14: u64,
+ r13: u64,
+ r12: u64,
+ r11: u64,
+ r10: u64,
+ r9: u64,
+ r8: u64,
+ rbp: u64,
+ rdi: u64,
+ rsi: u64,
+ rdx: u64,
+ rcx: u64,
+ rbx: u64,
+ rax: u64,
+ int_num: u64,
+ error_code: u64,
+ rip: u64,
+ cs: u64,
+ rflags: u64,
+ rsp: u64,
+ ss: u64,
+};
diff --git a/mirai.old/drivers/ahci/ahci.zig b/mirai.old/drivers/ahci/ahci.zig
new file mode 100644
index 0000000..c9073bd
--- /dev/null
+++ b/mirai.old/drivers/ahci/ahci.zig
@@ -0,0 +1,232 @@
+//! AHCI SATA driver
+
+const ahci_const = @import("../../common/constants/ahci.zig");
+const ahci_limits = @import("../../common/limits/ahci.zig");
+const ata_const = @import("../../common/constants/ata.zig");
+const ata_limits = @import("../../common/limits/ata.zig");
+const fis = @import("fis.zig");
+const int = @import("../../utils/types/int.zig");
+const pci = @import("../pci/pci.zig");
+const pci_const = @import("../../common/constants/pci.zig");
+const pmm = @import("../../memory/pmm.zig");
+const port_ops = @import("port.zig");
+const serial = @import("../serial/serial.zig");
+const memory_const = @import("../../common/constants/memory.zig");
+const types = @import("types.zig");
+
+pub const SECTOR_SIZE = ata_limits.SECTOR_SIZE;
+
+const HIGHER_HALF = memory_const.HIGHER_HALF_START;
+const PAGE_SIZE = memory_const.PAGE_SIZE;
+
+var hba_mem: ?*volatile types.HBAMemory = null;
+var port_num: u8 = 0;
+var dma_buffer_phys: u64 = 0;
+var dma_buffer_virt: u64 = 0;
+
+pub fn find_and_init() !void {
+ for (pci.get_devices()) |*dev| {
+ if (dev.class_code == pci_const.CLASS_MASS_STORAGE and dev.subclass == pci_const.SUBCLASS_SATA) {
+ init(dev) catch |err| {
+ if (err == error.NoDriveFound) continue;
+ return err;
+ };
+ return;
+ }
+ }
+
+ return error.NoAHCIController;
+}
+
+pub fn init(ahci_device: *pci.Device) !void {
+ serial.printf("Initializing AHCI controller at {x}:{x}.{x}\n", .{
+ ahci_device.bus,
+ ahci_device.device,
+ ahci_device.function,
+ });
+
+ pci.enable_bus_mastering(ahci_device);
+ pci.enable_memory_space(ahci_device);
+
+ const abar_phys = ahci_device.bar5 & ahci_const.BAR_MASK;
+ const abar_virt = abar_phys + HIGHER_HALF;
+
+ serial.printf(" ABAR physical: {x}\n", .{abar_phys});
+
+ if (dma_buffer_phys == 0) {
+ dma_buffer_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ dma_buffer_virt = dma_buffer_phys + HIGHER_HALF;
+ }
+
+ hba_mem = @ptrFromInt(abar_virt);
+ const hba = hba_mem.?;
+
+ serial.printf(" AHCI Version: {x}\n", .{hba.vs});
+
+ hba.ghc = hba.ghc | ahci_const.GHC_AE;
+
+ serial.printf(" Ports Implemented: {x}\n", .{hba.pi});
+
+ var found = false;
+ var i: u8 = 0;
+ while (i < ahci_limits.MAX_PORTS) : (i += 1) {
+ if ((hba.pi & (int.u32_of(1) << int.u5_of(i))) != 0) {
+ const port = port_ops.get(hba, i);
+ if (port_ops.check_type(port) == ahci_const.PORT_TYPE_SATA) {
+ serial.printf(" Found SATA drive on port {}\n", .{i});
+ port_num = i;
+
+ try rebase_port(port);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) return error.NoDriveFound;
+}
+
+fn rebase_port(port: *volatile types.HBAPort) !void {
+ port_ops.stop_cmd(port);
+
+ const clb_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ const fb_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+
+ port.clb = clb_phys;
+ port.fb = fb_phys;
+
+ const clb_virt = clb_phys + HIGHER_HALF;
+ const fb_virt = fb_phys + HIGHER_HALF;
+
+ @memset(@as([*]u8, @ptrFromInt(clb_virt))[0..PAGE_SIZE], 0);
+ @memset(@as([*]u8, @ptrFromInt(fb_virt))[0..PAGE_SIZE], 0);
+
+ const cmdheader = @as([*]volatile types.CmdHeader, @ptrFromInt(clb_virt));
+
+ var i: usize = 0;
+ while (i < ahci_limits.MAX_CMD_SLOTS) : (i += 1) {
+ cmdheader[i].prdtl = 8;
+
+ const cmdtable_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ cmdheader[i].ctba = cmdtable_phys;
+
+ @memset(@as([*]u8, @ptrFromInt(cmdtable_phys + HIGHER_HALF))[0..PAGE_SIZE], 0);
+ }
+
+ port.serr = 0xFFFFFFFF;
+ port.is = 0xFFFFFFFF;
+
+ port_ops.start_cmd(port);
+}
+
+fn do_read_sector(lba: u64, buffer: *[SECTOR_SIZE]u8) bool {
+ const hba = hba_mem orelse return false;
+ const port = port_ops.get(hba, port_num);
+
+ port.is = 0xFFFFFFFF;
+
+ const slot = port_ops.find_cmd_slot(port) orelse return false;
+
+ const clb_virt = port.clb + HIGHER_HALF;
+ const cmdheader = @as([*]volatile types.CmdHeader, @ptrFromInt(clb_virt));
+
+ cmdheader[slot].cfl = @sizeOf(types.FISRegH2D) / 4;
+ cmdheader[slot].prdtl = 1;
+ cmdheader[slot].prdbc = 0;
+
+ const cmdtbl_virt = cmdheader[slot].ctba + HIGHER_HALF;
+ const cmdtbl = @as(*volatile types.CmdTable, @ptrFromInt(cmdtbl_virt));
+ @memset(@as([*]u8, @ptrFromInt(cmdtbl_virt))[0..ahci_limits.CMD_TABLE_CLEAR_SIZE], 0);
+
+ cmdtbl.prdt_entry[0].dba = dma_buffer_phys;
+ cmdtbl.prdt_entry[0].dbc = (SECTOR_SIZE - 1) | ahci_const.PRDT_INTERRUPT;
+ cmdtbl.prdt_entry[0]._rsv0 = 0;
+
+ const cmdfis = @as(*volatile types.FISRegH2D, @ptrCast(&cmdtbl.cfis));
+ fis.setup_read(cmdfis, lba);
+
+ var spin: u32 = 0;
+ while ((port.tfd & (ata_const.STATUS_BSY | ata_const.STATUS_DRQ)) != 0 and spin < ahci_limits.TIMEOUT_CMD) : (spin += 1) {}
+ if (spin == ahci_limits.TIMEOUT_CMD) return false;
+
+ const ci_bit = int.u32_of(1) << int.u5_of(slot);
+ port.ci = ci_bit;
+
+ spin = 0;
+ while (spin < ahci_limits.TIMEOUT_WAIT) : (spin += 1) {
+ if ((port.ci & ci_bit) == 0) break;
+ if ((port.is & ahci_const.PxIS_TFES) != 0) return false;
+ }
+
+ if ((port.ci & ci_bit) != 0) return false;
+
+ const dma = @as([*]u8, @ptrFromInt(dma_buffer_virt));
+ for (buffer, 0..) |*byte, i| {
+ byte.* = dma[i];
+ }
+
+ return true;
+}
+
+fn do_write_sector(lba: u64, buffer: *const [SECTOR_SIZE]u8) bool {
+ const hba = hba_mem orelse return false;
+ const port = port_ops.get(hba, port_num);
+
+ port.is = 0xFFFFFFFF;
+
+ const slot = port_ops.find_cmd_slot(port) orelse return false;
+
+ const clb_virt = port.clb + HIGHER_HALF;
+ const cmdheader = @as([*]volatile types.CmdHeader, @ptrFromInt(clb_virt));
+
+ cmdheader[slot].cfl = @sizeOf(types.FISRegH2D) / 4;
+ cmdheader[slot].prdtl = 1;
+ cmdheader[slot].prdbc = 0;
+
+ const cmdtbl_virt = cmdheader[slot].ctba + HIGHER_HALF;
+ const cmdtbl = @as(*volatile types.CmdTable, @ptrFromInt(cmdtbl_virt));
+ @memset(@as([*]u8, @ptrFromInt(cmdtbl_virt))[0..ahci_limits.CMD_TABLE_CLEAR_SIZE], 0);
+
+ const dma = @as([*]u8, @ptrFromInt(dma_buffer_virt));
+ for (buffer, 0..) |byte, i| {
+ dma[i] = byte;
+ }
+
+ cmdtbl.prdt_entry[0].dba = dma_buffer_phys;
+ cmdtbl.prdt_entry[0].dbc = (SECTOR_SIZE - 1) | ahci_const.PRDT_INTERRUPT;
+ cmdtbl.prdt_entry[0]._rsv0 = 0;
+
+ const cmdfis = @as(*volatile types.FISRegH2D, @ptrCast(&cmdtbl.cfis));
+ fis.setup_write(cmdfis, lba);
+
+ var spin: u32 = 0;
+ while ((port.tfd & (ata_const.STATUS_BSY | ata_const.STATUS_DRQ)) != 0 and spin < ahci_limits.TIMEOUT_CMD) : (spin += 1) {}
+ if (spin == ahci_limits.TIMEOUT_CMD) return false;
+
+ const ci_bit = int.u32_of(1) << int.u5_of(slot);
+ port.ci = ci_bit;
+
+ spin = 0;
+ while (spin < ahci_limits.TIMEOUT_WAIT) : (spin += 1) {
+ if ((port.ci & ci_bit) == 0) break;
+ if ((port.is & ahci_const.PxIS_TFES) != 0) return false;
+ }
+
+ return (port.ci & ci_bit) == 0;
+}
+
+pub const BlockDevice = struct {
+ pub fn init() BlockDevice {
+ return BlockDevice{};
+ }
+
+ pub fn read_sector(self: *BlockDevice, lba: u64, buffer: *[SECTOR_SIZE]u8) bool {
+ _ = self;
+ return do_read_sector(lba, buffer);
+ }
+
+ pub fn write_sector(self: *BlockDevice, lba: u64, buffer: *const [SECTOR_SIZE]u8) bool {
+ _ = self;
+ return do_write_sector(lba, buffer);
+ }
+};
diff --git a/mirai.old/drivers/ahci/fis.zig b/mirai.old/drivers/ahci/fis.zig
new file mode 100644
index 0000000..d182493
--- /dev/null
+++ b/mirai.old/drivers/ahci/fis.zig
@@ -0,0 +1,40 @@
+//! AHCI FIS utilities
+
+const ahci_const = @import("../../common/constants/ahci.zig");
+const ata = @import("../../common/constants/ata.zig");
+const int = @import("../../utils/types/int.zig");
+const types = @import("types.zig");
+
+pub fn setup_read(fis: *volatile types.FISRegH2D, lba: u64) void {
+ fis.fis_type = ahci_const.FIS_TYPE_REG_H2D;
+ fis.pmport_c = 0x80;
+ fis.command = ata.CMD_READ_DMA_EX;
+
+ fis.lba0 = int.u8_of(lba);
+ fis.lba1 = int.u8_of(lba >> 8);
+ fis.lba2 = int.u8_of(lba >> 16);
+ fis.device = 1 << 6;
+ fis.lba3 = int.u8_of(lba >> 24);
+ fis.lba4 = int.u8_of(lba >> 32);
+ fis.lba5 = int.u8_of(lba >> 40);
+
+ fis.countl = 1;
+ fis.counth = 0;
+}
+
+pub fn setup_write(fis: *volatile types.FISRegH2D, lba: u64) void {
+ fis.fis_type = ahci_const.FIS_TYPE_REG_H2D;
+ fis.pmport_c = 0x80;
+ fis.command = ata.CMD_WRITE_DMA_EX;
+
+ fis.lba0 = int.u8_of(lba);
+ fis.lba1 = int.u8_of(lba >> 8);
+ fis.lba2 = int.u8_of(lba >> 16);
+ fis.device = 1 << 6;
+ fis.lba3 = int.u8_of(lba >> 24);
+ fis.lba4 = int.u8_of(lba >> 32);
+ fis.lba5 = int.u8_of(lba >> 40);
+
+ fis.countl = 1;
+ fis.counth = 0;
+}
diff --git a/mirai.old/drivers/ahci/port.zig b/mirai.old/drivers/ahci/port.zig
new file mode 100644
index 0000000..c9f79bf
--- /dev/null
+++ b/mirai.old/drivers/ahci/port.zig
@@ -0,0 +1,64 @@
+//! AHCI port operations
+
+const ahci_const = @import("../../common/constants/ahci.zig");
+const ahci_limits = @import("../../common/limits/ahci.zig");
+const ata_const = @import("../../common/constants/ata.zig");
+const int = @import("../../utils/types/int.zig");
+const types = @import("types.zig");
+
+pub fn get(hba: *volatile types.HBAMemory, index: u8) *volatile types.HBAPort {
+ const base = @intFromPtr(hba);
+ const offset = ahci_const.PORT_OFFSET_BASE + (int.usize_of(index) * @sizeOf(types.HBAPort));
+ return @ptrFromInt(base + offset);
+}
+
+pub fn stop_cmd(port: *volatile types.HBAPort) void {
+ port.cmd = port.cmd & ~ahci_const.PORT_CMD_ST;
+ port.cmd = port.cmd & ~ahci_const.PORT_CMD_FRE;
+
+ var spin: u32 = 0;
+ while (spin < ahci_limits.TIMEOUT_SPIN) : (spin += 1) {
+ if ((port.cmd & ahci_const.PORT_CMD_FR) != 0) continue;
+ if ((port.cmd & ahci_const.PORT_CMD_CR) != 0) continue;
+ break;
+ }
+}
+
+pub fn start_cmd(port: *volatile types.HBAPort) void {
+ var spin: u32 = 0;
+ while ((port.cmd & ahci_const.PORT_CMD_CR) != 0 and spin < ahci_limits.TIMEOUT_SPIN) : (spin += 1) {}
+
+ port.cmd = port.cmd | ahci_const.PORT_CMD_FRE;
+ port.cmd = port.cmd | ahci_const.PORT_CMD_ST;
+}
+
+pub fn find_cmd_slot(port: *volatile types.HBAPort) ?u8 {
+ const slots = port.sact | port.ci;
+
+ var i: u8 = 0;
+ while (i < ahci_limits.MAX_CMD_SLOTS) : (i += 1) {
+ if ((slots & (int.u32_of(1) << int.u5_of(i))) == 0) {
+ return i;
+ }
+ }
+
+ return null;
+}
+
+pub fn check_type(port: *volatile types.HBAPort) u32 {
+ const ssts = port.ssts;
+ const ipm = int.u8_of((ssts >> 8) & 0x0F);
+ const det = int.u8_of(ssts & 0x0F);
+
+ if (det != ahci_const.SSTS_DET_PRESENT or ipm != ahci_const.SSTS_IPM_ACTIVE) {
+ return ahci_const.PORT_TYPE_NONE;
+ }
+
+ return switch (port.sig) {
+ ata_const.SIG_ATA => ahci_const.PORT_TYPE_SATA,
+ ata_const.SIG_ATAPI => ahci_const.PORT_TYPE_ATAPI,
+ ata_const.SIG_SEMB => ahci_const.PORT_TYPE_SEMB,
+ ata_const.SIG_PM => ahci_const.PORT_TYPE_PM,
+ else => ahci_const.PORT_TYPE_NONE,
+ };
+}
diff --git a/mirai.old/drivers/ahci/types.zig b/mirai.old/drivers/ahci/types.zig
new file mode 100644
index 0000000..0d18259
--- /dev/null
+++ b/mirai.old/drivers/ahci/types.zig
@@ -0,0 +1,78 @@
+//! AHCI type definitions
+
+pub const HBAPort = extern struct {
+ clb: u64,
+ fb: u64,
+ is: u32,
+ ie: u32,
+ cmd: u32,
+ _rsv0: u32,
+ tfd: u32,
+ sig: u32,
+ ssts: u32,
+ sctl: u32,
+ serr: u32,
+ sact: u32,
+ ci: u32,
+ sntf: u32,
+ fbs: u32,
+ _rsv1: [11]u32,
+ vendor: [4]u32,
+};
+
+pub const HBAMemory = extern struct {
+ cap: u32,
+ ghc: u32,
+ is: u32,
+ pi: u32,
+ vs: u32,
+ ccc_ctl: u32,
+ ccc_pts: u32,
+ em_loc: u32,
+ em_ctl: u32,
+ cap2: u32,
+ bohc: u32,
+ _rsv: [13]u32,
+ vendor: [24]u32,
+};
+
+pub const CmdHeader = extern struct {
+ cfl: u8,
+ prdtl: u16,
+ prdbc: u32,
+ ctba: u64,
+ _rsv: [4]u32,
+};
+
+pub const PRDTEntry = extern struct {
+ dba: u64,
+ _rsv0: u32,
+ dbc: u32,
+};
+
+pub const CmdTable = extern struct {
+ cfis: [64]u8,
+ acmd: [16]u8,
+ _rsv: [48]u8,
+ prdt_entry: [1]PRDTEntry,
+};
+
+pub const FISRegH2D = extern struct {
+ fis_type: u8,
+ pmport_c: u8,
+ command: u8,
+ featurel: u8,
+ lba0: u8,
+ lba1: u8,
+ lba2: u8,
+ device: u8,
+ lba3: u8,
+ lba4: u8,
+ lba5: u8,
+ featureh: u8,
+ countl: u8,
+ counth: u8,
+ icc: u8,
+ control: u8,
+ _rsv: [4]u8,
+};
diff --git a/mirai.old/drivers/ata/ata.zig b/mirai.old/drivers/ata/ata.zig
new file mode 100644
index 0000000..bf5a912
--- /dev/null
+++ b/mirai.old/drivers/ata/ata.zig
@@ -0,0 +1,63 @@
+//! ATA PIO block device driver
+
+const ata_const = @import("../../common/constants/ata.zig");
+const ata_limits = @import("../../common/limits/ata.zig");
+const int = @import("../../utils/types/int.zig");
+const io = @import("../../asm/io.zig");
+const serial = @import("../serial/serial.zig");
+
+pub const SECTOR_SIZE = ata_limits.SECTOR_SIZE;
+
+fn wait_ready() void {
+ while ((io.in_byte(ata_const.PRIMARY_STATUS) & ata_const.STATUS_BSY) != 0) {}
+}
+
+fn wait_data_ready() bool {
+ var timeout: u32 = 0;
+ while (timeout < ata_limits.TIMEOUT_DATA_READY) : (timeout += 1) {
+ const status = io.in_byte(ata_const.PRIMARY_STATUS);
+ if ((status & ata_const.STATUS_ERR) != 0) return false;
+ if ((status & ata_const.STATUS_DRQ) != 0) return true;
+ }
+ return false;
+}
+
+pub const BlockDevice = struct {
+ disk_id: u8,
+
+ pub fn init() BlockDevice {
+ serial.printf("Initializing ATA PIO block device...\n", .{});
+ return BlockDevice{ .disk_id = 0 };
+ }
+
+ pub fn read_sector(self: *BlockDevice, lba: u64, buffer: []u8) bool {
+ if (buffer.len < SECTOR_SIZE) {
+ serial.printf("ERROR: Buffer too small for sector\n", .{});
+ return false;
+ }
+
+ wait_ready();
+
+ const drive_bits: u8 = 0xE0 | (self.disk_id << 4) | int.u8_of((lba >> 24) & 0x0F);
+ io.out_byte(ata_const.PRIMARY_DRIVE_SELECT, drive_bits);
+ io.out_byte(ata_const.PRIMARY_SECTOR_COUNT, 1);
+ io.out_byte(ata_const.PRIMARY_LBA_LOW, int.u8_of(lba));
+ io.out_byte(ata_const.PRIMARY_LBA_MID, int.u8_of(lba >> 8));
+ io.out_byte(ata_const.PRIMARY_LBA_HIGH, int.u8_of(lba >> 16));
+ io.out_byte(ata_const.PRIMARY_COMMAND, ata_const.CMD_READ_SECTORS);
+
+ if (!wait_data_ready()) {
+ serial.printf("ERROR: ATA read timeout\n", .{});
+ return false;
+ }
+
+ var i: usize = 0;
+ while (i < SECTOR_SIZE) : (i += 2) {
+ const word = io.in_word(ata_const.PRIMARY_DATA);
+ buffer[i] = int.u8_of(word);
+ buffer[i + 1] = int.u8_of(word >> 8);
+ }
+
+ return true;
+ }
+};
diff --git a/mirai.old/drivers/keyboard/keyboard.zig b/mirai.old/drivers/keyboard/keyboard.zig
new file mode 100644
index 0000000..760e172
--- /dev/null
+++ b/mirai.old/drivers/keyboard/keyboard.zig
@@ -0,0 +1,93 @@
+//! PS/2 Keyboard Driver
+
+const ahci_const = @import("../../common/constants/ahci.zig");
+const io = @import("../../asm/io.zig");
+const kb_const = @import("../../common/constants/keyboard.zig");
+const kb_limits = @import("../../common/limits/keyboard.zig");
+const ports = @import("../../common/constants/ports.zig");
+const scancode = @import("scancode.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const serial = @import("../serial/serial.zig");
+
+var shift_pressed = false;
+var ctrl_pressed = false;
+var alt_pressed = false;
+var caps_lock = false;
+
+var key_buffer: [kb_limits.BUFFER_SIZE]u8 = undefined;
+var read_pos: usize = 0;
+var write_pos: usize = 0;
+
+pub fn init() void {
+ serial.printf("Initializing PS/2 keyboard...\n", .{});
+
+ while ((io.in_byte(ports.KEYBOARD_STATUS) & 0x01) != 0) {
+ _ = io.in_byte(ports.KEYBOARD_DATA);
+ }
+
+ serial.printf("Keyboard ready\n", .{});
+}
+
+export fn keyboard_handler() void {
+ const code = io.in_byte(ports.KEYBOARD_DATA);
+ handle_scancode(code);
+ io.out_byte(ports.PIC1_CMD, ahci_const.PIC_EOI);
+}
+
+fn handle_scancode(code: u8) void {
+ const released = (code & kb_const.SCANCODE_RELEASED) != 0;
+ const key = code & ~kb_const.SCANCODE_RELEASED;
+
+ switch (key) {
+ kb_const.SCANCODE_LSHIFT, kb_const.SCANCODE_RSHIFT => {
+ shift_pressed = !released;
+ return;
+ },
+ kb_const.SCANCODE_LCTRL => {
+ ctrl_pressed = !released;
+ return;
+ },
+ kb_const.SCANCODE_LALT => {
+ alt_pressed = !released;
+ return;
+ },
+ kb_const.SCANCODE_CAPS => {
+ if (!released) caps_lock = !caps_lock;
+ return;
+ },
+ else => {},
+ }
+
+ if (released) return;
+
+ var ascii: u8 = if (shift_pressed) scancode.shifted[key] else scancode.normal[key];
+
+ if (caps_lock and ascii >= 'a' and ascii <= 'z') {
+ ascii -= kb_const.LOWERCASE_TO_UPPERCASE;
+ }
+
+ if (ascii != 0) {
+ enqueue(ascii);
+ }
+}
+
+fn enqueue(ascii: u8) void {
+ const next = (write_pos + 1) % kb_limits.BUFFER_SIZE;
+ if (next != read_pos) {
+ key_buffer[write_pos] = ascii;
+ write_pos = next;
+ sensei.wake_one_blocked_kata();
+ }
+}
+
+pub fn has_input() bool {
+ return read_pos != write_pos;
+}
+
+pub fn read_char() ?u8 {
+ if (read_pos == write_pos) return null;
+
+ const char = key_buffer[read_pos];
+ read_pos = (read_pos + 1) % kb_limits.BUFFER_SIZE;
+ return char;
+}
diff --git a/mirai.old/drivers/keyboard/scancode.zig b/mirai.old/drivers/keyboard/scancode.zig
new file mode 100644
index 0000000..a76aafa
--- /dev/null
+++ b/mirai.old/drivers/keyboard/scancode.zig
@@ -0,0 +1,39 @@
+//! Keyboard scancode tables
+
+pub const normal: [128]u8 = .{
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', '\x08', '\t',
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '[', ']', '\n', 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+ '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '/', 0, '*',
+ 0, ' ', 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, '7',
+ '8', '9', '-', '4', '5', '6', '+', '1',
+ '2', '3', '0', '.', 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+pub const shifted: [128]u8 = .{
+ 0, 27, '!', '@', '#', '$', '%', '^',
+ '&', '*', '(', ')', '_', '+', '\x08', '\t',
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', '{', '}', '\n', 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+ '"', '~', 0, '|', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', '<', '>', '?', 0, '*',
+ 0, ' ', 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, '7',
+ '8', '9', '-', '4', '5', '6', '+', '1',
+ '2', '3', '0', '.', 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
diff --git a/mirai.old/drivers/pci/pci.zig b/mirai.old/drivers/pci/pci.zig
new file mode 100644
index 0000000..2c3369b
--- /dev/null
+++ b/mirai.old/drivers/pci/pci.zig
@@ -0,0 +1,120 @@
+//! PCI bus enumeration
+
+const int = @import("../../utils/types/int.zig");
+const io = @import("../../asm/io.zig");
+const pci_const = @import("../../common/constants/pci.zig");
+const pci_limits = @import("../../common/limits/pci.zig");
+const ports = @import("../../common/constants/ports.zig");
+const types = @import("types.zig");
+
+pub const Device = types.Device;
+
+var devices: [pci_limits.MAX_DEVICES]Device = undefined;
+var device_count: usize = 0;
+
+fn config_address(bus: u8, device: u8, function: u8, offset: u8) u32 {
+ return pci_const.CONFIG_ENABLE |
+ (int.u32_of(bus) << pci_const.CONFIG_BUS_SHIFT) |
+ (int.u32_of(device) << pci_const.CONFIG_DEVICE_SHIFT) |
+ (int.u32_of(function) << pci_const.CONFIG_FUNCTION_SHIFT) |
+ (int.u32_of(offset) & pci_const.CONFIG_OFFSET_MASK);
+}
+
+fn read_u32(bus: u8, device: u8, function: u8, offset: u8) u32 {
+ io.out_long(ports.PCI_CONFIG_ADDRESS, config_address(bus, device, function, offset));
+ return io.in_long(ports.PCI_CONFIG_DATA);
+}
+
+fn read_u16(bus: u8, device: u8, function: u8, offset: u8) u16 {
+ const value = read_u32(bus, device, function, offset);
+ const shift = int.u5_of((offset & 2) * 8);
+ return int.u16_of(value >> shift);
+}
+
+fn read_u8(bus: u8, device: u8, function: u8, offset: u8) u8 {
+ const value = read_u32(bus, device, function, offset);
+ const shift = int.u5_of((offset & 3) * 8);
+ return int.u8_of(value >> shift);
+}
+
+fn write_u32(bus: u8, device: u8, function: u8, offset: u8, value: u32) void {
+ io.out_long(ports.PCI_CONFIG_ADDRESS, config_address(bus, device, function, offset));
+ io.out_long(ports.PCI_CONFIG_DATA, value);
+}
+
+fn write_u16(bus: u8, device: u8, function: u8, offset: u8, value: u16) void {
+ const old = read_u32(bus, device, function, offset);
+ const shift = int.u5_of((offset & 2) * 8);
+ const mask = int.u32_of(0xFFFF) << shift;
+ const new = (old & ~mask) | (int.u32_of(value) << shift);
+ write_u32(bus, device, function, offset, new);
+}
+
+pub fn scan_bus() void {
+ var bus: u16 = 0;
+ while (bus < pci_limits.MAX_BUS) : (bus += 1) {
+ var device: u8 = 0;
+ while (device < pci_limits.MAX_DEVICE) : (device += 1) {
+ var function: u8 = 0;
+ while (function < pci_limits.MAX_FUNCTION) : (function += 1) {
+ const vendor = read_u16(int.u8_of(bus), device, function, pci_const.REG_VENDOR_ID);
+
+ if (vendor == pci_const.VENDOR_INVALID) {
+ if (function == 0) break;
+ continue;
+ }
+
+ if (device_count >= pci_limits.MAX_DEVICES) return;
+
+ var dev = &devices[device_count];
+ dev.bus = int.u8_of(bus);
+ dev.device = device;
+ dev.function = function;
+ dev.vendor_id = vendor;
+ dev.device_id = read_u16(int.u8_of(bus), device, function, pci_const.REG_DEVICE_ID);
+ dev.revision = read_u8(int.u8_of(bus), device, function, pci_const.REG_REVISION);
+ dev.prog_if = read_u8(int.u8_of(bus), device, function, pci_const.REG_PROG_IF);
+ dev.subclass = read_u8(int.u8_of(bus), device, function, pci_const.REG_SUBCLASS);
+ dev.class_code = read_u8(int.u8_of(bus), device, function, pci_const.REG_CLASS_CODE);
+ dev.bar0 = read_u32(int.u8_of(bus), device, function, pci_const.REG_BAR0);
+ dev.bar1 = read_u32(int.u8_of(bus), device, function, pci_const.REG_BAR1);
+ dev.bar2 = read_u32(int.u8_of(bus), device, function, pci_const.REG_BAR2);
+ dev.bar3 = read_u32(int.u8_of(bus), device, function, pci_const.REG_BAR3);
+ dev.bar4 = read_u32(int.u8_of(bus), device, function, pci_const.REG_BAR4);
+ dev.bar5 = read_u32(int.u8_of(bus), device, function, pci_const.REG_BAR5);
+ dev.interrupt_line = read_u8(int.u8_of(bus), device, function, pci_const.REG_INTERRUPT_LINE);
+ dev.interrupt_pin = read_u8(int.u8_of(bus), device, function, pci_const.REG_INTERRUPT_PIN);
+
+ device_count += 1;
+
+ if (function == 0) {
+ const header = read_u8(int.u8_of(bus), device, function, pci_const.REG_HEADER_TYPE);
+ if ((header & pci_const.HEADER_MULTIFUNCTION) == 0) break;
+ }
+ }
+ }
+ }
+}
+
+pub fn find_by_class(class: u8, subclass: u8) ?*Device {
+ for (devices[0..device_count]) |*dev| {
+ if (dev.class_code == class and dev.subclass == subclass) {
+ return dev;
+ }
+ }
+ return null;
+}
+
+pub fn enable_bus_mastering(dev: *Device) void {
+ const cmd = read_u16(dev.bus, dev.device, dev.function, pci_const.REG_COMMAND);
+ write_u16(dev.bus, dev.device, dev.function, pci_const.REG_COMMAND, cmd | pci_const.CMD_BUS_MASTER);
+}
+
+pub fn enable_memory_space(dev: *Device) void {
+ const cmd = read_u16(dev.bus, dev.device, dev.function, pci_const.REG_COMMAND);
+ write_u16(dev.bus, dev.device, dev.function, pci_const.REG_COMMAND, cmd | pci_const.CMD_MEMORY_SPACE);
+}
+
+pub fn get_devices() []Device {
+ return devices[0..device_count];
+}
diff --git a/mirai.old/drivers/pci/types.zig b/mirai.old/drivers/pci/types.zig
new file mode 100644
index 0000000..1884aca
--- /dev/null
+++ b/mirai.old/drivers/pci/types.zig
@@ -0,0 +1,21 @@
+//! PCI type definitions
+
+pub const Device = struct {
+ bus: u8,
+ device: u8,
+ function: u8,
+ vendor_id: u16,
+ device_id: u16,
+ class_code: u8,
+ subclass: u8,
+ prog_if: u8,
+ revision: u8,
+ bar0: u32,
+ bar1: u32,
+ bar2: u32,
+ bar3: u32,
+ bar4: u32,
+ bar5: u32,
+ interrupt_line: u8,
+ interrupt_pin: u8,
+};
diff --git a/mirai.old/drivers/pit/pit.zig b/mirai.old/drivers/pit/pit.zig
new file mode 100644
index 0000000..93609b4
--- /dev/null
+++ b/mirai.old/drivers/pit/pit.zig
@@ -0,0 +1,12 @@
+//! PIT (Programmable Interval Timer) Driver
+
+const io = @import("../../asm/io.zig");
+const pit = @import("../../common/constants/pit.zig");
+
+pub fn init() void {
+ // 100 Hz for stable operation
+ const command = pit.SELECT_CHANNEL_0 | pit.ACCESS_LOHI | pit.MODE_RATE_GENERATOR;
+ io.out_byte(pit.COMMAND, command);
+ io.out_byte(pit.CHANNEL_0, @truncate(pit.DIVISOR_100HZ));
+ io.out_byte(pit.CHANNEL_0, @truncate(pit.DIVISOR_100HZ >> 8));
+}
diff --git a/mirai.old/drivers/serial/serial.zig b/mirai.old/drivers/serial/serial.zig
new file mode 100644
index 0000000..b793c1a
--- /dev/null
+++ b/mirai.old/drivers/serial/serial.zig
@@ -0,0 +1,175 @@
+//! Serial port driver (COM1)
+
+const int = @import("../../utils/types/int.zig");
+const io = @import("../../asm/io.zig");
+const ports = @import("../../common/constants/ports.zig");
+const serial_const = @import("../../common/constants/serial.zig");
+
+pub fn init() void {
+ io.out_byte(ports.COM1_INT_ENABLE, 0x00);
+ io.out_byte(ports.COM1_LINE_CTRL, 0x80);
+ io.out_byte(ports.COM1_DATA, 0x03);
+ io.out_byte(ports.COM1_INT_ENABLE, 0x00);
+ io.out_byte(ports.COM1_LINE_CTRL, 0x03);
+ io.out_byte(ports.COM1_FIFO_CTRL, 0xC7);
+ io.out_byte(ports.COM1_MODEM_CTRL, 0x0B);
+}
+
+fn is_tx_empty() bool {
+ return (io.in_byte(ports.COM1_LINE_STATUS) & serial_const.LINE_STATUS_TX_EMPTY) != 0;
+}
+
+pub fn write(byte: u8) void {
+ while (!is_tx_empty()) {}
+ io.out_byte(ports.COM1_DATA, byte);
+}
+
+pub fn print(str: []const u8) void {
+ for (str) |c| {
+ write(c);
+ }
+}
+
+pub fn print_hex(value: u64) void {
+ const hex = "0123456789ABCDEF";
+ var i: u6 = 60;
+ while (true) : (i -%= 4) {
+ write(hex[int.u8_of((value >> i) & 0xF)]);
+ if (i == 0) break;
+ }
+}
+
+pub fn print_hex_u32(value: u32) void {
+ const hex = "0123456789ABCDEF";
+ var i: u5 = 28;
+ while (true) : (i -%= 4) {
+ write(hex[int.u8_of((value >> i) & 0xF)]);
+ if (i == 0) break;
+ }
+}
+
+pub fn print_hex_u8(value: u8) void {
+ const hex = "0123456789ABCDEF";
+ write(hex[value >> 4]);
+ write(hex[value & 0xF]);
+}
+
+pub fn printf(comptime fmt: []const u8, args: anytype) void {
+ comptime var i: usize = 0;
+ comptime var arg_index: usize = 0;
+
+ inline while (i < fmt.len) {
+ if (fmt[i] == '{' and i + 1 < fmt.len and fmt[i + 1] == '}') {
+ const arg = args[arg_index];
+ print_arg(arg);
+ arg_index += 1;
+ i += 2;
+ } else if (fmt[i] == '{' and i + 2 < fmt.len and fmt[i + 2] == '}') {
+ const spec = fmt[i + 1];
+ const arg = args[arg_index];
+ if (spec == 'x') {
+ print_arg_hex(arg);
+ } else if (spec == 'd') {
+ print_arg_decimal(arg);
+ } else if (spec == 's') {
+ print_arg_string(arg);
+ } else {
+ write('{');
+ write(spec);
+ write('}');
+ }
+ arg_index += 1;
+ i += 3;
+ } else {
+ write(fmt[i]);
+ i += 1;
+ }
+ }
+}
+
+fn print_arg(arg: anytype) void {
+ const T = @TypeOf(arg);
+ if (T == []const u8) {
+ print(arg);
+ } else if (T == u8) {
+ write(arg);
+ } else if (@typeInfo(T) == .int or @typeInfo(T) == .comptime_int) {
+ print_decimal(arg);
+ } else {
+ print("?");
+ }
+}
+
+fn print_arg_decimal(arg: anytype) void {
+ const T = @TypeOf(arg);
+ if (@typeInfo(T) == .int or @typeInfo(T) == .comptime_int) {
+ print_decimal(arg);
+ } else {
+ print("?");
+ }
+}
+
+fn print_arg_string(arg: anytype) void {
+ const T = @TypeOf(arg);
+ const info = @typeInfo(T);
+ if (T == []const u8) {
+ print(arg);
+ } else if (info == .pointer) {
+ const child = info.pointer.child;
+ const child_info = @typeInfo(child);
+ if (child == u8) {
+ // [*]const u8 - null-terminated string pointer
+ var i: usize = 0;
+ while (arg[i] != 0) : (i += 1) {
+ write(arg[i]);
+ }
+ } else if (child_info == .array and child_info.array.child == u8) {
+ // *const [N]u8 or *const [N:0]u8 - pointer to string literal
+ const slice = arg.*;
+ for (slice) |c| {
+ if (c == 0) break;
+ write(c);
+ }
+ } else {
+ print("?ptr");
+ }
+ } else {
+ print("?type");
+ }
+}
+
+fn print_arg_hex(arg: anytype) void {
+ const T = @TypeOf(arg);
+ if (T == u8) {
+ print_hex_u8(arg);
+ } else if (T == u32) {
+ print_hex_u32(arg);
+ } else if (@typeInfo(T) == .int or @typeInfo(T) == .comptime_int) {
+ print_hex(@as(u64, @intCast(arg)));
+ } else {
+ print("?");
+ }
+}
+
+fn print_decimal(value: anytype) void {
+ if (value == 0) {
+ write('0');
+ return;
+ }
+
+ var v: u64 = if (value < 0) @intCast(-value) else @intCast(value);
+ if (value < 0) write('-');
+
+ var buf: [20]u8 = undefined;
+ var len: usize = 0;
+
+ while (v > 0) : (v /= 10) {
+ buf[len] = int.u8_of(v % 10) + '0';
+ len += 1;
+ }
+
+ while (len > 0) {
+ len -= 1;
+ write(buf[len]);
+ }
+}
diff --git a/mirai.old/drivers/vga/vga.zig b/mirai.old/drivers/vga/vga.zig
new file mode 100644
index 0000000..0173e59
--- /dev/null
+++ b/mirai.old/drivers/vga/vga.zig
@@ -0,0 +1,42 @@
+//! VGA text mode driver
+
+const ports = @import("../../common/constants/ports.zig");
+const vga_const = @import("../../common/constants/vga.zig");
+const vga_limits = @import("../../common/limits/vga.zig");
+
+const buffer: *volatile [vga_limits.HEIGHT * vga_limits.WIDTH]u16 = @ptrFromInt(ports.VGA_BUFFER_ADDR);
+
+var row: usize = 0;
+var col: usize = 0;
+
+pub fn clear() void {
+ for (buffer) |*cell| {
+ cell.* = vga_const.DEFAULT_ATTR;
+ }
+ row = 0;
+ col = 0;
+}
+
+pub fn put_char(c: u8) void {
+ if (c == '\n') {
+ row += 1;
+ col = 0;
+ return;
+ }
+
+ if (row >= vga_limits.HEIGHT) row = 0;
+ if (col >= vga_limits.WIDTH) {
+ col = 0;
+ row += 1;
+ }
+
+ const index = row * vga_limits.WIDTH + col;
+ buffer[index] = vga_const.DEFAULT_ATTR | c;
+ col += 1;
+}
+
+pub fn print(str: []const u8) void {
+ for (str) |c| {
+ put_char(c);
+ }
+}
diff --git a/mirai.old/fs/afs/afs.zig b/mirai.old/fs/afs/afs.zig
new file mode 100644
index 0000000..a7217a2
--- /dev/null
+++ b/mirai.old/fs/afs/afs.zig
@@ -0,0 +1,125 @@
+//! Akiba File System Driver
+
+const cache = @import("cache.zig");
+const cluster_ops = @import("cluster.zig");
+const compare = @import("../../utils/string/compare.zig");
+const fs = @import("../../common/constants/fs.zig");
+const info = @import("info.zig");
+const ptr = @import("../../utils/types/ptr.zig");
+const read = @import("read.zig");
+const types = @import("types.zig");
+const write = @import("write.zig");
+
+pub const BootSector = types.BootSector;
+pub const Entry = types.Entry;
+pub const StackItem = types.StackItem;
+pub const DiskInfo = types.DiskInfo;
+
+pub fn AFS(comptime BlockDeviceType: type) type {
+ return struct {
+ device: *BlockDeviceType,
+ partition_offset: u64,
+ bytes_per_sector: u32,
+ sectors_per_cluster: u32,
+ total_clusters: u32,
+ root_cluster: u32,
+ alloc_table_sector: u32,
+ alloc_table_size: u32,
+ data_area_sector: u32,
+ used_clusters: u32,
+ parent_cache: [fs.PARENT_CACHE_SIZE]types.ParentCacheEntry,
+ parent_cache_count: usize,
+
+ const Self = @This();
+
+ pub fn init(device: *BlockDeviceType, partition_offset: u64) !Self {
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!device.read_sector(partition_offset, &sector)) {
+ return error.ReadFailed;
+ }
+
+ const boot = ptr.of(BootSector, @intFromPtr(&sector));
+
+ if (!compare.equals_bytes(boot.signature[0..8], fs.AFS_SIGNATURE)) {
+ return error.InvalidFilesystem;
+ }
+
+ if (boot.boot_signature != fs.AFS_BOOT_SIG) {
+ return error.InvalidFilesystem;
+ }
+
+ return Self{
+ .device = device,
+ .partition_offset = partition_offset,
+ .bytes_per_sector = boot.bytes_per_sector,
+ .sectors_per_cluster = boot.sectors_per_cluster,
+ .total_clusters = boot.total_clusters,
+ .root_cluster = boot.root_cluster,
+ .alloc_table_sector = boot.alloc_table_sector,
+ .alloc_table_size = boot.alloc_table_size,
+ .data_area_sector = boot.data_area_sector,
+ .used_clusters = boot.used_clusters,
+ .parent_cache = undefined,
+ .parent_cache_count = 0,
+ };
+ }
+
+ // Cluster operations
+
+ pub fn cluster_to_lba(self: *Self, cluster: u32) u64 {
+ return cluster_ops.to_lba(self, cluster);
+ }
+
+ pub fn increment_used(self: *Self) void {
+ self.used_clusters += 1;
+ }
+
+ pub fn decrement_used(self: *Self) void {
+ if (self.used_clusters > 0) {
+ self.used_clusters -= 1;
+ }
+ }
+
+ // Cache operations
+
+ pub fn get_parent_cluster(self: *Self, cluster: u32) ?u32 {
+ return cache.lookup(self, cluster);
+ }
+
+ // Read operations
+
+ pub fn find_entry(self: *Self, stack_cluster: u32, identity: []const u8) ?Entry {
+ return read.find_entry(self, stack_cluster, identity);
+ }
+
+ pub fn view_unit(self: *Self, entry: Entry, buffer: []u8) !usize {
+ return read.view_unit(self, entry, buffer);
+ }
+
+ pub fn list_stack(self: *Self, stack_cluster: u32, items: []StackItem) !usize {
+ return read.list_stack(self, stack_cluster, items);
+ }
+
+ pub fn get_unit_size(self: *Self, location: []const u8) !u64 {
+ return read.get_unit_size(self, location);
+ }
+
+ pub fn view_unit_at(self: *Self, location: []const u8, buffer: []u8) !usize {
+ return read.view_unit_at(self, location, buffer);
+ }
+
+ // Write operations
+
+ pub fn create_unit(self: *Self, location: []const u8) !void {
+ return write.create_unit(self, location);
+ }
+
+ pub fn mark_unit(self: *Self, location: []const u8, data: []const u8) !void {
+ return write.mark_unit(self, location, data);
+ }
+
+ pub fn get_disk_info(self: *Self) DiskInfo {
+ return info.get_disk_info(self);
+ }
+ };
+}
diff --git a/mirai.old/fs/afs/cache.zig b/mirai.old/fs/afs/cache.zig
new file mode 100644
index 0000000..f9800fb
--- /dev/null
+++ b/mirai.old/fs/afs/cache.zig
@@ -0,0 +1,32 @@
+//! AFS parent cache operations
+
+const types = @import("types.zig");
+
+pub fn store(afs: anytype, child: u32, parent: u32) void {
+ for (afs.parent_cache[0..afs.parent_cache_count]) |*e| {
+ if (e.cluster == child) {
+ e.parent = parent;
+ return;
+ }
+ }
+
+ if (afs.parent_cache_count < afs.parent_cache.len) {
+ afs.parent_cache[afs.parent_cache_count] = types.ParentCacheEntry{
+ .cluster = child,
+ .parent = parent,
+ };
+ afs.parent_cache_count += 1;
+ }
+}
+
+pub fn lookup(afs: anytype, cluster: u32) ?u32 {
+ if (cluster == afs.root_cluster) return afs.root_cluster;
+
+ for (afs.parent_cache[0..afs.parent_cache_count]) |e| {
+ if (e.cluster == cluster) {
+ return e.parent;
+ }
+ }
+
+ return null;
+}
diff --git a/mirai.old/fs/afs/cluster.zig b/mirai.old/fs/afs/cluster.zig
new file mode 100644
index 0000000..50f3ca2
--- /dev/null
+++ b/mirai.old/fs/afs/cluster.zig
@@ -0,0 +1,70 @@
+//! AFS cluster operations
+
+const endian = @import("../../utils/bytes/endian.zig");
+const fs = @import("../../common/constants/fs.zig");
+
+pub fn is_valid(cluster: u32) bool {
+ return cluster >= fs.CLUSTER_MIN and cluster < fs.CLUSTER_END;
+}
+
+pub fn to_lba(afs: anytype, cluster: u32) u64 {
+ return afs.partition_offset + afs.data_area_sector + (cluster - fs.CLUSTER_MIN);
+}
+
+pub fn get_next(afs: anytype, cluster: u32) !u32 {
+ const entry_offset = cluster * 4;
+ const table_sector = afs.partition_offset + afs.alloc_table_sector + (entry_offset / afs.bytes_per_sector);
+ const offset = entry_offset % afs.bytes_per_sector;
+
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(table_sector, &sector)) {
+ return error.ReadFailed;
+ }
+
+ return endian.read_u32_le(sector[offset..]);
+}
+
+pub fn write_alloc(afs: anytype, cluster: u32, value: u32) !void {
+ const entry_offset = cluster * 4;
+ const table_sector = afs.partition_offset + afs.alloc_table_sector + (entry_offset / afs.bytes_per_sector);
+ const offset = entry_offset % afs.bytes_per_sector;
+
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(table_sector, &sector)) {
+ return error.ReadFailed;
+ }
+
+ endian.write_u32_le(sector[offset..], value);
+
+ if (!afs.device.write_sector(table_sector, &sector)) {
+ return error.WriteFailed;
+ }
+}
+
+pub fn allocate(afs: anytype) !u32 {
+ var cluster: u32 = fs.CLUSTER_MIN;
+
+ while (cluster < afs.total_clusters) : (cluster += 1) {
+ const entry_offset = cluster * 4;
+ const table_sector = afs.partition_offset + afs.alloc_table_sector + (entry_offset / afs.bytes_per_sector);
+ const offset = entry_offset % afs.bytes_per_sector;
+
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(table_sector, &sector)) {
+ return error.ReadFailed;
+ }
+
+ if (endian.read_u32_le(sector[offset..]) == fs.CLUSTER_FREE) {
+ try write_alloc(afs, cluster, fs.CLUSTER_END);
+ afs.increment_used();
+ return cluster;
+ }
+ }
+
+ return error.DiskFull;
+}
+
+pub fn free(afs: anytype, cluster: u32) !void {
+ try write_alloc(afs, cluster, fs.CLUSTER_FREE);
+ afs.decrement_used();
+}
diff --git a/mirai.old/fs/afs/info.zig b/mirai.old/fs/afs/info.zig
new file mode 100644
index 0000000..8d9889e
--- /dev/null
+++ b/mirai.old/fs/afs/info.zig
@@ -0,0 +1,15 @@
+//! AFS disk info operations
+
+const fs = @import("../../common/constants/fs.zig");
+const types = @import("types.zig");
+
+pub fn get_disk_info(afs: anytype) types.DiskInfo {
+ const cluster_size: u64 = @as(u64, afs.sectors_per_cluster) * fs.SECTOR_SIZE;
+ const total = @as(u64, afs.total_clusters) * cluster_size;
+ const used = @as(u64, afs.used_clusters) * cluster_size;
+
+ return types.DiskInfo{
+ .total_bytes = total,
+ .used_bytes = used,
+ };
+}
diff --git a/mirai.old/fs/afs/location.zig b/mirai.old/fs/afs/location.zig
new file mode 100644
index 0000000..c5ede65
--- /dev/null
+++ b/mirai.old/fs/afs/location.zig
@@ -0,0 +1,30 @@
+//! AFS location utilities
+
+pub fn parent(location: []const u8) []const u8 {
+ var i: usize = location.len;
+ while (i > 0) : (i -= 1) {
+ if (location[i - 1] == '/') {
+ if (i == 1) return "/";
+ return location[0 .. i - 1];
+ }
+ }
+ return "/";
+}
+
+pub fn identity(location: []const u8) []const u8 {
+ var i: usize = location.len;
+ while (i > 0) : (i -= 1) {
+ if (location[i - 1] == '/') {
+ return location[i..];
+ }
+ }
+ return location;
+}
+
+pub fn is_absolute(location: []const u8) bool {
+ return location.len > 0 and location[0] == '/';
+}
+
+pub fn skip_root(location: []const u8) usize {
+ return if (is_absolute(location)) 1 else 0;
+}
diff --git a/mirai.old/fs/afs/read.zig b/mirai.old/fs/afs/read.zig
new file mode 100644
index 0000000..f14a8d3
--- /dev/null
+++ b/mirai.old/fs/afs/read.zig
@@ -0,0 +1,188 @@
+//! AFS read operations
+
+const cache = @import("cache.zig");
+const cluster = @import("cluster.zig");
+const compare = @import("../../utils/string/compare.zig");
+const copy = @import("../../utils/mem/copy.zig");
+const fs = @import("../../common/constants/fs.zig");
+const int = @import("../../utils/types/int.zig");
+const location = @import("location.zig");
+const ptr = @import("../../utils/types/ptr.zig");
+const types = @import("types.zig");
+
+pub fn find_entry(afs: anytype, stack_cluster: u32, identity: []const u8) ?types.Entry {
+ var current = stack_cluster;
+
+ while (cluster.is_valid(current)) {
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(cluster.to_lba(afs, current), &sector)) {
+ return null;
+ }
+
+ const entry = ptr.of(types.Entry, @intFromPtr(&sector));
+
+ if (entry.is_end()) return null;
+
+ if (entry.is_unit() or entry.is_stack()) {
+ if (compare.equals(entry.get_identity(), identity)) {
+ if (entry.is_stack()) {
+ cache.store(afs, entry.first_cluster, stack_cluster);
+ }
+ return entry.*;
+ }
+ }
+
+ current = cluster.get_next(afs, current) catch return null;
+ }
+
+ return null;
+}
+
+pub fn view_unit(afs: anytype, entry: types.Entry, buffer: []u8) !usize {
+ var current = entry.first_cluster;
+ var viewed: usize = 0;
+
+ while (cluster.is_valid(current)) {
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(cluster.to_lba(afs, current), &sector)) {
+ return error.ReadFailed;
+ }
+
+ const to_copy = @min(fs.SECTOR_SIZE, entry.size - viewed);
+ const end = @min(viewed + to_copy, buffer.len);
+ copy.bytes(buffer[viewed..end], sector[0 .. end - viewed]);
+
+ viewed += to_copy;
+ if (viewed >= entry.size) break;
+
+ current = try cluster.get_next(afs, current);
+ }
+
+ return viewed;
+}
+
+pub fn list_stack(afs: anytype, stack_cluster: u32, items: []types.StackItem) !usize {
+ var current = stack_cluster;
+ var count: usize = 0;
+
+ while (cluster.is_valid(current)) {
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(cluster.to_lba(afs, current), &sector)) {
+ return error.ReadFailed;
+ }
+
+ const entry = ptr.of(types.Entry, @intFromPtr(&sector));
+
+ if (entry.is_end()) return count;
+
+ if (entry.is_unit() or entry.is_stack()) {
+ if (count >= items.len) return count;
+
+ copy.bytes(items[count].identity[0..entry.name_len], entry.get_identity());
+ items[count].identity_len = entry.name_len;
+ items[count].is_stack = entry.is_stack();
+
+ copy.bytes(items[count].owner_name[0..entry.owner_name_len], entry.get_owner());
+ items[count].owner_name_len = entry.owner_name_len;
+ items[count].permission_type = entry.permission_type;
+ items[count].modified_time = entry.modified_time;
+
+ items[count].size = if (entry.is_stack())
+ int.u32_of(calculate_stack_size(afs, entry.first_cluster) catch 0)
+ else
+ int.u32_of(entry.size);
+
+ count += 1;
+ }
+
+ current = cluster.get_next(afs, current) catch return count;
+ }
+
+ return count;
+}
+
+pub fn calculate_stack_size(afs: anytype, stack_cluster: u32) !u64 {
+ var current = stack_cluster;
+ var total: u64 = 0;
+
+ while (cluster.is_valid(current)) {
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(cluster.to_lba(afs, current), &sector)) {
+ return error.ReadFailed;
+ }
+
+ const entry = ptr.of(types.Entry, @intFromPtr(&sector));
+
+ if (entry.is_end()) return total;
+
+ if (entry.is_unit()) {
+ total += entry.size;
+ } else if (entry.is_stack()) {
+ total += calculate_stack_size(afs, entry.first_cluster) catch 0;
+ }
+
+ current = cluster.get_next(afs, current) catch return total;
+ }
+
+ return total;
+}
+
+pub fn get_unit_size(afs: anytype, loc: []const u8) !u64 {
+ if (loc.len == 0) return error.InvalidLocation;
+
+ var current = afs.root_cluster;
+ var start: usize = location.skip_root(loc);
+ var i: usize = start;
+
+ while (i <= loc.len) : (i += 1) {
+ const is_end = (i == loc.len);
+ const is_slash = !is_end and loc[i] == '/';
+
+ if (is_slash or is_end) {
+ if (i > start) {
+ const component = loc[start..i];
+ const entry = find_entry(afs, current, component) orelse return error.NotFound;
+
+ if (is_end) {
+ if (!entry.is_unit()) return error.NotAUnit;
+ return entry.size;
+ } else {
+ if (!entry.is_stack()) return error.NotAStack;
+ current = entry.first_cluster;
+ }
+ }
+ start = i + 1;
+ }
+ }
+
+ return error.InvalidLocation;
+}
+
+pub fn view_unit_at(afs: anytype, loc: []const u8, buffer: []u8) !usize {
+ var current = afs.root_cluster;
+ var start: usize = location.skip_root(loc);
+ var i: usize = start;
+
+ while (i <= loc.len) : (i += 1) {
+ const is_end = (i == loc.len);
+ const is_slash = !is_end and loc[i] == '/';
+
+ if (is_slash or is_end) {
+ if (i > start) {
+ const component = loc[start..i];
+ const entry = find_entry(afs, current, component) orelse return error.NotFound;
+
+ if (is_end) {
+ if (!entry.is_unit()) return error.NotAUnit;
+ return view_unit(afs, entry, buffer);
+ } else {
+ if (!entry.is_stack()) return error.NotAStack;
+ current = entry.first_cluster;
+ }
+ }
+ start = i + 1;
+ }
+ }
+
+ return error.InvalidLocation;
+}
diff --git a/mirai.old/fs/afs/types.zig b/mirai.old/fs/afs/types.zig
new file mode 100644
index 0000000..77ad084
--- /dev/null
+++ b/mirai.old/fs/afs/types.zig
@@ -0,0 +1,81 @@
+//! AFS type definitions
+
+const fs = @import("../../common/constants/fs.zig");
+
+pub const BootSector = extern struct {
+ signature: [8]u8,
+ version: u32,
+ bytes_per_sector: u32,
+ sectors_per_cluster: u32,
+ total_clusters: u32,
+ root_cluster: u32,
+ alloc_table_sector: u32,
+ alloc_table_size: u32,
+ data_area_sector: u32,
+ used_clusters: u32,
+ reserved: [462]u8,
+ boot_signature: u16,
+};
+
+pub const Entry = extern struct {
+ entry_type: u8,
+ name_len: u8,
+ name: [fs.MAX_IDENTITY_LEN]u8,
+ owner_name_len: u8,
+ owner_name: [fs.MAX_OWNER_NAME_LEN]u8,
+ permission_type: u8,
+ reserved: u8,
+ first_cluster: u32,
+ size: u64,
+ created_time: u64,
+ modified_time: u64,
+
+ pub fn get_identity(self: *const Entry) []const u8 {
+ return self.name[0..self.name_len];
+ }
+
+ pub fn get_owner(self: *const Entry) []const u8 {
+ return self.owner_name[0..self.owner_name_len];
+ }
+
+ pub fn is_unit(self: *const Entry) bool {
+ return self.entry_type == fs.ENTRY_TYPE_UNIT;
+ }
+
+ pub fn is_stack(self: *const Entry) bool {
+ return self.entry_type == fs.ENTRY_TYPE_STACK;
+ }
+
+ pub fn is_end(self: *const Entry) bool {
+ return self.entry_type == fs.ENTRY_TYPE_END;
+ }
+};
+
+pub const StackItem = struct {
+ identity: [fs.MAX_LOCATION_LENGTH]u8,
+ identity_len: usize,
+ is_stack: bool,
+ size: u32,
+ modified_time: u64,
+ owner_name: [fs.MAX_OWNER_NAME_LEN]u8,
+ owner_name_len: usize,
+ permission_type: u8,
+
+ pub fn get_identity(self: *const StackItem) []const u8 {
+ return self.identity[0..self.identity_len];
+ }
+
+ pub fn get_owner(self: *const StackItem) []const u8 {
+ return self.owner_name[0..self.owner_name_len];
+ }
+};
+
+pub const ParentCacheEntry = struct {
+ cluster: u32,
+ parent: u32,
+};
+
+pub const DiskInfo = struct {
+ total_bytes: u64,
+ used_bytes: u64,
+};
diff --git a/mirai.old/fs/afs/write.zig b/mirai.old/fs/afs/write.zig
new file mode 100644
index 0000000..6f3a03d
--- /dev/null
+++ b/mirai.old/fs/afs/write.zig
@@ -0,0 +1,197 @@
+//! AFS write operations
+
+const cluster = @import("cluster.zig");
+const compare = @import("../../utils/string/compare.zig");
+const copy = @import("../../utils/mem/copy.zig");
+const fs = @import("../../common/constants/fs.zig");
+const int = @import("../../utils/types/int.zig");
+const location = @import("location.zig");
+const ptr = @import("../../utils/types/ptr.zig");
+const read = @import("read.zig");
+const types = @import("types.zig");
+
+pub fn navigate_to_stack(afs: anytype, loc: []const u8) !u32 {
+ var current = afs.root_cluster;
+ var start: usize = location.skip_root(loc);
+ var i: usize = start;
+
+ while (i <= loc.len) : (i += 1) {
+ const is_end = (i == loc.len);
+ const is_slash = !is_end and loc[i] == '/';
+
+ if (is_slash or is_end) {
+ if (i > start) {
+ const component = loc[start..i];
+ const entry = read.find_entry(afs, current, component) orelse return error.NotFound;
+
+ if (!entry.is_stack()) return error.NotAStack;
+ current = entry.first_cluster;
+ }
+ start = i + 1;
+ }
+ }
+
+ return current;
+}
+
+pub fn create_unit(afs: anytype, loc: []const u8) !void {
+ const parent_loc = location.parent(loc);
+ const ident = location.identity(loc);
+
+ if (ident.len == 0 or ident.len > fs.MAX_IDENTITY_LEN) {
+ return error.InvalidIdentity;
+ }
+
+ var parent_cluster = afs.root_cluster;
+ if (parent_loc.len > 1) {
+ parent_cluster = try navigate_to_stack(afs, parent_loc);
+ }
+
+ if (read.find_entry(afs, parent_cluster, ident)) |_| {
+ return error.UnitExists;
+ }
+
+ const unit_cluster = try cluster.allocate(afs);
+
+ var new_entry = types.Entry{
+ .entry_type = fs.ENTRY_TYPE_UNIT,
+ .name_len = int.u8_of(ident.len),
+ .name = undefined,
+ .owner_name_len = 0,
+ .owner_name = undefined,
+ .permission_type = fs.PERM_OWNER,
+ .reserved = 0,
+ .first_cluster = unit_cluster,
+ .size = 0,
+ .created_time = 0,
+ .modified_time = 0,
+ };
+
+ copy.zero(&new_entry.name);
+ copy.zero(&new_entry.owner_name);
+ copy.bytes(new_entry.name[0..ident.len], ident);
+
+ try add_stack_entry(afs, parent_cluster, new_entry);
+}
+
+pub fn mark_unit(afs: anytype, loc: []const u8, data: []const u8) !void {
+ const parent_loc = location.parent(loc);
+ const ident = location.identity(loc);
+
+ var parent_cluster = afs.root_cluster;
+ if (parent_loc.len > 1) {
+ parent_cluster = try navigate_to_stack(afs, parent_loc);
+ }
+
+ const entry = read.find_entry(afs, parent_cluster, ident) orelse {
+ try create_unit(afs, loc);
+ const new_entry = read.find_entry(afs, parent_cluster, ident) orelse return error.CreateFailed;
+ try mark_unit_data(afs, new_entry.first_cluster, data);
+ try update_unit_size(afs, parent_cluster, ident, data.len);
+ return;
+ };
+
+ try mark_unit_data(afs, entry.first_cluster, data);
+ try update_unit_size(afs, parent_cluster, ident, data.len);
+}
+
+pub fn mark_unit_data(afs: anytype, start_cluster: u32, data: []const u8) !void {
+ var remaining = data.len;
+ var offset: usize = 0;
+ var current = start_cluster;
+
+ while (remaining > 0) {
+ const chunk = @min(remaining, fs.SECTOR_SIZE);
+ const lba = cluster.to_lba(afs, current);
+
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ copy.zero(&sector);
+ copy.bytes(sector[0..chunk], data[offset .. offset + chunk]);
+
+ if (!afs.device.write_sector(lba, &sector)) {
+ return error.WriteFailed;
+ }
+
+ remaining -= chunk;
+ offset += chunk;
+
+ if (remaining > 0) {
+ const next = try cluster.allocate(afs);
+ try cluster.write_alloc(afs, current, next);
+ current = next;
+ } else {
+ try cluster.write_alloc(afs, current, fs.CLUSTER_END);
+ }
+ }
+}
+
+pub fn add_stack_entry(afs: anytype, stack_cluster: u32, entry: types.Entry) !void {
+ var current = stack_cluster;
+
+ while (cluster.is_valid(current)) {
+ const lba = cluster.to_lba(afs, current);
+
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(lba, &sector)) {
+ return error.ReadFailed;
+ }
+
+ const existing = ptr.of(types.Entry, @intFromPtr(&sector));
+
+ if (existing.is_end()) {
+ const entry_bytes = @as([*]const u8, @ptrCast(&entry))[0..@sizeOf(types.Entry)];
+ copy.bytes(sector[0..@sizeOf(types.Entry)], entry_bytes);
+
+ if (!afs.device.write_sector(lba, &sector)) {
+ return error.WriteFailed;
+ }
+ return;
+ }
+
+ current = cluster.get_next(afs, current) catch {
+ const new_cluster = try cluster.allocate(afs);
+ try cluster.write_alloc(afs, stack_cluster, new_cluster);
+
+ copy.zero(&sector);
+ const entry_bytes = @as([*]const u8, @ptrCast(&entry))[0..@sizeOf(types.Entry)];
+ copy.bytes(sector[0..@sizeOf(types.Entry)], entry_bytes);
+
+ if (!afs.device.write_sector(cluster.to_lba(afs, new_cluster), &sector)) {
+ return error.WriteFailed;
+ }
+ return;
+ };
+ }
+
+ return error.StackFull;
+}
+
+pub fn update_unit_size(afs: anytype, stack_cluster: u32, ident: []const u8, new_size: usize) !void {
+ var current = stack_cluster;
+
+ while (cluster.is_valid(current)) {
+ const lba = cluster.to_lba(afs, current);
+
+ var sector: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!afs.device.read_sector(lba, &sector)) {
+ return error.ReadFailed;
+ }
+
+ const entry = ptr.of(types.Entry, @intFromPtr(&sector));
+
+ if (entry.is_end()) return error.NotFound;
+
+ if (compare.equals(entry.get_identity(), ident)) {
+ entry.size = new_size;
+
+ if (!afs.device.write_sector(lba, &sector)) {
+ return error.WriteFailed;
+ }
+ return;
+ }
+
+ current = cluster.get_next(afs, current) catch return error.NotFound;
+ }
+
+ return error.NotFound;
+}
diff --git a/mirai.old/fs/gpt/gpt.zig b/mirai.old/fs/gpt/gpt.zig
new file mode 100644
index 0000000..898f5b2
--- /dev/null
+++ b/mirai.old/fs/gpt/gpt.zig
@@ -0,0 +1,41 @@
+//! GPT Partition Table Parser
+
+const compare = @import("../../utils/string/compare.zig");
+const endian = @import("../../utils/bytes/endian.zig");
+const fs = @import("../../common/constants/fs.zig");
+const types = @import("types.zig");
+
+pub const Partition = types.Partition;
+
+pub fn find_afs_partition(device: anytype) ?Partition {
+ var header: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!device.read_sector(fs.GPT_HEADER_SECTOR, &header)) {
+ return null;
+ }
+
+ if (!compare.equals_bytes(header[0..8], fs.GPT_SIGNATURE)) {
+ return null;
+ }
+
+ const partition_lba = endian.read_u64_le(header[fs.GPT_HEADER_PARTITION_LBA_OFFSET..]);
+
+ var entries: [fs.SECTOR_SIZE]u8 align(fs.SECTOR_ALIGN) = undefined;
+ if (!device.read_sector(partition_lba, &entries)) {
+ return null;
+ }
+
+ var i: usize = 0;
+ while (i < fs.GPT_PARTITION_ENTRIES_MAX) : (i += 1) {
+ const offset = i * fs.GPT_ENTRY_SIZE;
+ const type_guid = entries[offset .. offset + fs.GPT_ENTRY_TYPE_GUID_SIZE];
+
+ if (compare.equals_bytes(type_guid, &fs.AFS_PARTITION_GUID)) {
+ return Partition{
+ .start_lba = endian.read_u64_le(entries[offset + fs.GPT_ENTRY_START_LBA_OFFSET ..]),
+ .end_lba = endian.read_u64_le(entries[offset + fs.GPT_ENTRY_END_LBA_OFFSET ..]),
+ };
+ }
+ }
+
+ return null;
+}
diff --git a/mirai.old/fs/gpt/types.zig b/mirai.old/fs/gpt/types.zig
new file mode 100644
index 0000000..74d8417
--- /dev/null
+++ b/mirai.old/fs/gpt/types.zig
@@ -0,0 +1,6 @@
+//! GPT type definitions
+
+pub const Partition = struct {
+ start_lba: u64,
+ end_lba: u64,
+};
diff --git a/mirai.old/graphics/fonts/psf.zig b/mirai.old/graphics/fonts/psf.zig
new file mode 100644
index 0000000..9e96dd4
--- /dev/null
+++ b/mirai.old/graphics/fonts/psf.zig
@@ -0,0 +1,137 @@
+//! PSF font loading and rendering
+
+const boot = @import("../../boot/multiboot/multiboot.zig");
+const psf = @import("../../common/constants/psf.zig");
+const int = @import("../../utils/types/int.zig");
+const pixel = @import("../../utils/graphics/pixel.zig");
+const ptr = @import("../../utils/types/ptr.zig");
+const terminal_limits = @import("../../common/limits/terminal.zig");
+
+const PSF1Header = packed struct {
+ magic: u16,
+ mode: u8,
+ charsize: u8,
+};
+
+const PSF2Header = packed struct {
+ magic: u32,
+ version: u32,
+ header_size: u32,
+ flags: u32,
+ num_glyphs: u32,
+ bytes_per_glyph: u32,
+ height: u32,
+ width: u32,
+};
+
+const FontInfo = struct {
+ width: u32,
+ height: u32,
+ num_glyphs: u32,
+ bytes_per_glyph: u32,
+ glyph_data_offset: u32,
+};
+
+var font_data: ?[]const u8 = null;
+var font_info: ?FontInfo = null;
+
+pub fn init(data: []const u8) !void {
+ if (data.len < 4) return error.InvalidFont;
+
+ if (try_init_psf1(data)) return;
+ if (try_init_psf2(data)) return;
+
+ return error.InvalidFont;
+}
+
+fn try_init_psf1(data: []const u8) bool {
+ if (data.len < @sizeOf(PSF1Header)) return false;
+
+ const hdr = ptr.of_const(PSF1Header, @intFromPtr(data.ptr));
+ if (hdr.magic != psf.PSF1_MAGIC) return false;
+
+ const num_glyphs = if ((hdr.mode & psf.PSF1_MODE_512) != 0)
+ psf.PSF1_GLYPHS_512
+ else
+ psf.PSF1_GLYPHS_256;
+
+ font_info = FontInfo{
+ .width = terminal_limits.DEFAULT_CHAR_WIDTH,
+ .height = hdr.charsize,
+ .num_glyphs = num_glyphs,
+ .bytes_per_glyph = hdr.charsize,
+ .glyph_data_offset = @sizeOf(PSF1Header),
+ };
+
+ font_data = data;
+ return true;
+}
+
+fn try_init_psf2(data: []const u8) bool {
+ if (data.len < @sizeOf(PSF2Header)) return false;
+
+ const hdr = ptr.of_const(PSF2Header, @intFromPtr(data.ptr));
+ if (hdr.magic != psf.PSF2_MAGIC) return false;
+
+ font_info = FontInfo{
+ .width = hdr.width,
+ .height = hdr.height,
+ .num_glyphs = hdr.num_glyphs,
+ .bytes_per_glyph = hdr.bytes_per_glyph,
+ .glyph_data_offset = hdr.header_size,
+ };
+
+ font_data = data;
+ return true;
+}
+
+fn get_glyph(char: u8) ?[]const u8 {
+ const info = font_info orelse return null;
+ const data = font_data orelse return null;
+
+ if (char >= info.num_glyphs) return null;
+
+ const offset = info.glyph_data_offset + (char * info.bytes_per_glyph);
+ if (offset + info.bytes_per_glyph > data.len) return null;
+
+ return data[offset .. offset + info.bytes_per_glyph];
+}
+
+pub fn render_char(char: u8, x: u32, y: u32, fb: boot.FramebufferInfo, c: u32) void {
+ const glyph = get_glyph(char) orelse return;
+ const info = font_info orelse return;
+
+ var row: u32 = 0;
+ while (row < info.height) : (row += 1) {
+ if (row >= glyph.len) break;
+
+ const byte = glyph[row];
+ var col: u32 = 0;
+ while (col < 8) : (col += 1) {
+ const bit = 7 - int.u3_of(col);
+ if ((byte & (@as(u8, 1) << bit)) != 0) {
+ pixel.put(fb, x + col, y + row, c);
+ }
+ }
+ }
+}
+
+pub fn render_text(text: []const u8, x: u32, y: u32, fb: boot.FramebufferInfo, c: u32) void {
+ const info = font_info orelse return;
+ var current_x = x;
+
+ for (text) |char| {
+ render_char(char, current_x, y, fb, c);
+ current_x += info.width;
+ }
+}
+
+pub fn get_width() u32 {
+ if (font_info) |info| return info.width;
+ return terminal_limits.DEFAULT_CHAR_WIDTH;
+}
+
+pub fn get_height() u32 {
+ if (font_info) |info| return info.height;
+ return terminal_limits.DEFAULT_CHAR_HEIGHT;
+}
diff --git a/mirai.old/graphics/terminal/terminal.zig b/mirai.old/graphics/terminal/terminal.zig
new file mode 100644
index 0000000..f1f2e8f
--- /dev/null
+++ b/mirai.old/graphics/terminal/terminal.zig
@@ -0,0 +1,184 @@
+//! Terminal - Console output with cursor and scrolling
+
+const ascii = @import("../../common/constants/ascii.zig");
+const boot = @import("../../boot/multiboot/multiboot.zig");
+const colors = @import("../../common/constants/colors.zig");
+const font = @import("../fonts/psf.zig");
+const pixel = @import("../../utils/graphics/pixel.zig");
+const terminal_limits = @import("../../common/limits/terminal.zig");
+const video = @import("../video/video.zig");
+
+const LineType = enum { Hard, Soft };
+
+var fb: ?boot.FramebufferInfo = null;
+var cursor_x: u32 = 0;
+var cursor_y: u32 = 0;
+var char_width: u32 = terminal_limits.DEFAULT_CHAR_WIDTH;
+var char_height: u32 = terminal_limits.DEFAULT_CHAR_HEIGHT;
+var max_line_width: u32 = 0;
+var line_types: [terminal_limits.MAX_LINES]LineType = [_]LineType{.Hard} ** terminal_limits.MAX_LINES;
+var current_line: usize = 0;
+
+pub fn init(framebuffer: boot.FramebufferInfo) void {
+ fb = framebuffer;
+ cursor_x = 0;
+ cursor_y = 0;
+ current_line = 0;
+
+ char_width = font.get_width();
+ char_height = font.get_height();
+ max_line_width = pixel.line_width(framebuffer);
+
+ video.init(framebuffer);
+ video.clear(colors.BLACK);
+
+ reset_line_types();
+}
+
+fn reset_line_types() void {
+ for (&line_types) |*lt| {
+ lt.* = .Hard;
+ }
+}
+
+pub fn put_char(char: u8) void {
+ put_char_color(char, colors.WHITE);
+}
+
+pub fn put_char_color(char: u8, color: u32) void {
+ const f = fb orelse return;
+
+ switch (char) {
+ ascii.NEWLINE => handle_newline(f),
+ ascii.BACKSPACE => handle_backspace(f),
+ ascii.TAB => handle_tab(f),
+ else => handle_printable(f, char, color),
+ }
+}
+
+fn handle_newline(f: boot.FramebufferInfo) void {
+ cursor_x = 0;
+ cursor_y += char_height;
+ mark_line(.Hard);
+ check_scroll(f);
+}
+
+fn handle_backspace(f: boot.FramebufferInfo) void {
+ if (cursor_x >= char_width) {
+ cursor_x -= char_width;
+ clear_char_at_cursor(f);
+ } else if (cursor_x == 0 and cursor_y > 0) {
+ const line_num = cursor_y / char_height;
+ if (line_num > 0 and line_num < terminal_limits.MAX_LINES) {
+ if (line_types[line_num] == .Soft) {
+ cursor_y -= char_height;
+ current_line = cursor_y / char_height;
+ cursor_x = ((max_line_width - char_width) / char_width) * char_width;
+ clear_char_at_cursor(f);
+ }
+ }
+ }
+}
+
+fn handle_tab(f: boot.FramebufferInfo) void {
+ cursor_x += char_width * terminal_limits.TAB_WIDTH;
+ if (cursor_x >= max_line_width) {
+ cursor_x = 0;
+ cursor_y += char_height;
+ mark_line(.Soft);
+ check_scroll(f);
+ }
+}
+
+fn handle_printable(f: boot.FramebufferInfo, char: u8, color: u32) void {
+ if (char < ascii.PRINTABLE_START or char > ascii.PRINTABLE_END) return;
+
+ if (cursor_x + char_width > max_line_width) {
+ cursor_x = 0;
+ cursor_y += char_height;
+ mark_line(.Soft);
+ check_scroll(f);
+ }
+
+ font.render_char(char, cursor_x, cursor_y, f, color);
+ cursor_x += char_width;
+}
+
+fn mark_line(line_type: LineType) void {
+ const new_line = cursor_y / char_height;
+ if (new_line != current_line and new_line < terminal_limits.MAX_LINES) {
+ line_types[new_line] = line_type;
+ }
+ current_line = new_line;
+}
+
+fn check_scroll(f: boot.FramebufferInfo) void {
+ if (cursor_y + char_height >= f.height) {
+ scroll(f);
+ }
+}
+
+fn clear_char_at_cursor(f: boot.FramebufferInfo) void {
+ pixel.fill_rect(f, cursor_x, cursor_y, char_width, char_height, colors.BLACK);
+}
+
+fn scroll(f: boot.FramebufferInfo) void {
+ var dst_y: u32 = 0;
+ var src_y: u32 = char_height;
+
+ while (src_y < f.height) : ({
+ src_y += 1;
+ dst_y += 1;
+ }) {
+ pixel.copy_row(f, dst_y, src_y);
+ }
+
+ while (dst_y < f.height) : (dst_y += 1) {
+ pixel.clear_row(f, dst_y, colors.BLACK);
+ }
+
+ cursor_y -= char_height;
+
+ var i: usize = 1;
+ while (i < terminal_limits.MAX_LINES) : (i += 1) {
+ line_types[i - 1] = line_types[i];
+ }
+ line_types[terminal_limits.MAX_LINES - 1] = .Hard;
+
+ if (current_line > 0) current_line -= 1;
+}
+
+pub fn print(text: []const u8) void {
+ for (text) |char| {
+ put_char(char);
+ }
+}
+
+pub fn print_color(text: []const u8, color: u32) void {
+ for (text) |char| {
+ put_char_color(char, color);
+ }
+}
+
+pub fn clear_screen() void {
+ const f = fb orelse return;
+
+ pixel.fill(f, colors.BLACK);
+ cursor_x = 0;
+ cursor_y = 0;
+ current_line = 0;
+ reset_line_types();
+}
+
+pub fn get_cursor_x() u32 {
+ return cursor_x;
+}
+
+pub fn get_cursor_y() u32 {
+ return cursor_y;
+}
+
+pub fn set_cursor(x: u32, y: u32) void {
+ cursor_x = x;
+ cursor_y = y;
+}
diff --git a/mirai.old/graphics/video/video.zig b/mirai.old/graphics/video/video.zig
new file mode 100644
index 0000000..a110cbe
--- /dev/null
+++ b/mirai.old/graphics/video/video.zig
@@ -0,0 +1,42 @@
+//! Framebuffer video operations
+
+const boot = @import("../../boot/multiboot/multiboot.zig");
+const pixel = @import("../../utils/graphics/pixel.zig");
+
+var fb: ?boot.FramebufferInfo = null;
+
+pub fn init(framebuffer: boot.FramebufferInfo) void {
+ fb = framebuffer;
+}
+
+pub fn clear(c: u32) void {
+ if (fb) |f| {
+ pixel.fill(f, c);
+ }
+}
+
+pub fn put_pixel(x: u32, y: u32, c: u32) void {
+ if (fb) |f| {
+ pixel.put(f, x, y, c);
+ }
+}
+
+pub fn fill_rect(x: u32, y: u32, w: u32, h: u32, c: u32) void {
+ if (fb) |f| {
+ pixel.fill_rect(f, x, y, w, h, c);
+ }
+}
+
+pub fn get_width() u32 {
+ if (fb) |f| return f.width;
+ return 0;
+}
+
+pub fn get_height() u32 {
+ if (fb) |f| return f.height;
+ return 0;
+}
+
+pub fn get_framebuffer() ?boot.FramebufferInfo {
+ return fb;
+}
diff --git a/mirai.old/hikari/elf/elf.zig b/mirai.old/hikari/elf/elf.zig
new file mode 100644
index 0000000..02e719d
--- /dev/null
+++ b/mirai.old/hikari/elf/elf.zig
@@ -0,0 +1,10 @@
+//! ELF module
+
+pub const types = @import("types.zig");
+pub const parser = @import("parser.zig");
+
+pub const Header = types.Header;
+pub const ProgramHeader = types.ProgramHeader;
+pub const Info = types.Info;
+
+pub const parse = parser.parse;
diff --git a/mirai.old/hikari/elf/parser.zig b/mirai.old/hikari/elf/parser.zig
new file mode 100644
index 0000000..1ce2f7b
--- /dev/null
+++ b/mirai.old/hikari/elf/parser.zig
@@ -0,0 +1,73 @@
+//! ELF parser
+
+const elf_const = @import("../../common/constants/elf.zig");
+const memory_limits = @import("../../common/limits/memory.zig");
+const types = @import("types.zig");
+
+pub fn parse(data: []const u8) !types.Info {
+ if (data.len < @sizeOf(types.Header)) {
+ return error.TooSmall;
+ }
+
+ const header = @as(*const types.Header, @ptrCast(@alignCast(data.ptr)));
+
+ if (header.magic[0] != elf_const.MAGIC[0] or
+ header.magic[1] != elf_const.MAGIC[1] or
+ header.magic[2] != elf_const.MAGIC[2] or
+ header.magic[3] != elf_const.MAGIC[3])
+ {
+ return error.InvalidMagic;
+ }
+
+ if (header.class != elf_const.CLASS_64) {
+ return error.Not64Bit;
+ }
+
+ if (header.data != elf_const.DATA_LSB) {
+ return error.NotLittleEndian;
+ }
+
+ if (header.type != elf_const.TYPE_EXEC and header.type != elf_const.TYPE_DYN) {
+ return error.NotExecutable;
+ }
+
+ if (!memory_limits.is_valid_kata_pointer(header.entry)) {
+ return error.InvalidEntryPoint;
+ }
+
+ if (header.phnum == 0) {
+ return error.NoProgramHeaders;
+ }
+
+ if (header.phentsize != @sizeOf(types.ProgramHeader)) {
+ return error.InvalidProgramHeaderSize;
+ }
+
+ const ph_table_size = @as(u64, header.phnum) * @as(u64, header.phentsize);
+ if (header.phoff + ph_table_size > data.len) {
+ return error.InvalidProgramHeaders;
+ }
+
+ const ph_start = data.ptr + header.phoff;
+ const ph_ptr: [*]const types.ProgramHeader = @ptrCast(@alignCast(ph_start));
+ const program_headers = ph_ptr[0..header.phnum];
+
+ for (program_headers) |phdr| {
+ if (phdr.type == elf_const.PT_LOAD) {
+ if (phdr.offset + phdr.filesz > data.len) {
+ return error.SegmentOutOfBounds;
+ }
+ if (phdr.filesz > phdr.memsz) {
+ return error.InvalidSegmentSize;
+ }
+ if (!memory_limits.is_kata_range(phdr.vaddr, phdr.memsz)) {
+ return error.SegmentAddressOutOfRange;
+ }
+ }
+ }
+
+ return types.Info{
+ .entry_point = header.entry,
+ .program_headers = program_headers,
+ };
+}
diff --git a/mirai.old/hikari/elf/types.zig b/mirai.old/hikari/elf/types.zig
new file mode 100644
index 0000000..4e864ed
--- /dev/null
+++ b/mirai.old/hikari/elf/types.zig
@@ -0,0 +1,40 @@
+//! ELF type definitions
+
+pub const Header = extern struct {
+ magic: [4]u8,
+ class: u8,
+ data: u8,
+ version: u8,
+ osabi: u8,
+ abiversion: u8,
+ padding: [7]u8,
+ type: u16,
+ machine: u16,
+ version2: u32,
+ entry: u64,
+ phoff: u64,
+ shoff: u64,
+ flags: u32,
+ ehsize: u16,
+ phentsize: u16,
+ phnum: u16,
+ shentsize: u16,
+ shnum: u16,
+ shstrndx: u16,
+};
+
+pub const ProgramHeader = extern struct {
+ type: u32,
+ flags: u32,
+ offset: u64,
+ vaddr: u64,
+ paddr: u64,
+ filesz: u64,
+ memsz: u64,
+ alignment: u64,
+};
+
+pub const Info = struct {
+ entry_point: u64,
+ program_headers: []const ProgramHeader,
+};
diff --git a/mirai.old/hikari/format/format.zig b/mirai.old/hikari/format/format.zig
new file mode 100644
index 0000000..183bd07
--- /dev/null
+++ b/mirai.old/hikari/format/format.zig
@@ -0,0 +1,11 @@
+//! Akiba format module
+
+pub const types = @import("types.zig");
+pub const parser = @import("parser.zig");
+
+pub const Header = types.Header;
+pub const Metadata = types.Metadata;
+pub const Executable = types.Executable;
+
+pub const parse = parser.parse;
+pub const validate_magic = parser.validate_magic;
diff --git a/mirai.old/hikari/format/parser.zig b/mirai.old/hikari/format/parser.zig
new file mode 100644
index 0000000..6274b3b
--- /dev/null
+++ b/mirai.old/hikari/format/parser.zig
@@ -0,0 +1,64 @@
+//! Akiba format parser
+
+const akiba_const = @import("../../common/constants/akiba.zig");
+const types = @import("types.zig");
+
+pub fn parse(data: []const u8) !types.Executable {
+ if (data.len < @sizeOf(types.Header)) {
+ return error.TooSmall;
+ }
+
+ const header = @as(*const types.Header, @ptrCast(@alignCast(data.ptr)));
+
+ for (akiba_const.MAGIC, 0..) |byte, i| {
+ if (header.magic[i] != byte) {
+ return error.InvalidMagic;
+ }
+ }
+
+ if (header.version == 0 or header.version > akiba_const.VERSION) {
+ return error.UnsupportedVersion;
+ }
+
+ if (header.exec_type > akiba_const.TYPE_LIBRARY) {
+ return error.InvalidExecutableType;
+ }
+
+ if (header.elf_size == 0) {
+ return error.EmptyELF;
+ }
+
+ if (header.elf_offset < @sizeOf(types.Header)) {
+ return error.InvalidELFOffset;
+ }
+
+ if (header.elf_offset + header.elf_size > data.len) {
+ return error.InvalidELFBounds;
+ }
+
+ const elf_data = data[header.elf_offset .. header.elf_offset + header.elf_size];
+
+ var metadata: ?types.Metadata = null;
+ if (header.metadata_offset > 0 and header.metadata_size > 0) {
+ if (header.metadata_offset + header.metadata_size <= data.len) {
+ const meta_ptr = data.ptr + header.metadata_offset;
+ metadata = @as(*const types.Metadata, @ptrCast(@alignCast(meta_ptr))).*;
+ }
+ }
+
+ return types.Executable{
+ .header = header.*,
+ .elf_data = elf_data,
+ .metadata = metadata,
+ };
+}
+
+pub fn validate_magic(data: []const u8) bool {
+ if (data.len < 8) return false;
+
+ for (akiba_const.MAGIC, 0..) |byte, i| {
+ if (data[i] != byte) return false;
+ }
+
+ return true;
+}
diff --git a/mirai.old/hikari/format/types.zig b/mirai.old/hikari/format/types.zig
new file mode 100644
index 0000000..82d8d54
--- /dev/null
+++ b/mirai.old/hikari/format/types.zig
@@ -0,0 +1,28 @@
+//! Akiba format type definitions
+
+pub const Header = extern struct {
+ magic: [8]u8,
+ version: u32,
+ exec_type: u32,
+ elf_offset: u64,
+ elf_size: u64,
+ metadata_offset: u64,
+ metadata_size: u64,
+ entry_point: u64,
+ reserved: [16]u8,
+};
+
+pub const Metadata = struct {
+ name: [64]u8,
+ version: [16]u8,
+ author: [64]u8,
+ description: [256]u8,
+ icon_offset: u64,
+ icon_size: u64,
+};
+
+pub const Executable = struct {
+ header: Header,
+ elf_data: []const u8,
+ metadata: ?Metadata,
+};
diff --git a/mirai.old/hikari/hikari.zig b/mirai.old/hikari/hikari.zig
new file mode 100644
index 0000000..cc34917
--- /dev/null
+++ b/mirai.old/hikari/hikari.zig
@@ -0,0 +1,9 @@
+//! Hikari - The program loader
+
+pub const elf = @import("elf/elf.zig");
+pub const format = @import("format/format.zig");
+pub const loader = @import("loader.zig");
+
+pub const init = loader.init;
+pub const load = loader.load;
+pub const load_with_args = loader.load_with_args;
diff --git a/mirai.old/hikari/loader.zig b/mirai.old/hikari/loader.zig
new file mode 100644
index 0000000..a09b4ce
--- /dev/null
+++ b/mirai.old/hikari/loader.zig
@@ -0,0 +1,181 @@
+//! Hikari - Program loader
+
+const afs = @import("../fs/afs/afs.zig");
+const ahci = @import("../drivers/ahci/ahci.zig");
+const elf = @import("elf/elf.zig");
+const elf_const = @import("../common/constants/elf.zig");
+const format = @import("format/format.zig");
+const gdt = @import("../boot/gdt/gdt.zig");
+const heap = @import("../memory/heap.zig");
+const kata_const = @import("../common/constants/kata.zig");
+const kata_limits = @import("../common/limits/kata.zig");
+const kata_memory = @import("../kata/memory.zig");
+const kata_mod = @import("../kata/kata.zig");
+const memory_const = @import("../common/constants/memory.zig");
+const multiboot = @import("../boot/multiboot/multiboot.zig");
+const paging = @import("../memory/paging.zig");
+const sensei = @import("../kata/sensei/sensei.zig");
+const serial = @import("../drivers/serial/serial.zig");
+const fs_limits = @import("../common/limits/fs.zig");
+
+const INIT_LOCATION = "/system/akiba/pulse.gen";
+
+pub fn init(fs: *afs.AFS(ahci.BlockDevice)) !u32 {
+ const init_size = fs.get_unit_size(INIT_LOCATION) catch |err| {
+ serial.print("FATAL: Cannot find init at ");
+ serial.print(INIT_LOCATION);
+ serial.print("\n");
+ return err;
+ };
+
+ if (init_size == 0) {
+ serial.print("FATAL: Init is empty\n");
+ return error.EmptyUnit;
+ }
+
+ var args: [1][]const u8 = .{INIT_LOCATION};
+ return load_with_args(fs, INIT_LOCATION, &args);
+}
+
+pub fn load(fs: *afs.AFS(ahci.BlockDevice), location: []const u8) !u32 {
+ var args: [1][]const u8 = .{location};
+ return load_with_args(fs, location, &args);
+}
+
+pub fn load_with_args(
+ fs: *afs.AFS(ahci.BlockDevice),
+ location: []const u8,
+ args: []const []const u8,
+) !u32 {
+ if (location.len == 0 or location.len > fs_limits.MAX_LOCATION_LENGTH) {
+ return error.InvalidLocation;
+ }
+
+ const unit_size = try fs.get_unit_size(location);
+
+ if (unit_size == 0) return error.EmptyUnit;
+ if (unit_size > fs_limits.MAX_UNIT_SIZE) return error.UnitTooLarge;
+
+ const buffer_ptr = heap.alloc(@intCast(unit_size)) orelse return error.OutOfMemory;
+ defer heap.free(buffer_ptr, @intCast(unit_size));
+ const buffer = buffer_ptr[0..@intCast(unit_size)];
+
+ const bytes_read = try fs.view_unit_at(location, buffer);
+
+ if (bytes_read != unit_size) {
+ return error.IncompleteRead;
+ }
+
+ const akiba_data = try format.parse(buffer[0..bytes_read]);
+ const elf_info = try elf.parse(akiba_data.elf_data);
+
+ const kata = try kata_mod.create_kata();
+ errdefer kata_mod.dissolve_kata(kata.id);
+
+ if (sensei.get_current_kata()) |parent| {
+ kata.parent_id = parent.id;
+
+ for (0..parent.current_location_len) |i| {
+ kata.current_location[i] = parent.current_location[i];
+ }
+ kata.current_location_len = parent.current_location_len;
+ kata.current_cluster = parent.current_cluster;
+ }
+
+ const fb_info = multiboot.get_framebuffer();
+ const fb_phys = if (fb_info) |fb| fb.addr else 0;
+ const fb_size = if (fb_info) |fb| fb.height * fb.pitch else 0;
+ try kata_memory.setup(kata, fb_phys, fb_size);
+
+ for (elf_info.program_headers) |phdr| {
+ if (phdr.type == elf_const.PT_LOAD) {
+ try kata_memory.load_segment(
+ kata,
+ phdr.vaddr,
+ akiba_data.elf_data,
+ phdr.offset,
+ phdr.filesz,
+ phdr.memsz,
+ phdr.flags,
+ );
+ }
+ }
+
+ const adjusted_stack = try setup_stack_args(kata, args);
+ setup_context(kata, elf_info.entry_point, adjusted_stack);
+
+ sensei.enqueue_kata(kata);
+
+ return kata.id;
+}
+
+fn setup_stack_args(kata: *kata_mod.Kata, args: []const []const u8) !u64 {
+ var stack_top = kata.user_stack_top;
+ const pc: u64 = args.len;
+
+ var string_addrs: [kata_limits.MAX_PARAMETERS]u64 = undefined;
+
+ for (args, 0..) |arg, i| {
+ const str_len = arg.len + 1;
+ stack_top -= str_len;
+ stack_top &= ~@as(u64, 0x7);
+
+ string_addrs[i] = stack_top;
+
+ const phys_addr = paging.virt_to_phys(kata.page_table, stack_top) orelse return error.StackNotMapped;
+ const dest = @as([*]u8, @ptrFromInt(phys_addr + memory_const.HIGHER_HALF_START));
+
+ for (arg, 0..) |c, j| {
+ dest[j] = c;
+ }
+ dest[arg.len] = 0;
+ }
+
+ const pv_size = pc * 8;
+ stack_top -= pv_size;
+ stack_top &= ~@as(u64, 0x7);
+
+ const pv_addr = stack_top;
+
+ for (0..pc) |i| {
+ const ptr_phys = paging.virt_to_phys(kata.page_table, pv_addr + i * 8) orelse return error.StackNotMapped;
+ const ptr_dest = @as(*u64, @ptrFromInt(ptr_phys + memory_const.HIGHER_HALF_START));
+ ptr_dest.* = string_addrs[i];
+ }
+
+ stack_top &= ~@as(u64, 0xF);
+
+ stack_top -= 8;
+ const pv_ptr_phys = paging.virt_to_phys(kata.page_table, stack_top) orelse return error.StackNotMapped;
+ @as(*u64, @ptrFromInt(pv_ptr_phys + memory_const.HIGHER_HALF_START)).* = pv_addr;
+
+ stack_top -= 8;
+ const pc_phys = paging.virt_to_phys(kata.page_table, stack_top) orelse return error.StackNotMapped;
+ @as(*u64, @ptrFromInt(pc_phys + memory_const.HIGHER_HALF_START)).* = pc;
+
+ return stack_top;
+}
+
+fn setup_context(kata: *kata_mod.Kata, entry_point: u64, stack_pointer: u64) void {
+ kata.context.rip = entry_point;
+ kata.context.rsp = stack_pointer;
+ kata.context.rflags = kata_const.USER_RFLAGS;
+ kata.context.cs = gdt.USER_CODE | 3;
+ kata.context.ss = gdt.USER_DATA | 3;
+
+ kata.context.rax = 0;
+ kata.context.rbx = 0;
+ kata.context.rcx = 0;
+ kata.context.rdx = 0;
+ kata.context.rsi = 0;
+ kata.context.rdi = 0;
+ kata.context.rbp = 0;
+ kata.context.r8 = 0;
+ kata.context.r9 = 0;
+ kata.context.r10 = 0;
+ kata.context.r11 = 0;
+ kata.context.r12 = 0;
+ kata.context.r13 = 0;
+ kata.context.r14 = 0;
+ kata.context.r15 = 0;
+}
diff --git a/mirai.old/interrupts/handlers.zig b/mirai.old/interrupts/handlers.zig
new file mode 100644
index 0000000..7f8ea06
--- /dev/null
+++ b/mirai.old/interrupts/handlers.zig
@@ -0,0 +1,9 @@
+//! IRQ handlers
+
+const pic = @import("pic.zig");
+const sensei = @import("../kata/sensei/sensei.zig");
+
+export fn timer_handler() void {
+ pic.send_eoi_master();
+ sensei.on_tick();
+}
diff --git a/mirai.old/interrupts/idt.zig b/mirai.old/interrupts/idt.zig
new file mode 100644
index 0000000..97fefcf
--- /dev/null
+++ b/mirai.old/interrupts/idt.zig
@@ -0,0 +1,72 @@
+//! Interrupt Descriptor Table
+
+const cpu = @import("../asm/cpu.zig");
+const gdt_const = @import("../common/constants/gdt.zig");
+const idt_const = @import("../common/constants/idt.zig");
+const isr = @import("../asm/isr.zig");
+const pic = @import("pic.zig");
+const serial = @import("../drivers/serial/serial.zig");
+const types = @import("types.zig");
+
+comptime {
+ _ = @import("../crimson/exception.zig");
+ _ = @import("handlers.zig");
+}
+
+var table: [idt_const.NUM_ENTRIES]types.Entry align(16) = [_]types.Entry{.{
+ .offset_low = 0,
+ .selector = 0,
+ .ist = 0,
+ .type_attr = 0,
+ .offset_mid = 0,
+ .offset_high = 0,
+ .reserved = 0,
+}} ** idt_const.NUM_ENTRIES;
+
+var pointer: types.Pointer = undefined;
+
+pub fn init() void {
+ serial.print("\n=== IDT ===\n");
+
+ for (0..idt_const.NUM_EXCEPTIONS) |i| {
+ const handler_addr = @intFromPtr(isr.get_exception_handler(@intCast(i)));
+ // Use IST1 for double fault (8) and page fault (14)
+ if (i == 8 or i == 14) {
+ set_gate_with_ist(@intCast(i), handler_addr, gdt_const.KERNEL_CODE, idt_const.GATE_INTERRUPT, 1);
+ } else {
+ set_gate(@intCast(i), handler_addr, gdt_const.KERNEL_CODE, idt_const.GATE_INTERRUPT);
+ }
+ }
+
+ set_gate(idt_const.VECTOR_TIMER, @intFromPtr(isr.get_irq_handler(0)), gdt_const.KERNEL_CODE, idt_const.GATE_INTERRUPT);
+ set_gate(idt_const.VECTOR_KEYBOARD, @intFromPtr(isr.get_irq_handler(1)), gdt_const.KERNEL_CODE, idt_const.GATE_INTERRUPT);
+
+ pointer = types.Pointer{
+ .limit = @sizeOf(@TypeOf(table)) - 1,
+ .base = @intFromPtr(&table),
+ };
+
+ cpu.load_interrupt_descriptor_table(@intFromPtr(&pointer));
+
+ pic.remap();
+
+ cpu.enable_interrupts();
+
+ serial.print("IDT loaded\n");
+}
+
+fn set_gate(num: u8, handler: u64, selector: u16, type_attr: u8) void {
+ set_gate_with_ist(num, handler, selector, type_attr, 0);
+}
+
+fn set_gate_with_ist(num: u8, handler: u64, selector: u16, type_attr: u8, ist: u8) void {
+ table[num] = types.Entry{
+ .offset_low = @truncate(handler & 0xFFFF),
+ .selector = selector,
+ .ist = ist,
+ .type_attr = type_attr,
+ .offset_mid = @truncate((handler >> 16) & 0xFFFF),
+ .offset_high = @truncate(handler >> 32),
+ .reserved = 0,
+ };
+}
diff --git a/mirai.old/interrupts/pic.zig b/mirai.old/interrupts/pic.zig
new file mode 100644
index 0000000..df26eb3
--- /dev/null
+++ b/mirai.old/interrupts/pic.zig
@@ -0,0 +1,30 @@
+//! PIC remapping
+
+const io = @import("../asm/io.zig");
+const pic = @import("../common/constants/pic.zig");
+
+pub fn remap() void {
+ io.out_byte(pic.MASTER_CMD, pic.ICW1_INIT);
+ io.out_byte(pic.SLAVE_CMD, pic.ICW1_INIT);
+
+ io.out_byte(pic.MASTER_DATA, pic.MASTER_OFFSET);
+ io.out_byte(pic.SLAVE_DATA, pic.SLAVE_OFFSET);
+
+ io.out_byte(pic.MASTER_DATA, pic.MASTER_CASCADE);
+ io.out_byte(pic.SLAVE_DATA, pic.SLAVE_CASCADE);
+
+ io.out_byte(pic.MASTER_DATA, pic.ICW4_8086);
+ io.out_byte(pic.SLAVE_DATA, pic.ICW4_8086);
+
+ io.out_byte(pic.MASTER_DATA, pic.MASK_TIMER_KEYBOARD);
+ io.out_byte(pic.SLAVE_DATA, pic.MASK_ALL);
+}
+
+pub fn send_eoi_master() void {
+ io.out_byte(pic.MASTER_CMD, pic.EOI);
+}
+
+pub fn send_eoi_slave() void {
+ io.out_byte(pic.SLAVE_CMD, pic.EOI);
+ io.out_byte(pic.MASTER_CMD, pic.EOI);
+}
diff --git a/mirai.old/interrupts/types.zig b/mirai.old/interrupts/types.zig
new file mode 100644
index 0000000..ecd8513
--- /dev/null
+++ b/mirai.old/interrupts/types.zig
@@ -0,0 +1,16 @@
+//! IDT type definitions
+
+pub const Entry = packed struct {
+ offset_low: u16,
+ selector: u16,
+ ist: u8,
+ type_attr: u8,
+ offset_mid: u16,
+ offset_high: u32,
+ reserved: u32,
+};
+
+pub const Pointer = packed struct {
+ limit: u16,
+ base: u64,
+};
diff --git a/mirai.old/invocations/fs/getlocation.zig b/mirai.old/invocations/fs/getlocation.zig
new file mode 100644
index 0000000..43a42c9
--- /dev/null
+++ b/mirai.old/invocations/fs/getlocation.zig
@@ -0,0 +1,35 @@
+//! Getlocation invocation - Get current stack location
+
+const copy = @import("../../utils/mem/copy.zig");
+const handler = @import("../handler.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const memory_limits = @import("../../common/limits/memory.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const slice = @import("../../utils/mem/slice.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const buffer_ptr = ctx.rdi;
+ const buffer_len = ctx.rsi;
+
+ if (!memory_limits.is_valid_kata_pointer(buffer_ptr)) {
+ return result.set_error(ctx);
+ }
+
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ const location_len = kata.current_location_len;
+
+ if (location_len == 0 or location_len > kata_limits.MAX_LOCATION_LENGTH) {
+ const dest = slice.byte_ptr(buffer_ptr);
+ dest[0] = '/';
+ return result.set_value(ctx, 1);
+ }
+
+ if (location_len > buffer_len) {
+ return result.set_error(ctx);
+ }
+
+ copy.to_ptr(buffer_ptr, kata.current_location[0..location_len]);
+ result.set_value(ctx, location_len);
+}
diff --git a/mirai.old/invocations/fs/setlocation.zig b/mirai.old/invocations/fs/setlocation.zig
new file mode 100644
index 0000000..f2164fd
--- /dev/null
+++ b/mirai.old/invocations/fs/setlocation.zig
@@ -0,0 +1,64 @@
+//! Setlocation invocation - Change current stack location
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const copy = @import("../../utils/mem/copy.zig");
+const fs_limits = @import("../../common/limits/fs.zig");
+const handler = @import("../handler.zig");
+const heap = @import("../../memory/heap.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const location = @import("../../utils/fs/location.zig");
+const memory_limits = @import("../../common/limits/memory.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+
+var afs_instance: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn set_afs_instance(fs: *afs.AFS(ahci.BlockDevice)) void {
+ afs_instance = fs;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const fs = afs_instance orelse return result.set_error(ctx);
+
+ const location_ptr = ctx.rdi;
+ const location_len = ctx.rsi;
+
+ if (!memory_limits.is_valid_kata_pointer(location_ptr)) return result.set_error(ctx);
+ if (location_len > fs_limits.MAX_LOCATION_LENGTH) return result.set_error(ctx);
+
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ var location_buf: [fs_limits.MAX_LOCATION_LENGTH]u8 = undefined;
+ copy.from_ptr(&location_buf, location_ptr, location_len);
+ const loc = location_buf[0..location_len];
+
+ const target_cluster = location.resolve_to_cluster(fs, loc, kata.current_cluster) orelse {
+ return result.set_error(ctx);
+ };
+
+ const canonical = location.canonicalize(kata.current_location[0..kata.current_location_len], loc);
+
+ copy.bytes(kata.current_location[0..canonical.len], canonical.buf[0..canonical.len]);
+ kata.current_location_len = canonical.len;
+ kata.current_cluster = target_cluster;
+
+ if (kata.parent_id != 0) {
+ if (kata_mod.get_kata(kata.parent_id)) |parent| {
+ const len: u16 = @intCast(canonical.len);
+ if (parent.letter_capacity < len) {
+ if (parent.letter_data) |old| {
+ heap.free(@ptrCast(old), parent.letter_capacity);
+ }
+ const new_buf = heap.alloc(len) orelse return result.set_error(ctx);
+ parent.letter_data = new_buf;
+ parent.letter_capacity = len;
+ }
+ parent.letter_type = 1;
+ parent.letter_len = len;
+ copy.bytes(parent.letter_data.?[0..len], canonical.buf[0..canonical.len]);
+ }
+ }
+
+ result.set_ok(ctx);
+}
diff --git a/mirai.old/invocations/fs/viewstack.zig b/mirai.old/invocations/fs/viewstack.zig
new file mode 100644
index 0000000..c3c9b6d
--- /dev/null
+++ b/mirai.old/invocations/fs/viewstack.zig
@@ -0,0 +1,87 @@
+//! Viewstack invocation - List contents of a stack
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const copy = @import("../../utils/mem/copy.zig");
+const fs_limits = @import("../../common/limits/fs.zig");
+const handler = @import("../handler.zig");
+const int = @import("../../utils/types/int.zig");
+const location = @import("../../utils/fs/location.zig");
+const memory_limits = @import("../../common/limits/memory.zig");
+const ptr = @import("../../utils/types/ptr.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const slice = @import("../../utils/mem/slice.zig");
+
+const MAX_ENTRY_IDENTITY: usize = 64;
+const MAX_ENTRY_OWNER: usize = 64;
+
+const UserStackEntry = extern struct {
+ identity: [MAX_ENTRY_IDENTITY]u8,
+ identity_len: u8,
+ is_stack: bool,
+ owner_name_len: u8,
+ permission_type: u8,
+ size: u32,
+ modified_time: u64,
+ owner_name: [MAX_ENTRY_OWNER]u8,
+};
+
+var afs_instance: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn set_afs_instance(fs: *afs.AFS(ahci.BlockDevice)) void {
+ afs_instance = fs;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const fs = afs_instance orelse return result.set_error(ctx);
+
+ const location_ptr = ctx.rdi;
+ const location_len = ctx.rsi;
+ const entries_ptr = ctx.rdx;
+ const max_entries = ctx.r10;
+
+ if (!memory_limits.is_valid_kata_pointer(location_ptr) or !memory_limits.is_valid_kata_pointer(entries_ptr)) {
+ return result.set_error(ctx);
+ }
+
+ if (location_len > fs_limits.MAX_LOCATION_LENGTH) return result.set_error(ctx);
+
+ var location_buf: [fs_limits.MAX_LOCATION_LENGTH]u8 = undefined;
+ copy.from_ptr(&location_buf, location_ptr, location_len);
+ const loc = location_buf[0..location_len];
+
+ const kata = sensei.get_current_kata();
+ const current_cluster: u64 = if (kata) |k| k.current_cluster else 0;
+
+ const target_cluster = location.resolve_to_cluster(fs, loc, current_cluster) orelse {
+ return result.set_error(ctx);
+ };
+
+ var items: [fs_limits.MAX_STACK_ITEMS]afs.StackItem = undefined;
+ const item_count = fs.list_stack(target_cluster, &items) catch return result.set_error(ctx);
+
+ const user_entries = slice.typed_ptr(UserStackEntry, entries_ptr);
+ const copy_count = @min(item_count, max_entries);
+
+ for (0..copy_count) |i| {
+ const item = &items[i];
+ var user_entry = &user_entries[i];
+
+ const identity = item.get_identity();
+ const id_len = @min(identity.len, MAX_ENTRY_IDENTITY - 1);
+ copy.bytes(user_entry.identity[0..id_len], identity[0..id_len]);
+ user_entry.identity_len = @intCast(id_len);
+ user_entry.size = item.size;
+ user_entry.is_stack = item.is_stack;
+ user_entry.modified_time = item.modified_time;
+
+ const owner = item.get_owner();
+ const owner_len = @min(owner.len, MAX_ENTRY_OWNER - 1);
+ copy.bytes(user_entry.owner_name[0..owner_len], owner[0..owner_len]);
+ user_entry.owner_name_len = @intCast(owner_len);
+ user_entry.permission_type = item.permission_type;
+ }
+
+ result.set_value(ctx, copy_count);
+}
diff --git a/mirai.old/invocations/handler.zig b/mirai.old/invocations/handler.zig
new file mode 100644
index 0000000..bf3fa47
--- /dev/null
+++ b/mirai.old/invocations/handler.zig
@@ -0,0 +1,92 @@
+//! Invocation Handler - Entry point for Kata programs calling Mirai
+
+const afs = @import("../fs/afs/afs.zig");
+const ahci = @import("../drivers/ahci/ahci.zig");
+const invocations = @import("../common/constants/invocations.zig");
+const result = @import("../utils/types/result.zig");
+const syscall = @import("syscall.zig");
+
+const attach = @import("io/attach.zig");
+const getkeychar = @import("io/getkeychar.zig");
+const mark = @import("io/mark.zig");
+const seal = @import("io/seal.zig");
+const view = @import("io/view.zig");
+const wipe = @import("io/wipe.zig");
+
+const getlocation = @import("fs/getlocation.zig");
+const setlocation = @import("fs/setlocation.zig");
+const viewstack = @import("fs/viewstack.zig");
+
+const exit = @import("kata/exit.zig");
+const postman = @import("kata/postman.zig");
+const reap = @import("kata/reap.zig");
+const spawn = @import("kata/spawn.zig");
+const wait = @import("kata/wait.zig");
+const yield = @import("kata/yield.zig");
+
+const cpuinfo = @import("os/cpuinfo.zig");
+const diskinfo = @import("os/diskinfo.zig");
+const gettime = @import("os/gettime.zig");
+const meminfo = @import("os/meminfo.zig");
+const uptime = @import("os/uptime.zig");
+
+pub const InvocationContext = struct {
+ rax: u64,
+ rdi: u64,
+ rsi: u64,
+ rdx: u64,
+ r10: u64,
+ r8: u64,
+ r9: u64,
+ rbx: u64,
+ rcx: u64,
+ rbp: u64,
+ rsp: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ rip: u64,
+ rflags: u64,
+ cs: u64,
+ ss: u64,
+};
+
+pub fn init(fs: *afs.AFS(ahci.BlockDevice)) void {
+ exit.set_afs_instance(fs);
+ attach.set_afs_instance(fs);
+ seal.set_afs_instance(fs);
+ spawn.set_afs_instance(fs);
+ viewstack.set_afs_instance(fs);
+ setlocation.set_afs_instance(fs);
+ diskinfo.set_afs_instance(fs);
+
+ syscall.init();
+}
+
+pub fn handle(ctx: *InvocationContext) void {
+ switch (ctx.rax) {
+ invocations.EXIT => exit.invoke(ctx),
+ invocations.ATTACH => attach.invoke(ctx),
+ invocations.SEAL => seal.invoke(ctx),
+ invocations.VIEW => view.invoke(ctx),
+ invocations.MARK => mark.invoke(ctx),
+ invocations.SPAWN => spawn.invoke(ctx),
+ invocations.WAIT => wait.invoke(ctx),
+ invocations.YIELD => yield.invoke(ctx),
+ invocations.GETKEYCHAR => getkeychar.invoke(ctx),
+ invocations.VIEWSTACK => viewstack.invoke(ctx),
+ invocations.GETLOCATION => getlocation.invoke(ctx),
+ invocations.SETLOCATION => setlocation.invoke(ctx),
+ invocations.POSTMAN => postman.invoke(ctx),
+ invocations.WIPE => wipe.invoke(ctx),
+ invocations.CPUINFO => cpuinfo.invoke(ctx),
+ invocations.MEMINFO => meminfo.invoke(ctx),
+ invocations.UPTIME => uptime.invoke(ctx),
+ invocations.GETTIME => gettime.invoke(ctx),
+ invocations.DISKINFO => diskinfo.invoke(ctx),
+ invocations.REAP => reap.invoke(ctx),
+ else => result.set_error(ctx),
+ }
+}
diff --git a/mirai.old/invocations/io/attach.zig b/mirai.old/invocations/io/attach.zig
new file mode 100644
index 0000000..4923eb0
--- /dev/null
+++ b/mirai.old/invocations/io/attach.zig
@@ -0,0 +1,112 @@
+//! Attach invocation - Open attachment
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const attachment_const = @import("../../common/constants/attachment.zig");
+const attachment_util = @import("../../utils/kata/attachment.zig");
+const compare = @import("../../utils/string/compare.zig");
+const copy = @import("../../utils/mem/copy.zig");
+const handler = @import("../handler.zig");
+const heap = @import("../../memory/heap.zig");
+const int = @import("../../utils/types/int.zig");
+const kata_attachment = @import("../../kata/attachment.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const location_util = @import("../../utils/fs/location.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const string_copy = @import("../../utils/string/copy.zig");
+
+var afs_instance: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn set_afs_instance(fs: *afs.AFS(ahci.BlockDevice)) void {
+ afs_instance = fs;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ const location_ptr = ctx.rdi;
+ const flags = int.u32_of(ctx.rsi);
+
+ var location_buf: [256]u8 = undefined;
+ const location_len = string_copy.from_kata(&location_buf, location_ptr) catch return result.set_error(ctx);
+ const location = location_buf[0..location_len];
+
+ if (location_util.is_device(location)) {
+ const fd = open_device(kata, location, flags) catch return result.set_error(ctx);
+ return result.set_value(ctx, fd);
+ }
+
+ const fd = open_unit(kata, location, flags) catch return result.set_error(ctx);
+ result.set_value(ctx, fd);
+}
+
+fn open_device(kata: *kata_mod.Kata, location: []const u8, flags: u32) !u32 {
+ const name = location_util.device_name(location);
+
+ const device_type: kata_attachment.DeviceType =
+ if (compare.equals(name, "source")) .Source else if (compare.equals(name, "stream")) .Stream else if (compare.equals(name, "trace")) .Trace else if (compare.equals(name, "void")) .Void else if (compare.equals(name, "chaos")) .Chaos else if (compare.equals(name, "zero")) .Zero else if (compare.equals(name, "console")) .Console else return error.UnknownDevice;
+
+ const fd = try attachment_util.allocate(kata);
+ const entry = kata_attachment.alloc() orelse return error.OutOfMemory;
+ entry.* = .{
+ .attachment_type = .Device,
+ .device_type = device_type,
+ .flags = flags,
+ };
+ kata.attachments[fd] = entry;
+
+ return fd;
+}
+
+fn open_unit(kata: *kata_mod.Kata, location: []const u8, flags: u32) !u32 {
+ const fs = afs_instance orelse return error.NoFilesystem;
+
+ var full_location_buf: [512]u8 = undefined;
+ const full_location = location_util.resolve(kata, location, &full_location_buf);
+
+ const fd = try attachment_util.allocate(kata);
+ const entry = kata_attachment.alloc() orelse return error.OutOfMemory;
+
+ var unit_buffer: [1024 * 1024]u8 = undefined;
+ const bytes_read = fs.view_unit_at(full_location, &unit_buffer) catch {
+ if (flags & attachment_const.CREATE != 0) {
+ fs.create_unit(full_location) catch {
+ kata_attachment.free(entry);
+ return error.CannotCreate;
+ };
+
+ entry.* = .{
+ .attachment_type = .Unit,
+ .unit_size = 0,
+ .position = 0,
+ .flags = flags,
+ .location_len = location.len,
+ };
+ copy.bytes(entry.location[0..location.len], location);
+ kata.attachments[fd] = entry;
+ return fd;
+ }
+ kata_attachment.free(entry);
+ return error.UnitNotFound;
+ };
+
+ const unit_data = heap.alloc(bytes_read) orelse {
+ kata_attachment.free(entry);
+ return error.OutOfMemory;
+ };
+ copy.bytes(unit_data[0..bytes_read], unit_buffer[0..bytes_read]);
+
+ entry.* = .{
+ .attachment_type = .Unit,
+ .buffer = unit_data[0..bytes_read],
+ .unit_size = bytes_read,
+ .position = if (flags & attachment_const.EXTEND != 0) bytes_read else 0,
+ .flags = flags,
+ .location_len = location.len,
+ };
+ copy.bytes(entry.location[0..location.len], location);
+ kata.attachments[fd] = entry;
+
+ return fd;
+}
diff --git a/mirai.old/invocations/io/getkeychar.zig b/mirai.old/invocations/io/getkeychar.zig
new file mode 100644
index 0000000..895c78d
--- /dev/null
+++ b/mirai.old/invocations/io/getkeychar.zig
@@ -0,0 +1,13 @@
+//! Getkeychar invocation - View one character from keyboard
+
+const handler = @import("../handler.zig");
+const keyboard = @import("../../drivers/keyboard/keyboard.zig");
+const result = @import("../../utils/types/result.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ if (keyboard.read_char()) |char| {
+ result.set_value(ctx, char);
+ } else {
+ result.set_no_data(ctx);
+ }
+}
diff --git a/mirai.old/invocations/io/mark.zig b/mirai.old/invocations/io/mark.zig
new file mode 100644
index 0000000..2566d83
--- /dev/null
+++ b/mirai.old/invocations/io/mark.zig
@@ -0,0 +1,62 @@
+//! Mark invocation - Write to attachment
+
+const copy = @import("../../utils/mem/copy.zig");
+const fd_mod = @import("../../kata/attachment.zig");
+const handler = @import("../handler.zig");
+const int = @import("../../utils/types/int.zig");
+const io_limits = @import("../../common/limits/io.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const memory_limits = @import("../../common/limits/memory.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const terminal = @import("../../graphics/terminal/terminal.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ const fd = int.u32_of(ctx.rdi);
+ const buffer_ptr = ctx.rsi;
+ const count = ctx.rdx;
+ const color = int.u32_of(ctx.r10);
+
+ if (fd >= kata_limits.MAX_ATTACHMENTS) return result.set_error(ctx);
+ const entry = kata.attachments[fd] orelse return result.set_error(ctx);
+
+ const bytes = mark_to_attachment(entry, buffer_ptr, count, color) catch return result.set_error(ctx);
+ result.set_value(ctx, bytes);
+}
+
+fn mark_to_attachment(entry: *fd_mod.Attachment, buffer_ptr: u64, count: u64, color: u32) !u64 {
+ if (count == 0) return 0;
+ if (count > io_limits.MAX_MARK_SIZE) return error.MarkTooLarge;
+
+ if (entry.attachment_type == .Device) {
+ const device = entry.device_type orelse return error.InvalidDevice;
+ return mark_to_device(device, buffer_ptr, count, color);
+ }
+
+ entry.dirty = true;
+ return count;
+}
+
+fn mark_to_device(device: fd_mod.DeviceType, buffer_ptr: u64, count: u64, color: u32) !u64 {
+ if (!memory_limits.is_valid_kata_pointer(buffer_ptr)) {
+ return error.InvalidPointer;
+ }
+
+ var mirai_buffer: [io_limits.MIRAI_COPY_BUFFER_SIZE]u8 = undefined;
+ const copy_size = @min(count, io_limits.MIRAI_COPY_BUFFER_SIZE);
+
+ copy.from_ptr(&mirai_buffer, buffer_ptr, copy_size);
+
+ switch (device) {
+ .Stream, .Trace, .Console => {
+ for (mirai_buffer[0..copy_size]) |c| {
+ terminal.put_char_color(c, color);
+ }
+ return count;
+ },
+ .Void => return count,
+ else => return error.CannotMark,
+ }
+}
diff --git a/mirai.old/invocations/io/seal.zig b/mirai.old/invocations/io/seal.zig
new file mode 100644
index 0000000..d798752
--- /dev/null
+++ b/mirai.old/invocations/io/seal.zig
@@ -0,0 +1,28 @@
+//! Seal invocation - Close attachment
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const attachment = @import("../../utils/kata/attachment.zig");
+const handler = @import("../handler.zig");
+const int = @import("../../utils/types/int.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+
+var afs_instance: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn set_afs_instance(fs: *afs.AFS(ahci.BlockDevice)) void {
+ afs_instance = fs;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ const fd = int.u32_of(ctx.rdi);
+
+ if (fd >= kata_limits.MAX_ATTACHMENTS) return result.set_error(ctx);
+ if (kata.attachments[fd] == null) return result.set_error(ctx);
+
+ attachment.seal(kata, fd, afs_instance);
+ result.set_ok(ctx);
+}
diff --git a/mirai.old/invocations/io/view.zig b/mirai.old/invocations/io/view.zig
new file mode 100644
index 0000000..349af7a
--- /dev/null
+++ b/mirai.old/invocations/io/view.zig
@@ -0,0 +1,74 @@
+//! View invocation - Read from attachment
+
+const copy = @import("../../utils/mem/copy.zig");
+const handler = @import("../handler.zig");
+const int = @import("../../utils/types/int.zig");
+const kata_attachment = @import("../../kata/attachment.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const keyboard = @import("../../drivers/keyboard/keyboard.zig");
+const random = @import("../../utils/random/xorshift.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const slice = @import("../../utils/mem/slice.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ const fd = int.u32_of(ctx.rdi);
+ const buffer_ptr = ctx.rsi;
+ const count = ctx.rdx;
+
+ if (fd >= kata_limits.MAX_ATTACHMENTS) return result.set_error(ctx);
+ const entry = kata.attachments[fd] orelse return result.set_error(ctx);
+
+ const bytes = view_from_attachment(entry, buffer_ptr, count) catch return result.set_error(ctx);
+ result.set_value(ctx, bytes);
+}
+
+fn view_from_attachment(entry: *kata_attachment.Attachment, buffer_ptr: u64, count: u64) !u64 {
+ if (entry.attachment_type == .Device) {
+ const device = entry.device_type orelse return error.InvalidDevice;
+ return view_from_device(device, buffer_ptr, count);
+ }
+
+ const unit_buffer = entry.buffer orelse return 0;
+ const remaining = entry.unit_size - entry.position;
+ const to_view = @min(count, remaining);
+
+ if (to_view == 0) return 0;
+
+ const src = slice.from_ptr_const(u8, int.from_ptr(unit_buffer.ptr) + entry.position, to_view);
+ const dest = slice.from_ptr(u8, buffer_ptr, to_view);
+ copy.bytes(dest, src);
+
+ entry.position += to_view;
+ return to_view;
+}
+
+fn view_from_device(device: kata_attachment.DeviceType, buffer_ptr: u64, count: u64) !u64 {
+ const dest = slice.byte_ptr(buffer_ptr);
+
+ switch (device) {
+ .Source => {
+ var i: u64 = 0;
+ while (i < count) : (i += 1) {
+ if (keyboard.read_char()) |char| {
+ dest[i] = char;
+ } else break;
+ }
+ return i;
+ },
+ .Zero => {
+ copy.zero(slice.from_ptr(u8, buffer_ptr, count));
+ return count;
+ },
+ .Chaos => {
+ var i: u64 = 0;
+ while (i < count) : (i += 1) {
+ dest[i] = random.byte();
+ }
+ return count;
+ },
+ else => return error.CannotView,
+ }
+}
diff --git a/mirai.old/invocations/io/wipe.zig b/mirai.old/invocations/io/wipe.zig
new file mode 100644
index 0000000..a2b555b
--- /dev/null
+++ b/mirai.old/invocations/io/wipe.zig
@@ -0,0 +1,10 @@
+//! Wipe invocation - Clear the terminal screen
+
+const handler = @import("../handler.zig");
+const result = @import("../../utils/types/result.zig");
+const terminal = @import("../../graphics/terminal/terminal.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ terminal.clear_screen();
+ result.set_ok(ctx);
+}
diff --git a/mirai.old/invocations/kata/exit.zig b/mirai.old/invocations/kata/exit.zig
new file mode 100644
index 0000000..fd9ec9d
--- /dev/null
+++ b/mirai.old/invocations/kata/exit.zig
@@ -0,0 +1,33 @@
+//! Exit invocation - Dissolve Kata
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const attachment = @import("../../utils/kata/attachment.zig");
+const handler = @import("../handler.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+
+var afs_instance: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn set_afs_instance(fs: *afs.AFS(ahci.BlockDevice)) void {
+ afs_instance = fs;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const kata = sensei.get_current_kata() orelse return;
+ const exit_code = ctx.rdi;
+
+ var i: u32 = 0;
+ while (i < kata_limits.MAX_ATTACHMENTS) : (i += 1) {
+ if (kata.attachments[i]) |entry| {
+ if (entry.attachment_type == .Unit) {
+ attachment.seal(kata, i, afs_instance);
+ }
+ }
+ }
+
+ kata.exit_code = exit_code;
+ kata_mod.dissolve_kata(kata.id);
+ sensei.schedule();
+}
diff --git a/mirai.old/invocations/kata/postman.zig b/mirai.old/invocations/kata/postman.zig
new file mode 100644
index 0000000..6bea674
--- /dev/null
+++ b/mirai.old/invocations/kata/postman.zig
@@ -0,0 +1,80 @@
+//! Postman invocation - Letter passing between parent and child Kata
+
+const copy = @import("../../utils/mem/copy.zig");
+const handler = @import("../handler.zig");
+const heap = @import("../../memory/heap.zig");
+const int = @import("../../utils/types/int.zig");
+const kata_constants = @import("../../common/constants/kata.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const memory_limits = @import("../../common/limits/memory.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const slice = @import("../../utils/mem/slice.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ switch (ctx.rdi) {
+ kata_constants.POSTMAN_SEND => send_letter(ctx),
+ kata_constants.POSTMAN_READ => read_letter(ctx),
+ else => result.set_error(ctx),
+ }
+}
+
+fn send_letter(ctx: *handler.InvocationContext) void {
+ const letter_type = int.u8_of(ctx.rsi);
+ const data_ptr = ctx.rdx;
+ const data_len = int.u16_of(ctx.r10);
+
+ if (data_len > kata_limits.MAX_LETTER_LENGTH) return result.set_error(ctx);
+ if (data_len > 0 and !memory_limits.is_valid_kata_pointer(data_ptr)) return result.set_error(ctx);
+
+ const sender = sensei.get_current_kata() orelse return result.set_error(ctx);
+ const parent = kata_mod.get_kata(sender.parent_id) orelse return result.set_error(ctx);
+
+ parent.letter_type = letter_type;
+ parent.letter_len = data_len;
+
+ if (data_len > 0) {
+ if (parent.letter_capacity < data_len) {
+ if (parent.letter_data) |old| {
+ heap.free(@ptrCast(old), parent.letter_capacity);
+ }
+ const new_buf = heap.alloc(data_len) orelse return result.set_error(ctx);
+ parent.letter_data = new_buf;
+ parent.letter_capacity = data_len;
+ }
+ copy.from_ptr(parent.letter_data.?[0..data_len], data_ptr, data_len);
+ }
+
+ result.set_ok(ctx);
+}
+
+fn read_letter(ctx: *handler.InvocationContext) void {
+ const buffer_ptr = ctx.rsi;
+ const buffer_len = ctx.rdx;
+
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ if (kata.letter_type == kata_constants.LETTER_NONE) {
+ return result.set_value(ctx, 0);
+ }
+
+ if (kata.letter_len > 0) {
+ if (!memory_limits.is_valid_kata_pointer(buffer_ptr)) return result.set_error(ctx);
+ if (kata.letter_len > buffer_len) return result.set_error(ctx);
+
+ if (kata.letter_data) |data| {
+ copy.to_ptr(buffer_ptr, data[0..kata.letter_len]);
+ }
+
+ if (kata.letter_len < buffer_len) {
+ slice.byte_ptr(buffer_ptr)[kata.letter_len] = 0;
+ }
+ }
+
+ const letter_type = kata.letter_type;
+ kata.letter_type = kata_constants.LETTER_NONE;
+ kata.letter_len = 0;
+
+ result.set_value(ctx, letter_type);
+}
diff --git a/mirai.old/invocations/kata/reap.zig b/mirai.old/invocations/kata/reap.zig
new file mode 100644
index 0000000..fd47794
--- /dev/null
+++ b/mirai.old/invocations/kata/reap.zig
@@ -0,0 +1,62 @@
+//! Reap invocation - Shinigami cleans up zombie katas
+
+const handler = @import("../handler.zig");
+const memory = @import("../../kata/memory.zig");
+const paging_const = @import("../../common/constants/paging.zig");
+const pool = @import("../../kata/pool.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+const types = @import("../../kata/types.zig");
+
+const HIGHER_HALF: u64 = 0xFFFF800000000000;
+
+fn check_ash_pd256() u64 {
+ for (&pool.pool, 0..) |*k, i| {
+ if (pool.used[i] and k.id == 3 and k.page_table != 0) {
+ const pml4: [*]volatile u64 = @ptrFromInt(k.page_table + HIGHER_HALF);
+ if ((pml4[0] & 1) == 0) return 0xDEAD0001;
+ const pdpt: [*]volatile u64 = @ptrFromInt((pml4[0] & paging_const.PTE_MASK) + HIGHER_HALF);
+ if ((pdpt[0] & 1) == 0) return 0xDEAD0002;
+ const pd: [*]volatile u64 = @ptrFromInt((pdpt[0] & paging_const.PTE_MASK) + HIGHER_HALF);
+ return pd[256];
+ }
+ }
+ return 0xDEAD0000;
+}
+
+/// Reap zombie katas - called by Shinigami
+/// Returns the number of zombies reaped
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ var reaped: u64 = 0;
+
+ for (&pool.pool, 0..) |*kata, i| {
+ if (!pool.used[i]) continue;
+ if (kata.state != .Zombie) continue;
+
+ // Found a zombie - reap it
+ const kata_id = kata.id;
+ const pt = kata.page_table;
+
+ const pd256_before = check_ash_pd256();
+ serial.printf("Shinigami: reaping kata {d} pt={x} ash_pd256={x}\n", .{ kata_id, pt, pd256_before });
+
+ // Destroy the page table
+ if (kata.page_table != 0) {
+ memory.destroy_zombie_page_table(kata.page_table);
+ kata.page_table = 0;
+ }
+
+ const pd256_after = check_ash_pd256();
+ if (pd256_after != pd256_before) {
+ serial.printf("Shinigami: CORRUPTION during reap! pd256: {x} -> {x}\n", .{ pd256_before, pd256_after });
+ }
+
+ // Mark as dissolved and free the slot
+ kata.state = .Dissolved;
+ pool.used[i] = false;
+
+ serial.printf("Shinigami: reaped kata {d}\n", .{kata_id});
+ reaped += 1;
+ }
+
+ ctx.rax = reaped;
+}
diff --git a/mirai.old/invocations/kata/spawn.zig b/mirai.old/invocations/kata/spawn.zig
new file mode 100644
index 0000000..83ae61d
--- /dev/null
+++ b/mirai.old/invocations/kata/spawn.zig
@@ -0,0 +1,163 @@
+//! Spawn invocation - Create new Kata from executable
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const copy = @import("../../utils/mem/copy.zig");
+const fs_limits = @import("../../common/limits/fs.zig");
+const handler = @import("../handler.zig");
+const hikari = @import("../../hikari/loader.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const memory_limits = @import("../../common/limits/memory.zig");
+const paging_const = @import("../../common/constants/paging.zig");
+const pmm = @import("../../memory/pmm.zig");
+const pool = @import("../../kata/pool.zig");
+const result = @import("../../utils/types/result.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+const slice = @import("../../utils/mem/slice.zig");
+
+const HIGHER_HALF: u64 = 0xFFFF800000000000;
+
+var afs_instance: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn set_afs_instance(fs: *afs.AFS(ahci.BlockDevice)) void {
+ afs_instance = fs;
+}
+
+fn check_ash_pd256() u64 {
+ for (&pool.pool, 0..) |*k, i| {
+ if (pool.used[i] and k.id == 3 and k.page_table != 0) {
+ const pml4: [*]volatile u64 = @ptrFromInt(k.page_table + HIGHER_HALF);
+ if ((pml4[0] & 1) == 0) return 0xDEAD0001;
+ const pdpt: [*]volatile u64 = @ptrFromInt((pml4[0] & paging_const.PTE_MASK) + HIGHER_HALF);
+ if ((pdpt[0] & 1) == 0) return 0xDEAD0002;
+ const pd: [*]volatile u64 = @ptrFromInt((pdpt[0] & paging_const.PTE_MASK) + HIGHER_HALF);
+ return pd[256];
+ }
+ }
+ return 0xDEAD0000;
+}
+
+fn verify_kernel_mapping(phys: u64) bool {
+ // Check if physical address is correctly mapped via higher half
+ const virt = phys + HIGHER_HALF;
+
+ // Read from that address and write back - if mapping is wrong, we'd write to wrong place
+ const ptr: [*]volatile u64 = @ptrFromInt(virt);
+ const val = ptr[0];
+ _ = val;
+
+ // Check kernel's CR3 mapping of this address
+ const asm_memory = @import("../../asm/memory.zig");
+ const kernel_cr3 = asm_memory.read_page_table_base() & ~@as(u64, 0xFFF);
+
+ const pml4_idx = (virt >> 39) & 0x1FF;
+ const pdpt_idx = (virt >> 30) & 0x1FF;
+ const pd_idx = (virt >> 21) & 0x1FF;
+ const pt_idx = (virt >> 12) & 0x1FF;
+
+ const pml4: [*]volatile u64 = @ptrFromInt(kernel_cr3 + HIGHER_HALF);
+ if ((pml4[pml4_idx] & 1) == 0) {
+ serial.printf("VERIFY: pml4[{d}] not present for phys {x}\n", .{ pml4_idx, phys });
+ return false;
+ }
+
+ const pdpt: [*]volatile u64 = @ptrFromInt((pml4[pml4_idx] & paging_const.PTE_MASK) + HIGHER_HALF);
+ if ((pdpt[pdpt_idx] & 1) == 0) {
+ serial.printf("VERIFY: pdpt[{d}] not present for phys {x}\n", .{ pdpt_idx, phys });
+ return false;
+ }
+
+ const pd: [*]volatile u64 = @ptrFromInt((pdpt[pdpt_idx] & paging_const.PTE_MASK) + HIGHER_HALF);
+ if ((pd[pd_idx] & 1) == 0) {
+ serial.printf("VERIFY: pd[{d}] not present for phys {x}\n", .{ pd_idx, phys });
+ return false;
+ }
+
+ const pt: [*]volatile u64 = @ptrFromInt((pd[pd_idx] & paging_const.PTE_MASK) + HIGHER_HALF);
+ if ((pt[pt_idx] & 1) == 0) {
+ serial.printf("VERIFY: pt[{d}] not present for phys {x}\n", .{ pt_idx, phys });
+ return false;
+ }
+
+ const mapped_phys = pt[pt_idx] & paging_const.PTE_MASK;
+ if (mapped_phys != phys) {
+ serial.printf("VERIFY: phys {x} maps to {x} instead!\n", .{ phys, mapped_phys });
+ return false;
+ }
+
+ return true;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const fs = afs_instance orelse return result.set_error(ctx);
+
+ const location_ptr = ctx.rdi;
+ const location_len = ctx.rsi;
+ const pv_ptr = ctx.rdx;
+ const pc = ctx.r10;
+
+ if (!memory_limits.is_valid_kata_pointer(location_ptr)) return result.set_error(ctx);
+ if (location_len > fs_limits.MAX_LOCATION_LENGTH) return result.set_error(ctx);
+
+ var location_buf: [fs_limits.MAX_LOCATION_LENGTH]u8 = undefined;
+ copy.from_ptr(&location_buf, location_ptr, location_len);
+ const location = location_buf[0..location_len];
+
+ var params: [kata_limits.MAX_PARAMETERS][]const u8 = undefined;
+ var param_count: usize = 1;
+ params[0] = location;
+
+ if (pc > 1 and pv_ptr != 0 and memory_limits.is_valid_kata_pointer(pv_ptr)) {
+ const pv = slice.typed_ptr_const(u64, pv_ptr);
+
+ var i: usize = 1;
+ while (i < pc and param_count < kata_limits.MAX_PARAMETERS) : (i += 1) {
+ const param_ptr = pv[i];
+ if (!memory_limits.is_valid_kata_pointer(param_ptr)) break;
+
+ const param_str = slice.null_term_ptr(param_ptr);
+ var len: usize = 0;
+ while (param_str[len] != 0 and len < kata_limits.MAX_LOCATION_LENGTH) : (len += 1) {}
+
+ params[param_count] = param_str[0..len];
+ param_count += 1;
+ }
+ }
+
+ const pd256_before = check_ash_pd256();
+
+ // Verify kernel can correctly access Ash's PD
+ if (pmm.ash_pd_phys != 0) {
+ if (!verify_kernel_mapping(pmm.ash_pd_phys)) {
+ serial.printf("spawn: Kernel mapping of Ash's PD is WRONG!\n", .{});
+ }
+ }
+
+ const kata_id = hikari.load_with_args(fs, location, params[0..param_count]) catch {
+ return result.set_error(ctx);
+ };
+
+ const pd256_after = check_ash_pd256();
+
+ // Track Ash's PD page (kata 3 is Ash)
+ if (kata_id == 3) {
+ if (pool.get(kata_id)) |kata| {
+ if (kata.page_table != 0) {
+ const pml4: [*]volatile u64 = @ptrFromInt(kata.page_table + HIGHER_HALF);
+ if ((pml4[0] & 1) != 0) {
+ const pdpt: [*]volatile u64 = @ptrFromInt((pml4[0] & paging_const.PTE_MASK) + HIGHER_HALF);
+ if ((pdpt[0] & 1) != 0) {
+ const pd_phys = pdpt[0] & paging_const.PTE_MASK;
+ pmm.set_ash_pd(pd_phys);
+ }
+ }
+ }
+ }
+ } else if (pd256_after != pd256_before and pd256_before != 0xDEAD0000) {
+ serial.printf("spawn: CORRUPTION during spawn of kata {d}! pd256: {x} -> {x}\n", .{ kata_id, pd256_before, pd256_after });
+ }
+
+ serial.printf("spawn: created kata {d}\n", .{kata_id});
+
+ result.set_value(ctx, kata_id);
+}
diff --git a/mirai.old/invocations/kata/wait.zig b/mirai.old/invocations/kata/wait.zig
new file mode 100644
index 0000000..e657e90
--- /dev/null
+++ b/mirai.old/invocations/kata/wait.zig
@@ -0,0 +1,46 @@
+//! Wait invocation - Wait for child Kata to dissolve
+
+const handler = @import("../handler.zig");
+const int = @import("../../utils/types/int.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const target_id = int.u32_of(ctx.rdi);
+
+ const target = kata_mod.get_kata(target_id) orelse return result.set_error(ctx);
+
+ // Zombie or Dissolved means the child has exited
+ if (target.state == .Zombie or target.state == .Dissolved) {
+ return result.set_value(ctx, target.exit_code);
+ }
+
+ const current = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ current.state = .Stalled;
+ current.waiting_for = target_id;
+
+ current.context.rax = 0;
+ current.context.rbx = ctx.rbx;
+ current.context.rcx = ctx.rcx;
+ current.context.rdx = ctx.rdx;
+ current.context.rsi = ctx.rsi;
+ current.context.rdi = ctx.rdi;
+ current.context.rbp = ctx.rbp;
+ current.context.rsp = ctx.rsp;
+ current.context.r8 = ctx.r8;
+ current.context.r9 = ctx.r9;
+ current.context.r10 = ctx.r10;
+ current.context.r11 = ctx.r11;
+ current.context.r12 = ctx.r12;
+ current.context.r13 = ctx.r13;
+ current.context.r14 = ctx.r14;
+ current.context.r15 = ctx.r15;
+ current.context.rip = ctx.rip;
+ current.context.rflags = ctx.rflags;
+ current.context.cs = ctx.cs;
+ current.context.ss = ctx.ss;
+
+ sensei.schedule();
+}
diff --git a/mirai.old/invocations/kata/yield.zig b/mirai.old/invocations/kata/yield.zig
new file mode 100644
index 0000000..0da91af
--- /dev/null
+++ b/mirai.old/invocations/kata/yield.zig
@@ -0,0 +1,42 @@
+//! Yield invocation - Voluntarily give up CPU time to Sensei
+
+const handler = @import("../handler.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const kata = sensei.get_current_kata() orelse return result.set_error(ctx);
+
+ kata.state = kata_mod.State.Alive;
+
+ // Save context before switching - schedule() may not return
+ kata.context.rax = 0; // Return value for yield
+ kata.context.rbx = ctx.rbx;
+ kata.context.rcx = ctx.rcx;
+ kata.context.rdx = ctx.rdx;
+ kata.context.rsi = ctx.rsi;
+ kata.context.rdi = ctx.rdi;
+ kata.context.rbp = ctx.rbp;
+ kata.context.rsp = ctx.rsp;
+ kata.context.r8 = ctx.r8;
+ kata.context.r9 = ctx.r9;
+ kata.context.r10 = ctx.r10;
+ kata.context.r11 = ctx.r11;
+ kata.context.r12 = ctx.r12;
+ kata.context.r13 = ctx.r13;
+ kata.context.r14 = ctx.r14;
+ kata.context.r15 = ctx.r15;
+ kata.context.rip = ctx.rip;
+ kata.context.rflags = ctx.rflags;
+ kata.context.cs = ctx.cs;
+ kata.context.ss = ctx.ss;
+
+ if (!sensei.is_in_queue(kata)) {
+ sensei.enqueue_kata(kata);
+ }
+
+ sensei.schedule();
+
+ result.set_ok(ctx);
+}
diff --git a/mirai.old/invocations/os/cpuinfo.zig b/mirai.old/invocations/os/cpuinfo.zig
new file mode 100644
index 0000000..900902b
--- /dev/null
+++ b/mirai.old/invocations/os/cpuinfo.zig
@@ -0,0 +1,42 @@
+//! CPUINFO invocation - Get CPU model string
+
+const cpuid = @import("../../asm/cpuid.zig");
+const handler = @import("../handler.zig");
+const result = @import("../../utils/types/result.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const buffer_ptr: [*]u8 = @ptrFromInt(ctx.rdi);
+ const buffer_len: usize = @intCast(ctx.rsi);
+
+ if (buffer_len < 48) {
+ result.set_error(ctx);
+ return;
+ }
+
+ if (!cpuid.has_brand_string()) {
+ const generic = "Unknown CPU";
+ for (generic, 0..) |c, i| {
+ buffer_ptr[i] = c;
+ }
+ result.set_value(ctx, generic.len);
+ return;
+ }
+
+ var brand: [48]u8 = undefined;
+ cpuid.get_brand_string(&brand);
+
+ // Trim leading spaces
+ var start: usize = 0;
+ while (start < 48 and brand[start] == ' ') : (start += 1) {}
+
+ // Trim trailing spaces and nulls
+ var end: usize = 48;
+ while (end > start and (brand[end - 1] == ' ' or brand[end - 1] == 0)) : (end -= 1) {}
+
+ const trimmed_len = end - start;
+ for (0..trimmed_len) |i| {
+ buffer_ptr[i] = brand[start + i];
+ }
+
+ result.set_value(ctx, trimmed_len);
+}
diff --git a/mirai.old/invocations/os/diskinfo.zig b/mirai.old/invocations/os/diskinfo.zig
new file mode 100644
index 0000000..62c73dc
--- /dev/null
+++ b/mirai.old/invocations/os/diskinfo.zig
@@ -0,0 +1,26 @@
+//! DISKINFO invocation - Get disk usage information
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const handler = @import("../handler.zig");
+const result = @import("../../utils/types/result.zig");
+
+var fs_instance: ?*afs.AFS(ahci.BlockDevice) = null;
+
+pub fn set_afs_instance(fs: *afs.AFS(ahci.BlockDevice)) void {
+ fs_instance = fs;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const total_ptr: *u64 = @ptrFromInt(ctx.rdi);
+ const used_ptr: *u64 = @ptrFromInt(ctx.rsi);
+
+ if (fs_instance) |fs| {
+ const info = fs.get_disk_info();
+ total_ptr.* = info.total_bytes;
+ used_ptr.* = info.used_bytes;
+ result.set_ok(ctx);
+ } else {
+ result.set_error(ctx);
+ }
+}
diff --git a/mirai.old/invocations/os/gettime.zig b/mirai.old/invocations/os/gettime.zig
new file mode 100644
index 0000000..8645093
--- /dev/null
+++ b/mirai.old/invocations/os/gettime.zig
@@ -0,0 +1,72 @@
+//! GETTIME invocation - Get current unix timestamp
+
+const cmos = @import("../../asm/cmos.zig");
+const handler = @import("../handler.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const time_const = @import("../../common/constants/time.zig");
+
+var boot_timestamp: u64 = 0;
+
+pub fn set_boot_timestamp(timestamp: u64) void {
+ boot_timestamp = timestamp;
+}
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ if (boot_timestamp == 0) {
+ const rtc_time = read_rtc();
+ result.set_value(ctx, rtc_time);
+ } else {
+ const ticks = sensei.get_tick_count();
+ const seconds = ticks / time_const.TICKS_PER_SECOND;
+ result.set_value(ctx, boot_timestamp + seconds);
+ }
+}
+
+fn read_rtc() u64 {
+ const sec = cmos.bcd_to_bin(cmos.read_seconds());
+ const min = cmos.bcd_to_bin(cmos.read_minutes());
+ const hr = cmos.bcd_to_bin(cmos.read_hours());
+ const d = cmos.bcd_to_bin(cmos.read_day());
+ const m = cmos.bcd_to_bin(cmos.read_month());
+ const y = cmos.bcd_to_bin(cmos.read_year());
+ const c = cmos.bcd_to_bin(cmos.read_century());
+
+ const full_year = @as(u64, c) * 100 + @as(u64, y);
+
+ return to_unix_timestamp(full_year, m, d, hr, min, sec);
+}
+
+fn to_unix_timestamp(year: u64, month: u8, day: u8, hour: u8, minute: u8, second: u8) u64 {
+ const DAYS_BEFORE_MONTH = [_]u64{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+ var days: u64 = 0;
+
+ // Days from years since 1970
+ var y = time_const.EPOCH_YEAR;
+ while (y < year) : (y += 1) {
+ days += if (is_leap(y)) 366 else 365;
+ }
+
+ // Days from months
+ if (month > 0 and month <= 12) {
+ days += DAYS_BEFORE_MONTH[month - 1];
+ }
+
+ // Add leap day if past February in a leap year
+ if (month > 2 and is_leap(year)) {
+ days += 1;
+ }
+
+ // Days in current month
+ days += day - 1;
+
+ return days * time_const.SECONDS_PER_DAY +
+ @as(u64, hour) * time_const.SECONDS_PER_HOUR +
+ @as(u64, minute) * time_const.SECONDS_PER_MINUTE +
+ @as(u64, second);
+}
+
+fn is_leap(year: u64) bool {
+ return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0);
+}
diff --git a/mirai.old/invocations/os/meminfo.zig b/mirai.old/invocations/os/meminfo.zig
new file mode 100644
index 0000000..e1f3b3c
--- /dev/null
+++ b/mirai.old/invocations/os/meminfo.zig
@@ -0,0 +1,20 @@
+//! MEMINFO invocation - Get memory information
+
+const handler = @import("../handler.zig");
+const memory = @import("../../common/constants/memory.zig");
+const pmm = @import("../../memory/pmm.zig");
+const result = @import("../../utils/types/result.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const total_ptr: *u64 = @ptrFromInt(ctx.rdi);
+ const used_ptr: *u64 = @ptrFromInt(ctx.rsi);
+ const free_ptr: *u64 = @ptrFromInt(ctx.rdx);
+
+ const info = pmm.get_info();
+
+ total_ptr.* = info.total * memory.PAGE_SIZE;
+ used_ptr.* = info.used * memory.PAGE_SIZE;
+ free_ptr.* = (info.total - info.used) * memory.PAGE_SIZE;
+
+ result.set_ok(ctx);
+}
diff --git a/mirai.old/invocations/os/uptime.zig b/mirai.old/invocations/os/uptime.zig
new file mode 100644
index 0000000..2d28df5
--- /dev/null
+++ b/mirai.old/invocations/os/uptime.zig
@@ -0,0 +1,12 @@
+//! UPTIME invocation - Get seconds since boot
+
+const handler = @import("../handler.zig");
+const result = @import("../../utils/types/result.zig");
+const sensei = @import("../../kata/sensei/sensei.zig");
+const time = @import("../../common/constants/time.zig");
+
+pub fn invoke(ctx: *handler.InvocationContext) void {
+ const ticks = sensei.get_tick_count();
+ const seconds = ticks / time.TICKS_PER_SECOND;
+ result.set_value(ctx, seconds);
+}
diff --git a/mirai.old/invocations/syscall.zig b/mirai.old/invocations/syscall.zig
new file mode 100644
index 0000000..73a7b68
--- /dev/null
+++ b/mirai.old/invocations/syscall.zig
@@ -0,0 +1,85 @@
+//! SYSCALL/SYSRET implementation - Invocation mechanism using MSRs
+
+const entry = @import("../asm/entry.zig");
+const gdt = @import("../boot/gdt/gdt.zig");
+const handler = @import("handler.zig");
+const int = @import("../utils/types/int.zig");
+const msr_const = @import("../common/constants/msr.zig");
+const msr = @import("../asm/msr.zig");
+const ptr = @import("../utils/types/ptr.zig");
+const tss = @import("../boot/tss/tss.zig");
+
+const SYSRET_USER_BASE_OFFSET: u64 = 16;
+
+const SyscallContext = packed struct {
+ rax: u64,
+ rbx: u64,
+ rcx: u64,
+ rdx: u64,
+ rsi: u64,
+ rdi: u64,
+ rbp: u64,
+ r8: u64,
+ r9: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ user_cs: u64,
+ user_rip: u64,
+ user_ss: u64,
+ user_rflags: u64,
+ user_rsp: u64,
+};
+
+pub fn init() void {
+ const kernel_cs = int.u64_of(gdt.KERNEL_CODE);
+ const user_base = int.u64_of(gdt.USER_CODE) - SYSRET_USER_BASE_OFFSET;
+
+ const star_value =
+ (kernel_cs << msr_const.STAR_KERNEL_CS_SHIFT) |
+ (user_base << msr_const.STAR_USER_BASE_SHIFT);
+
+ msr.write(msr_const.IA32_STAR, star_value);
+ msr.write(msr_const.IA32_LSTAR, entry.get_entry_address());
+ msr.write(msr_const.IA32_FMASK, msr_const.FMASK_IF);
+
+ const efer = msr.read(msr_const.IA32_EFER);
+ msr.write(msr_const.IA32_EFER, efer | msr_const.EFER_SCE);
+}
+
+export fn handle_syscall(ctx_ptr: u64) void {
+ const regs = ptr.of(SyscallContext, ctx_ptr);
+
+ var ctx = handler.InvocationContext{
+ .rax = regs.rax,
+ .rbx = regs.rbx,
+ .rcx = regs.rcx,
+ .rdx = regs.rdx,
+ .rsi = regs.rsi,
+ .rdi = regs.rdi,
+ .rbp = regs.rbp,
+ .rsp = regs.user_rsp,
+ .r8 = regs.r8,
+ .r9 = regs.r9,
+ .r10 = regs.r10,
+ .r11 = regs.r11,
+ .r12 = regs.r12,
+ .r13 = regs.r13,
+ .r14 = regs.r14,
+ .r15 = regs.r15,
+ .rip = regs.user_rip,
+ .rflags = regs.user_rflags,
+ .cs = regs.user_cs,
+ .ss = regs.user_ss,
+ };
+
+ handler.handle(&ctx);
+ regs.rax = ctx.rax;
+}
+
+export fn get_kernel_stack() u64 {
+ return tss.get_kernel_stack();
+}
diff --git a/mirai.old/kata/attachment.zig b/mirai.old/kata/attachment.zig
new file mode 100644
index 0000000..9d9ed35
--- /dev/null
+++ b/mirai.old/kata/attachment.zig
@@ -0,0 +1,59 @@
+//! Attachment management
+
+const attachment_limits = @import("../common/limits/attachment.zig");
+const kata_limits = @import("../common/limits/kata.zig");
+
+pub const Type = enum {
+ Unit,
+ Device,
+ Closed,
+};
+
+pub const DeviceType = enum {
+ Source,
+ Stream,
+ Trace,
+ Void,
+ Chaos,
+ Zero,
+ Console,
+};
+
+pub const Attachment = struct {
+ attachment_type: Type = .Closed,
+
+ location: [attachment_limits.MAX_LOCATION_LENGTH]u8 = undefined,
+ location_len: usize = 0,
+ position: u64 = 0,
+ unit_size: u64 = 0,
+ buffer: ?[]u8 = null,
+ flags: u32 = 0,
+ dirty: bool = false,
+
+ device_type: ?DeviceType = null,
+};
+
+const POOL_SIZE = kata_limits.MAX_KATAS * kata_limits.MAX_ATTACHMENTS;
+var pool: [POOL_SIZE]Attachment = undefined;
+var pool_used: [POOL_SIZE]bool = [_]bool{false} ** POOL_SIZE;
+
+pub fn alloc() ?*Attachment {
+ for (&pool, 0..) |*entry, i| {
+ if (!pool_used[i]) {
+ pool_used[i] = true;
+ entry.* = Attachment{};
+ return entry;
+ }
+ }
+ return null;
+}
+
+pub fn free(ptr: *Attachment) void {
+ const addr = @intFromPtr(ptr);
+ const base = @intFromPtr(&pool[0]);
+ const size = @sizeOf(Attachment);
+ if (addr >= base and addr < base + POOL_SIZE * size) {
+ const idx = (addr - base) / size;
+ pool_used[idx] = false;
+ }
+}
diff --git a/mirai.old/kata/kata.zig b/mirai.old/kata/kata.zig
new file mode 100644
index 0000000..9f95187
--- /dev/null
+++ b/mirai.old/kata/kata.zig
@@ -0,0 +1,29 @@
+//! Kata - Process management
+
+pub const types = @import("types.zig");
+pub const pool = @import("pool.zig");
+pub const attachment = @import("attachment.zig");
+pub const memory = @import("memory.zig");
+pub const sensei = @import("sensei/sensei.zig");
+pub const shift = @import("shift.zig");
+
+pub const Kata = types.Kata;
+pub const Context = types.Context;
+pub const State = types.State;
+pub const InterruptContext = types.InterruptContext;
+
+pub const Attachment = attachment.Attachment;
+pub const AttachmentType = attachment.Type;
+pub const DeviceType = attachment.DeviceType;
+
+pub const init = pool.init;
+pub const create_kata = pool.create;
+pub const get_kata = pool.get;
+pub const dissolve_kata = pool.dissolve;
+
+pub const kata_pool = pool.pool;
+pub const kata_used = pool.used;
+
+pub const setup_kata_memory = memory.setup;
+pub const load_segment = memory.load_segment;
+pub const VirtualBuffer = memory.VirtualBuffer;
diff --git a/mirai.old/kata/memory.zig b/mirai.old/kata/memory.zig
new file mode 100644
index 0000000..f90c9f0
--- /dev/null
+++ b/mirai.old/kata/memory.zig
@@ -0,0 +1,288 @@
+//! Kata memory management
+
+const asm_memory = @import("../asm/memory.zig");
+const memory_const = @import("../common/constants/memory.zig");
+const memory_limits = @import("../common/limits/memory.zig");
+const paging = @import("../memory/paging.zig");
+const paging_const = @import("../common/constants/paging.zig");
+const pmm = @import("../memory/pmm.zig");
+const types = @import("types.zig");
+
+const HIGHER_HALF = memory_const.HIGHER_HALF_START;
+const PAGE_SIZE = memory_const.PAGE_SIZE;
+
+const KERNEL_VMALLOC_START: u64 = 0xFFFFFF8000000000;
+var next_vmalloc_addr: u64 = KERNEL_VMALLOC_START;
+
+pub const VirtualBuffer = struct {
+ data: []u8,
+ virt_base: u64,
+ phys_pages: [1024]u64,
+ num_pages: usize,
+
+ pub fn alloc(size: usize) !VirtualBuffer {
+ const num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+ if (num_pages > 1024) return error.AllocationTooLarge;
+
+ var buffer: VirtualBuffer = undefined;
+ buffer.num_pages = num_pages;
+
+ var i: usize = 0;
+ errdefer {
+ var j: usize = 0;
+ while (j < i) : (j += 1) {
+ pmm.free_page(buffer.phys_pages[j]);
+ }
+ }
+
+ while (i < num_pages) : (i += 1) {
+ buffer.phys_pages[i] = pmm.alloc_page() orelse return error.OutOfMemory;
+ }
+
+ buffer.virt_base = next_vmalloc_addr;
+ next_vmalloc_addr += num_pages * PAGE_SIZE;
+
+ i = 0;
+ errdefer {
+ var j: usize = 0;
+ while (j < num_pages) : (j += 1) {
+ pmm.free_page(buffer.phys_pages[j]);
+ }
+ }
+
+ while (i < num_pages) : (i += 1) {
+ const virt_addr = buffer.virt_base + (i * PAGE_SIZE);
+ const phys_addr = buffer.phys_pages[i];
+
+ try paging.map_page(virt_addr, phys_addr, paging.PAGE_WRITABLE);
+
+ const zero_ptr: [*]volatile u8 = @ptrFromInt(phys_addr + HIGHER_HALF);
+ for (0..PAGE_SIZE) |j| {
+ zero_ptr[j] = 0;
+ }
+ }
+
+ const data_ptr: [*]u8 = @ptrFromInt(buffer.virt_base);
+ buffer.data = data_ptr[0..size];
+
+ return buffer;
+ }
+
+ pub fn free(self: *VirtualBuffer) void {
+ for (0..self.num_pages) |i| {
+ pmm.free_page(self.phys_pages[i]);
+ }
+ }
+};
+
+pub fn setup(kata: *types.Kata, framebuffer_phys: u64, framebuffer_size: u64) !void {
+ kata.page_table = try paging.create_page_table();
+
+ kata.user_stack_top = memory_const.USER_STACK_TOP;
+ kata.user_stack_bottom = memory_const.USER_STACK_TOP - (memory_const.USER_STACK_MAX_PAGES * PAGE_SIZE);
+ kata.user_stack_committed = memory_const.USER_STACK_TOP - (memory_const.USER_STACK_INITIAL_PAGES * PAGE_SIZE);
+
+ const serial = @import("../drivers/serial/serial.zig");
+
+ for (0..memory_const.USER_STACK_INITIAL_PAGES) |i| {
+ const page = pmm.alloc_page() orelse return error.OutOfMemory;
+
+ // Check if we got Ash's PD
+ if (pmm.ash_pd_phys != 0 and page == pmm.ash_pd_phys) {
+ serial.printf("SETUP: Got Ash's PD {x} for user stack page!\n", .{page});
+ }
+
+ const virt = kata.user_stack_committed + (i * PAGE_SIZE);
+ _ = try paging.map_page_in_table(kata.page_table, virt, page, paging.PAGE_WRITABLE | paging.PAGE_USER);
+
+ const page_ptr: [*]volatile u8 = @ptrFromInt(page + HIGHER_HALF);
+ for (0..PAGE_SIZE) |j| {
+ page_ptr[j] = 0;
+ }
+ }
+
+ const first_page = pmm.alloc_page() orelse return error.OutOfMemory;
+
+ // Check if we got Ash's PD for kernel stack
+ if (pmm.ash_pd_phys != 0 and first_page == pmm.ash_pd_phys) {
+ serial.printf("SETUP: Got Ash's PD {x} for kernel stack!\n", .{first_page});
+ }
+
+ const kernel_stack_base = first_page;
+
+ // Identity map first page
+ _ = try paging.map_page_in_table(kata.page_table, first_page, first_page, paging.PAGE_WRITABLE);
+
+ // Zero the page
+ var page_ptr: [*]volatile u8 = @ptrFromInt(first_page + HIGHER_HALF);
+ for (0..PAGE_SIZE) |j| {
+ page_ptr[j] = 0;
+ }
+
+ // Allocate remaining pages, checking for contiguity
+ var i: u64 = 1;
+ while (i < memory_const.KERNEL_STACK_PAGES) : (i += 1) {
+ const page = pmm.alloc_page() orelse return error.OutOfMemory;
+ const expected = first_page + (i * PAGE_SIZE);
+
+ if (page != expected) {
+ // Non-contiguous - free what we got and use smaller stack
+ pmm.free_page(page);
+ break;
+ }
+
+ // Identity map this page
+ _ = try paging.map_page_in_table(kata.page_table, page, page, paging.PAGE_WRITABLE);
+
+ // Zero the page
+ page_ptr = @ptrFromInt(page + HIGHER_HALF);
+ for (0..PAGE_SIZE) |j| {
+ page_ptr[j] = 0;
+ }
+ }
+
+ // Stack top is at the end of allocated contiguous pages (identity address)
+ const actual_stack_size = i * PAGE_SIZE;
+ kata.stack_top = kernel_stack_base + actual_stack_size;
+
+ if (framebuffer_phys != 0 and framebuffer_size > 0) {
+ const fb_pages = (framebuffer_size + PAGE_SIZE - 1) / PAGE_SIZE;
+ for (0..fb_pages) |fi| {
+ const phys = framebuffer_phys + (fi * PAGE_SIZE);
+ _ = try paging.map_page_in_table(
+ kata.page_table,
+ phys,
+ phys,
+ paging.PAGE_WRITABLE | paging.PAGE_USER,
+ );
+ }
+ }
+}
+
+pub fn load_segment(
+ kata: *types.Kata,
+ vaddr: u64,
+ elf_data: []const u8,
+ data_offset: u64,
+ data_size: u64,
+ mem_size: u64,
+ flags: u32,
+) !void {
+ if (mem_size == 0) return;
+ if (data_size > mem_size) return error.InvalidSegment;
+ if (data_offset + data_size > elf_data.len) return error.SegmentOutOfBounds;
+
+ if (!memory_limits.is_kata_range(vaddr, mem_size)) {
+ return error.InvalidAddress;
+ }
+
+ const page_aligned_vaddr = vaddr & ~@as(u64, 0xFFF);
+ const offset_in_page = vaddr - page_aligned_vaddr;
+
+ const total_size = offset_in_page + mem_size;
+ const num_pages = (total_size + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ var page_flags: u64 = paging.PAGE_USER;
+ if ((flags & 0x2) != 0) {
+ page_flags |= paging.PAGE_WRITABLE;
+ }
+
+ for (0..num_pages) |i| {
+ const page_vaddr = page_aligned_vaddr + (i * PAGE_SIZE);
+
+ var page_phys: u64 = 0;
+ const existing_entry = paging.get_page_entry(kata.page_table, page_vaddr);
+
+ if (existing_entry != null and (existing_entry.? & paging.PAGE_PRESENT) != 0) {
+ page_phys = existing_entry.? & ~@as(u64, 0xFFF);
+
+ const existing_writable = (existing_entry.? & paging.PAGE_WRITABLE) != 0;
+ const new_writable = (page_flags & paging.PAGE_WRITABLE) != 0;
+
+ if (new_writable and !existing_writable) {
+ _ = try paging.map_page_in_table(kata.page_table, page_vaddr, page_phys, page_flags);
+ }
+ } else {
+ page_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+
+ // Check if we got Ash's PD
+ if (pmm.ash_pd_phys != 0 and page_phys == pmm.ash_pd_phys) {
+ const serial = @import("../drivers/serial/serial.zig");
+ serial.printf("KATA-MEM: Got Ash's PD {x} for data page at vaddr {x}!\n", .{ page_phys, page_vaddr });
+ }
+
+ _ = try paging.map_page_in_table(kata.page_table, page_vaddr, page_phys, page_flags);
+
+ const zero_ptr: [*]volatile u8 = @ptrFromInt(page_phys + HIGHER_HALF);
+ for (0..PAGE_SIZE) |j| {
+ zero_ptr[j] = 0;
+ }
+ }
+
+ const dest_ptr: [*]volatile u8 = @ptrFromInt(page_phys + HIGHER_HALF);
+
+ const page_offset = if (i == 0) offset_in_page else 0;
+ const segment_pos: u64 = if (i == 0) 0 else (i * PAGE_SIZE - offset_in_page);
+
+ if (segment_pos < data_size) {
+ const bytes_remaining = data_size - segment_pos;
+ const bytes_to_copy = @min(PAGE_SIZE - page_offset, bytes_remaining);
+ const elf_pos = data_offset + segment_pos;
+
+ if (elf_pos + bytes_to_copy <= elf_data.len) {
+ const src = elf_data[elf_pos .. elf_pos + bytes_to_copy];
+
+ for (0..bytes_to_copy) |k| {
+ dest_ptr[page_offset + k] = src[k];
+ }
+ }
+ }
+ }
+}
+
+pub fn cleanup(kata: *types.Kata) void {
+ if (kata.page_table != 0) {
+ const current_cr3 = asm_memory.read_page_table_base();
+ if (current_cr3 != kata.page_table) {
+ paging.destroy_page_table(kata.page_table);
+ kata.page_table = 0;
+ }
+ // If CR3 == kata.page_table, leave page_table intact for Shinigami
+ }
+ kata.stack_top = 0;
+ kata.user_stack_top = 0;
+ kata.user_stack_bottom = 0;
+ kata.user_stack_committed = 0;
+}
+
+/// Called by Shinigami to destroy a zombie's page table.
+/// Safe because Shinigami runs with its own page table.
+pub fn destroy_zombie_page_table(page_table: u64) void {
+ paging.destroy_page_table(page_table);
+}
+
+pub fn grow_stack(kata: *types.Kata, fault_addr: u64) bool {
+ const page_addr = fault_addr & ~@as(u64, 0xFFF);
+
+ if (page_addr >= kata.user_stack_committed or page_addr < kata.user_stack_bottom) {
+ return false;
+ }
+
+ var addr = kata.user_stack_committed - PAGE_SIZE;
+ while (addr >= page_addr) : (addr -= PAGE_SIZE) {
+ const page = pmm.alloc_page() orelse return false;
+ _ = paging.map_page_in_table(kata.page_table, addr, page, paging.PAGE_WRITABLE | paging.PAGE_USER) catch return false;
+
+ const page_ptr: [*]volatile u8 = @ptrFromInt(page + HIGHER_HALF);
+ for (0..PAGE_SIZE) |j| {
+ page_ptr[j] = 0;
+ }
+
+ asm_memory.invalidate_page(addr);
+
+ if (addr == 0) break;
+ }
+
+ kata.user_stack_committed = page_addr;
+ return true;
+}
diff --git a/mirai.old/kata/pool.zig b/mirai.old/kata/pool.zig
new file mode 100644
index 0000000..08814d8
--- /dev/null
+++ b/mirai.old/kata/pool.zig
@@ -0,0 +1,145 @@
+//! Kata pool management
+
+const attachment = @import("attachment.zig");
+const attachment_const = @import("../common/constants/attachment.zig");
+const heap = @import("../memory/heap.zig");
+const kata_const = @import("../common/constants/kata.zig");
+const kata_limits = @import("../common/limits/kata.zig");
+const memory = @import("memory.zig");
+const types = @import("types.zig");
+const waker = @import("sensei/waker.zig");
+
+pub var pool: [kata_limits.MAX_KATAS]types.Kata = undefined;
+pub var used: [kata_limits.MAX_KATAS]bool = [_]bool{false} ** kata_limits.MAX_KATAS;
+var next_id: u32 = 1;
+
+pub fn init() void {
+ for (&pool, 0..) |*kata, i| {
+ kata.* = create_empty();
+ used[i] = false;
+ }
+}
+
+pub fn create() !*types.Kata {
+ for (&pool, 0..) |*kata, i| {
+ if (!used[i]) {
+ used[i] = true;
+
+ const kata_id = next_id;
+ next_id += 1;
+
+ kata.* = create_empty();
+ kata.id = kata_id;
+ kata.state = .Alive;
+
+ kata.current_location[0] = '/';
+
+ const stdin = attachment.alloc() orelse return error.OutOfMemory;
+ stdin.* = .{
+ .attachment_type = .Device,
+ .device_type = .Source,
+ .flags = attachment_const.VIEW_ONLY,
+ };
+ kata.attachments[0] = stdin;
+
+ const stdout = attachment.alloc() orelse return error.OutOfMemory;
+ stdout.* = .{
+ .attachment_type = .Device,
+ .device_type = .Stream,
+ .flags = attachment_const.MARK_ONLY,
+ };
+ kata.attachments[1] = stdout;
+
+ const stderr = attachment.alloc() orelse return error.OutOfMemory;
+ stderr.* = .{
+ .attachment_type = .Device,
+ .device_type = .Trace,
+ .flags = attachment_const.MARK_ONLY,
+ };
+ kata.attachments[2] = stderr;
+
+ return kata;
+ }
+ }
+
+ return error.TooManyKata;
+}
+
+pub fn get(id: u32) ?*types.Kata {
+ for (&pool, 0..) |*kata, i| {
+ if (used[i] and kata.id == id) {
+ return kata;
+ }
+ }
+ return null;
+}
+
+pub fn dissolve(kata_id: u32) void {
+ for (&pool, 0..) |*kata, i| {
+ if (used[i] and kata.id == kata_id) {
+ // Mark as dying
+ kata.state = .Dying;
+
+ // Clean up attachments
+ for (&kata.attachments) |*slot| {
+ if (slot.*) |ptr| {
+ attachment.free(ptr);
+ slot.* = null;
+ }
+ }
+
+ // Clean up letter data
+ if (kata.letter_data) |data| {
+ heap.free(@ptrCast(data), kata.letter_capacity);
+ kata.letter_data = null;
+ kata.letter_capacity = 0;
+ }
+
+ // Try to clean up memory
+ memory.cleanup(kata);
+
+ // Check if page table was destroyed or needs Shinigami
+ if (kata.page_table != 0) {
+ // Page table still exists - become zombie for Shinigami to reap
+ kata.state = .Zombie;
+ // Keep used[i] = true so slot isn't reused yet
+ } else {
+ // Fully cleaned up
+ kata.state = .Dissolved;
+ used[i] = false;
+ }
+
+ waker.wake_waiting(kata_id);
+ return;
+ }
+ }
+}
+
+fn create_empty() types.Kata {
+ return types.Kata{
+ .id = 0,
+ .state = .Dissolved,
+ .mode = .Persona,
+ .context = types.Context.init(),
+ .page_table = 0,
+ .stack_top = 0,
+ .user_stack_top = 0,
+ .user_stack_bottom = 0,
+ .user_stack_committed = 0,
+ .attachments = [_]?*attachment.Attachment{null} ** kata_limits.MAX_ATTACHMENTS,
+ .current_location = undefined,
+ .current_location_len = 1,
+ .current_cluster = 0,
+ .parent_id = 0,
+ .letter_type = 0,
+ .letter_data = null,
+ .letter_len = 0,
+ .letter_capacity = 0,
+ .vruntime = 0,
+ .weight = kata_const.DEFAULT_WEIGHT,
+ .last_run = 0,
+ .next = null,
+ .waiting_for = 0,
+ .exit_code = 0,
+ };
+}
diff --git a/mirai.old/kata/sensei/queue.zig b/mirai.old/kata/sensei/queue.zig
new file mode 100644
index 0000000..943d70c
--- /dev/null
+++ b/mirai.old/kata/sensei/queue.zig
@@ -0,0 +1,70 @@
+//! Run queue management
+
+const types = @import("../types.zig");
+
+var head: ?*types.Kata = null;
+
+pub fn get_head() ?*types.Kata {
+ return head;
+}
+
+pub fn enqueue(kata: *types.Kata) void {
+ kata.state = .Alive;
+
+ if (head == null) {
+ head = kata;
+ kata.next = null;
+ return;
+ }
+
+ var prev: ?*types.Kata = null;
+ var current = head;
+
+ while (current) |curr| {
+ if (kata.vruntime < curr.vruntime) {
+ kata.next = curr;
+ if (prev) |p| {
+ p.next = kata;
+ } else {
+ head = kata;
+ }
+ return;
+ }
+ prev = curr;
+ current = curr.next;
+ }
+
+ if (prev) |p| {
+ p.next = kata;
+ kata.next = null;
+ }
+}
+
+pub fn dequeue(kata: *types.Kata) void {
+ if (head == null) return;
+
+ if (head == kata) {
+ head = kata.next;
+ kata.next = null;
+ return;
+ }
+
+ var prev = head;
+ while (prev) |p| {
+ if (p.next == kata) {
+ p.next = kata.next;
+ kata.next = null;
+ return;
+ }
+ prev = p.next;
+ }
+}
+
+pub fn is_queued(kata: *types.Kata) bool {
+ var current = head;
+ while (current) |k| {
+ if (k == kata) return true;
+ current = k.next;
+ }
+ return false;
+}
diff --git a/mirai.old/kata/sensei/sensei.zig b/mirai.old/kata/sensei/sensei.zig
new file mode 100644
index 0000000..77f9cc9
--- /dev/null
+++ b/mirai.old/kata/sensei/sensei.zig
@@ -0,0 +1,140 @@
+//! Sensei - Kata scheduler
+
+const cpu = @import("../../asm/cpu.zig");
+const pool = @import("../pool.zig");
+const queue = @import("queue.zig");
+const serial = @import("../../drivers/serial/serial.zig");
+const shift = @import("../shift.zig");
+const types = @import("../types.zig");
+const waker = @import("waker.zig");
+
+var current: ?*types.Kata = null;
+var min_vruntime: u64 = 0;
+var tick_count: u64 = 0;
+
+const TICK_NS: u64 = 1_000_000;
+
+pub fn on_tick() void {
+ tick_count += 1;
+}
+
+pub fn get_tick_count() u64 {
+ return tick_count;
+}
+
+pub fn schedule() void {
+ waker.wake_all_waiting();
+
+ if (current) |kata| {
+ const elapsed = tick_count - kata.last_run;
+ if (elapsed > 0) {
+ const delta = (TICK_NS * 1024 * elapsed) / kata.weight;
+ kata.vruntime += delta;
+ kata.last_run = tick_count;
+ }
+ }
+
+ const next = pick_next();
+
+ if (next == null and current == null) {
+ halt_loop();
+ }
+
+ if (next == current and next != null) {
+ queue.dequeue(next.?);
+ next.?.state = .Flowing;
+ return;
+ }
+
+ if (current) |curr| {
+ if (curr.state == .Flowing) {
+ curr.state = .Alive;
+ queue.enqueue(curr);
+ }
+ }
+
+ if (next) |n| {
+ queue.dequeue(n);
+ n.state = .Flowing;
+ n.last_run = tick_count;
+ current = n;
+ shift.to_kata(n);
+ unreachable;
+ }
+
+ clear_current();
+ halt_loop();
+}
+
+pub fn enqueue_kata(kata: *types.Kata) void {
+ queue.enqueue(kata);
+}
+
+pub fn dequeue_kata(kata: *types.Kata) void {
+ queue.dequeue(kata);
+}
+
+pub fn is_in_queue(kata: *types.Kata) bool {
+ return queue.is_queued(kata);
+}
+
+pub fn get_current_kata() ?*types.Kata {
+ return current;
+}
+
+pub fn clear_current_kata() void {
+ current = null;
+}
+
+pub fn wake_waiting_katas(target_id: u32) void {
+ waker.wake_waiting(target_id);
+}
+
+pub fn wake_one_blocked_kata() void {
+ waker.wake_one_blocked();
+}
+
+pub fn wake_kata(kata_id: u32) void {
+ waker.wake(kata_id);
+}
+
+fn pick_next() ?*types.Kata {
+ var curr = queue.get_head();
+ var local_min: u64 = 0xFFFFFFFFFFFFFFFF;
+ var chosen: ?*types.Kata = null;
+ var fallback: ?*types.Kata = null;
+
+ while (curr) |kata| {
+ if (kata.state == .Alive and kata.vruntime < local_min) {
+ local_min = kata.vruntime;
+ chosen = kata;
+ }
+ if (kata.state == .Flowing) {
+ fallback = kata;
+ }
+ curr = kata.next;
+ }
+
+ if (chosen) |k| {
+ min_vruntime = k.vruntime;
+ return k;
+ }
+
+ if (current) |c| {
+ if (c.state == .Flowing) return c;
+ }
+
+ if (fallback) |k| return k;
+
+ return null;
+}
+
+fn clear_current() void {
+ current = null;
+}
+
+fn halt_loop() noreturn {
+ while (true) {
+ cpu.halt_processor();
+ }
+}
diff --git a/mirai.old/kata/sensei/waker.zig b/mirai.old/kata/sensei/waker.zig
new file mode 100644
index 0000000..d18388b
--- /dev/null
+++ b/mirai.old/kata/sensei/waker.zig
@@ -0,0 +1,74 @@
+//! Wake functions
+
+const kata_limits = @import("../../common/limits/kata.zig");
+const keyboard = @import("../../drivers/keyboard/keyboard.zig");
+const pool = @import("../pool.zig");
+const queue = @import("queue.zig");
+const types = @import("../types.zig");
+
+pub fn wake_all_waiting() void {
+ for (0..kata_limits.MAX_KATAS) |i| {
+ if (!pool.used[i]) continue;
+
+ const kata = &pool.pool[i];
+ if (kata.state != .Stalled) continue;
+
+ const target = pool.get(kata.waiting_for);
+ // Wake if target doesn't exist, is zombie, or dissolved
+ if (target == null or target.?.state == .Zombie or target.?.state == .Dissolved) {
+ kata.state = .Alive;
+ queue.enqueue(kata);
+ kata.waiting_for = 0;
+ }
+ }
+}
+
+pub fn wake_waiting(target_id: u32) void {
+ for (0..kata_limits.MAX_KATAS) |i| {
+ if (!pool.used[i]) continue;
+
+ const kata = &pool.pool[i];
+ if (kata.state == .Stalled and kata.waiting_for == target_id) {
+ kata.state = .Alive;
+ queue.enqueue(kata);
+ kata.waiting_for = 0;
+ }
+ }
+}
+
+pub fn wake_blocked() void {
+ if (!keyboard.has_input()) return;
+
+ for (0..kata_limits.MAX_KATAS) |i| {
+ if (!pool.used[i]) continue;
+
+ const kata = &pool.pool[i];
+ if (kata.state == .Frozen) {
+ kata.state = .Alive;
+ queue.enqueue(kata);
+ return;
+ }
+ }
+}
+
+pub fn wake_one_blocked() void {
+ for (0..kata_limits.MAX_KATAS) |i| {
+ if (!pool.used[i]) continue;
+
+ const kata = &pool.pool[i];
+ if (kata.state == .Frozen) {
+ kata.state = .Alive;
+ queue.enqueue(kata);
+ return;
+ }
+ }
+}
+
+pub fn wake(kata_id: u32) void {
+ const kata = pool.get(kata_id) orelse return;
+
+ if (kata.state == .Frozen) {
+ kata.state = .Alive;
+ queue.enqueue(kata);
+ }
+}
diff --git a/mirai.old/kata/shift.zig b/mirai.old/kata/shift.zig
new file mode 100644
index 0000000..52e87fb
--- /dev/null
+++ b/mirai.old/kata/shift.zig
@@ -0,0 +1,53 @@
+//! Context shifting
+
+const context = @import("../asm/context.zig");
+const gdt = @import("../boot/gdt/gdt.zig");
+const paging = @import("../memory/paging.zig");
+const serial = @import("../drivers/serial/serial.zig");
+const tss = @import("../boot/tss/tss.zig");
+const types = @import("types.zig");
+
+var current_context: ?*types.Context = null;
+
+pub fn to_kata(kata: *types.Kata) void {
+ // Validate that the target RIP is mapped in the page table
+ if (paging.virt_to_phys(kata.page_table, kata.context.rip) == null) {
+ serial.printf("shift: FATAL - kata {d} rip {x} not mapped!\n", .{ kata.id, kata.context.rip });
+ paging.dump_pt_structure(kata.page_table, "corrupted");
+ while (true) {}
+ }
+
+ tss.set_kernel_stack(kata.stack_top);
+ current_context = &kata.context;
+
+ context.switch_to_context(
+ &kata.context,
+ kata.page_table,
+ kata.stack_top,
+ );
+}
+
+pub fn save_current(int_ctx: *const types.InterruptContext) void {
+ if (current_context) |ctx| {
+ ctx.rax = int_ctx.rax;
+ ctx.rbx = int_ctx.rbx;
+ ctx.rcx = int_ctx.rcx;
+ ctx.rdx = int_ctx.rdx;
+ ctx.rsi = int_ctx.rsi;
+ ctx.rdi = int_ctx.rdi;
+ ctx.rbp = int_ctx.rbp;
+ ctx.rsp = int_ctx.rsp;
+ ctx.r8 = int_ctx.r8;
+ ctx.r9 = int_ctx.r9;
+ ctx.r10 = int_ctx.r10;
+ ctx.r11 = int_ctx.r11;
+ ctx.r12 = int_ctx.r12;
+ ctx.r13 = int_ctx.r13;
+ ctx.r14 = int_ctx.r14;
+ ctx.r15 = int_ctx.r15;
+ ctx.rip = int_ctx.rip;
+ ctx.rflags = int_ctx.rflags;
+ ctx.cs = int_ctx.cs;
+ ctx.ss = int_ctx.ss;
+ }
+}
diff --git a/mirai.old/kata/types.zig b/mirai.old/kata/types.zig
new file mode 100644
index 0000000..c416212
--- /dev/null
+++ b/mirai.old/kata/types.zig
@@ -0,0 +1,128 @@
+//! Kata type definitions
+
+const attachment = @import("attachment.zig");
+const kata_const = @import("../common/constants/kata.zig");
+const kata_limits = @import("../common/limits/kata.zig");
+
+pub const State = enum {
+ Born, // Just created, being initialized
+ Alive, // Ready to run, waiting for scheduler
+ Flowing, // Currently executing
+ Stalled, // Waiting for child/event
+ Frozen, // Blocked on I/O
+ Dying, // Exit called, cleanup starting
+ Zombie, // Exited, awaiting Shinigami
+ Dissolved, // Gone, slot reusable
+};
+
+pub const Mode = enum {
+ Persona, // Normal user process - can be reaped
+ Protected, // System process - page tables are protected
+};
+
+pub const Context = packed struct {
+ rax: u64,
+ rbx: u64,
+ rcx: u64,
+ rdx: u64,
+ rsi: u64,
+ rdi: u64,
+ rbp: u64,
+ rsp: u64,
+ r8: u64,
+ r9: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ rip: u64,
+ rflags: u64,
+ cs: u64,
+ ss: u64,
+
+ pub fn init() Context {
+ return Context{
+ .rax = 0,
+ .rbx = 0,
+ .rcx = 0,
+ .rdx = 0,
+ .rsi = 0,
+ .rdi = 0,
+ .rbp = 0,
+ .rsp = 0,
+ .r8 = 0,
+ .r9 = 0,
+ .r10 = 0,
+ .r11 = 0,
+ .r12 = 0,
+ .r13 = 0,
+ .r14 = 0,
+ .r15 = 0,
+ .rip = 0,
+ .rflags = kata_const.KERNEL_RFLAGS,
+ .cs = 0,
+ .ss = 0,
+ };
+ }
+};
+
+pub const Kata = struct {
+ id: u32,
+ state: State,
+ mode: Mode,
+ context: Context,
+
+ page_table: u64,
+ stack_top: u64,
+ user_stack_top: u64,
+ user_stack_bottom: u64,
+ user_stack_committed: u64,
+
+ attachments: [kata_limits.MAX_ATTACHMENTS]?*attachment.Attachment,
+
+ current_location: [kata_limits.MAX_LOCATION_LENGTH]u8,
+ current_location_len: usize,
+ current_cluster: u64,
+
+ parent_id: u32,
+
+ letter_type: u8,
+ letter_data: ?[*]u8,
+ letter_len: u16,
+ letter_capacity: u16,
+
+ vruntime: u64,
+ weight: u32,
+ last_run: u64,
+ next: ?*Kata,
+
+ waiting_for: u32,
+ exit_code: u64,
+};
+
+pub const InterruptContext = packed struct {
+ r15: u64,
+ r14: u64,
+ r13: u64,
+ r12: u64,
+ r11: u64,
+ r10: u64,
+ r9: u64,
+ r8: u64,
+ rbp: u64,
+ rdi: u64,
+ rsi: u64,
+ rdx: u64,
+ rcx: u64,
+ rbx: u64,
+ rax: u64,
+ int_num: u64,
+ error_code: u64,
+ rip: u64,
+ cs: u64,
+ rflags: u64,
+ rsp: u64,
+ ss: u64,
+};
diff --git a/mirai.old/memory/heap.zig b/mirai.old/memory/heap.zig
new file mode 100644
index 0000000..e21456e
--- /dev/null
+++ b/mirai.old/memory/heap.zig
@@ -0,0 +1,215 @@
+//! Kernel Heap Allocator
+
+const heap_const = @import("../common/constants/heap.zig");
+const memory_const = @import("../common/constants/memory.zig");
+const pmm = @import("pmm.zig");
+const serial = @import("../drivers/serial/serial.zig");
+
+const PAGE_SIZE = memory_const.PAGE_SIZE;
+const HIGHER_HALF = memory_const.HIGHER_HALF_START;
+
+const SlabHeader = struct {
+ free_count: usize,
+ total_count: usize,
+ object_size: usize,
+ next_slab: ?*SlabHeader,
+ prev_slab: ?*SlabHeader,
+ first_free: ?*FreeObject,
+};
+
+const FreeObject = struct {
+ next: ?*FreeObject,
+};
+
+const SlabCache = struct {
+ object_size: usize,
+ objects_per_slab: usize,
+ slab_list: ?*SlabHeader,
+};
+
+var caches: [heap_const.NUM_CACHES]SlabCache = undefined;
+var initialized: bool = false;
+
+pub fn init() void {
+ serial.print("\n=== Heap ===\n");
+
+ for (0..heap_const.NUM_CACHES) |i| {
+ const size = heap_const.SIZE_CLASSES[i];
+ const usable_space = PAGE_SIZE - @sizeOf(SlabHeader);
+ const objects_per_slab = usable_space / size;
+
+ caches[i] = SlabCache{
+ .object_size = size,
+ .objects_per_slab = objects_per_slab,
+ .slab_list = null,
+ };
+
+ serial.printf("Cache {}B: {} objects/slab\n", .{ size, objects_per_slab });
+ }
+
+ initialized = true;
+ serial.print("Heap ready\n");
+}
+
+pub fn alloc(size: usize) ?[*]u8 {
+ if (!initialized) return null;
+ if (size == 0) return null;
+
+ if (size > heap_const.LARGE_ALLOC_THRESHOLD) {
+ return alloc_large(size);
+ }
+
+ const cache_idx = find_cache_index(size);
+ return alloc_from_cache(&caches[cache_idx]);
+}
+
+pub fn free(ptr: [*]u8, size: usize) void {
+ if (!initialized) return;
+ if (size == 0) return;
+
+ if (size > heap_const.LARGE_ALLOC_THRESHOLD) {
+ free_large(ptr, size);
+ return;
+ }
+
+ const cache_idx = find_cache_index(size);
+ free_to_cache(&caches[cache_idx], ptr);
+}
+
+fn find_cache_index(size: usize) usize {
+ for (0..heap_const.NUM_CACHES) |i| {
+ if (size <= heap_const.SIZE_CLASSES[i]) {
+ return i;
+ }
+ }
+ return heap_const.NUM_CACHES - 1;
+}
+
+fn alloc_from_cache(cache: *SlabCache) ?[*]u8 {
+ var current_slab = cache.slab_list;
+ while (current_slab) |slab| {
+ if (slab.free_count > 0) {
+ return alloc_from_slab(slab);
+ }
+ current_slab = slab.next_slab;
+ }
+
+ const new_slab = create_slab(cache) orelse return null;
+ new_slab.next_slab = cache.slab_list;
+ new_slab.prev_slab = null;
+
+ if (cache.slab_list) |first| {
+ first.prev_slab = new_slab;
+ }
+
+ cache.slab_list = new_slab;
+
+ return alloc_from_slab(new_slab);
+}
+
+fn create_slab(cache: *SlabCache) ?*SlabHeader {
+ const phys_page = pmm.alloc_page() orelse return null;
+ const virt_addr = phys_page + HIGHER_HALF;
+
+ const page_ptr: [*]volatile u8 = @ptrFromInt(virt_addr);
+ for (0..PAGE_SIZE) |i| {
+ page_ptr[i] = 0;
+ }
+
+ const slab: *SlabHeader = @ptrFromInt(virt_addr);
+ slab.free_count = cache.objects_per_slab;
+ slab.total_count = cache.objects_per_slab;
+ slab.object_size = cache.object_size;
+ slab.next_slab = null;
+ slab.prev_slab = null;
+
+ const first_obj_addr = virt_addr + @sizeOf(SlabHeader);
+ for (0..cache.objects_per_slab) |j| {
+ const obj_addr = first_obj_addr + (j * cache.object_size);
+ const obj: *FreeObject = @ptrFromInt(obj_addr);
+
+ if (j + 1 < cache.objects_per_slab) {
+ const next_addr = obj_addr + cache.object_size;
+ obj.next = @ptrFromInt(next_addr);
+ } else {
+ obj.next = null;
+ }
+ }
+
+ slab.first_free = @ptrFromInt(first_obj_addr);
+
+ return slab;
+}
+
+fn alloc_from_slab(slab: *SlabHeader) ?[*]u8 {
+ if (slab.first_free) |free_obj| {
+ slab.first_free = free_obj.next;
+ slab.free_count -= 1;
+
+ return @ptrFromInt(@intFromPtr(free_obj));
+ }
+
+ return null;
+}
+
+fn free_to_cache(cache: *SlabCache, ptr: [*]u8) void {
+ const ptr_addr = @intFromPtr(ptr);
+ const slab_addr = ptr_addr & ~@as(u64, PAGE_SIZE - 1);
+ const slab: *SlabHeader = @ptrFromInt(slab_addr);
+
+ const obj: *FreeObject = @ptrFromInt(ptr_addr);
+ obj.next = slab.first_free;
+ slab.first_free = obj;
+ slab.free_count += 1;
+
+ if (slab.free_count == slab.total_count) {
+ if (slab.next_slab != null or slab.prev_slab != null) {
+ return_slab_to_pmm(cache, slab);
+ }
+ }
+}
+
+fn return_slab_to_pmm(cache: *SlabCache, slab: *SlabHeader) void {
+ if (slab.prev_slab) |prev| {
+ prev.next_slab = slab.next_slab;
+ } else {
+ cache.slab_list = slab.next_slab;
+ }
+
+ if (slab.next_slab) |next| {
+ next.prev_slab = slab.prev_slab;
+ }
+
+ const slab_addr = @intFromPtr(slab);
+ const phys_addr = slab_addr - HIGHER_HALF;
+ pmm.free_page(phys_addr);
+}
+
+fn alloc_large(size: usize) ?[*]u8 {
+ const num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ var pages: [heap_const.MAX_LARGE_PAGES]u64 = undefined;
+ if (num_pages > heap_const.MAX_LARGE_PAGES) return null;
+
+ for (0..num_pages) |i| {
+ pages[i] = pmm.alloc_page() orelse {
+ for (0..i) |j| {
+ pmm.free_page(pages[j]);
+ }
+ return null;
+ };
+ }
+
+ const result: [*]u8 = @ptrFromInt(pages[0] + HIGHER_HALF);
+ return result;
+}
+
+fn free_large(ptr: [*]u8, size: usize) void {
+ const num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+ const base_virt = @intFromPtr(ptr);
+ const base_phys = base_virt - HIGHER_HALF;
+
+ for (0..num_pages) |i| {
+ pmm.free_page(base_phys + (i * PAGE_SIZE));
+ }
+}
diff --git a/mirai.old/memory/paging.zig b/mirai.old/memory/paging.zig
new file mode 100644
index 0000000..ea3e7e1
--- /dev/null
+++ b/mirai.old/memory/paging.zig
@@ -0,0 +1,415 @@
+//! Page Table Manager
+
+const asm_memory = @import("../asm/memory.zig");
+const memory_const = @import("../common/constants/memory.zig");
+const paging_const = @import("../common/constants/paging.zig");
+const pmm = @import("pmm.zig");
+const pmm_const = @import("../common/constants/pmm.zig");
+
+pub const PAGE_SIZE = memory_const.PAGE_SIZE;
+pub const HIGHER_HALF_START = memory_const.HIGHER_HALF_START;
+
+pub const PAGE_PRESENT = paging_const.PTE_PRESENT;
+pub const PAGE_WRITABLE = paging_const.PTE_WRITABLE;
+pub const PAGE_USER = paging_const.PTE_USER;
+
+fn zero_page(virt: u64) void {
+ const ptr: [*]volatile u8 = @ptrFromInt(virt);
+ for (0..PAGE_SIZE) |i| {
+ ptr[i] = 0;
+ }
+}
+
+pub fn map_page(virt: u64, phys: u64, flags: u64) !void {
+ const pml4_index = (virt >> 39) & 0x1FF;
+ const pdpt_index = (virt >> 30) & 0x1FF;
+ const pd_index = (virt >> 21) & 0x1FF;
+ const pt_index = (virt >> 12) & 0x1FF;
+
+ const pml4_phys = asm_memory.read_page_table_base() & ~@as(u64, paging_const.OFFSET_MASK);
+ const pml4: [*]volatile u64 = @ptrFromInt(pml4_phys + HIGHER_HALF_START);
+
+ var pdpt_phys: u64 = undefined;
+ var pdpt_was_new = false;
+ if ((pml4[pml4_index] & PAGE_PRESENT) == 0) {
+ pdpt_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ pml4[pml4_index] = pdpt_phys | PAGE_PRESENT | PAGE_WRITABLE | flags;
+ pdpt_was_new = true;
+ } else {
+ pdpt_phys = pml4[pml4_index] & paging_const.PTE_MASK;
+ }
+
+ const pdpt: [*]volatile u64 = @ptrFromInt(pdpt_phys + HIGHER_HALF_START);
+
+ if (pdpt_was_new) {
+ pdpt[pdpt_index] = 0;
+ }
+
+ var pd_phys: u64 = undefined;
+ var pd_was_new = false;
+ if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0) {
+ pd_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ pdpt[pdpt_index] = pd_phys | PAGE_PRESENT | PAGE_WRITABLE | flags;
+ pd_was_new = true;
+ } else {
+ pd_phys = pdpt[pdpt_index] & paging_const.PTE_MASK;
+ }
+
+ const pd: [*]volatile u64 = @ptrFromInt(pd_phys + HIGHER_HALF_START);
+
+ if (pd_was_new) {
+ pd[pd_index] = 0;
+ }
+
+ var pt_phys: u64 = undefined;
+ var pt_was_new = false;
+ if ((pd[pd_index] & PAGE_PRESENT) == 0) {
+ pt_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ pd[pd_index] = pt_phys | PAGE_PRESENT | PAGE_WRITABLE | flags;
+ pt_was_new = true;
+ } else {
+ pt_phys = pd[pd_index] & paging_const.PTE_MASK;
+ }
+
+ const pt: [*]volatile u64 = @ptrFromInt(pt_phys + HIGHER_HALF_START);
+
+ if (pt_was_new) {
+ pt[pt_index] = 0;
+ }
+
+ pt[pt_index] = phys | PAGE_PRESENT | flags;
+
+ asm_memory.invalidate_page(virt);
+}
+
+pub fn create_page_table() !u64 {
+ const serial = @import("../drivers/serial/serial.zig");
+
+ const new_pml4_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+
+ // Check if we're about to zero Ash's PD (used as PML4)
+ if (pmm.ash_pd_phys != 0 and new_pml4_phys == pmm.ash_pd_phys) {
+ serial.printf("PAGING: About to zero Ash's PD {x} (as PML4)!\n", .{new_pml4_phys});
+ }
+
+ const new_pml4: [*]volatile u64 = @ptrFromInt(new_pml4_phys + HIGHER_HALF_START);
+
+ for (0..paging_const.PML4_ENTRIES) |i| {
+ new_pml4[i] = 0;
+ }
+
+ const kernel_pml4_phys = asm_memory.read_page_table_base() & ~@as(u64, paging_const.OFFSET_MASK);
+ const kernel_pml4: [*]volatile u64 = @ptrFromInt(kernel_pml4_phys + HIGHER_HALF_START);
+
+ for (paging_const.KERNEL_PML4_START..paging_const.PML4_ENTRIES) |i| {
+ new_pml4[i] = kernel_pml4[i];
+ }
+
+ var addr: u64 = pmm_const.KERNEL_BASE;
+ while (addr < pmm_const.KERNEL_MAP_END) : (addr += PAGE_SIZE) {
+ _ = try map_page_in_table(new_pml4_phys, addr, addr, PAGE_PRESENT | PAGE_WRITABLE);
+ }
+
+ addr = pmm_const.MMIO_FRAMEBUFFER_BASE;
+ while (addr < pmm_const.MMIO_FRAMEBUFFER_BASE + 0x2000000) : (addr += PAGE_SIZE) {
+ _ = try map_page_in_table(new_pml4_phys, addr, addr, PAGE_PRESENT | PAGE_WRITABLE);
+ }
+
+ return new_pml4_phys;
+}
+
+pub fn map_page_in_table(page_table_phys: u64, virt: u64, phys: u64, flags: u64) !struct { bool, u64 } {
+ const serial = @import("../drivers/serial/serial.zig");
+ const pool = @import("../kata/pool.zig");
+
+ const pml4_index = (virt >> 39) & 0x1FF;
+ const pdpt_index = (virt >> 30) & 0x1FF;
+ const pd_index = (virt >> 21) & 0x1FF;
+ const pt_index = (virt >> 12) & 0x1FF;
+
+ const pml4: [*]volatile u64 = @ptrFromInt(page_table_phys + HIGHER_HALF_START);
+
+ // Helper to check Ash's pd[256]
+ const ash_pd256 = struct {
+ fn get() u64 {
+ for (&pool.pool, 0..) |*k, i| {
+ if (pool.used[i] and k.id == 3 and k.page_table != 0) {
+ const pml4_ptr: [*]volatile u64 = @ptrFromInt(k.page_table + HIGHER_HALF_START);
+ if ((pml4_ptr[0] & 1) == 0) return 0xDEAD0001;
+ const pdpt: [*]volatile u64 = @ptrFromInt((pml4_ptr[0] & paging_const.PTE_MASK) + HIGHER_HALF_START);
+ if ((pdpt[0] & 1) == 0) return 0xDEAD0002;
+ const pd: [*]volatile u64 = @ptrFromInt((pdpt[0] & paging_const.PTE_MASK) + HIGHER_HALF_START);
+ return pd[256];
+ }
+ }
+ return 0;
+ }
+ };
+
+ const before = ash_pd256.get();
+
+ var pdpt_phys: u64 = undefined;
+ if ((pml4[pml4_index] & PAGE_PRESENT) == 0) {
+ pdpt_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ if (pmm.ash_pd_phys != 0 and pdpt_phys == pmm.ash_pd_phys) {
+ serial.printf("PAGING: About to zero Ash's PD {x} (as PDPT) for virt {x}!\n", .{ pdpt_phys, virt });
+ }
+ zero_page(pdpt_phys + HIGHER_HALF_START);
+ pml4[pml4_index] = pdpt_phys | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
+
+ const after = ash_pd256.get();
+ if (before != 0 and after != before) {
+ serial.printf("CORRUPTION after PDPT alloc! virt={x} pdpt={x} before={x} after={x}\n", .{ virt, pdpt_phys, before, after });
+ }
+ } else {
+ pdpt_phys = pml4[pml4_index] & paging_const.PTE_MASK;
+ }
+
+ const pdpt: [*]volatile u64 = @ptrFromInt(pdpt_phys + HIGHER_HALF_START);
+
+ var pd_phys: u64 = undefined;
+ if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0) {
+ pd_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ if (pmm.ash_pd_phys != 0 and pd_phys == pmm.ash_pd_phys) {
+ serial.printf("PAGING: About to zero Ash's PD {x} for virt {x}!\n", .{ pd_phys, virt });
+ }
+ zero_page(pd_phys + HIGHER_HALF_START);
+ pdpt[pdpt_index] = pd_phys | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
+
+ const after = ash_pd256.get();
+ if (before != 0 and after != before) {
+ serial.printf("CORRUPTION after PD alloc! virt={x} pd={x} before={x} after={x}\n", .{ virt, pd_phys, before, after });
+ }
+ } else {
+ pd_phys = pdpt[pdpt_index] & paging_const.PTE_MASK;
+ }
+
+ const pd: [*]volatile u64 = @ptrFromInt(pd_phys + HIGHER_HALF_START);
+
+ var pt_phys: u64 = undefined;
+ if ((pd[pd_index] & PAGE_PRESENT) == 0) {
+ pt_phys = pmm.alloc_page() orelse return error.OutOfMemory;
+ if (pmm.ash_pd_phys != 0 and pt_phys == pmm.ash_pd_phys) {
+ serial.printf("PAGING: About to zero Ash's PD {x} (as PT) for virt {x}!\n", .{ pt_phys, virt });
+ }
+ zero_page(pt_phys + HIGHER_HALF_START);
+ pd[pd_index] = pt_phys | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
+
+ const after = ash_pd256.get();
+ if (before != 0 and after != before) {
+ serial.printf("CORRUPTION after PT alloc! virt={x} pt={x} pd_idx={d} before={x} after={x}\n", .{ virt, pt_phys, pd_index, before, after });
+ }
+ } else {
+ pt_phys = pd[pd_index] & paging_const.PTE_MASK;
+ }
+
+ const pt: [*]volatile u64 = @ptrFromInt(pt_phys + HIGHER_HALF_START);
+
+ const was_mapped = (pt[pt_index] & PAGE_PRESENT) != 0;
+ if (!was_mapped) {
+ pt[pt_index] = phys | flags | PAGE_PRESENT;
+
+ const after = ash_pd256.get();
+ if (before != 0 and after != before) {
+ serial.printf("CORRUPTION after PT entry write! virt={x} pt={x} pt_idx={d} before={x} after={x}\n", .{ virt, pt_phys, pt_index, before, after });
+ }
+
+ return .{ false, phys };
+ } else {
+ const existing_phys = pt[pt_index] & paging_const.PTE_MASK;
+ pt[pt_index] = existing_phys | flags | PAGE_PRESENT;
+ return .{ true, existing_phys };
+ }
+}
+
+pub fn get_physical_address(page_table: u64, vaddr: u64) !u64 {
+ const pml4_index = (vaddr >> 39) & 0x1FF;
+ const pdp_index = (vaddr >> 30) & 0x1FF;
+ const pd_index = (vaddr >> 21) & 0x1FF;
+ const pt_index = (vaddr >> 12) & 0x1FF;
+ const offset = vaddr & paging_const.OFFSET_MASK;
+
+ const pml4 = @as([*]u64, @ptrFromInt(page_table + HIGHER_HALF_START));
+ if ((pml4[pml4_index] & PAGE_PRESENT) == 0) return error.NotMapped;
+
+ const pdp = @as([*]u64, @ptrFromInt((pml4[pml4_index] & paging_const.PTE_MASK) + HIGHER_HALF_START));
+ if ((pdp[pdp_index] & PAGE_PRESENT) == 0) return error.NotMapped;
+
+ const pd = @as([*]u64, @ptrFromInt((pdp[pdp_index] & paging_const.PTE_MASK) + HIGHER_HALF_START));
+ if ((pd[pd_index] & PAGE_PRESENT) == 0) return error.NotMapped;
+
+ const pt = @as([*]u64, @ptrFromInt((pd[pd_index] & paging_const.PTE_MASK) + HIGHER_HALF_START));
+ if ((pt[pt_index] & PAGE_PRESENT) == 0) return error.NotMapped;
+
+ return (pt[pt_index] & paging_const.PTE_MASK) + offset;
+}
+
+pub fn get_page_entry(page_table_phys: u64, virt: u64) ?u64 {
+ const pml4_index = (virt >> 39) & 0x1FF;
+ const pdpt_index = (virt >> 30) & 0x1FF;
+ const pd_index = (virt >> 21) & 0x1FF;
+ const pt_index = (virt >> 12) & 0x1FF;
+
+ const pml4: [*]volatile u64 = @ptrFromInt(page_table_phys + HIGHER_HALF_START);
+ if ((pml4[pml4_index] & PAGE_PRESENT) == 0) return null;
+
+ const pdpt_phys = pml4[pml4_index] & paging_const.PTE_MASK;
+ const pdpt: [*]volatile u64 = @ptrFromInt(pdpt_phys + HIGHER_HALF_START);
+ if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0) return null;
+
+ const pd_phys = pdpt[pdpt_index] & paging_const.PTE_MASK;
+ const pd: [*]volatile u64 = @ptrFromInt(pd_phys + HIGHER_HALF_START);
+ if ((pd[pd_index] & PAGE_PRESENT) == 0) return null;
+
+ const pt_phys = pd[pd_index] & paging_const.PTE_MASK;
+ const pt: [*]volatile u64 = @ptrFromInt(pt_phys + HIGHER_HALF_START);
+
+ return pt[pt_index];
+}
+
+pub fn virt_to_phys(cr3: u64, virt: u64) ?u64 {
+ const pml4_addr = cr3 + HIGHER_HALF_START;
+ const pml4 = @as([*]u64, @ptrFromInt(pml4_addr));
+
+ const pml4_index = (virt >> 39) & 0x1FF;
+ const pml4_entry = pml4[pml4_index];
+
+ if ((pml4_entry & PAGE_PRESENT) == 0) return null;
+
+ const pdp_addr = (pml4_entry & paging_const.PTE_MASK) + HIGHER_HALF_START;
+ const pdp = @as([*]u64, @ptrFromInt(pdp_addr));
+
+ const pdp_index = (virt >> 30) & 0x1FF;
+ const pdp_entry = pdp[pdp_index];
+
+ if ((pdp_entry & PAGE_PRESENT) == 0) return null;
+
+ const pd_addr = (pdp_entry & paging_const.PTE_MASK) + HIGHER_HALF_START;
+ const pd = @as([*]u64, @ptrFromInt(pd_addr));
+
+ const pd_index = (virt >> 21) & 0x1FF;
+ const pd_entry = pd[pd_index];
+
+ if ((pd_entry & PAGE_PRESENT) == 0) return null;
+
+ const pt_addr = (pd_entry & paging_const.PTE_MASK) + HIGHER_HALF_START;
+ const pt = @as([*]u64, @ptrFromInt(pt_addr));
+
+ const pt_index = (virt >> 12) & 0x1FF;
+ const pt_entry = pt[pt_index];
+
+ if ((pt_entry & PAGE_PRESENT) == 0) return null;
+
+ const phys_base = pt_entry & paging_const.PTE_MASK;
+ const offset = virt & paging_const.OFFSET_MASK;
+
+ return phys_base + offset;
+}
+
+fn should_free_page(virt: u64, phys: u64) bool {
+ if (virt < pmm_const.KERNEL_MAP_END) return false;
+ if (phys >= pmm_const.MMIO_FRAMEBUFFER_BASE and
+ phys < pmm_const.MMIO_FRAMEBUFFER_BASE + pmm_const.MMIO_FRAMEBUFFER_SIZE)
+ {
+ return false;
+ }
+ return true;
+}
+
+pub fn dump_pt_structure(page_table_phys: u64, label: []const u8) void {
+ const serial = @import("../drivers/serial/serial.zig");
+ serial.print("PT dump: ");
+ serial.print(label);
+ serial.printf(" pml4={x}\n", .{page_table_phys});
+
+ const pml4: [*]volatile u64 = @ptrFromInt(page_table_phys + HIGHER_HALF_START);
+
+ // Just dump PML4[0] chain since that's where user code is
+ const pml4_entry = pml4[0];
+ if ((pml4_entry & PAGE_PRESENT) == 0) {
+ serial.print("PT dump: pml4[0] not present\n");
+ return;
+ }
+
+ const pdpt_phys = pml4_entry & paging_const.PTE_MASK;
+ serial.printf("PT dump: pml4[0]={x} -> pdpt={x}\n", .{ pml4_entry, pdpt_phys });
+
+ const pdpt: [*]volatile u64 = @ptrFromInt(pdpt_phys + HIGHER_HALF_START);
+ const pdpt_entry = pdpt[0];
+ if ((pdpt_entry & PAGE_PRESENT) == 0) {
+ serial.print("PT dump: pdpt[0] not present\n");
+ return;
+ }
+
+ const pd_phys = pdpt_entry & paging_const.PTE_MASK;
+ serial.printf("PT dump: pdpt[0]={x} -> pd={x}\n", .{ pdpt_entry, pd_phys });
+
+ // Check PD[256] which is where 0x20000000 maps (256 = 0x20000000 >> 21 & 0x1FF)
+ const pd: [*]volatile u64 = @ptrFromInt(pd_phys + HIGHER_HALF_START);
+ const pd_entry = pd[256];
+ if ((pd_entry & PAGE_PRESENT) == 0) {
+ serial.print("PT dump: pd[256] not present\n");
+ return;
+ }
+
+ const pt_phys = pd_entry & paging_const.PTE_MASK;
+ serial.printf("PT dump: pd[256]={x} -> pt={x}\n", .{ pd_entry, pt_phys });
+}
+
+pub fn set_shinigami_pt_addrs(pdpt: u64, pd: u64, pt: u64) void {
+ // No longer used, kept for compatibility
+ _ = pdpt;
+ _ = pd;
+ _ = pt;
+}
+
+pub fn destroy_page_table(page_table_phys: u64) void {
+ const pml4: [*]volatile u64 = @ptrFromInt(page_table_phys + HIGHER_HALF_START);
+
+ for (0..paging_const.KERNEL_PML4_START) |pml4_idx| {
+ const pml4_entry = pml4[pml4_idx];
+ if ((pml4_entry & PAGE_PRESENT) == 0) continue;
+
+ const pdpt_phys = pml4_entry & paging_const.PTE_MASK;
+ const pdpt: [*]volatile u64 = @ptrFromInt(pdpt_phys + HIGHER_HALF_START);
+
+ for (0..512) |pdpt_idx| {
+ const pdpt_entry = pdpt[pdpt_idx];
+ if ((pdpt_entry & PAGE_PRESENT) == 0) continue;
+
+ const pd_phys = pdpt_entry & paging_const.PTE_MASK;
+ const pd: [*]volatile u64 = @ptrFromInt(pd_phys + HIGHER_HALF_START);
+
+ for (0..512) |pd_idx| {
+ const pd_entry = pd[pd_idx];
+ if ((pd_entry & PAGE_PRESENT) == 0) continue;
+
+ const pt_phys = pd_entry & paging_const.PTE_MASK;
+ const pt: [*]volatile u64 = @ptrFromInt(pt_phys + HIGHER_HALF_START);
+
+ for (0..512) |pt_idx| {
+ const pt_entry = pt[pt_idx];
+ if ((pt_entry & PAGE_PRESENT) == 0) continue;
+
+ const page_phys = pt_entry & paging_const.PTE_MASK;
+ const virt: u64 = (@as(u64, pml4_idx) << 39) |
+ (@as(u64, pdpt_idx) << 30) |
+ (@as(u64, pd_idx) << 21) |
+ (@as(u64, pt_idx) << 12);
+
+ if (should_free_page(virt, page_phys)) {
+ pmm.free_page(page_phys);
+ }
+ }
+
+ pmm.free_page(pt_phys);
+ }
+
+ pmm.free_page(pd_phys);
+ }
+
+ pmm.free_page(pdpt_phys);
+ }
+
+ pmm.free_page(page_table_phys);
+}
diff --git a/mirai.old/memory/pmm.zig b/mirai.old/memory/pmm.zig
new file mode 100644
index 0000000..cf66855
--- /dev/null
+++ b/mirai.old/memory/pmm.zig
@@ -0,0 +1,229 @@
+//! Physical Memory Manager
+
+const asm_memory = @import("../asm/memory.zig");
+const memory_const = @import("../common/constants/memory.zig");
+const multiboot = @import("../boot/multiboot/multiboot.zig");
+const paging_const = @import("../common/constants/paging.zig");
+const pmm_const = @import("../common/constants/pmm.zig");
+const serial = @import("../drivers/serial/serial.zig");
+
+const PAGE_SIZE = memory_const.PAGE_SIZE;
+const HIGHER_HALF = memory_const.HIGHER_HALF_START;
+
+var bitmap: [*]u8 = undefined;
+var bitmap_size: usize = 0;
+var bitmap_phys_start: u64 = 0;
+var bitmap_phys_end: u64 = 0;
+var total_pages: u64 = 0;
+var used_pages: u64 = 0;
+var initialized: bool = false;
+
+pub const MemoryInfo = struct {
+ total: u64,
+ used: u64,
+};
+
+pub fn get_bitmap_range() struct { start: u64, end: u64 } {
+ return .{ .start = bitmap_phys_start, .end = bitmap_phys_end };
+}
+
+pub fn is_in_bitmap(phys: u64) bool {
+ return phys >= bitmap_phys_start and phys < bitmap_phys_end;
+}
+
+pub fn init(kernel_end_phys: u64, memory_map: []multiboot.MemoryEntry) void {
+ serial.print("\n=== PMM ===\n");
+
+ var highest_addr: u64 = 0;
+ for (memory_map) |entry| {
+ if (entry.entry_type == pmm_const.MEMORY_AVAILABLE) {
+ const end = entry.base + entry.length;
+ if (end > highest_addr) {
+ highest_addr = end;
+ }
+ }
+ }
+
+ total_pages = highest_addr / PAGE_SIZE;
+ bitmap_size = (total_pages + 7) / 8;
+
+ serial.printf("Memory: {} MB, {} pages\n", .{ highest_addr / (1024 * 1024), total_pages });
+
+ bitmap_phys_start = align_up(kernel_end_phys, PAGE_SIZE);
+ bitmap = @ptrFromInt(bitmap_phys_start + HIGHER_HALF);
+
+ const bitmap_pages = (bitmap_size + PAGE_SIZE - 1) / PAGE_SIZE;
+ bitmap_phys_end = bitmap_phys_start + bitmap_pages * PAGE_SIZE;
+ serial.printf("Bitmap: phys={x}-{x} size={x} pages={d}\n", .{ bitmap_phys_start, bitmap_phys_end, bitmap_size, bitmap_pages });
+
+ for (0..bitmap_size) |i| {
+ bitmap[i] = pmm_const.BITMAP_MARK_USED;
+ }
+ used_pages = total_pages;
+
+ for (memory_map) |entry| {
+ if (entry.entry_type == pmm_const.MEMORY_AVAILABLE) {
+ free_region(entry.base, entry.length);
+ }
+ }
+
+ const kernel_size = kernel_end_phys - pmm_const.KERNEL_BASE;
+
+ reserve_region(0, pmm_const.FIRST_MB);
+ reserve_region(pmm_const.KERNEL_BASE, kernel_size);
+ reserve_region(bitmap_phys_start, bitmap_phys_end - bitmap_phys_start);
+
+ reserve_region(pmm_const.MMIO_PCI_BASE, pmm_const.MMIO_PCI_SIZE);
+ reserve_region(pmm_const.MMIO_FRAMEBUFFER_BASE, pmm_const.MMIO_FRAMEBUFFER_SIZE);
+
+ reserve_active_page_tables();
+
+ serial.printf("Free: {} pages ({} MB)\n", .{ total_pages - used_pages, (total_pages - used_pages) * PAGE_SIZE / (1024 * 1024) });
+
+ initialized = true;
+}
+
+pub var ash_pd_phys: u64 = 0;
+
+pub fn set_ash_pd(phys: u64) void {
+ ash_pd_phys = phys;
+ serial.printf("PMM: Tracking Ash PD at {x}\n", .{phys});
+
+ // Verify it's marked as used
+ const page = phys / PAGE_SIZE;
+ if (is_page_used(page)) {
+ serial.printf("PMM: Ash PD page {x} is correctly marked USED\n", .{phys});
+ } else {
+ serial.printf("PMM: BUG! Ash PD page {x} is marked FREE!\n", .{phys});
+ }
+}
+
+pub fn check_ash_pd_status() void {
+ if (ash_pd_phys == 0) return;
+ const page = ash_pd_phys / PAGE_SIZE;
+ if (!is_page_used(page)) {
+ serial.printf("PMM: Ash PD {x} became FREE!\n", .{ash_pd_phys});
+ }
+}
+
+pub fn alloc_page() ?u64 {
+ if (!initialized) return null;
+
+ for (0..total_pages) |i| {
+ if (!is_page_used(i)) {
+ const phys = i * PAGE_SIZE;
+ if (ash_pd_phys != 0 and phys == ash_pd_phys) {
+ serial.printf("PMM: ALLOCATING Ash's PD {x}! BUG - should be marked used!\n", .{phys});
+ }
+ set_page_used(i);
+ return phys;
+ }
+ }
+ return null;
+}
+
+pub fn free_page(phys_addr: u64) void {
+ if (!initialized) return;
+
+ const page = phys_addr / PAGE_SIZE;
+ if (page < total_pages) {
+ if (ash_pd_phys != 0 and phys_addr == ash_pd_phys) {
+ serial.printf("PMM: FREEING Ash's PD {x}!\n", .{phys_addr});
+ }
+ set_page_free(page);
+ }
+}
+
+fn free_region(base: u64, length: u64) void {
+ const start_page = align_up(base, PAGE_SIZE) / PAGE_SIZE;
+ const end_page = align_down(base + length, PAGE_SIZE) / PAGE_SIZE;
+
+ for (start_page..end_page) |page| {
+ if (page < total_pages) {
+ set_page_free(page);
+ }
+ }
+}
+
+fn reserve_region(base: u64, length: u64) void {
+ const start_page = base / PAGE_SIZE;
+ const end_page = (base + length + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ for (start_page..end_page) |page| {
+ if (page < total_pages) {
+ set_page_used(page);
+ }
+ }
+}
+
+fn is_page_used(page: u64) bool {
+ const byte_index = page / 8;
+ const bit_index: u3 = @truncate(page % 8);
+ return (bitmap[byte_index] & (@as(u8, 1) << bit_index)) != 0;
+}
+
+fn set_page_used(page: u64) void {
+ const byte_index = page / 8;
+ const bit_index: u3 = @truncate(page % 8);
+ if ((bitmap[byte_index] & (@as(u8, 1) << bit_index)) == 0) {
+ bitmap[byte_index] |= (@as(u8, 1) << bit_index);
+ used_pages += 1;
+ }
+}
+
+fn set_page_free(page: u64) void {
+ const byte_index = page / 8;
+ const bit_index: u3 = @truncate(page % 8);
+ if ((bitmap[byte_index] & (@as(u8, 1) << bit_index)) != 0) {
+ bitmap[byte_index] &= ~(@as(u8, 1) << bit_index);
+ used_pages -= 1;
+ }
+}
+
+fn align_up(addr: u64, alignment: u64) u64 {
+ return (addr + alignment - 1) & ~(alignment - 1);
+}
+
+fn align_down(addr: u64, alignment: u64) u64 {
+ return addr & ~(alignment - 1);
+}
+
+fn reserve_active_page_tables() void {
+ const cr3 = asm_memory.read_page_table_base() & ~@as(u64, paging_const.OFFSET_MASK);
+
+ reserve_region(cr3, PAGE_SIZE);
+
+ const pml4: [*]volatile u64 = @ptrFromInt(cr3 + HIGHER_HALF);
+
+ for (0..paging_const.PML4_ENTRIES) |pml4_i| {
+ if ((pml4[pml4_i] & paging_const.PTE_PRESENT) == 0) continue;
+
+ const pdpt_phys = pml4[pml4_i] & paging_const.PTE_MASK;
+ reserve_region(pdpt_phys, PAGE_SIZE);
+
+ const pdpt: [*]volatile u64 = @ptrFromInt(pdpt_phys + HIGHER_HALF);
+
+ for (0..paging_const.PML4_ENTRIES) |pdpt_i| {
+ if ((pdpt[pdpt_i] & paging_const.PTE_PRESENT) == 0) continue;
+
+ const pd_phys = pdpt[pdpt_i] & paging_const.PTE_MASK;
+ reserve_region(pd_phys, PAGE_SIZE);
+
+ const pd: [*]volatile u64 = @ptrFromInt(pd_phys + HIGHER_HALF);
+
+ for (0..paging_const.PML4_ENTRIES) |pd_i| {
+ if ((pd[pd_i] & paging_const.PTE_PRESENT) == 0) continue;
+
+ const pt_phys = pd[pd_i] & paging_const.PTE_MASK;
+ reserve_region(pt_phys, PAGE_SIZE);
+ }
+ }
+ }
+}
+
+pub fn get_info() MemoryInfo {
+ return MemoryInfo{
+ .total = total_pages,
+ .used = used_pages,
+ };
+}
diff --git a/mirai.old/mirai.zig b/mirai.old/mirai.zig
new file mode 100644
index 0000000..c3e2cfe
--- /dev/null
+++ b/mirai.old/mirai.zig
@@ -0,0 +1,25 @@
+const cpu = @import("asm/cpu.zig");
+const crimson = @import("crimson/crimson.zig");
+const sequence = @import("boot/sequence/sequence.zig");
+const serial = @import("drivers/serial/serial.zig");
+
+export fn mirai(multiboot_info_addr: u64) noreturn {
+ serial.init();
+
+ serial.print("\n═══════════════════════════════════\n");
+ serial.print(" Welcome to Akiba \n");
+ serial.print("═══════════════════════════════════\n\n");
+
+ // Run boot sequence
+ sequence.run(multiboot_info_addr);
+
+ serial.print("\n** Boot Complete **\n");
+
+ while (true) {
+ cpu.halt_processor();
+ }
+}
+
+pub fn panic(message: []const u8, _: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn {
+ crimson.collapse(message, null);
+}
diff --git a/mirai.old/utils/bytes/endian.zig b/mirai.old/utils/bytes/endian.zig
new file mode 100644
index 0000000..3e39b33
--- /dev/null
+++ b/mirai.old/utils/bytes/endian.zig
@@ -0,0 +1,49 @@
+//! Byte order conversion utilities
+
+const int = @import("../types/int.zig");
+
+pub inline fn read_u16_le(bytes: []const u8) u16 {
+ return int.u16_of(bytes[0]) |
+ (int.u16_of(bytes[1]) << 8);
+}
+
+pub inline fn read_u32_le(bytes: []const u8) u32 {
+ return int.u32_of(bytes[0]) |
+ (int.u32_of(bytes[1]) << 8) |
+ (int.u32_of(bytes[2]) << 16) |
+ (int.u32_of(bytes[3]) << 24);
+}
+
+pub inline fn read_u64_le(bytes: []const u8) u64 {
+ return int.u64_of(bytes[0]) |
+ (int.u64_of(bytes[1]) << 8) |
+ (int.u64_of(bytes[2]) << 16) |
+ (int.u64_of(bytes[3]) << 24) |
+ (int.u64_of(bytes[4]) << 32) |
+ (int.u64_of(bytes[5]) << 40) |
+ (int.u64_of(bytes[6]) << 48) |
+ (int.u64_of(bytes[7]) << 56);
+}
+
+pub inline fn write_u16_le(bytes: []u8, value: u16) void {
+ bytes[0] = int.u8_of(value);
+ bytes[1] = int.u8_of(value >> 8);
+}
+
+pub inline fn write_u32_le(bytes: []u8, value: u32) void {
+ bytes[0] = int.u8_of(value);
+ bytes[1] = int.u8_of(value >> 8);
+ bytes[2] = int.u8_of(value >> 16);
+ bytes[3] = int.u8_of(value >> 24);
+}
+
+pub inline fn write_u64_le(bytes: []u8, value: u64) void {
+ bytes[0] = int.u8_of(value);
+ bytes[1] = int.u8_of(value >> 8);
+ bytes[2] = int.u8_of(value >> 16);
+ bytes[3] = int.u8_of(value >> 24);
+ bytes[4] = int.u8_of(value >> 32);
+ bytes[5] = int.u8_of(value >> 40);
+ bytes[6] = int.u8_of(value >> 48);
+ bytes[7] = int.u8_of(value >> 56);
+}
diff --git a/mirai.old/utils/fs/location.zig b/mirai.old/utils/fs/location.zig
new file mode 100644
index 0000000..a0a75ad
--- /dev/null
+++ b/mirai.old/utils/fs/location.zig
@@ -0,0 +1,162 @@
+//! Location utilities
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const compare = @import("../string/compare.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const fs_limits = @import("../../common/limits/fs.zig");
+
+const DEVICE_PREFIX = "/system/devices/";
+
+pub fn resolve_to_cluster(
+ fs: *afs.AFS(ahci.BlockDevice),
+ location: []const u8,
+ current_cluster: u64,
+) ?u32 {
+ const current: u32 = if (current_cluster == 0) fs.root_cluster else @as(u32, @intCast(current_cluster));
+
+ if (location.len == 0) return current;
+
+ if (location.len == 1 and location[0] == '^') {
+ return fs.get_parent_cluster(current) orelse fs.root_cluster;
+ }
+
+ var cluster: u32 = undefined;
+ var start: usize = 0;
+
+ if (location[0] == '/') {
+ cluster = fs.root_cluster;
+ start = 1;
+ } else {
+ cluster = current;
+ }
+
+ if (start >= location.len) return cluster;
+
+ var i: usize = start;
+ while (i <= location.len) : (i += 1) {
+ const is_end = (i == location.len);
+ const is_slash = !is_end and location[i] == '/';
+
+ if (is_slash or is_end) {
+ if (i > start) {
+ const component = location[start..i];
+
+ if (compare.equals(component, "^")) {
+ cluster = fs.get_parent_cluster(cluster) orelse fs.root_cluster;
+ } else {
+ const entry = fs.find_entry(cluster, component) orelse return null;
+ if (!entry.is_stack()) return null;
+ cluster = entry.first_cluster;
+ }
+ }
+ start = i + 1;
+ }
+ }
+
+ return cluster;
+}
+
+pub const Canonical = struct {
+ buf: [fs_limits.MAX_LOCATION_LENGTH]u8,
+ len: usize,
+
+ pub fn slice(self: *const Canonical) []const u8 {
+ return self.buf[0..self.len];
+ }
+};
+
+pub fn canonicalize(current: []const u8, location: []const u8) Canonical {
+ var result: Canonical = .{ .buf = undefined, .len = 0 };
+
+ if (location.len == 1 and location[0] == '^') {
+ @memcpy(result.buf[0..current.len], current);
+ result.len = current.len;
+ strip_last(&result);
+ return result;
+ }
+
+ if (location.len > 0 and location[0] == '/') {
+ result.buf[0] = '/';
+ result.len = 1;
+ process_components(&result, location[1..]);
+ return result;
+ }
+
+ @memcpy(result.buf[0..current.len], current);
+ result.len = current.len;
+ process_components(&result, location);
+
+ return result;
+}
+
+fn strip_last(result: *Canonical) void {
+ if (result.len > 1) {
+ result.len -= 1;
+ while (result.len > 1 and result.buf[result.len - 1] != '/') {
+ result.len -= 1;
+ }
+ if (result.len > 1) result.len -= 1;
+ }
+ if (result.len == 0) {
+ result.buf[0] = '/';
+ result.len = 1;
+ }
+}
+
+fn process_components(result: *Canonical, location: []const u8) void {
+ var i: usize = 0;
+ while (i < location.len) {
+ while (i < location.len and location[i] == '/') : (i += 1) {}
+ if (i >= location.len) break;
+
+ const start = i;
+ while (i < location.len and location[i] != '/') : (i += 1) {}
+ const component = location[start..i];
+
+ if (compare.equals(component, "^")) {
+ strip_last(result);
+ } else {
+ if (result.len > 1) {
+ result.buf[result.len] = '/';
+ result.len += 1;
+ }
+ for (component) |c| {
+ if (result.len < fs_limits.MAX_LOCATION_LENGTH) {
+ result.buf[result.len] = c;
+ result.len += 1;
+ }
+ }
+ }
+ }
+}
+
+pub fn resolve(kata: *kata_mod.Kata, location: []const u8, buffer: []u8) []const u8 {
+ if (location.len > 0 and location[0] == '/') {
+ @memcpy(buffer[0..location.len], location);
+ return buffer[0..location.len];
+ }
+
+ const cwd = kata.current_location[0..kata.current_location_len];
+ var len: usize = cwd.len;
+
+ @memcpy(buffer[0..cwd.len], cwd);
+
+ if (cwd[cwd.len - 1] != '/') {
+ buffer[len] = '/';
+ len += 1;
+ }
+
+ @memcpy(buffer[len .. len + location.len], location);
+ len += location.len;
+
+ return buffer[0..len];
+}
+
+pub fn is_device(location: []const u8) bool {
+ return compare.starts_with(location, DEVICE_PREFIX);
+}
+
+pub fn device_name(location: []const u8) []const u8 {
+ return location[DEVICE_PREFIX.len..];
+}
diff --git a/mirai.old/utils/graphics/color.zig b/mirai.old/utils/graphics/color.zig
new file mode 100644
index 0000000..4e1b5ac
--- /dev/null
+++ b/mirai.old/utils/graphics/color.zig
@@ -0,0 +1,33 @@
+//! Color utilities
+
+const int = @import("../types/int.zig");
+
+pub const RGB = struct {
+ r: u8,
+ g: u8,
+ b: u8,
+};
+
+pub inline fn extract(color: u32) RGB {
+ return RGB{
+ .r = int.u8_of(color >> 16),
+ .g = int.u8_of(color >> 8),
+ .b = int.u8_of(color),
+ };
+}
+
+pub inline fn from_rgb(r: u8, g: u8, b: u8) u32 {
+ return (int.u32_of(r) << 16) | (int.u32_of(g) << 8) | int.u32_of(b);
+}
+
+pub inline fn red(color: u32) u8 {
+ return int.u8_of(color >> 16);
+}
+
+pub inline fn green(color: u32) u8 {
+ return int.u8_of(color >> 8);
+}
+
+pub inline fn blue(color: u32) u8 {
+ return int.u8_of(color);
+}
diff --git a/mirai.old/utils/graphics/pixel.zig b/mirai.old/utils/graphics/pixel.zig
new file mode 100644
index 0000000..29bd446
--- /dev/null
+++ b/mirai.old/utils/graphics/pixel.zig
@@ -0,0 +1,197 @@
+//! Pixel and framebuffer utilities
+
+const boot = @import("../../boot/multiboot/multiboot.zig");
+const color = @import("color.zig");
+const video = @import("../../common/constants/video.zig");
+
+pub inline fn ptr_32(addr: u64) [*]volatile u32 {
+ return @as([*]volatile u32, @ptrFromInt(addr));
+}
+
+pub inline fn ptr_8(addr: u64) [*]volatile u8 {
+ return @as([*]volatile u8, @ptrFromInt(addr));
+}
+
+pub inline fn offset_32(fb: boot.FramebufferInfo, x: u32, y: u32) usize {
+ return y * (fb.pitch / video.BYTES_PER_PIXEL_32) + x;
+}
+
+pub inline fn offset_24(fb: boot.FramebufferInfo, x: u32, y: u32) usize {
+ return y * fb.pitch + x * video.BYTES_PER_PIXEL_24;
+}
+
+pub inline fn pixels_per_row(fb: boot.FramebufferInfo) u32 {
+ return fb.pitch / video.BYTES_PER_PIXEL_32;
+}
+
+pub fn put(fb: boot.FramebufferInfo, x: u32, y: u32, c: u32) void {
+ if (x >= fb.width or y >= fb.height) return;
+
+ if (fb.bpp == video.BPP_32) {
+ put_32(fb, x, y, c);
+ } else if (fb.bpp == video.BPP_24) {
+ put_24(fb, x, y, c);
+ }
+}
+
+pub fn put_32(fb: boot.FramebufferInfo, x: u32, y: u32, c: u32) void {
+ const pixels = ptr_32(fb.addr);
+ pixels[offset_32(fb, x, y)] = c;
+}
+
+pub fn put_24(fb: boot.FramebufferInfo, x: u32, y: u32, c: u32) void {
+ const pixels = ptr_8(fb.addr);
+ const off = offset_24(fb, x, y);
+ const rgb = color.extract(c);
+ pixels[off] = rgb.b;
+ pixels[off + 1] = rgb.g;
+ pixels[off + 2] = rgb.r;
+}
+
+pub fn fill(fb: boot.FramebufferInfo, c: u32) void {
+ if (fb.bpp == video.BPP_32) {
+ fill_32(fb, c);
+ } else if (fb.bpp == video.BPP_24) {
+ fill_24(fb, c);
+ }
+}
+
+pub fn fill_32(fb: boot.FramebufferInfo, c: u32) void {
+ const pixels = ptr_32(fb.addr);
+ const total = fb.width * fb.height;
+ var i: usize = 0;
+ while (i < total) : (i += 1) {
+ pixels[i] = c;
+ }
+}
+
+pub fn fill_24(fb: boot.FramebufferInfo, c: u32) void {
+ const pixels = ptr_8(fb.addr);
+ const rgb = color.extract(c);
+
+ var y: u32 = 0;
+ while (y < fb.height) : (y += 1) {
+ var x: u32 = 0;
+ while (x < fb.width) : (x += 1) {
+ const off = offset_24(fb, x, y);
+ pixels[off] = rgb.b;
+ pixels[off + 1] = rgb.g;
+ pixels[off + 2] = rgb.r;
+ }
+ }
+}
+
+pub fn fill_rect(fb: boot.FramebufferInfo, x: u32, y: u32, w: u32, h: u32, c: u32) void {
+ if (fb.bpp == video.BPP_32) {
+ fill_rect_32(fb, x, y, w, h, c);
+ } else if (fb.bpp == video.BPP_24) {
+ fill_rect_24(fb, x, y, w, h, c);
+ }
+}
+
+pub fn fill_rect_32(fb: boot.FramebufferInfo, x: u32, y: u32, w: u32, h: u32, c: u32) void {
+ const pixels = ptr_32(fb.addr);
+
+ var row: u32 = 0;
+ while (row < h) : (row += 1) {
+ var col: u32 = 0;
+ while (col < w) : (col += 1) {
+ const px = x + col;
+ const py = y + row;
+ if (px < fb.width and py < fb.height) {
+ pixels[offset_32(fb, px, py)] = c;
+ }
+ }
+ }
+}
+
+pub fn fill_rect_24(fb: boot.FramebufferInfo, x: u32, y: u32, w: u32, h: u32, c: u32) void {
+ const pixels = ptr_8(fb.addr);
+ const rgb = color.extract(c);
+
+ var row: u32 = 0;
+ while (row < h) : (row += 1) {
+ var col: u32 = 0;
+ while (col < w) : (col += 1) {
+ const px = x + col;
+ const py = y + row;
+ if (px < fb.width and py < fb.height) {
+ const off = offset_24(fb, px, py);
+ pixels[off] = rgb.b;
+ pixels[off + 1] = rgb.g;
+ pixels[off + 2] = rgb.r;
+ }
+ }
+ }
+}
+
+pub fn copy_row(fb: boot.FramebufferInfo, dst_y: u32, src_y: u32) void {
+ if (fb.bpp == video.BPP_32) {
+ copy_row_32(fb, dst_y, src_y);
+ } else if (fb.bpp == video.BPP_24) {
+ copy_row_24(fb, dst_y, src_y);
+ }
+}
+
+pub fn copy_row_32(fb: boot.FramebufferInfo, dst_y: u32, src_y: u32) void {
+ const pixels = ptr_32(fb.addr);
+ const ppr = pixels_per_row(fb);
+
+ var x: u32 = 0;
+ while (x < fb.width) : (x += 1) {
+ pixels[dst_y * ppr + x] = pixels[src_y * ppr + x];
+ }
+}
+
+pub fn copy_row_24(fb: boot.FramebufferInfo, dst_y: u32, src_y: u32) void {
+ const pixels = ptr_8(fb.addr);
+
+ var x: u32 = 0;
+ while (x < fb.width) : (x += 1) {
+ const src_off = offset_24(fb, x, src_y);
+ const dst_off = offset_24(fb, x, dst_y);
+ pixels[dst_off] = pixels[src_off];
+ pixels[dst_off + 1] = pixels[src_off + 1];
+ pixels[dst_off + 2] = pixels[src_off + 2];
+ }
+}
+
+pub fn clear_row(fb: boot.FramebufferInfo, y: u32, c: u32) void {
+ if (fb.bpp == video.BPP_32) {
+ clear_row_32(fb, y, c);
+ } else if (fb.bpp == video.BPP_24) {
+ clear_row_24(fb, y, c);
+ }
+}
+
+pub fn clear_row_32(fb: boot.FramebufferInfo, y: u32, c: u32) void {
+ const pixels = ptr_32(fb.addr);
+ const ppr = pixels_per_row(fb);
+
+ var x: u32 = 0;
+ while (x < fb.width) : (x += 1) {
+ pixels[y * ppr + x] = c;
+ }
+}
+
+pub fn clear_row_24(fb: boot.FramebufferInfo, y: u32, c: u32) void {
+ const pixels = ptr_8(fb.addr);
+ const rgb = color.extract(c);
+
+ var x: u32 = 0;
+ while (x < fb.width) : (x += 1) {
+ const off = offset_24(fb, x, y);
+ pixels[off] = rgb.b;
+ pixels[off + 1] = rgb.g;
+ pixels[off + 2] = rgb.r;
+ }
+}
+
+pub fn line_width(fb: boot.FramebufferInfo) u32 {
+ if (fb.bpp == video.BPP_32) {
+ return fb.pitch / video.BYTES_PER_PIXEL_32;
+ } else if (fb.bpp == video.BPP_24) {
+ return fb.pitch / video.BYTES_PER_PIXEL_24;
+ }
+ return fb.width;
+}
diff --git a/mirai.old/utils/kata/attachment.zig b/mirai.old/utils/kata/attachment.zig
new file mode 100644
index 0000000..592b2c8
--- /dev/null
+++ b/mirai.old/utils/kata/attachment.zig
@@ -0,0 +1,37 @@
+//! Attachment utilities
+
+const afs = @import("../../fs/afs/afs.zig");
+const ahci = @import("../../drivers/ahci/ahci.zig");
+const attachment = @import("../../kata/attachment.zig");
+const heap = @import("../../memory/heap.zig");
+const kata_limits = @import("../../common/limits/kata.zig");
+const kata_mod = @import("../../kata/kata.zig");
+const location_util = @import("../fs/location.zig");
+
+pub fn allocate(kata: *kata_mod.Kata) !u32 {
+ var i: u32 = 3;
+ while (i < kata_limits.MAX_ATTACHMENTS) : (i += 1) {
+ if (kata.attachments[i] == null) {
+ return i;
+ }
+ }
+ return error.TooManyAttachments;
+}
+
+pub fn seal(kata: *kata_mod.Kata, fd: u32, fs: ?*afs.AFS(ahci.BlockDevice)) void {
+ const entry = kata.attachments[fd] orelse return;
+
+ if (entry.attachment_type == .Unit and entry.dirty) {
+ if (entry.buffer) |buffer| {
+ if (fs) |filesystem| {
+ var full_location_buf: [512]u8 = undefined;
+ const full_location = location_util.resolve(kata, entry.location[0..entry.location_len], &full_location_buf);
+ filesystem.mark_unit(full_location, buffer) catch {};
+ }
+ heap.free(@ptrCast(buffer.ptr), buffer.len);
+ }
+ }
+
+ attachment.free(entry);
+ kata.attachments[fd] = null;
+}
diff --git a/mirai.old/utils/mem/copy.zig b/mirai.old/utils/mem/copy.zig
new file mode 100644
index 0000000..405a55b
--- /dev/null
+++ b/mirai.old/utils/mem/copy.zig
@@ -0,0 +1,27 @@
+//! Memory copy utilities
+
+pub inline fn bytes(dest: []u8, src: []const u8) void {
+ @memcpy(dest[0..src.len], src);
+}
+
+pub inline fn bytes_n(dest: []u8, src: []const u8, n: usize) void {
+ @memcpy(dest[0..n], src[0..n]);
+}
+
+pub inline fn fill(dest: []u8, value: u8) void {
+ @memset(dest, value);
+}
+
+pub inline fn zero(dest: []u8) void {
+ @memset(dest, 0);
+}
+
+pub inline fn from_ptr(dest: []u8, src_ptr: u64, len: usize) void {
+ const src = @as([*]const u8, @ptrFromInt(src_ptr));
+ @memcpy(dest[0..len], src[0..len]);
+}
+
+pub inline fn to_ptr(dest_ptr: u64, src: []const u8) void {
+ const dest = @as([*]u8, @ptrFromInt(dest_ptr));
+ @memcpy(dest[0..src.len], src);
+}
diff --git a/mirai.old/utils/mem/slice.zig b/mirai.old/utils/mem/slice.zig
new file mode 100644
index 0000000..f750d6c
--- /dev/null
+++ b/mirai.old/utils/mem/slice.zig
@@ -0,0 +1,29 @@
+//! Slice and pointer utilities
+
+pub inline fn from_ptr(comptime T: type, ptr: u64, len: usize) []T {
+ return @as([*]T, @ptrFromInt(ptr))[0..len];
+}
+
+pub inline fn from_ptr_const(comptime T: type, ptr: u64, len: usize) []const T {
+ return @as([*]const T, @ptrFromInt(ptr))[0..len];
+}
+
+pub inline fn byte_ptr(ptr: u64) [*]u8 {
+ return @as([*]u8, @ptrFromInt(ptr));
+}
+
+pub inline fn byte_ptr_const(ptr: u64) [*]const u8 {
+ return @as([*]const u8, @ptrFromInt(ptr));
+}
+
+pub inline fn null_term_ptr(ptr: u64) [*:0]const u8 {
+ return @as([*:0]const u8, @ptrFromInt(ptr));
+}
+
+pub inline fn typed_ptr(comptime T: type, ptr: u64) [*]T {
+ return @as([*]T, @ptrFromInt(ptr));
+}
+
+pub inline fn typed_ptr_const(comptime T: type, ptr: u64) [*]const T {
+ return @as([*]const T, @ptrFromInt(ptr));
+}
diff --git a/mirai.old/utils/random/xorshift.zig b/mirai.old/utils/random/xorshift.zig
new file mode 100644
index 0000000..79ccffe
--- /dev/null
+++ b/mirai.old/utils/random/xorshift.zig
@@ -0,0 +1,26 @@
+//! Xorshift64 random number generation
+
+var state: u64 = 0x853C49E6748FEA9B;
+
+pub fn set_seed(s: u64) void {
+ state = if (s == 0) 0x853C49E6748FEA9B else s;
+}
+
+pub fn next_u64() u64 {
+ state ^= state << 13;
+ state ^= state >> 7;
+ state ^= state << 17;
+ return state;
+}
+
+pub fn next_u32() u32 {
+ return @truncate(next_u64());
+}
+
+pub fn byte() u8 {
+ return @truncate(next_u64());
+}
+
+pub fn range(min: u64, max: u64) u64 {
+ return min + (next_u64() % (max - min + 1));
+}
diff --git a/mirai.old/utils/string/compare.zig b/mirai.old/utils/string/compare.zig
new file mode 100644
index 0000000..8fc3266
--- /dev/null
+++ b/mirai.old/utils/string/compare.zig
@@ -0,0 +1,22 @@
+//! String comparison utilities
+
+pub inline fn equals(a: []const u8, b: []const u8) bool {
+ if (a.len != b.len) return false;
+ for (a, b) |ac, bc| {
+ if (ac != bc) return false;
+ }
+ return true;
+}
+
+pub inline fn equals_bytes(a: []const u8, b: anytype) bool {
+ if (a.len != b.len) return false;
+ for (a, 0..) |ac, i| {
+ if (ac != b[i]) return false;
+ }
+ return true;
+}
+
+pub inline fn starts_with(str: []const u8, prefix: []const u8) bool {
+ if (str.len < prefix.len) return false;
+ return equals(str[0..prefix.len], prefix);
+}
diff --git a/mirai.old/utils/string/copy.zig b/mirai.old/utils/string/copy.zig
new file mode 100644
index 0000000..693f910
--- /dev/null
+++ b/mirai.old/utils/string/copy.zig
@@ -0,0 +1,23 @@
+//! String copy utilities
+
+const memory_limits = @import("../../common/limits/memory.zig");
+const slice = @import("../mem/slice.zig");
+
+pub fn from_kata(dest: []u8, kata_ptr: u64) !usize {
+ if (!memory_limits.is_valid_kata_pointer(kata_ptr)) {
+ return error.InvalidPointer;
+ }
+
+ const src = slice.null_term_ptr(kata_ptr);
+
+ var len: usize = 0;
+ while (src[len] != 0 and len < dest.len) : (len += 1) {
+ dest[len] = src[len];
+ }
+
+ if (len >= dest.len and src[len] != 0) {
+ return error.StringTooLong;
+ }
+
+ return len;
+}
diff --git a/mirai.old/utils/types/int.zig b/mirai.old/utils/types/int.zig
new file mode 100644
index 0000000..e8529c5
--- /dev/null
+++ b/mirai.old/utils/types/int.zig
@@ -0,0 +1,41 @@
+//! Integer conversion utilities
+
+pub inline fn u3_of(value: anytype) u3 {
+ return @as(u3, @truncate(value));
+}
+
+pub inline fn u5_of(value: anytype) u5 {
+ return @as(u5, @truncate(value));
+}
+
+pub inline fn u8_of(value: anytype) u8 {
+ return @as(u8, @truncate(value));
+}
+
+pub inline fn u16_of(value: anytype) u16 {
+ const T = @TypeOf(value);
+ if (@typeInfo(T) == .int and @typeInfo(T).int.bits > 16) {
+ return @as(u16, @truncate(value));
+ }
+ return @as(u16, @intCast(value));
+}
+
+pub inline fn u32_of(value: anytype) u32 {
+ const T = @TypeOf(value);
+ if (@typeInfo(T) == .int and @typeInfo(T).int.bits > 32) {
+ return @as(u32, @truncate(value));
+ }
+ return @as(u32, @intCast(value));
+}
+
+pub inline fn u64_of(value: anytype) u64 {
+ return @as(u64, @intCast(value));
+}
+
+pub inline fn usize_of(value: anytype) usize {
+ return @as(usize, @intCast(value));
+}
+
+pub inline fn from_ptr(ptr: anytype) u64 {
+ return @intFromPtr(ptr);
+}
diff --git a/mirai.old/utils/types/ptr.zig b/mirai.old/utils/types/ptr.zig
new file mode 100644
index 0000000..aebbeba
--- /dev/null
+++ b/mirai.old/utils/types/ptr.zig
@@ -0,0 +1,13 @@
+//! Pointer conversion utilities
+
+pub inline fn of(comptime T: type, addr: u64) *T {
+ return @as(*T, @ptrFromInt(addr));
+}
+
+pub inline fn of_const(comptime T: type, addr: u64) *const T {
+ return @as(*const T, @ptrFromInt(addr));
+}
+
+pub inline fn to_addr(p: anytype) u64 {
+ return @intFromPtr(p);
+}
diff --git a/mirai.old/utils/types/result.zig b/mirai.old/utils/types/result.zig
new file mode 100644
index 0000000..9596353
--- /dev/null
+++ b/mirai.old/utils/types/result.zig
@@ -0,0 +1,27 @@
+//! Invocation result utilities
+
+const invocations = @import("../../common/constants/invocations.zig");
+const handler = @import("../../invocations/handler.zig");
+
+pub const ERROR = invocations.ERROR;
+pub const NO_DATA = invocations.NO_DATA;
+
+pub inline fn set_error(ctx: *handler.InvocationContext) void {
+ ctx.rax = ERROR;
+}
+
+pub inline fn set_no_data(ctx: *handler.InvocationContext) void {
+ ctx.rax = NO_DATA;
+}
+
+pub inline fn set_ok(ctx: *handler.InvocationContext) void {
+ ctx.rax = 0;
+}
+
+pub inline fn set_value(ctx: *handler.InvocationContext, value: u64) void {
+ ctx.rax = value;
+}
+
+pub inline fn is_error(value: u64) bool {
+ return value == ERROR;
+}