diff --git a/README.md b/README.md index 4bbee51..c3adad7 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ A kernel - Add a checklist/roadmap - Move tty and drivers out of kernel space - Replace json with zon -- Replace ArrayList(Unmanaged) with HashMap where needed - Replace unreachable with @panic - Provide compatibility with Linux ABI - Support RISC-V64, aarch64 and x86_64 +- Replace @import("root") with @import("main.zig") to allow for testing - Replace limine with a custom bootloader? # Clone, build and run diff --git a/kernel/arch/x86_64/idt.zig b/kernel/arch/x86_64/idt.zig index 5a6dc14..cedaa05 100644 --- a/kernel/arch/x86_64/idt.zig +++ b/kernel/arch/x86_64/idt.zig @@ -64,7 +64,7 @@ const IDTDescriptor = extern struct { base: u64 align(1) = undefined, }; -const exceptions = [_]?[]const u8{ +const exceptions = [_][]const u8{ "Division by zero", "Debug", "Non-maskable Interrupt", @@ -80,23 +80,23 @@ const exceptions = [_]?[]const u8{ "Stack-Segment Fault", "General Protection Fault", "Page Fault", - null, + "Reserved", "x87 Floating-Point Exception", "Alignment Check", "Machine Check", "SIMD Floating-Point Exception", "Virtualization Exception", "Control Protection Exception", - null, - null, - null, - null, - null, - null, + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", "Hypervisor Injection Exception", "VMM Communication Exception", "Security Exception", - null, + "Reserved", }; // TODO: replace isr with a interrupt dispatcher func? @@ -174,7 +174,7 @@ fn defaultHandler(ctx: *Context) callconv(.SysV) void { const cr3 = x86.readRegister("cr3"); std.debug.panic( - \\Unhandled interruption "{?s}" triggered, dumping context + \\Unhandled interruption "{s}" triggered, dumping context \\vector: 0x{x:0>2} error code: 0x{x} \\ds: 0x{x:0>16} es: 0x{x:0>16} \\rax: 0x{x:0>16} rbx: 0x{x:0>16} @@ -189,7 +189,7 @@ fn defaultHandler(ctx: *Context) callconv(.SysV) void { \\rsp: 0x{x:0>16} ss: 0x{x:0>16} \\cr2: 0x{x:0>16} cr3: 0x{x:0>16} , .{ - if (ctx.vector < exceptions.len) exceptions[ctx.vector] else null, + if (ctx.vector < exceptions.len) exceptions[ctx.vector] else "Unknown", ctx.vector, ctx.error_code, ctx.ds, @@ -249,7 +249,6 @@ export fn commonStub() callconv(.Naked) void { \\je 1f \\swapgs \\1: - \\cld \\push %r15 \\push %r14 \\push %r13 diff --git a/kernel/arch/x86_64/mem.zig b/kernel/arch/x86_64/mem.zig index 7dd7798..6936cd5 100644 --- a/kernel/arch/x86_64/mem.zig +++ b/kernel/arch/x86_64/mem.zig @@ -2,6 +2,7 @@ pub fn cpy(noalias dst: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv( @setRuntimeSafety(false); asm volatile ( + \\cld \\rep movsb : : [_] "{rdi}" (dst), @@ -17,6 +18,7 @@ pub fn set(dst: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 { @setRuntimeSafety(false); asm volatile ( + \\cld \\rep stosb : : [_] "{rdi}" (dst), diff --git a/kernel/arch/x86_64/x86_64.zig b/kernel/arch/x86_64/x86_64.zig index 99c5ea0..cc52f5a 100644 --- a/kernel/arch/x86_64/x86_64.zig +++ b/kernel/arch/x86_64/x86_64.zig @@ -41,7 +41,7 @@ pub const MSR = enum(u32) { kernel_gs_base = 0xc0000102, }; -pub const CPUID = struct { +pub const CpuID = struct { eax: u32, ebx: u32, ecx: u32, @@ -137,7 +137,7 @@ pub inline fn writeRegister(comptime reg: []const u8, value: u64) void { ); } -pub inline fn cpuid(leaf: u32, subleaf: u32) CPUID { +pub inline fn cpuid(leaf: u32, subleaf: u32) CpuID { var eax: u32 = undefined; var ebx: u32 = undefined; var ecx: u32 = undefined; @@ -153,7 +153,7 @@ pub inline fn cpuid(leaf: u32, subleaf: u32) CPUID { [_] "{ecx}" (subleaf), ); - return CPUID{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx }; + return .{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx }; } pub inline fn rdmsr(msr: MSR) u64 { diff --git a/kernel/main.zig b/kernel/main.zig index c8a979a..d65bede 100644 --- a/kernel/main.zig +++ b/kernel/main.zig @@ -38,7 +38,6 @@ pub const os = struct { }; var gpa = std.heap.GeneralPurposeAllocator(.{ - .thread_safe = false, .MutexType = lib.SpinLock, .verbose_log = if (builtin.mode == .Debug) true else false, }){}; @@ -77,7 +76,7 @@ export fn _start() noreturn { pmm.init(); vmm.init(); acpi.init(); - event.init(); + event.init(); // TODO + futex // syscall.init(); // TODO sched.init(); smp.init(); @@ -85,7 +84,9 @@ export fn _start() noreturn { const kernel_thread = sched.Thread.initKernel(@ptrCast(&main), null, 1) catch unreachable; sched.enqueue(kernel_thread) catch unreachable; - sched.wait(); + + arch.enableInterrupts(); + arch.halt(); } fn main() noreturn { diff --git a/kernel/mm/pmm.zig b/kernel/mm/pmm.zig index 8e23c17..9ac242c 100644 --- a/kernel/mm/pmm.zig +++ b/kernel/mm/pmm.zig @@ -24,7 +24,7 @@ pub fn init() void { const entries = memory_map.entries(); var highest_addr: u64 = 0; - log.info("memory_map[{}] dump:", .{entries.len}); + log.info("memory_map[{}]:", .{entries.len}); for (entries) |entry| { log.info("0x{x}-0x{x} {s}", .{ entry.base, diff --git a/kernel/mm/vmm.zig b/kernel/mm/vmm.zig index 03c92ba..ce093f3 100644 --- a/kernel/mm/vmm.zig +++ b/kernel/mm/vmm.zig @@ -18,6 +18,7 @@ const MAP = std.os.MAP; // TODO: mapRange/unmapRange? // TODO: move paging code to paging.zig +// TODO: use ArenaAllocator for vmm pub const MapError = error{ OutOfMemory, @@ -89,7 +90,7 @@ pub const PageFaultError = packed struct(u32) { // TODO: init and deinit func? const MMapRangeGlobal = struct { shadow_addr_space: *AddressSpace, - locals: std.ArrayListUnmanaged(*MMapRangeLocal) = .{}, + locals: std.ArrayListUnmanaged(*MMapRangeLocal) = .{}, // HashMap? // resource: *Resource, // TODO: vnode? base: usize, length: usize, @@ -137,7 +138,7 @@ const Addr2Range = struct { pub const AddressSpace = struct { pml4: *[512]PTE, lock: SpinLock = .{}, - mmap_ranges: std.ArrayListUnmanaged(*MMapRangeLocal) = .{}, + mmap_ranges: std.ArrayListUnmanaged(*MMapRangeLocal) = .{}, // HashMap? pub fn init() !*AddressSpace { const addr_space = try root.allocator.create(AddressSpace); @@ -310,10 +311,11 @@ pub const AddressSpace = struct { // TODO: lock? fn flush(self: *AddressSpace, vaddr: u64) void { - std.debug.assert(arch.interruptState() == false); - // const old_state = arch.toggleInterrupts(false); - // defer _ = arch.toggleInterrupts(old_state); + // std.debug.assert(arch.interruptState() == false); + const old_state = arch.toggleInterrupts(false); + defer _ = arch.toggleInterrupts(old_state); + // TODO: is `if` useful? if (self.cr3() == arch.readRegister("cr3")) { arch.invlpg(vaddr); } @@ -338,10 +340,10 @@ pub const AddressSpace = struct { prot: i32, flags: i32, ) !void { - flags |= MAP.ANONYMOUS; // TODO + // flags |= MAP.ANONYMOUS; // TODO - const aligned_vaddr = alignBackward(vaddr, page_size); - const aligned_len = alignForward(len + (vaddr - aligned_vaddr), page_size); + const aligned_vaddr = alignBackward(u64, vaddr, page_size); + const aligned_len = alignForward(u64, len + (vaddr - aligned_vaddr), page_size); const global_range = try root.allocator.create(MMapRangeGlobal); errdefer root.allocator.destroy(global_range); @@ -552,6 +554,7 @@ fn tlbShootdownHandler(ctx: *arch.Context) callconv(.SysV) void { _ = ctx; if (smp.thisCpu().tlb_shootdown_cr3 == arch.readRegister("cr3")) { + // TODO: invlpg cpu.tlb_Shootdown_addr? arch.writeRegister("cr3", arch.readRegister("cr3")); } diff --git a/kernel/sched.zig b/kernel/sched.zig index 2a63a07..774c584 100644 --- a/kernel/sched.zig +++ b/kernel/sched.zig @@ -28,6 +28,7 @@ pub const Process = struct { child_events: std.ArrayListUnmanaged(*Event), event: Event, // running_time: usize, // TODO + // status: enum { running, ready, blocked (io) }, // TODO: in threads? + atomic (replace lock) user: std.os.uid_t, group: std.os.gid_t, @@ -111,7 +112,6 @@ pub const Thread = struct { // running_time: usize, // TODO - const Set = std.AutoArrayHashMapUnmanaged(*Thread, void); pub const max_events = 32; pub const stack_size = 8 * 1024 * 1024; // 8MiB pub const stack_pages = stack_size / std.mem.page_size; @@ -299,13 +299,12 @@ pub const Thread = struct { }; /// reschedule every 5ms -pub const timeslice = 5_000; -/// wait for 10ms if there is no thread -pub const wait_timeslice = 10_000; +const quantum = 5_000; pub var kernel_process: *Process = undefined; pub var processes: std.ArrayListUnmanaged(*Process) = .{}; // TODO: hashmap with pid? -var running_threads: Thread.Set = .{}; +var running_threads: std.AutoArrayHashMapUnmanaged(*Thread, void) = .{}; +var waiting_threads: std.AutoArrayHashMapUnmanaged(*Thread, void) = .{}; var total_tickets: usize = 0; var sched_vector: u8 = undefined; @@ -365,6 +364,12 @@ pub fn enqueue(thread: *Thread) !void { for (smp.cpus) |cpu| { if (cpu.current_thread == null) { + // only for debug build + // fix to not crash when starting the first kernel thread + if (comptime @import("builtin").mode == .Debug) { + if (cpu.id == smp.thisCpu().id) continue; + } + apic.sendIPI(cpu.lapic_id, .{ .vector = sched_vector }); break; } @@ -401,7 +406,7 @@ fn schedHandler(ctx: *arch.Context) callconv(.SysV) void { // if (cpu.scheduling_disabled) { // apic.eoi(); - // apic.timerOneShot(timeslice, sched_vector); + // apic.timerOneShot(quantum, sched_vector); // return; // } @@ -412,7 +417,7 @@ fn schedHandler(ctx: *arch.Context) callconv(.SysV) void { if (maybe_next_thread == null and current_thread.enqueued) { apic.eoi(); - apic.timerOneShot(timeslice, sched_vector); + apic.timerOneShot(quantum, sched_vector); return; } @@ -449,22 +454,23 @@ fn schedHandler(ctx: *arch.Context) callconv(.SysV) void { } apic.eoi(); - apic.timerOneShot(timeslice, sched_vector); + apic.timerOneShot(quantum, sched_vector); contextSwitch(&next_thread.ctx); } else { cpu.current_thread = null; vmm.switchPageTable(vmm.kaddr_space.cr3()); apic.eoi(); - wait(); + arch.halt(); } unreachable; } -pub fn wait() noreturn { - arch.disableInterrupts(); - apic.timerOneShot(wait_timeslice, sched_vector); - arch.enableInterrupts(); - arch.halt(); +// TODO: wake is the same but reverse +fn blockThread(thread: *Thread) void { + dequeue(thread); + sched_lock.lock(); + waiting_threads.putNoClobber(root.allocator, thread, {}) catch unreachable; + sched_lock.unlock(); } pub fn yield() noreturn { @@ -486,18 +492,27 @@ pub fn yieldAwait() void { apic.timerStop(); const thread = currentThread(); - thread.yield_await.lock(); + + const use_yield_await = false; + if (use_yield_await) { + thread.yield_await.lock(); + } else { + blockThread(); + } apic.sendIPI(undefined, .{ .vector = sched_vector, .destination_shorthand = .self }); arch.enableInterrupts(); - // TODO: useless since yield_await should already be unlocked - thread.yield_await.lock(); - thread.yield_await.unlock(); + if (use_yield_await) { + // TODO: useless since yield_await should already be unlocked + thread.yield_await.lock(); + thread.yield_await.unlock(); + } } fn contextSwitch(ctx: *arch.Context) noreturn { std.debug.assert(ctx.cs == gdt.kernel_code); + // TODO: switch page table? asm volatile ( \\mov %[ctx], %rsp \\pop %rax diff --git a/kernel/smp.zig b/kernel/smp.zig index 2c628e7..057bea5 100644 --- a/kernel/smp.zig +++ b/kernel/smp.zig @@ -65,5 +65,6 @@ fn initAp(smp_info: *limine.SmpInfo) callconv(.C) noreturn { log.info("processor {} is online", .{cpu_local.id}); _ = @atomicRmw(usize, &cpus_started, .Add, 1, .Release); - sched.wait(); + arch.enableInterrupts(); + arch.halt(); } diff --git a/lib/ubik.zig b/lib/ubik.zig index 069eb93..ffa26d1 100644 --- a/lib/ubik.zig +++ b/lib/ubik.zig @@ -4,6 +4,9 @@ const std = @import("std"); const linux = std.os.linux; pub const PATH_MAX = 4096; +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; pub const T = linux.T; pub const S = linux.S;