// SPDX-FileCopyrightText: 2024 Himbeer // // SPDX-License-Identifier: AGPL-3.0-or-later const std = @import("std"); const Console = @import("lib/Console.zig"); const hwinfo = @import("lib/hwinfo.zig"); const instructions = @import("lib/instructions.zig"); const interrupts = @import("lib/interrupts.zig"); const mem = @import("lib/mem.zig"); const paging = @import("lib/paging.zig"); const pci = @import("lib/pci.zig"); const plic = @import("lib/plic.zig"); const process = @import("lib/process.zig"); const userinit = @import("lib/userinit.zig"); const Error = error{ HartIdOutOfRange, }; const HartData = packed struct(usize) { hart_id: u16, reserved: u48, pub inline fn loadSScratch() HartData { return asm volatile ( \\ csrr %[hart_data], sscratch : [hart_data] "=r" (-> HartData), ); } pub inline fn storeSScratch(self: HartData) void { asm volatile ( \\ csrw sscratch, %[hart_data] : : [hart_data] "r" (self), ); } }; pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn { _ = &error_return_trace; _ = &ret_addr; @setCold(true); var sstatus = instructions.sstatus.read(); sstatus.user_interrupt_enable = 0; sstatus.supervisor_interrupt_enable = 0; instructions.sstatus.write(sstatus); instructions.sie.write(interrupts.Enable.none); instructions.sip.write(interrupts.Enable.none); const console = Console.autoChoose() orelse while (true) asm volatile ("wfi"); const w = console.writer(); w.print("\r\n", .{}) catch while (true) asm volatile ("wfi"); w.print("= !! ========== !! =\r\n", .{}) catch while (true) asm volatile ("wfi"); w.print("!!! KERNEL PANIC !!!\r\n", .{}) catch while (true) asm volatile ("wfi"); w.print("{s}\r\n", .{msg}) catch while (true) asm volatile ("wfi"); w.print("= !! ========== !! =\r\n", .{}) catch while (true) asm volatile ("wfi"); while (true) asm volatile ("wfi"); } export fn _start() callconv(.Naked) noreturn { // Stack grows down, use (exclusive) end instead of start. asm volatile ( \\ .option push \\ .option norelax \\ la gp, _global_pointer \\ .option pop \\ \\ la sp, _stack_end \\ j %[function] : : [function] "s" (&kmain), ); } fn kmain(hart_id: usize, _: usize) noreturn { run(hart_id) catch |err| std.debug.panic("Hart {d}: {any}", .{ hart_id, err }); } fn run(hart_id: usize) !noreturn { if (hart_id > ~@as(u16, 0)) return Error.HartIdOutOfRange; const hart_data = HartData{ .hart_id = @intCast(hart_id), .reserved = 0, }; hart_data.storeSScratch(); paging.init(); const kmem: *paging.Table = @alignCast(@ptrCast(try paging.zeroedAlloc(1))); try kmem.mapKernel(); instructions.satp.write(kmem.satp(0)); asm volatile ( \\ la sp, _stack_end \\ j %[function] : : [function] "s" (&pagedStart), ); unreachable; } fn pagedStart() callconv(.Naked) noreturn { asm volatile ( \\ la sp, _stack_end \\ j %[function] : : [function] "s" (&pagedMain), ); } fn pagedMain() noreturn { pagedRun() catch |err| std.debug.panic("Paged: {any}", .{err}); } fn pagedRun() !noreturn { const console = Console.autoChoose() orelse while (true) asm volatile ("wfi"); const w = console.writer(); try w.print("\r\n", .{}); try w.print("Console init\r\n", .{}); try w.print("\r\n", .{}); const hart_data = HartData.loadSScratch(); try w.print("Hart : {d}\r\n", .{hart_data.hart_id}); const kmem: *paging.Table = @ptrFromInt(instructions.satp.read().ppn << 12); try w.print("Paging : Sv39\r\n", .{}); interrupts.init(hart_data.hart_id); instructions.sie.write(interrupts.Enable.all); try w.print("Interrupts : All\r\n", .{}); // var chunk_allocator = try mem.ChunkAllocator(.{ .auto_merge_free = true }).init(128); // const allocator = chunk_allocator.allocator(); const allocator = mem.page_allocator; try w.print("\r\n", .{}); try w.print("===================== Kernel Page Table =====================\r\n", .{}); try w.print(".text: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (r-x)\r\n", .{ @intFromPtr(paging.text_start), @intFromPtr(paging.text_end) }); try w.print(".rodata: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (r--)\r\n", .{ @intFromPtr(paging.rodata_start), @intFromPtr(paging.rodata_end) }); try w.print(".data: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (rw-)\r\n", .{ @intFromPtr(paging.data_start), @intFromPtr(paging.data_end) }); try w.print(".bss: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (rw-)\r\n", .{ @intFromPtr(paging.bss_start), @intFromPtr(paging.bss_end) }); try w.print("Stack: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (rw-)\r\n", .{ @intFromPtr(paging.stack_start), @intFromPtr(paging.stack_end) }); try w.print("Trap Stack: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (rw-)\r\n", .{ @intFromPtr(paging.stvec_stack_start), @intFromPtr(paging.stvec_stack_end) }); try w.print("\r\n", .{}); try w.print("Heap: 0x{x:0>8} - 0x{x:0>8} -> identity mapped (rw-)\r\n", .{ @intFromPtr(paging.heap_start), @intFromPtr(paging.heap_end) }); try w.print("=============================================================\r\n", .{}); try w.print("\r\n", .{}); try w.print("Timer : {d} Hz\r\n", .{1 / (@as(f64, process.schedule_interval_millis) / 1000)}); var plic_dev = try hwinfo.byKind(.plic); if (try plic_dev.next()) |plic_handle| { _ = &plic_handle; try w.print("PLIC : Disabled\r\n", .{}); } else { try w.print("PLIC : Not present\r\n", .{}); } try w.print("\r\n", .{}); if (pci.controllerFromHwInfo(kmem)) |pci_controller| { for (0..256) |bus| { for (0..32) |device| { const cfg_space = pci_controller.cfgSpace(@intCast(bus), @intCast(device), 0); const vendor_id = cfg_space.getVendorId(); const device_id = cfg_space.getDeviceId(); const class = cfg_space.getClass(); const subclass = cfg_space.getSubclass(); if (vendor_id != 0xffff) { try w.print("PCI {:0>3}.{:0>3}: Vendor = 0x{x:0>4}, Device = 0x{x:0>4}, Class = 0x{x:0>4}, Subclass = 0x{x:0>4}\r\n", .{ bus, device, vendor_id, device_id, class, subclass }); } } } try w.print("\r\n", .{}); } else |err| { switch (err) { error.NoPciController => try w.print("PCI not present\r\n", .{}), else => return err, } } try w.print("Start init process\r\n", .{}); var userinit_stream = std.io.fixedBufferStream(userinit.tarball); try process.runInit(allocator, userinit_stream.reader()); }