diff --git a/build.zig b/build.zig index 0e8cd39..c0d799b 100644 --- a/build.zig +++ b/build.zig @@ -50,8 +50,16 @@ pub fn build(b: *Builder) !void { cp_elf_cmd.step.dependOn(&grub_cmd.step); cp_elf_cmd.step.dependOn(&exec.step); + const modules_path = try fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso", "modules" }); + const mkdir_modules_cmd = b.addSystemCommand([_][]const u8{ "mkdir", "-p", modules_path }); + + const map_file_path = try fs.path.join(b.allocator, [_][]const u8{ modules_path, "kernel.map" }); + const map_file_cmd = b.addSystemCommand([_][]const u8{ "./make_map.sh", elf_path, map_file_path }); + map_file_cmd.step.dependOn(&cp_elf_cmd.step); + map_file_cmd.step.dependOn(&mkdir_modules_cmd.step); + const iso_cmd = b.addSystemCommand([_][]const u8{ "grub-mkrescue", "-o", iso_path, iso_dir_path }); - iso_cmd.step.dependOn(&cp_elf_cmd.step); + iso_cmd.step.dependOn(&map_file_cmd.step); b.default_step.dependOn(&iso_cmd.step); const run_step = b.step("run", "Run with qemu"); diff --git a/grub/grub.cfg b/grub/grub.cfg index 8d3fd21..2474c0f 100644 --- a/grub/grub.cfg +++ b/grub/grub.cfg @@ -3,5 +3,6 @@ set default=0 menuentry "pluto" { multiboot /boot/pluto.elf + module /modules/kernel.map kernel.map boot } diff --git a/make_map.sh b/make_map.sh new file mode 100755 index 0000000..a7d7d93 --- /dev/null +++ b/make_map.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# Read the symbols from the binary, remove all the unnecessary columns with awk and emit to a map file +readelf -s $1 | grep -F "FUNC" | awk '{$1=$3=$4=$5=$6=$7=""; print $0}' | sort -k 1 > $2 +echo "" >> $2 diff --git a/src/kernel/arch/x86/boot.zig b/src/kernel/arch/x86/boot.zig index 96de75f..b8e4dee 100644 --- a/src/kernel/arch/x86/boot.zig +++ b/src/kernel/arch/x86/boot.zig @@ -99,7 +99,7 @@ export nakedcc fn start_higher_half() noreturn { asm volatile ( \\.extern KERNEL_STACK_END \\mov $KERNEL_STACK_END, %%esp - \\mov %%esp, %%ebp + \\xor %%ebp, %%ebp ); // Push the bootloader magic number and multiboot header address with virtual offset diff --git a/src/kernel/arch/x86/paging.zig b/src/kernel/arch/x86/paging.zig index da12e8f..2b98eaf 100644 --- a/src/kernel/arch/x86/paging.zig +++ b/src/kernel/arch/x86/paging.zig @@ -331,9 +331,9 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile // Map in each boot module for (mem_profile.boot_modules) |*module| { - const mod_p_struct_start = std.mem.alignBackward(@ptrToInt(module), PAGE_SIZE_4KB); - const mod_p_struct_end = std.mem.alignForward(mod_p_struct_start + @sizeOf(multiboot.multiboot_module_t), PAGE_SIZE_4KB); - mapDir(kernel_directory, mem.physToVirt(mod_p_struct_start), mem.physToVirt(mod_p_struct_end), mod_p_struct_start, mod_p_struct_end, allocator) catch |e| { + const mod_v_struct_start = std.mem.alignBackward(@ptrToInt(module), PAGE_SIZE_4KB); + const mod_v_struct_end = std.mem.alignForward(mod_v_struct_start + @sizeOf(multiboot.multiboot_module_t), PAGE_SIZE_4KB); + mapDir(kernel_directory, mod_v_struct_start, mod_v_struct_end, mem.virtToPhys(mod_v_struct_start), mem.virtToPhys(mod_v_struct_end), allocator) catch |e| { panic(@errorReturnTrace(), "Failed to map module struct: {}\n", e); }; const mod_p_start = std.mem.alignBackward(module.mod_start, PAGE_SIZE_4KB); diff --git a/src/kernel/kmain.zig b/src/kernel/kmain.zig index 34f12df..eb9c0ef 100644 --- a/src/kernel/kmain.zig +++ b/src/kernel/kmain.zig @@ -10,7 +10,8 @@ const vga = @import("vga.zig"); const log = @import("log.zig"); const serial = @import("serial.zig"); const mem = if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.zig"); -const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic; +const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig") else @import("panic.zig"); +const options = @import("build_options"); comptime { switch (builtin.arch) { @@ -26,14 +27,14 @@ export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefi // Just call the panic function, as this need to be in the root source file pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); - panic_root(error_return_trace, "{}", msg); + panic_root.panic(error_return_trace, "{}", msg); } export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void { if (mb_magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC) { // Booted with compatible bootloader serial.init(serial.DEFAULT_BAUDRATE, serial.Port.COM1) catch |e| { - panic_root(@errorReturnTrace(), "Failed to initialise serial: {}", e); + panic_root.panic(@errorReturnTrace(), "Failed to initialise serial: {}", e); }; if (build_options.rt_test) log.runtimeTests(); @@ -44,10 +45,15 @@ export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void { log.logInfo("Init arch " ++ @tagName(builtin.arch) ++ "\n"); arch.init(mb_info, &mem_profile, &fixed_allocator.allocator); log.logInfo("Arch init done\n"); + panic_root.init(&mem_profile, &fixed_allocator.allocator) catch |e| { + panic_root.panic(@errorReturnTrace(), "Failed to initialise panic: {}", e); + }; vga.init(); tty.init(); log.logInfo("Init done\n"); tty.print("Hello Pluto from kernel :)\n"); + // The panic runtime tests must run last as they never return + if (options.rt_test) panic_root.runtimeTests(); } } diff --git a/src/kernel/mem.zig b/src/kernel/mem.zig index 137974a..f6ae59e 100644 --- a/src/kernel/mem.zig +++ b/src/kernel/mem.zig @@ -29,7 +29,7 @@ extern var KERNEL_PHYSADDR_START: *u32; extern var KERNEL_ADDR_OFFSET: *u32; /// The size of the fixed allocator used before the heap is set up. Set to 1MiB. -const FIXED_ALLOC_SIZE = 1024 * 1024; +const FIXED_ALLOC_SIZE: usize = 1024 * 1024; /// The kernel's virtual address offset. It's assigned in the init function and this file's tests. /// We can't just use KERNEL_ADDR_OFFSET since using externs in the virtToPhys test is broken in diff --git a/src/kernel/panic.zig b/src/kernel/panic.zig index 4643c52..9f3521f 100644 --- a/src/kernel/panic.zig +++ b/src/kernel/panic.zig @@ -1,13 +1,449 @@ +const std = @import("std"); const builtin = @import("builtin"); const tty = @import("tty.zig"); const arch = @import("arch.zig").internals; const log = @import("log.zig"); +const multiboot = @import("multiboot.zig"); +const mem = @import("mem.zig"); +const ArrayList = std.ArrayList; +const testing = std.testing; + +/// The possible errors from panic code +const PanicError = error{ + /// The symbol file is of an invalid format. + /// This could be because it lacks whitespace, a column or required newline characters. + InvalidSymbolFile, +}; + +/// An entry within a symbol map. Corresponds to one entry in a symbole file +const MapEntry = struct { + /// The address that the entry corresponds to + addr: usize, + + /// The name of the function that starts at the address + func_name: []const u8, +}; + +const SymbolMap = struct { + symbols: ArrayList(MapEntry), + + /// + /// Initialise an empty symbol map. + /// + /// Arguments: + /// IN allocator: *std.mem.Allocator - The allocator to use to initialise the array list. + /// + /// Return: SymbolMap + /// The symbol map. + /// + pub fn init(allocator: *std.mem.Allocator) SymbolMap { + return SymbolMap{ + .symbols = ArrayList(MapEntry).init(allocator), + }; + } + + /// + /// Deinitialise the symbol map, freeing all memory used. + /// + pub fn deinit(self: *SymbolMap) void { + self.symbols.deinit(); + } + + /// + /// Add a symbol map entry with a name and address. + /// + /// Arguments: + /// IN name: []const u8 - The name of the entry. + /// IN addr: usize - The address for the entry. + /// + /// Error: std.mem.Allocator.Error + /// * - See ArrayList.append + /// + pub fn add(self: *SymbolMap, name: []const u8, addr: u32) !void { + try self.addEntry(MapEntry{ .addr = addr, .func_name = name }); + } + + pub fn addEntry(self: *SymbolMap, entry: MapEntry) !void { + try self.symbols.append(entry); + } + + /// + /// Search for the function name associated with the address. + /// + /// Arguments: + /// IN addr: usize - The address to search for. + /// + /// Return: ?[]const u8 + /// The function name associated with that program address, or null if one wasn't found. + /// + pub fn search(self: *const SymbolMap, addr: usize) ?[]const u8 { + if (self.symbols.count() == 0) + return null; + // Find the first element whose address is greater than addr + var previous_name: ?[]const u8 = null; + var it = self.symbols.iterator(); + while (it.next()) |entry| { + if (entry.addr > addr) + return previous_name; + previous_name = entry.func_name; + } + return previous_name; + } +}; + +var symbol_map: ?SymbolMap = null; + +/// +/// Log a stacktrace address. Logs "(no symbols are available)" if no symbols are available, +/// "?????" if the address wasn't found in the symbol map, else logs the function name. +/// +/// Arguments: +/// IN addr: usize - The address to log. +/// +fn logTraceAddress(addr: usize) void { + const str = if (symbol_map) |syms| syms.search(addr) orelse "?????" else "(no symbols available)"; + log.logError("{x}: {}\n", addr, str); +} pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn { @setCold(true); - arch.disableInterrupts(); - log.logInfo("KERNEL PANIC\n"); - log.logInfo(format, args); - log.logInfo("HALTING\n"); + log.logError("Kernel panic: " ++ format ++ "\n", args); + if (trace) |trc| { + var last_addr: u64 = 0; + for (trc.instruction_addresses) |ret_addr| { + if (ret_addr != last_addr) logTraceAddress(ret_addr); + last_addr = ret_addr; + } + } else { + const first_ret_addr = @returnAddress(); + var last_addr: u64 = 0; + var it = std.debug.StackIterator.init(first_ret_addr); + while (it.next()) |ret_addr| { + if (ret_addr != last_addr) logTraceAddress(ret_addr); + last_addr = ret_addr; + } + } arch.haltNoInterrupts(); } + +/// +/// Parse a hexadecimal address from the pointer up until the end pointer. Must be terminated by a +/// whitespace character. +/// +/// Arguments: +/// INOUT ptr: *[*]const u8 - The address at which to start looking, updated after all +/// characters have been consumed. +/// IN end: *const u8 - The end address at which to start looking. A whitespace character must +/// be found before this. +/// +/// Return: usize +/// The address parsed. +/// +/// Error: PanicError || std.fmt.ParseUnsignedError +/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address. +/// std.fmt.ParseUnsignedError: See std.fmt.parseInt +/// +fn parseAddr(ptr: *[*]const u8, end: *const u8) !usize { + const addr_start = ptr.*; + ptr.* = try parseNonWhitespace(ptr.*, end); + const len = @ptrToInt(ptr.*) - @ptrToInt(addr_start); + const addr_str = addr_start[0..len]; + return try std.fmt.parseInt(usize, addr_str, 16); +} + +/// +/// Parse a single character. The address given cannot be greater than or equal to the end address +/// given. +/// +/// Arguments: +/// IN ptr: [*]const u8 - The address at which to get the character from. +/// IN end: *const u8 - The end address at which to start looking. ptr cannot be greater than or +/// equal to this. +/// +/// Return: u8 +/// The character parsed. +/// +/// Error: PanicError +/// PanicError.InvalidSymbolFile: The address given is greater than or equal to the end address. +/// +fn parseChar(ptr: [*]const u8, end: *const u8) PanicError!u8 { + if (@ptrToInt(ptr) >= @ptrToInt(end)) return PanicError.InvalidSymbolFile; + return ptr[0]; +} + +/// +/// Parse until a non-whitespace character. Must be terminated by a non-whitespace character before +/// the end address. +/// +/// Arguments: +/// IN ptr: [*]const u8 - The address at which to start looking. +/// IN end: *const u8 - The end address at which to start looking. A non-whitespace character +/// must be found before this. +/// +/// Return: [*]const u8 +/// ptr plus the number of whitespace characters consumed. +/// +/// Error: PanicError +/// PanicError.InvalidSymbolFile: A terminating non-whitespace character wasn't found before the +/// end address. +/// +fn parseWhitespace(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 { + var i: u32 = 0; + while (std.fmt.isWhiteSpace(try parseChar(ptr + i, end))) : (i += 1) {} + return ptr + i; +} + +/// +/// Parse until a whitespace character. Must be terminated by a whitespace character before the end +/// address. +/// +/// Arguments: +/// IN ptr: [*]const u8 - The address at which to start looking. +/// IN end: *const u8 - The end address at which to start looking. A whitespace character must +/// be found before this. +/// +/// Return: [*]const u8 +/// ptr plus the number of non-whitespace characters consumed. +/// +/// Error: PanicError +/// PanicError.InvalidSymbolFile: A terminating whitespace character wasn't found before the end +/// address. +/// +fn parseNonWhitespace(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 { + var i: u32 = 0; + while (!std.fmt.isWhiteSpace(try parseChar(ptr + i, end))) : (i += 1) {} + return ptr + i; +} + +/// +/// Parse a name from the pointer up until the end pointer. Must be terminated by a whitespace +/// character. +/// +/// Arguments: +/// INOUT ptr: *[*]const u8 - The address at which to start looking, updated after all +/// characters have been consumed. +/// IN end: *const u8 - The end address at which to start looking. A whitespace character must +/// be found before this. +/// +/// Return: []const u8 +/// The name parsed. +/// +/// Error: PanicError +/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address. +/// +fn parseName(ptr: *[*]const u8, end: *const u8) PanicError![]const u8 { + const name_start = ptr.*; + ptr.* = try parseNonWhitespace(ptr.*, end); + const len = @ptrToInt(ptr.*) - @ptrToInt(name_start); + return name_start[0..len]; +} + +/// +/// Parse a symbol map entry from the pointer up until the end pointer, +/// in the format of '\d+\w+[a-zA-Z0-9]+'. Must be terminated by a whitespace character. +/// +/// Arguments: +/// INOUT ptr: *[*]const u8 - The address at which to start looking, updated once after the +/// address has been consumed and once again after the name has been consumed. +/// IN end: *const u8 - The end address at which to start looking. A whitespace character must +/// be found before this. +/// +/// Return: MapEntry +/// The entry parsed. +/// +/// Error: PanicError || std.fmt.ParseUnsignedError +/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address. +/// std.fmt.ParseUnsignedError: See parseAddr. +/// +fn parseMapEntry(start: *[*]const u8, end: *const u8) !MapEntry { + var ptr = try parseWhitespace(start.*, end); + defer start.* = ptr; + const addr = try parseAddr(&ptr, end); + ptr = try parseWhitespace(ptr, end); + const name = try parseName(&ptr, end); + return MapEntry{ .addr = addr, .func_name = name }; +} + +/// +/// Initialise the panic subsystem by looking for a boot module called "kernel.map" and loading the +/// symbols from it. Exits early if no such module was found. +/// +/// Arguments: +/// IN mem_profile: *const mem.MemProfile - The memory profile from which to get the loaded boot +/// modules. +/// IN allocator: *std.mem.Allocator - The allocator to use to store the symbol map. +/// +/// Error: PanicError || std.fmt.ParseUnsignedError +/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address. +/// std.fmt.ParseUnsignedError: See parseMapEntry. +/// +pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) !void { + log.logInfo("Init panic\n"); + defer log.logInfo("Done\n"); + // Exit if we haven't loaded all debug modules + if (mem_profile.boot_modules.len < 1) + return; + var kmap_start: u32 = 0; + var kmap_end: u32 = 0; + for (mem_profile.boot_modules) |module| { + const mod_start = mem.physToVirt(module.mod_start); + const mod_end = mem.physToVirt(module.mod_end) - 1; + const mod_str_ptr = mem.physToVirt(@intToPtr([*]u8, module.cmdline)); + if (std.mem.eql(u8, std.mem.toSlice(u8, mod_str_ptr), "kernel.map")) { + kmap_start = mod_start; + kmap_end = mod_end; + break; + } + } + // Don't try to load the symbols if there was no symbol map file. This is a valid state so just + // exit early + if (kmap_start == 0 or kmap_end == 0) + return; + + var syms = SymbolMap.init(allocator); + errdefer syms.deinit(); + var file_index = kmap_start; + var kmap_ptr = @intToPtr([*]u8, kmap_start); + while (@ptrToInt(kmap_ptr) < kmap_end - 1) { + const entry = try parseMapEntry(&kmap_ptr, @intToPtr(*const u8, kmap_end)); + try syms.addEntry(entry); + } + symbol_map = syms; +} + +test "parseChar" { + const str: []const u8 = "plutoisthebest"; + const end = @ptrCast(*const u8, str.ptr + 14); + var char = try parseChar(str.ptr, end); + testing.expectEqual(char, 'p'); + char = try parseChar(str.ptr + 1, end); + testing.expectEqual(char, 'l'); + testing.expectError(PanicError.InvalidSymbolFile, parseChar(str.ptr + 14, end)); +} + +test "parseWhitespace" { + const str: []const u8 = " a"; + const end = @ptrCast(*const u8, str.ptr + 5); + var ptr = try parseWhitespace(str.ptr, end); + testing.expectEqual(@ptrToInt(str.ptr) + 4, @ptrToInt(ptr)); +} + +test "parseWhitespace fails without a terminating whitespace" { + const str: []const u8 = " "; + const end = @ptrCast(*const u8, str.ptr + 3); + testing.expectError(PanicError.InvalidSymbolFile, parseWhitespace(str.ptr, end)); +} + +test "parseNonWhitespace" { + const str: []const u8 = "ab "; + const end = @ptrCast(*const u8, str.ptr + 3); + var ptr = try parseNonWhitespace(str.ptr, end); + testing.expectEqual(@ptrToInt(str.ptr) + 2, @ptrToInt(ptr)); +} + +test "parseNonWhitespace fails without a terminating whitespace" { + const str: []const u8 = "abc"; + const end = @ptrCast(*const u8, str.ptr + 3); + testing.expectError(PanicError.InvalidSymbolFile, parseNonWhitespace(str.ptr, end)); +} + +test "parseAddr" { + const str: []const u8 = "1a2b3c4d "; + const end = @ptrCast(*const u8, str.ptr + 9); + var ptr = str.ptr; + testing.expectEqual(try parseAddr(&ptr, end), 0x1a2b3c4d); +} + +test "parseAddr fails without a terminating whitespace" { + const str: []const u8 = "1a2b3c4d"; + const end = @ptrCast(*const u8, str.ptr + 9); + var ptr = str.ptr; + testing.expectError(PanicError.InvalidSymbolFile, parseAddr(&ptr, end)); +} + +test "parseAddr fails with an invalid integer" { + const str: []const u8 = "1g2t "; + const end = @ptrCast(*const u8, str.ptr + 5); + var ptr = str.ptr; + testing.expectError(error.InvalidCharacter, parseAddr(&ptr, end)); +} + +test "parseName" { + const str: []const u8 = "func_name "; + const end = @ptrCast(*const u8, str.ptr + 10); + var ptr = str.ptr; + testing.expectEqualSlices(u8, try parseName(&ptr, end), "func_name"); +} + +test "parseName fails without a terminating whitespace" { + const str: []const u8 = "func_name"; + const end = @ptrCast(*const u8, str.ptr + 9); + var ptr = str.ptr; + testing.expectError(PanicError.InvalidSymbolFile, parseName(&ptr, end)); +} + +test "parseMapEntry" { + const str: []const u8 = "1a2b3c4d func_name\n5e6f7a8b func_name2\n"; + const end = @ptrCast(*const u8, str.ptr + 39); + var ptr = str.ptr; + + var actual = try parseMapEntry(&ptr, end); + var expected = MapEntry{ .addr = 0x1a2b3c4d, .func_name = "func_name" }; + testing.expectEqual(actual.addr, expected.addr); + testing.expectEqualSlices(u8, actual.func_name, expected.func_name); + + actual = try parseMapEntry(&ptr, end); + expected = MapEntry{ .addr = 0x5e6f7a8b, .func_name = "func_name2" }; + testing.expectEqual(actual.addr, expected.addr); + testing.expectEqualSlices(u8, actual.func_name, expected.func_name); +} + +test "parseMapEntry fails without a terminating whitespace" { + const str: []const u8 = "1a2b3c4d func_name"; + var ptr = str.ptr; + testing.expectError(PanicError.InvalidSymbolFile, parseMapEntry(&ptr, @ptrCast(*const u8, str.ptr + 18))); +} + +test "parseMapEntry fails without any characters" { + const str: []const u8 = " "; + var ptr = str.ptr; + testing.expectError(PanicError.InvalidSymbolFile, parseMapEntry(&ptr, @ptrCast(*const u8, str.ptr))); +} + +test "parseMapEntry fails with an invalid address" { + const str: []const u8 = "xyz func_name"; + var ptr = str.ptr; + testing.expectError(error.InvalidCharacter, parseMapEntry(&ptr, @ptrCast(*const u8, str.ptr + 13))); +} + +test "parseMapEntry fails without a name" { + const str: []const u8 = "123 "; + var ptr = str.ptr; + testing.expectError(PanicError.InvalidSymbolFile, parseMapEntry(&ptr, @ptrCast(*const u8, str.ptr + 4))); +} + +test "SymbolMap" { + var allocator = std.heap.direct_allocator; + var map = SymbolMap.init(allocator); + try map.add("abc"[0..], 123); + try map.addEntry(MapEntry{ .func_name = "def"[0..], .addr = 456 }); + try map.add("ghi"[0..], 789); + try map.addEntry(MapEntry{ .func_name = "jkl"[0..], .addr = 1010 }); + testing.expectEqual(map.search(54), null); + testing.expectEqual(map.search(122), null); + testing.expectEqual(map.search(123), "abc"); + testing.expectEqual(map.search(234), "abc"); + testing.expectEqual(map.search(455), "abc"); + testing.expectEqual(map.search(456), "def"); + testing.expectEqual(map.search(678), "def"); + testing.expectEqual(map.search(788), "def"); + testing.expectEqual(map.search(789), "ghi"); + testing.expectEqual(map.search(1009), "ghi"); + testing.expectEqual(map.search(1010), "jkl"); + testing.expectEqual(map.search(2345), "jkl"); +} + +pub fn runtimeTests() void { + var x: u8 = 255; + x += 1; +} diff --git a/test/mock/kernel/panic_mock.zig b/test/mock/kernel/panic_mock.zig index 546c040..7aab78c 100644 --- a/test/mock/kernel/panic_mock.zig +++ b/test/mock/kernel/panic_mock.zig @@ -1,7 +1,13 @@ const builtin = @import("builtin"); const std = @import("std"); +const MemProfile = @import("mem_mock.zig").MemProfile; pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn { @setCold(true); std.debug.panic(format, args); } + +pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) !void { + // This is never run so just return an arbitrary error to satisfy the compiler + return error.NotNeeded; +} diff --git a/test/rt-test.py b/test/rt-test.py index 92d27e1..30aea24 100644 --- a/test/rt-test.py +++ b/test/rt-test.py @@ -42,11 +42,13 @@ def get_pre_archinit_cases(): def get_post_archinit_cases(): return [ TestCase("Arch init finishes", [r"Arch init done"]), + TestCase("Panic init", [r"Init panic", r"Done"]), TestCase("VGA init", [r"Init vga", r"Done"]), TestCase("VGA tests", [r"VGA: Tested max scan line", r"VGA: Tested cursor shape", r"VGA: Tested updating cursor"]), TestCase("TTY init", [r"Init tty", r"Done"]), TestCase("TTY tests", [r"TTY: Tested globals", r"TTY: Tested printing"]), - TestCase("Init finishes", [r"Init done"]) + TestCase("Init finishes", [r"Init done"]), + TestCase("Panic tests", [r"Kernel panic: integer overflow", r"c[a-z\d]+: panic", r"c[a-z\d]+: panic.runtimeTests", r"c[a-z\d]+: kmain", r"c[a-z\d]+: start_higher_half"], "\[ERROR\] ") ] def read_messages(proc):