diff options
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | mirai/boot/multiboot2.zig | 10 | ||||
| -rw-r--r-- | mirai/boot/sequence.zig | 1 | ||||
| -rw-r--r-- | mirai/drivers/keyboard.zig | 67 | ||||
| -rw-r--r-- | mirai/hikari/loader.zig | 353 | ||||
| -rw-r--r-- | mirai/invocations/getkeychar.zig | 17 | ||||
| -rw-r--r-- | mirai/invocations/handler.zig | 2 | ||||
| -rw-r--r-- | mirai/invocations/syscall.zig | 22 | ||||
| -rw-r--r-- | mirai/invocations/wait.zig | 29 | ||||
| -rw-r--r-- | mirai/invocations/yield.zig | 7 | ||||
| -rw-r--r-- | mirai/kata/kata.zig | 7 | ||||
| -rw-r--r-- | mirai/kata/sensei.zig | 117 | ||||
| -rw-r--r-- | mirai/memory/paging.zig | 40 | ||||
| -rw-r--r-- | mirai/mirai.zig | 8 | ||||
| -rw-r--r-- | system/ash/ash.zig | 45 | ||||
| -rw-r--r-- | system/ash/ash.zon | 8 | ||||
| -rw-r--r-- | system/libraries/akiba/io.zig | 16 | ||||
| -rw-r--r-- | system/libraries/akiba/kata.zig | 12 | ||||
| -rw-r--r-- | system/libraries/akiba/sys.zig | 13 | ||||
| -rw-r--r-- | system/pulse/pulse.zig | 19 |
20 files changed, 546 insertions, 252 deletions
@@ -179,8 +179,9 @@ prepare-filesystem: build-grub echo " ✓ pulse.akibainit"; \ else \ echo " Wrapping $$sysname.akiba..."; \ - $(BUILD_DIR)/bin/akibabuilder "$(BUILD_DIR)/system/$$sysname" "$(FS_ROOT)/system/$$sysname.akiba" cli && \ - echo " ✓ $$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; \ fi; \ fi; \ diff --git a/mirai/boot/multiboot2.zig b/mirai/boot/multiboot2.zig index 3a8ae2a..8356a03 100644 --- a/mirai/boot/multiboot2.zig +++ b/mirai/boot/multiboot2.zig @@ -20,6 +20,16 @@ pub const FramebufferInfo = struct { const MAX_MEMORY_ENTRIES = 32; var memory_entries: [MAX_MEMORY_ENTRIES]MemoryEntry = undefined; +var saved_framebuffer: ?FramebufferInfo = null; + +pub fn init_framebuffer(fb: FramebufferInfo) void { + saved_framebuffer = fb; +} + +pub fn get_framebuffer() ?FramebufferInfo { + return saved_framebuffer; +} + pub fn parse_memory_map(addr: u64) []MemoryEntry { serial.print("\n=== Multiboot2 Memory Map ===\n"); diff --git a/mirai/boot/sequence.zig b/mirai/boot/sequence.zig index ed56adc..0dd66a9 100644 --- a/mirai/boot/sequence.zig +++ b/mirai/boot/sequence.zig @@ -172,6 +172,7 @@ pub fn run(multiboot_info_addr: u64) void { boot_print("Initializing framebuffer... "); terminal.init(fb); crimson.init(fb); + multiboot.init_framebuffer(fb); terminal_ready = true; replay_messages(); boot_ok(); diff --git a/mirai/drivers/keyboard.zig b/mirai/drivers/keyboard.zig index bb04f6e..1a50af6 100644 --- a/mirai/drivers/keyboard.zig +++ b/mirai/drivers/keyboard.zig @@ -1,6 +1,7 @@ //! PS/2 Keyboard Driver const serial = @import("serial.zig"); +const sensei = @import("../kata/sensei.zig"); const KEYBOARD_DATA_PORT: u16 = 0x60; const KEYBOARD_STATUS_PORT: u16 = 0x64; @@ -12,13 +13,11 @@ var ctrl_pressed = false; var alt_pressed = false; var caps_lock = false; -// Input buffer for shell +// Circular buffer for keyboard input const BUFFER_SIZE = 256; -var input_buffer: [BUFFER_SIZE]u8 = undefined; -var buffer_pos: usize = 0; - -// External function to send keys to terminal/shell -extern fn on_key_typed(char: u8) void; +var key_buffer: [BUFFER_SIZE]u8 = undefined; +var read_pos: usize = 0; +var write_pos: usize = 0; const SCANCODE_TO_ASCII: [128]u8 = .{ 0, 27, '1', '2', '3', '4', '5', '6', @@ -145,38 +144,38 @@ fn handle_scancode(scancode: u8) void { } fn on_key_press(ascii: u8) void { - // Echo to serial + // Echo to serial only serial.write(ascii); - - // Send to terminal/shell - on_key_typed(ascii); - - // Handle backspace - if (ascii == '\x08') { - if (buffer_pos > 0) { - buffer_pos -= 1; - } - return; - } - - // Handle enter - if (ascii == '\n') { - serial.print("\n"); - buffer_pos = 0; - return; - } - - // Add to buffer - if (buffer_pos < BUFFER_SIZE - 1) { - input_buffer[buffer_pos] = ascii; - buffer_pos += 1; + serial.print(" [keypress] write_pos="); + serial.print_hex(write_pos); + serial.print(", read_pos="); + serial.print_hex(read_pos); + serial.print("\n"); + + // Add to circular buffer + const next_write = (write_pos + 1) % BUFFER_SIZE; + if (next_write != read_pos) { + key_buffer[write_pos] = ascii; + write_pos = next_write; + + // Wake one blocked kata waiting for keyboard input + sensei.wake_one_blocked_kata(); + } else { + serial.print("Keyboard buffer full!\n"); } } -pub fn get_input_buffer() []const u8 { - return input_buffer[0..buffer_pos]; +pub fn has_input() bool { + return read_pos != write_pos; } -pub fn clear_buffer() void { - buffer_pos = 0; +// Non-blocking read +pub fn read_char() ?u8 { + if (read_pos == write_pos) { + return null; + } + + const char = key_buffer[read_pos]; + read_pos = (read_pos + 1) % BUFFER_SIZE; + return char; } diff --git a/mirai/hikari/loader.zig b/mirai/hikari/loader.zig index ae78ac9..a6fa00a 100644 --- a/mirai/hikari/loader.zig +++ b/mirai/hikari/loader.zig @@ -1,212 +1,275 @@ //! Hikari - The ELF loader //! Hikari (光) = Light - illuminates programs into execution -const ahci = @import("../drivers/ahci.zig"); +const serial = @import("../drivers/serial.zig"); +const heap = @import("../memory/heap.zig"); +const pmm = @import("../memory/pmm.zig"); +const paging = @import("../memory/paging.zig"); const afs = @import("../fs/afs.zig"); +const ahci = @import("../drivers/ahci.zig"); const elf = @import("elf.zig"); const format = @import("format.zig"); -const gdt = @import("../boot/gdt.zig"); const kata_mod = @import("../kata/kata.zig"); -const paging = @import("../memory/paging.zig"); -const pmm = @import("../memory/pmm.zig"); const sensei = @import("../kata/sensei.zig"); -const serial = @import("../drivers/serial.zig"); +const gdt = @import("../boot/gdt.zig"); +const boot = @import("../boot/multiboot2.zig"); -const PAGE_SIZE: u64 = 4096; const HIGHER_HALF: u64 = 0xFFFF800000000000; +const USER_STACK_TOP: u64 = 0x00007FFFFFF00000; +const USER_STACK_PAGES: u64 = 4; pub fn init() void { - serial.print("\n=== Hikari Loader ===\n"); - serial.print("Akiba executable loader initialized\n"); + serial.print("Hikari loader initialized\n"); } pub fn load_init_system(fs: *afs.AFS(ahci.BlockDevice)) !u32 { - serial.print("\n=== Loading Init System ===\n"); - - const pulse_path = "/system/akiba/pulse.akibainit"; - return load_program(fs, pulse_path); + return load_program(fs, "/system/akiba/pulse.akibainit"); } pub fn load_program(fs: *afs.AFS(ahci.BlockDevice), path: []const u8) !u32 { - serial.print("\n=== Loading Program ===\n"); - serial.print("Path: "); + serial.print("Loading program: "); serial.print(path); serial.print("\n"); - var file_buffer: [1024 * 1024]u8 = undefined; - const bytes_read = try fs.read_file_by_path(path, &file_buffer); - - const file_data = file_buffer[0..bytes_read]; - const akiba_exe = try format.parse_akiba(file_data); - const elf_info = try elf.parse_elf(akiba_exe.elf_data); - - const new_kata = try kata_mod.create_kata(); - - try setup_kata_memory(new_kata, &elf_info, akiba_exe.elf_data); - setup_kata_context(new_kata, elf_info.entry_point); + // Allocate file buffer directly from PMM (2MB is too large for heap slab allocator) + const buffer_size: usize = 2 * 1024 * 1024; + const num_pages = (buffer_size + 0xFFF) / 0x1000; // 512 pages + + // Allocate pages and track them in a fixed array + var pages: [512]u64 = undefined; + var pages_allocated: usize = 0; + + var i: usize = 0; + while (i < num_pages) : (i += 1) { + const page = pmm.alloc_page() orelse { + // Free already allocated pages on error + var j: usize = 0; + while (j < pages_allocated) : (j += 1) { + pmm.free_page(pages[j]); + } + return error.OutOfMemory; + }; + pages[i] = page; + pages_allocated += 1; + } + defer { + var j: usize = 0; + while (j < pages_allocated) : (j += 1) { + pmm.free_page(pages[j]); + } + } - sensei.enqueue_kata(new_kata); + // Map all pages into a contiguous virtual buffer + // For simplicity, just use the physical pages directly via higher half mapping + // This assumes PMM returns reasonably contiguous pages + const file_buffer_phys = pages[0]; + const file_buffer_ptr = @as([*]u8, @ptrFromInt(file_buffer_phys + HIGHER_HALF)); + const file_buffer = file_buffer_ptr[0..buffer_size]; + + const bytes_read = fs.read_file_by_path(path, file_buffer) catch |err| { + serial.print("Error reading file: "); + serial.print(@errorName(err)); + serial.print("\n"); + return err; + }; + + const akiba_data = format.parse_akiba(file_buffer[0..bytes_read]) catch |err| { + serial.print("Error parsing Akiba format: "); + serial.print(@errorName(err)); + serial.print("\n"); + return err; + }; + + const elf_info = elf.parse_elf(akiba_data.elf_data) catch |err| { + serial.print("Error parsing ELF: "); + serial.print(@errorName(err)); + serial.print("\n"); + return err; + }; + + serial.print(" Entry point: "); + serial.print_hex(elf_info.entry_point); + serial.print("\n"); - serial.print("Kata "); - serial.print_hex(new_kata.id); + const kata = kata_mod.create_kata() catch |err| { + serial.print("Error creating Kata: "); + serial.print(@errorName(err)); + serial.print("\n"); + return err; + }; + + setup_kata_memory(kata, akiba_data.elf_data, elf_info) catch |err| { + serial.print("Error setting up memory: "); + serial.print(@errorName(err)); + serial.print("\n"); + kata_mod.dissolve_kata(kata.id); + return err; + }; + + setup_kata_context(kata, elf_info.entry_point); + sensei.enqueue_kata(kata); + + serial.print(" Kata "); + serial.print_hex(kata.id); serial.print(" ready\n"); - return new_kata.id; + return kata.id; } -fn setup_kata_memory(kata: *kata_mod.Kata, elf_info: *const elf.ELFInfo, elf_data: []const u8) !void { - // Create isolated page table for this process - kata.page_table = try paging.create_page_table(); +fn setup_kata_memory(kata: *kata_mod.Kata, elf_data: []const u8, elf_info: elf.ELFInfo) !void { + serial.print(" Setting up memory...\n"); + + const new_cr3 = try paging.create_page_table(); + kata.page_table = new_cr3; - serial.print("Kata page table: "); - serial.print_hex(kata.page_table); + serial.print(" New CR3: "); + serial.print_hex(new_cr3); serial.print("\n"); - // Load program segments at their original ELF addresses - for (elf_info.program_headers) |ph| { - if (ph.type == elf.PT_LOAD) { - try load_segment(kata, ph, elf_data); + for (elf_info.program_headers) |phdr| { + if (phdr.type == elf.PT_LOAD) { + try load_segment(kata.page_table, elf_data, phdr); } } - // Allocate user stack in high canonical address - const user_stack_base: u64 = 0x00007FFFFFF00000; - const user_stack_size: u64 = PAGE_SIZE * 4; + const user_stack_base = USER_STACK_TOP - (USER_STACK_PAGES * 0x1000); + var i: u64 = 0; + while (i < USER_STACK_PAGES) : (i += 1) { + const page_phys = pmm.alloc_page() orelse return error.OutOfMemory; + const page_virt = user_stack_base + (i * 0x1000); - try allocate_user_stack(kata, user_stack_base, user_stack_size); - kata.user_stack_top = user_stack_base + user_stack_size; + _ = try paging.map_page_in_table(kata.page_table, page_virt, page_phys, 0b111); - serial.print("User stack: "); - serial.print_hex(kata.user_stack_top); - serial.print("\n"); + const zero_ptr = @as([*]volatile u8, @ptrFromInt(page_phys + HIGHER_HALF)); + @memset(zero_ptr[0..0x1000], 0); + } + + kata.user_stack_top = USER_STACK_TOP; - // Allocate kernel stack in higher-half - const kernel_stack_phys = pmm.alloc_page() orelse return error.OutOfMemory; - kata.stack_top = kernel_stack_phys + HIGHER_HALF + PAGE_SIZE; + const kernel_stack_page = pmm.alloc_page() orelse return error.OutOfMemory; + kata.stack_top = kernel_stack_page + HIGHER_HALF + 0x1000; - serial.print("Kernel stack: "); + serial.print(" User stack: "); + serial.print_hex(kata.user_stack_top); + serial.print("\n"); + serial.print(" Kernel stack: "); serial.print_hex(kata.stack_top); serial.print("\n"); -} -fn load_segment(kata: *kata_mod.Kata, ph: elf.ELF64ProgramHeader, elf_data: []const u8) !void { - serial.print("Loading segment at "); - serial.print_hex(ph.vaddr); - serial.print(", size: "); - serial.print_hex(ph.memsz); - serial.print(", file offset: "); - serial.print_hex(ph.offset); - serial.print("\n"); + if (boot.get_framebuffer()) |fb_info| { + serial.print(" Mapping framebuffer for userspace access...\n"); - var vaddr = ph.vaddr; - var remaining = ph.memsz; - var file_offset: u64 = 0; - - while (remaining > 0) { - const page_base = vaddr & ~@as(u64, PAGE_SIZE - 1); - const offset_in_page = vaddr - page_base; - const bytes_in_page = @min(PAGE_SIZE - offset_in_page, remaining); - - // Allocate and map page - const phys_page = pmm.alloc_page() orelse return error.OutOfMemory; - const result = try paging.map_page_in_table(kata.page_table, page_base, phys_page, 0b111); - const was_mapped = result[0]; - const actual_phys = result[1]; - - // DEBUG: Verify mapping - if (page_base == 0x400000) { - serial.print("DEBUG: Mapped 0x400000 -> phys "); - serial.print_hex(actual_phys); - serial.print(", was_mapped="); - serial.print(if (was_mapped) "true\n" else "false\n"); - - // Verify we can read it back - const verify_phys = paging.get_physical_address(kata.page_table, 0x400000) catch 0; - serial.print("DEBUG: Verify readback: "); - serial.print_hex(verify_phys); - serial.print("\n"); - } + const fb_start = fb_info.addr; + const fb_size = fb_info.height * fb_info.pitch; + const fb_pages = (fb_size + 0xFFF) / 0x1000; - if (was_mapped) { - pmm.free_page(phys_page); - } + serial.print(" FB addr: "); + serial.print_hex(fb_start); + serial.print(", size: "); + serial.print_hex(fb_size); + serial.print(", pages: "); + serial.print_hex(fb_pages); + serial.print("\n"); - // Zero page and write segment data - const dest_base = @as([*]u8, @ptrFromInt(actual_phys + HIGHER_HALF)); + var page: u64 = 0; + while (page < fb_pages) : (page += 1) { + const phys = fb_start + (page * 0x1000); + const virt = phys; - if (!was_mapped) { - var i: usize = 0; - while (i < PAGE_SIZE) : (i += 1) { - dest_base[i] = 0; - } + _ = try paging.map_page_in_table(kata.page_table, virt, phys, 0b111); } - if (file_offset < ph.filesz) { - const file_bytes = @min(bytes_in_page, ph.filesz - file_offset); - const src_offset = ph.offset + file_offset; - const src = elf_data[src_offset .. src_offset + file_bytes]; + serial.print(" Framebuffer mapped successfully\n"); + } else { + serial.print(" WARNING: No framebuffer info available\n"); + } +} - const dest = dest_base + offset_in_page; - for (src, 0..) |byte, j| { - dest[j] = byte; - } - } +fn load_segment(cr3: u64, elf_data: []const u8, phdr: elf.ELF64ProgramHeader) !void { + serial.print(" Loading segment at "); + serial.print_hex(phdr.vaddr); + serial.print(" ("); + serial.print_hex(phdr.memsz); + serial.print(" bytes)\n"); - vaddr += bytes_in_page; - file_offset += bytes_in_page; - remaining -= bytes_in_page; - } + const start_addr = phdr.vaddr; + const mem_size = phdr.memsz; + const file_size = phdr.filesz; - // Verify entry point was loaded correctly - if (ph.vaddr == 0x400000) { - serial.print("DEBUG: Verifying entry point at 0x"); - serial.print_hex(ph.vaddr); - serial.print(":\n Bytes: "); - const page_phys = paging.get_physical_address(kata.page_table, 0x400000) catch 0; - if (page_phys != 0) { - const bytes = @as([*]u8, @ptrFromInt(page_phys + HIGHER_HALF)); - var i: usize = 0; - while (i < 32) : (i += 1) { - serial.print_hex(bytes[i]); - serial.print(" "); - } - serial.print("\n"); - } + const start_page = start_addr & ~@as(u64, 0xFFF); + const end_addr = start_addr + mem_size; + const end_page = (end_addr + 0xFFF) & ~@as(u64, 0xFFF); + const num_pages = (end_page - start_page) / 0x1000; + + var page_idx: u64 = 0; + while (page_idx < num_pages) : (page_idx += 1) { + const page_virt = start_page + (page_idx * 0x1000); + const page_phys = pmm.alloc_page() orelse return error.OutOfMemory; + + _ = try paging.map_page_in_table(cr3, page_virt, page_phys, 0b111); + + const zero_ptr = @as([*]volatile u8, @ptrFromInt(page_phys + HIGHER_HALF)); + @memset(zero_ptr[0..0x1000], 0); } -} -fn allocate_user_stack(kata: *kata_mod.Kata, base: u64, size: u64) !void { - var addr = base; - var remaining = size; + if (file_size > 0) { + const source = elf_data[phdr.offset .. phdr.offset + file_size]; - while (remaining > 0) { - const phys_page = pmm.alloc_page() orelse return error.OutOfMemory; + var copied: u64 = 0; + while (copied < file_size) { + const virt_addr = start_addr + copied; + const page_virt = virt_addr & ~@as(u64, 0xFFF); - const flags: u64 = 0b111; // Present + Writable + User - const result = try paging.map_page_in_table(kata.page_table, addr, phys_page, flags); - const was_mapped = result[0]; - const actual_phys = result[1]; + const phys = paging.virt_to_phys(cr3, page_virt) orelse return error.PageNotMapped; - if (was_mapped) { - pmm.free_page(phys_page); - } + const page_offset = virt_addr & 0xFFF; + const bytes_in_page = @min(0x1000 - page_offset, file_size - copied); + + const dest_ptr = @as([*]volatile u8, @ptrFromInt(phys + HIGHER_HALF + page_offset)); + const src_ptr = source[copied .. copied + bytes_in_page]; - // Zero the stack page - const dest = @as([*]u8, @ptrFromInt(actual_phys + HIGHER_HALF)); - var i: usize = 0; - while (i < PAGE_SIZE) : (i += 1) { - dest[i] = 0; + @memcpy(dest_ptr[0..bytes_in_page], src_ptr); + copied += bytes_in_page; } + } - addr += PAGE_SIZE; - remaining -= PAGE_SIZE; + if (phdr.vaddr == 0x400000) { + const phys = paging.virt_to_phys(cr3, 0x400000) orelse return error.PageNotMapped; + const check_ptr = @as([*]volatile u8, @ptrFromInt(phys + HIGHER_HALF)); + serial.print(" Entry point bytes: "); + serial.print_hex(check_ptr[0]); + serial.print(" "); + serial.print_hex(check_ptr[1]); + serial.print(" "); + serial.print_hex(check_ptr[2]); + serial.print("\n"); } } fn setup_kata_context(kata: *kata_mod.Kata, entry_point: u64) void { + serial.print(" Setting up context...\n"); + kata.context.rip = entry_point; kata.context.rsp = kata.user_stack_top; kata.context.rflags = 0x3202; 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; + + serial.print(" Context ready\n"); } diff --git a/mirai/invocations/getkeychar.zig b/mirai/invocations/getkeychar.zig new file mode 100644 index 0000000..582574c --- /dev/null +++ b/mirai/invocations/getkeychar.zig @@ -0,0 +1,17 @@ +//! Getkeychar invocation - read one character from keyboard + +const keyboard = @import("../drivers/keyboard.zig"); +const handler = @import("handler.zig"); +const serial = @import("../drivers/serial.zig"); + +pub fn invoke(context: *handler.InvocationContext) void { + if (keyboard.read_char()) |char| { + serial.print("getkeychar: got char "); + serial.print_hex(char); + serial.print("\n"); + context.rax = char; + } else { + // No input available - return -2 (EAGAIN) + context.rax = @as(u64, @bitCast(@as(i64, -2))); + } +} diff --git a/mirai/invocations/handler.zig b/mirai/invocations/handler.zig index 348ee2a..a3476a2 100644 --- a/mirai/invocations/handler.zig +++ b/mirai/invocations/handler.zig @@ -14,6 +14,7 @@ const spawn = @import("spawn.zig"); const view = @import("view.zig"); const wait = @import("wait.zig"); const yield = @import("yield.zig"); +const getkeychar = @import("getkeychar.zig"); pub fn init(fs: *afs.AFS(ahci.BlockDevice)) void { serial.print("\n=== Invocation Handler ===\n"); @@ -41,6 +42,7 @@ pub fn handle_invocation(context: *InvocationContext) void { 0x06 => spawn.invoke(context), 0x07 => wait.invoke(context), 0x08 => yield.invoke(context), + 0x09 => getkeychar.invoke(context), else => { serial.print("Unknown invocation: "); serial.print_hex(invocation_num); diff --git a/mirai/invocations/syscall.zig b/mirai/invocations/syscall.zig index 0aa683e..4d0e96e 100644 --- a/mirai/invocations/syscall.zig +++ b/mirai/invocations/syscall.zig @@ -225,17 +225,17 @@ export fn handle_syscall(ctx_ptr: u64) void { // Write back return value (RAX) regs.rax = inv_ctx.rax; - // DEBUG: Log what we're returning - serial.print("Syscall complete, returning to RIP: "); - serial.print_hex(regs.user_rip); - serial.print(", RAX: "); - serial.print_hex(regs.rax); - serial.print(", CR3: "); - const cr3 = asm volatile ("mov %%cr3, %[result]" - : [result] "=r" (-> u64), - ); - serial.print_hex(cr3); - serial.print("\n"); + // // DEBUG: Log what we're returning + // serial.print("Syscall complete, returning to RIP: "); + // serial.print_hex(regs.user_rip); + // serial.print(", RAX: "); + // serial.print_hex(regs.rax); + // serial.print(", CR3: "); + // const cr3 = asm volatile ("mov %%cr3, %[result]" + // : [result] "=r" (-> u64), + // ); + // serial.print_hex(cr3); + // serial.print("\n"); } // Context saved on kernel stack diff --git a/mirai/invocations/wait.zig b/mirai/invocations/wait.zig index a627b00..2854860 100644 --- a/mirai/invocations/wait.zig +++ b/mirai/invocations/wait.zig @@ -29,20 +29,23 @@ pub fn invoke(ctx: *handler.InvocationContext) void { return; } - // Mark current Kata as waiting - if (sensei.get_current_kata()) |current| { - current.state = .Waiting; - current.waiting_for = target_id; - } + // Target is still running - block this kata + const current = sensei.get_current_kata() orelse { + ctx.rax = @as(u64, @bitCast(@as(i64, -1))); + return; + }; - // Schedule another Kata - sensei.schedule(); + serial.print(" Kata still running, blocking parent\n"); + current.state = .Waiting; + current.waiting_for = target_id; - // When we resume, the target has exited - const exit_code = target.exit_code; - serial.print(" Kata exited with code: "); - serial.print_hex(exit_code); - serial.print("\n"); + // Schedule another kata + sensei.schedule(); - ctx.rax = exit_code; + // When we return here, check if target has exited + if (target.state == .Dissolved) { + ctx.rax = target.exit_code; + } else { + ctx.rax = @as(u64, @bitCast(@as(i64, -1))); + } } diff --git a/mirai/invocations/yield.zig b/mirai/invocations/yield.zig index dd1a23a..da709b3 100644 --- a/mirai/invocations/yield.zig +++ b/mirai/invocations/yield.zig @@ -10,9 +10,14 @@ pub fn invoke(context: *handler.InvocationContext) void { return; }; - // Mark current kata as READY so scheduler can pick it again + // Mark current kata as READY current.state = kata_mod.KataState.Ready; + // Only enqueue if not already in queue + if (!sensei.is_in_queue(current)) { + sensei.enqueue_kata(current); + } + // Trigger scheduler to pick next ready kata (might pick us again if we're only one) sensei.schedule(); diff --git a/mirai/kata/kata.zig b/mirai/kata/kata.zig index 11db4b4..0337f2b 100644 --- a/mirai/kata/kata.zig +++ b/mirai/kata/kata.zig @@ -9,6 +9,7 @@ pub const KataState = enum { Ready, Running, Waiting, + Blocked, Dissolved, }; @@ -207,10 +208,16 @@ pub fn get_kata(id: u32) ?*Kata { } pub fn dissolve_kata(kata_id: u32) void { + const sensei = @import("sensei.zig"); + for (&kata_pool, 0..) |*kata, i| { if (kata_used[i] and kata.id == kata_id) { kata.state = .Dissolved; kata_used[i] = false; + + // Wake any katas waiting for this one + sensei.wake_waiting_katas(kata_id); + return; } } diff --git a/mirai/kata/sensei.zig b/mirai/kata/sensei.zig index baa94df..a84d42e 100644 --- a/mirai/kata/sensei.zig +++ b/mirai/kata/sensei.zig @@ -5,6 +5,7 @@ const kata_mod = @import("kata.zig"); const idt = @import("../interrupts/idt.zig"); const serial = @import("../drivers/serial.zig"); const shift = @import("shift.zig"); +const keyboard = @import("../drivers/keyboard.zig"); const Kata = kata_mod.Kata; @@ -86,12 +87,18 @@ pub fn dequeue_kata(target_kata: *Kata) void { } } +// Check if a kata is already in the run queue +pub fn is_in_queue(target_kata: *Kata) bool { + var current = run_queue_head; + while (current) |kata| { + if (kata == target_kata) return true; + current = kata.next; + } + return false; +} + // Pick next Kata to run (lowest vruntime) fn pick_next_kata() ?*Kata { - serial.print("pick_next_kata called, checking queue...\n"); - serial.print(" run_queue_head: "); - serial.print_hex(@intFromPtr(run_queue_head)); - serial.print("\n"); // Debug: show all katas var i: usize = 0; @@ -114,12 +121,6 @@ fn pick_next_kata() ?*Kata { serial.print(" Traversing queue:\n"); while (current) |kata| { - serial.print(" Checking Kata "); - serial.print_hex(kata.id); - serial.print(", state "); - serial.print_hex(@intFromEnum(kata.state)); - serial.print("\n"); - if (kata.state == .Ready and kata.vruntime < local_min_vruntime) { local_min_vruntime = kata.vruntime; chosen = kata; @@ -137,7 +138,7 @@ fn pick_next_kata() ?*Kata { return k; } - // NEW: If no Ready kata, check if current_kata should keep running + // If no Ready kata, check if current_kata should keep running if (current_kata) |curr| { if (curr.state == .Running) { serial.print(" No Ready kata, continuing current: "); @@ -163,6 +164,11 @@ fn pick_next_kata() ?*Kata { pub fn on_tick() void { tick_count += 1; + // Wake blocked katas if keyboard has input + if (tick_count % 5 == 0) { // Check every 5ms + wake_blocked_katas(); + } + if (current_kata) |kata| { const delta = (TICK_NANOSECONDS * 1024) / kata.weight; kata.vruntime += delta; @@ -182,14 +188,7 @@ pub fn on_tick() void { // Main scheduling decision pub fn schedule() void { - const rbp = asm volatile ("mov %%rbp, %[ret]" - : [ret] "=r" (-> u64), - ); - serial.print("\n=== Sensei Schedule (called from RBP: "); - serial.print_hex(rbp); - serial.print(") ===\n"); - serial.print("\n=== Sensei Schedule ===\n"); - wake_waiting_katas(); + wake_all_waiting_katas(); const next = pick_next_kata(); @@ -200,10 +199,12 @@ pub fn schedule() void { } } - // DON'T return early - always handle the shift properly + // If same kata is picked, we still need to dequeue it and set to Running if (next == current_kata and next != null) { - // Same kata, but we might be coming from interrupt - // Just continue - no shift needed + // Same kata picked - dequeue it since it was enqueued by yield + dequeue_kata(next.?); + next.?.state = .Running; + // No shift needed - just return return; } @@ -229,7 +230,7 @@ pub fn schedule() void { } } -fn wake_waiting_katas() void { +fn wake_all_waiting_katas() void { var i: usize = 0; while (i < kata_mod.MAX_KATA) : (i += 1) { if (!kata_mod.kata_used[i]) continue; @@ -254,6 +255,76 @@ fn wake_waiting_katas() void { } } +// Wake katas waiting for a specific kata (called when a kata dissolves) +pub fn wake_waiting_katas(target_id: u32) void { + var i: usize = 0; + while (i < kata_mod.MAX_KATA) : (i += 1) { + if (!kata_mod.kata_used[i]) continue; + + const kata = &kata_mod.kata_pool[i]; + if (kata.state == .Waiting and kata.waiting_for == target_id) { + serial.print("Sensei: Waking Kata "); + serial.print_hex(kata.id); + serial.print(" (target "); + serial.print_hex(target_id); + serial.print(" dissolved)\n"); + + kata.state = .Ready; + enqueue_kata(kata); + kata.waiting_for = 0; + } + } +} + +// Wake katas blocked on keyboard input +fn wake_blocked_katas() void { + // Only wake if keyboard has input + if (!keyboard.has_input()) return; + + // Wake only ONE blocked kata per character (FIFO order) + var i: usize = 0; + while (i < kata_mod.MAX_KATA) : (i += 1) { + if (!kata_mod.kata_used[i]) continue; + + const kata = &kata_mod.kata_pool[i]; + if (kata.state == .Blocked) { + kata.state = .Ready; + enqueue_kata(kata); + // Only wake one kata at a time for keyboard input + return; + } + } +} + +// Wake one blocked kata (called from keyboard interrupt) +pub fn wake_one_blocked_kata() void { + var i: usize = 0; + while (i < kata_mod.MAX_KATA) : (i += 1) { + if (!kata_mod.kata_used[i]) continue; + + const kata = &kata_mod.kata_pool[i]; + if (kata.state == .Blocked) { + kata.state = .Ready; + enqueue_kata(kata); + return; + } + } +} + +// Wake a specific kata by ID (called from interrupt handlers) +pub fn wake_kata(kata_id: u32) void { + const kata = kata_mod.get_kata(kata_id) orelse return; + + if (kata.state == .Blocked) { + serial.print("Sensei: Waking blocked Kata "); + serial.print_hex(kata_id); + serial.print("\n"); + + kata.state = .Ready; + enqueue_kata(kata); + } +} + pub fn get_current_kata() ?*Kata { return current_kata; } diff --git a/mirai/memory/paging.zig b/mirai/memory/paging.zig index c449afa..df01079 100644 --- a/mirai/memory/paging.zig +++ b/mirai/memory/paging.zig @@ -192,6 +192,46 @@ pub fn get_physical_address(page_table: u64, vaddr: u64) !u64 { return (pt[pt_index] & ~@as(u64, 0xFFF)) + offset; } +pub fn virt_to_phys(cr3: u64, virt: u64) ?u64 { + // Walk the 4-level page table + 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 & 1) == 0) return null; // Not present + + const pdp_addr = (pml4_entry & 0x000FFFFFFFFFF000) + 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 & 1) == 0) return null; // Not present + + const pd_addr = (pdp_entry & 0x000FFFFFFFFFF000) + 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 & 1) == 0) return null; // Not present + + const pt_addr = (pd_entry & 0x000FFFFFFFFFF000) + 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 & 1) == 0) return null; // Not present + + const phys_base = pt_entry & 0x000FFFFFFFFFF000; + const offset = virt & 0xFFF; + + return phys_base + offset; +} + pub fn init() void { serial.print("\n=== Page Table Manager ===\n"); serial.print("CR3: "); diff --git a/mirai/mirai.zig b/mirai/mirai.zig index 9280485..fbd4e26 100644 --- a/mirai/mirai.zig +++ b/mirai/mirai.zig @@ -1,4 +1,3 @@ -const ash = @import("ash/ash.zig"); const crimson = @import("crimson/panic.zig"); const serial = @import("drivers/serial.zig"); const sequence = @import("boot/sequence.zig"); @@ -13,9 +12,6 @@ export fn mirai(multiboot_info_addr: u64) noreturn { // Run boot sequence sequence.run(multiboot_info_addr); - // Initialize shell - ash.init(sequence.get_filesystem()); - serial.print("\n** Boot Complete **\n"); while (true) { @@ -26,7 +22,3 @@ export fn mirai(multiboot_info_addr: u64) noreturn { pub fn panic(message: []const u8, _: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn { crimson.collapse(message, null); } - -export fn on_key_typed(char: u8) void { - ash.on_key_press(char); -} diff --git a/system/ash/ash.zig b/system/ash/ash.zig new file mode 100644 index 0000000..577dea3 --- /dev/null +++ b/system/ash/ash.zig @@ -0,0 +1,45 @@ +//! Ash - Akiba Shell + +const akiba = @import("akiba"); + +const MAX_INPUT = 256; +var input_buffer: [MAX_INPUT]u8 = undefined; +var input_len: usize = 0; + +export fn _start() noreturn { + while (true) { + // Print prompt + akiba.io.print("/ >>> ") catch {}; + + // Read line + input_len = 0; + while (true) { + const char = akiba.io.getchar() catch continue; + + if (char == '\n') { + akiba.io.print("\n") catch {}; + break; + } else if (char == '\x08') { + // Backspace + if (input_len > 0) { + input_len -= 1; + // Echo backspace to screen + akiba.io.print("\x08") catch {}; + } + } else if (char >= 32 and char <= 126 and input_len < MAX_INPUT - 1) { + input_buffer[input_len] = char; + input_len += 1; + // Echo character to screen + const char_str = [_]u8{char}; + akiba.io.print(&char_str) catch {}; + } + } + + // TODO: Parse and execute command + if (input_len > 0) { + akiba.io.print("Command \"") catch {}; + akiba.io.print(input_buffer[0..input_len]) catch {}; + akiba.io.print("\" not implemented yet\n") catch {}; + } + } +} diff --git a/system/ash/ash.zon b/system/ash/ash.zon new file mode 100644 index 0000000..0c72775 --- /dev/null +++ b/system/ash/ash.zon @@ -0,0 +1,8 @@ +.{ + .name = "ash", + .version = "0.1.0", + .paths = .{""}, + .dependencies = .{ + .akiba = .{ .path = "../libraries/akiba" }, + }, +} diff --git a/system/libraries/akiba/io.zig b/system/libraries/akiba/io.zig index b8f8eef..35ff739 100644 --- a/system/libraries/akiba/io.zig +++ b/system/libraries/akiba/io.zig @@ -1,6 +1,7 @@ //! File I/O operations const sys = @import("sys.zig"); +const kata = @import("kata.zig"); pub const Error = error{ NotFound, @@ -51,6 +52,21 @@ pub fn mark(fd: FileDescriptor, data: []const u8) Error!usize { return result; } +pub fn getchar() !u8 { + // Loop until character is available, yielding CPU + while (true) { + const result = sys.syscall0(.getkeychar); + if (result != @as(u64, @bitCast(@as(i64, -2)))) { + if (result != @as(u64, @bitCast(@as(i64, -1)))) { + return @truncate(result); + } + return error.ReadFailed; + } + // No input yet (-2 = EAGAIN), yield and try again + kata.yield(); + } +} + pub fn print(text: []const u8) Error!void { _ = try mark(stream, text); } diff --git a/system/libraries/akiba/kata.zig b/system/libraries/akiba/kata.zig index 2283ce8..14f2946 100644 --- a/system/libraries/akiba/kata.zig +++ b/system/libraries/akiba/kata.zig @@ -20,9 +20,13 @@ pub fn spawn(path: []const u8) !u32 { } pub fn wait(pid: u32) !u64 { - const result = sys.syscall1(.wait, pid); - if (result == @as(u64, @bitCast(@as(i64, -1)))) { - return error.WaitFailed; + // Retry loop: keep checking until the target exits + while (true) { + const result = sys.syscall1(.wait, pid); + if (result != @as(u64, @bitCast(@as(i64, -1)))) { + return result; + } + // Target still running, yield and try again + yield(); } - return result; } diff --git a/system/libraries/akiba/sys.zig b/system/libraries/akiba/sys.zig index ee74777..10329ff 100644 --- a/system/libraries/akiba/sys.zig +++ b/system/libraries/akiba/sys.zig @@ -9,6 +9,7 @@ pub const Invocation = enum(u64) { spawn = 0x06, wait = 0x07, yield = 0x08, + getkeychar = 0x09, }; pub fn syscall0(inv: Invocation) u64 { @@ -16,8 +17,7 @@ pub fn syscall0(inv: Invocation) u64 { asm volatile ("syscall" : [ret] "={rax}" (result), : [inv] "{rax}" (@intFromEnum(inv)), - : .{ .rcx = true, .r11 = true, .memory = true } - ); + : .{ .rcx = true, .r11 = true, .memory = true }); return result; } @@ -27,8 +27,7 @@ pub fn syscall1(inv: Invocation, arg1: u64) u64 { : [ret] "={rax}" (result), : [inv] "{rax}" (@intFromEnum(inv)), [arg1] "{rdi}" (arg1), - : .{ .rcx = true, .r11 = true, .memory = true } - ); + : .{ .rcx = true, .r11 = true, .memory = true }); return result; } @@ -39,8 +38,7 @@ pub fn syscall2(inv: Invocation, arg1: u64, arg2: u64) u64 { : [inv] "{rax}" (@intFromEnum(inv)), [arg1] "{rdi}" (arg1), [arg2] "{rsi}" (arg2), - : .{ .rcx = true, .r11 = true, .memory = true } - ); + : .{ .rcx = true, .r11 = true, .memory = true }); return result; } @@ -52,7 +50,6 @@ pub fn syscall3(inv: Invocation, arg1: u64, arg2: u64, arg3: u64) u64 { [arg1] "{rdi}" (arg1), [arg2] "{rsi}" (arg2), [arg3] "{rdx}" (arg3), - : .{ .rcx = true, .r11 = true, .memory = true } - ); + : .{ .rcx = true, .r11 = true, .memory = true }); return result; } diff --git a/system/pulse/pulse.zig b/system/pulse/pulse.zig index c558eb3..2da96cf 100644 --- a/system/pulse/pulse.zig +++ b/system/pulse/pulse.zig @@ -4,9 +4,22 @@ const akiba = @import("akiba"); export fn _start() noreturn { - akiba.io.println("Pulse: System ready!") catch {}; - + // Init loop - respawn shell when it exits while (true) { - akiba.kata.yield(); + const shell_pid = akiba.kata.spawn("/system/ash/ash.akiba") catch { + akiba.io.println("Pulse: Failed to spawn shell") catch {}; + akiba.kata.yield(); + continue; + }; + + // Wait for shell to exit + _ = akiba.kata.wait(shell_pid) catch { + akiba.io.println("Pulse: Shell wait failed") catch {}; + akiba.kata.yield(); + continue; + }; + + // Shell exited, respawn + akiba.io.println("\nPulse: Shell exited, respawning...\n") catch {}; } } |
