diff options
| -rw-r--r-- | Makefile | 13 | ||||
| -rw-r--r-- | boot/grub/grub.cfg | 2 | ||||
| -rw-r--r-- | build.zig | 2 | ||||
| -rw-r--r-- | mirai/common/constants/invocations.zig | 1 | ||||
| -rw-r--r-- | mirai/drivers/serial/serial.zig | 6 | ||||
| -rw-r--r-- | mirai/hikari/loader.zig | 2 | ||||
| -rw-r--r-- | mirai/invocations/handler.zig | 2 | ||||
| -rw-r--r-- | mirai/invocations/kata/reap.zig | 62 | ||||
| -rw-r--r-- | mirai/invocations/kata/spawn.zig | 102 | ||||
| -rw-r--r-- | mirai/invocations/kata/wait.zig | 3 | ||||
| -rw-r--r-- | mirai/invocations/kata/yield.zig | 22 | ||||
| -rw-r--r-- | mirai/kata/memory.zig | 68 | ||||
| -rw-r--r-- | mirai/kata/pool.zig | 23 | ||||
| -rw-r--r-- | mirai/kata/sensei/sensei.zig | 1 | ||||
| -rw-r--r-- | mirai/kata/sensei/waker.zig | 3 | ||||
| -rw-r--r-- | mirai/kata/shift.zig | 12 | ||||
| -rw-r--r-- | mirai/kata/types.zig | 6 | ||||
| -rw-r--r-- | mirai/memory/paging.zig | 107 | ||||
| -rw-r--r-- | mirai/memory/pmm.zig | 53 | ||||
| -rw-r--r-- | resources/system/akiba.world | 17 | ||||
| -rw-r--r-- | system/libraries/format/format.zig | 7 | ||||
| -rw-r--r-- | system/libraries/kata/kata.zig | 5 | ||||
| -rw-r--r-- | system/libraries/sys/sys.zig | 4 | ||||
| -rw-r--r-- | system/pulse/pulse.zig | 10 | ||||
| -rw-r--r-- | system/shinigami/shinigami.zig | 17 | ||||
| -rw-r--r-- | system/shinigami/shinigami.zon | 9 |
26 files changed, 491 insertions, 68 deletions
@@ -146,7 +146,7 @@ prepare-filesystem: build-grub @echo "→ Building kernel..." @zig build --cache-dir $(BUILD_DIR)/cache --prefix $(BUILD_DIR) - @mv $(BUILD_DIR)/bin/mirai.akibakernel $(FS_ROOT)/system/akiba/ + @mv $(BUILD_DIR)/bin/mirai.kernel $(FS_ROOT)/system/akiba/ @echo "→ Building libraries..." @mkdir -p $(BUILD_DIR)/lib $(FS_ROOT)/system/libraries @@ -182,15 +182,12 @@ prepare-filesystem: build-grub echo " Compiling $$sysname..."; \ $(BUILD_DIR)/bin/akibacompile "$$sysdir" "$(BUILD_DIR)/system/$$sysname" "system/libraries" && \ if [ "$$sysname" = "pulse" ]; then \ - echo " Creating pulse.akibainit..."; \ - $(BUILD_DIR)/bin/akibabuilder "$(BUILD_DIR)/system/$$sysname" "$(FS_ROOT)/system/akiba/pulse.akibainit" init && \ - echo " ✓ pulse.akibainit"; \ + $(BUILD_DIR)/bin/akibabuilder "$(BUILD_DIR)/system/$$sysname" "$(FS_ROOT)/system/akiba/pulse.gen" init; \ else \ - echo " Wrapping $$sysname.akiba..."; \ mkdir -p "$(FS_ROOT)/system/$$sysname" && \ - $(BUILD_DIR)/bin/akibabuilder "$(BUILD_DIR)/system/$$sysname" "$(FS_ROOT)/system/$$sysname/$$sysname.akiba" cli && \ - echo " ✓ system/$$sysname/$$sysname.akiba"; \ - fi; \ + $(BUILD_DIR)/bin/akibabuilder "$(BUILD_DIR)/system/$$sysname" "$(FS_ROOT)/system/$$sysname/$$sysname.gen" cli; \ + fi && \ + echo " ✓ $$sysname.gen"; \ fi; \ fi; \ done diff --git a/boot/grub/grub.cfg b/boot/grub/grub.cfg index 8c4291f..3540af0 100644 --- a/boot/grub/grub.cfg +++ b/boot/grub/grub.cfg @@ -25,6 +25,6 @@ set root='hd0,gpt2' menuentry "Akiba" { echo "Loading kernel from AFS partition..." - multiboot2 /system/akiba/mirai.akibakernel + multiboot2 /system/akiba/mirai.kernel boot }
\ No newline at end of file @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { }); const kernel = b.addExecutable(.{ - .name = "mirai.akibakernel", + .name = "mirai.kernel", .root_module = mirai_module, }); diff --git a/mirai/common/constants/invocations.zig b/mirai/common/constants/invocations.zig index 775d39e..afeb9d4 100644 --- a/mirai/common/constants/invocations.zig +++ b/mirai/common/constants/invocations.zig @@ -19,6 +19,7 @@ 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/drivers/serial/serial.zig b/mirai/drivers/serial/serial.zig index 07d3af7..b793c1a 100644 --- a/mirai/drivers/serial/serial.zig +++ b/mirai/drivers/serial/serial.zig @@ -119,9 +119,9 @@ fn print_arg_string(arg: anytype) void { const child_info = @typeInfo(child); if (child == u8) { // [*]const u8 - null-terminated string pointer - var p = arg; - while (p[0] != 0) : (p += 1) { - write(p[0]); + 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 diff --git a/mirai/hikari/loader.zig b/mirai/hikari/loader.zig index 2d33672..a09b4ce 100644 --- a/mirai/hikari/loader.zig +++ b/mirai/hikari/loader.zig @@ -18,7 +18,7 @@ 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.akibainit"; +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| { diff --git a/mirai/invocations/handler.zig b/mirai/invocations/handler.zig index 0855c72..bf3fa47 100644 --- a/mirai/invocations/handler.zig +++ b/mirai/invocations/handler.zig @@ -19,6 +19,7 @@ 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"); @@ -85,6 +86,7 @@ pub fn handle(ctx: *InvocationContext) void { 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/invocations/kata/reap.zig b/mirai/invocations/kata/reap.zig new file mode 100644 index 0000000..fd47794 --- /dev/null +++ b/mirai/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/invocations/kata/spawn.zig b/mirai/invocations/kata/spawn.zig index 051810d..83ae61d 100644 --- a/mirai/invocations/kata/spawn.zig +++ b/mirai/invocations/kata/spawn.zig @@ -8,15 +8,86 @@ 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); @@ -53,9 +124,40 @@ pub fn invoke(ctx: *handler.InvocationContext) void { } } + 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/invocations/kata/wait.zig b/mirai/invocations/kata/wait.zig index e46b08b..e657e90 100644 --- a/mirai/invocations/kata/wait.zig +++ b/mirai/invocations/kata/wait.zig @@ -11,7 +11,8 @@ pub fn invoke(ctx: *handler.InvocationContext) void { const target = kata_mod.get_kata(target_id) orelse return result.set_error(ctx); - if (target.state == .Dissolved) { + // Zombie or Dissolved means the child has exited + if (target.state == .Zombie or target.state == .Dissolved) { return result.set_value(ctx, target.exit_code); } diff --git a/mirai/invocations/kata/yield.zig b/mirai/invocations/kata/yield.zig index bc4028a..0da91af 100644 --- a/mirai/invocations/kata/yield.zig +++ b/mirai/invocations/kata/yield.zig @@ -10,6 +10,28 @@ pub fn invoke(ctx: *handler.InvocationContext) void { 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); } diff --git a/mirai/kata/memory.zig b/mirai/kata/memory.zig index 5639bf5..f90c9f0 100644 --- a/mirai/kata/memory.zig +++ b/mirai/kata/memory.zig @@ -4,6 +4,7 @@ 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"); @@ -13,39 +14,6 @@ const PAGE_SIZE = memory_const.PAGE_SIZE; const KERNEL_VMALLOC_START: u64 = 0xFFFFFF8000000000; var next_vmalloc_addr: u64 = KERNEL_VMALLOC_START; -// Deferred page table destruction queue -const MAX_DEFERRED: usize = 16; -var deferred_page_tables: [MAX_DEFERRED]u64 = [_]u64{0} ** MAX_DEFERRED; -var deferred_count: usize = 0; - -/// Queue a page table for deferred destruction -fn queue_deferred_destroy(page_table: u64) void { - if (deferred_count < MAX_DEFERRED) { - deferred_page_tables[deferred_count] = page_table; - deferred_count += 1; - } - // If queue full, leak the page table (shouldn't happen in practice) -} - -/// Process deferred page table destructions. -/// Safe to call when CR3 points to a page table we want to keep. -pub fn process_deferred_cleanup(exclude_pt: u64) void { - const current_cr3 = asm_memory.read_page_table_base(); - var write_idx: usize = 0; - - for (0..deferred_count) |i| { - const pt = deferred_page_tables[i]; - if (pt != 0 and pt != current_cr3 and pt != exclude_pt) { - paging.destroy_page_table(pt); - } else if (pt != 0) { - // Keep in queue - deferred_page_tables[write_idx] = pt; - write_idx += 1; - } - } - deferred_count = write_idx; -} - pub const VirtualBuffer = struct { data: []u8, virt_base: u64, @@ -114,8 +82,16 @@ pub fn setup(kata: *types.Kata, framebuffer_phys: u64, framebuffer_size: u64) !v 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); @@ -126,6 +102,12 @@ pub fn setup(kata: *types.Kata, framebuffer_phys: u64, framebuffer_size: u64) !v } 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 @@ -222,6 +204,13 @@ pub fn load_segment( } } 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); @@ -255,13 +244,10 @@ 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) { - // Safe to destroy immediately paging.destroy_page_table(kata.page_table); - } else { - // Queue for deferred destruction - queue_deferred_destroy(kata.page_table); + kata.page_table = 0; } - kata.page_table = 0; + // If CR3 == kata.page_table, leave page_table intact for Shinigami } kata.stack_top = 0; kata.user_stack_top = 0; @@ -269,6 +255,12 @@ pub fn cleanup(kata: *types.Kata) void { 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); diff --git a/mirai/kata/pool.zig b/mirai/kata/pool.zig index 030909a..08814d8 100644 --- a/mirai/kata/pool.zig +++ b/mirai/kata/pool.zig @@ -77,20 +77,38 @@ pub fn get(id: u32) ?*types.Kata { 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); - kata.state = .Dissolved; - used[i] = false; + + // 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; } @@ -101,6 +119,7 @@ fn create_empty() types.Kata { return types.Kata{ .id = 0, .state = .Dissolved, + .mode = .Persona, .context = types.Context.init(), .page_table = 0, .stack_top = 0, diff --git a/mirai/kata/sensei/sensei.zig b/mirai/kata/sensei/sensei.zig index 7985ad9..77f9cc9 100644 --- a/mirai/kata/sensei/sensei.zig +++ b/mirai/kata/sensei/sensei.zig @@ -3,6 +3,7 @@ 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"); diff --git a/mirai/kata/sensei/waker.zig b/mirai/kata/sensei/waker.zig index c4334e3..d18388b 100644 --- a/mirai/kata/sensei/waker.zig +++ b/mirai/kata/sensei/waker.zig @@ -14,7 +14,8 @@ pub fn wake_all_waiting() void { if (kata.state != .Stalled) continue; const target = pool.get(kata.waiting_for); - if (target == null or target.?.state == .Dissolved) { + // 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; diff --git a/mirai/kata/shift.zig b/mirai/kata/shift.zig index 3ad64be..52e87fb 100644 --- a/mirai/kata/shift.zig +++ b/mirai/kata/shift.zig @@ -2,16 +2,20 @@ const context = @import("../asm/context.zig"); const gdt = @import("../boot/gdt/gdt.zig"); -const kata_memory = @import("memory.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 { - // Process any deferred page table cleanups before switching - // Exclude the target kata's page table from cleanup - kata_memory.process_deferred_cleanup(kata.page_table); + // 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; diff --git a/mirai/kata/types.zig b/mirai/kata/types.zig index 85f653f..c416212 100644 --- a/mirai/kata/types.zig +++ b/mirai/kata/types.zig @@ -15,6 +15,11 @@ pub const State = enum { 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, @@ -66,6 +71,7 @@ pub const Context = packed struct { pub const Kata = struct { id: u32, state: State, + mode: Mode, context: Context, page_table: u64, diff --git a/mirai/memory/paging.zig b/mirai/memory/paging.zig index 61c69b7..ea3e7e1 100644 --- a/mirai/memory/paging.zig +++ b/mirai/memory/paging.zig @@ -83,7 +83,15 @@ pub fn map_page(virt: u64, phys: u64, flags: u64) !void { } 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| { @@ -111,6 +119,9 @@ pub fn create_page_table() !u64 { } 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; @@ -118,11 +129,38 @@ pub fn map_page_in_table(page_table_phys: u64, virt: u64, phys: u64, flags: u64) 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; } @@ -132,8 +170,16 @@ pub fn map_page_in_table(page_table_phys: u64, virt: u64, phys: u64, flags: u64) 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; } @@ -143,8 +189,16 @@ pub fn map_page_in_table(page_table_phys: u64, virt: u64, phys: u64, flags: u64) 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; } @@ -154,6 +208,12 @@ pub fn map_page_in_table(page_table_phys: u64, virt: u64, phys: u64, flags: u64) 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; @@ -256,6 +316,53 @@ fn should_free_page(virt: u64, phys: u64) bool { 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); diff --git a/mirai/memory/pmm.zig b/mirai/memory/pmm.zig index 8207387..cf66855 100644 --- a/mirai/memory/pmm.zig +++ b/mirai/memory/pmm.zig @@ -12,6 +12,8 @@ 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; @@ -21,6 +23,14 @@ pub const MemoryInfo = struct { 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"); @@ -39,8 +49,12 @@ pub fn init(kernel_end_phys: u64, memory_map: []multiboot.MemoryEntry) void { serial.printf("Memory: {} MB, {} pages\n", .{ highest_addr / (1024 * 1024), total_pages }); - const bitmap_phys = align_up(kernel_end_phys, PAGE_SIZE); - bitmap = @ptrFromInt(bitmap_phys + HIGHER_HALF); + 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; @@ -54,11 +68,10 @@ pub fn init(kernel_end_phys: u64, memory_map: []multiboot.MemoryEntry) void { } const kernel_size = kernel_end_phys - pmm_const.KERNEL_BASE; - const bitmap_pages = (bitmap_size + PAGE_SIZE - 1) / PAGE_SIZE; reserve_region(0, pmm_const.FIRST_MB); reserve_region(pmm_const.KERNEL_BASE, kernel_size); - reserve_region(bitmap_phys, bitmap_pages * PAGE_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); @@ -70,13 +83,40 @@ pub fn init(kernel_end_phys: u64, memory_map: []multiboot.MemoryEntry) void { 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 i * PAGE_SIZE; + return phys; } } return null; @@ -87,6 +127,9 @@ pub fn free_page(phys_addr: u64) void { 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); } } diff --git a/resources/system/akiba.world b/resources/system/akiba.world new file mode 100644 index 0000000..8993cdd --- /dev/null +++ b/resources/system/akiba.world @@ -0,0 +1,17 @@ +world { + name = akiba + startup { + binary { + name = shinigami + path = "/system/shinigami/shinigami.gen" + mode = protected + respawn = true + } + binary { + name = ash + path = "/system/ash/ash.gen" + mode = protected + respawn = true + } + } +} diff --git a/system/libraries/format/format.zig b/system/libraries/format/format.zig index 02a5632..f9c8068 100644 --- a/system/libraries/format/format.zig +++ b/system/libraries/format/format.zig @@ -20,3 +20,10 @@ pub const colorf = printmod.colorf; pub const Table = tablemod.Table; pub const Column = tablemod.Column; pub const Alignment = tablemod.Alignment; + +/// Print a u64 value as decimal +pub fn print_u64(num: u64) void { + var buf: [20]u8 = undefined; + const str = int.toStr(num, &buf); + print(str); +} diff --git a/system/libraries/kata/kata.zig b/system/libraries/kata/kata.zig index 39f4556..1144294 100644 --- a/system/libraries/kata/kata.zig +++ b/system/libraries/kata/kata.zig @@ -43,3 +43,8 @@ pub fn wait(pid: u32) Error!u64 { yield(); } } + +/// Reap zombie katas - returns number of zombies reaped +pub fn reap() u64 { + return sys.syscall(.reap, .{}); +} diff --git a/system/libraries/sys/sys.zig b/system/libraries/sys/sys.zig index b080a90..56186f1 100644 --- a/system/libraries/sys/sys.zig +++ b/system/libraries/sys/sys.zig @@ -26,6 +26,7 @@ pub const Invocation = enum(u64) { uptime = 0x11, gettime = 0x12, diskinfo = 0x13, + reap = 0x14, }; pub inline fn syscall(invocation: Invocation, args: anytype) u64 { @@ -48,8 +49,7 @@ pub inline fn syscall(invocation: Invocation, args: anytype) u64 { [arg3] "{r10}" (arg3), [arg4] "{r8}" (arg4), [arg5] "{r9}" (arg5), - : .{ .rcx = true, .r11 = true, .memory = true } - ); + : .{ .rcx = true, .r11 = true, .memory = true }); } inline fn toU64(value: anytype) u64 { diff --git a/system/pulse/pulse.zig b/system/pulse/pulse.zig index 3ef1091..b917f8c 100644 --- a/system/pulse/pulse.zig +++ b/system/pulse/pulse.zig @@ -8,8 +8,16 @@ export fn main(pc: u32, pv: [*]const [*:0]const u8) u8 { _ = pc; _ = pv; + // Spawn Shinigami - the zombie reaper + _ = kata.spawn("/system/shinigami/shinigami.gen") catch { + format.println("Pulse: FATAL - Failed to spawn Shinigami"); + return 1; + }; + format.println("Pulse: Shinigami started"); + + // Main shell loop while (true) { - const shell_pid = kata.spawn("/system/ash/ash.akiba") catch { + const shell_pid = kata.spawn("/system/ash/ash.gen") catch { format.println("Pulse: Failed to spawn shell"); kata.yield(); continue; diff --git a/system/shinigami/shinigami.zig b/system/shinigami/shinigami.zig new file mode 100644 index 0000000..4f4973c --- /dev/null +++ b/system/shinigami/shinigami.zig @@ -0,0 +1,17 @@ +//! Shinigami - The Soul Reaper +//! Background service that reaps zombie katas + +const kata = @import("kata"); + +export fn main(pc: u32, pv: [*]const [*:0]const u8) u8 { + _ = pc; + _ = pv; + + // Shinigami runs forever, reaping zombie souls + while (true) { + _ = kata.reap(); + kata.yield(); + } + + return 0; +} diff --git a/system/shinigami/shinigami.zon b/system/shinigami/shinigami.zon new file mode 100644 index 0000000..2516af3 --- /dev/null +++ b/system/shinigami/shinigami.zon @@ -0,0 +1,9 @@ +.{ + .name = "shinigami", + .version = "0.1.0", + .dependencies = .{ + .sys, + .kata, + }, + .entry = "shinigami.zig", +} |
