From 420a09f0395d3026e024a90bf28c23ebabfcd7ea Mon Sep 17 00:00:00 2001 From: ED Date: Sun, 29 Sep 2019 12:55:34 +0100 Subject: [PATCH] Added runtime tests for VGA Added doc comments as well A little refactor of code Reordered Removed types --- src/kernel/arch/x86/irq.zig | 6 +- src/kernel/arch/x86/isr.zig | 8 +- src/kernel/arch/x86/paging.zig | 10 +- src/kernel/kmain.zig | 4 +- src/kernel/panic.zig | 11 +- src/kernel/vga.zig | 317 ++++++++++++++++++++++---------- test/kernel/arch/x86/rt-test.py | 2 +- test/mock/kernel/log_mock.zig | 29 +-- test/rt-test.py | 30 +-- 9 files changed, 267 insertions(+), 150 deletions(-) diff --git a/src/kernel/arch/x86/irq.zig b/src/kernel/arch/x86/irq.zig index eb18f66..4e81e8e 100644 --- a/src/kernel/arch/x86/irq.zig +++ b/src/kernel/arch/x86/irq.zig @@ -1,6 +1,6 @@ // Zig version: 0.4.0 -const panic = @import("../../panic.zig"); +const panic = @import("../../panic.zig").panic; const idt = @import("idt.zig"); const arch = @import("arch.zig"); const pic = @import("pic.zig"); @@ -39,7 +39,7 @@ var irq_handlers: [NUMBER_OF_ENTRIES]fn (*arch.InterruptContext) void = [_]fn (* /// fn unhandled(context: *arch.InterruptContext) void { const interrupt_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET); - panic.panicFmt(null, "Unhandled IRQ number {}", interrupt_num); + panic(null, "Unhandled IRQ number {}", interrupt_num); } /// @@ -69,7 +69,7 @@ export fn irqHandler(context: *arch.InterruptContext) void { fn openIrq(index: u8, handler: idt.InterruptHandler) void { idt.openInterruptGate(index, handler) catch |err| switch (err) { error.IdtEntryExists => { - panic.panicFmt(@errorReturnTrace(), "Error opening IRQ number: {} exists", index); + panic(@errorReturnTrace(), "Error opening IRQ number: {} exists", index); }, }; } diff --git a/src/kernel/arch/x86/isr.zig b/src/kernel/arch/x86/isr.zig index 42f4d3d..82ce01b 100644 --- a/src/kernel/arch/x86/isr.zig +++ b/src/kernel/arch/x86/isr.zig @@ -1,6 +1,6 @@ // Zig version: 0.4.0 -const panic = @import("../../panic.zig"); +const panic = @import("../../panic.zig").panic; const idt = @import("idt.zig"); const arch = @import("arch.zig"); const syscalls = @import("syscalls.zig"); @@ -98,7 +98,7 @@ var syscall_handler: IsrHandler = unhandled; /// fn unhandled(context: *arch.InterruptContext) void { const interrupt_num = context.int_num; - panic.panicFmt(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num); + panic(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num); } /// @@ -126,7 +126,7 @@ export fn isrHandler(context: *arch.InterruptContext) void { } else if (isValidIsr(isr_num)) { isr_handlers[isr_num](context); } else { - panic.panicFmt(null, "Unrecognised isr: {}\n", isr_num); + panic(null, "Unrecognised isr: {}\n", isr_num); } } @@ -140,7 +140,7 @@ export fn isrHandler(context: *arch.InterruptContext) void { fn openIsr(index: u8, handler: idt.InterruptHandler) void { idt.openInterruptGate(index, handler) catch |err| switch (err) { error.IdtEntryExists => { - panic.panicFmt(@errorReturnTrace(), "Error opening ISR number: {} exists", index); + panic(@errorReturnTrace(), "Error opening ISR number: {} exists", index); }, }; } diff --git a/src/kernel/arch/x86/paging.zig b/src/kernel/arch/x86/paging.zig index f6bfdfa..7c1682f 100644 --- a/src/kernel/arch/x86/paging.zig +++ b/src/kernel/arch/x86/paging.zig @@ -2,7 +2,7 @@ const std = @import("std"); const expectEqual = std.testing.expectEqual; const expect = std.testing.expect; const builtin = @import("builtin"); -const panic = @import("../../panic.zig"); +const panic = @import("../../panic.zig").panic; const arch = @import("arch.zig"); const isr = @import("isr.zig"); const MemProfile = @import("../../mem.zig").MemProfile; @@ -319,18 +319,18 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void const p_start = std.mem.alignBackward(@ptrToInt(mem_profile.physaddr_start), PAGE_SIZE_4KB); const p_end = std.mem.alignForward(@ptrToInt(mem_profile.physaddr_end) + mem_profile.fixed_alloc_size, PAGE_SIZE_4KB); - var tmp = allocator.alignedAlloc(Directory, @truncate(u29, PAGE_SIZE_4KB), 1) catch panic.panicFmt(@errorReturnTrace(), "Failed to allocate page directory"); + var tmp = allocator.alignedAlloc(Directory, @truncate(u29, PAGE_SIZE_4KB), 1) catch panic(@errorReturnTrace(), "Failed to allocate page directory"); var kernel_directory = @ptrCast(*Directory, tmp.ptr); @memset(@ptrCast([*]u8, kernel_directory), 0, @sizeOf(Directory)); // Map in kernel - mapDir(kernel_directory, p_start, p_end, v_start, v_end, allocator) catch panic.panicFmt(@errorReturnTrace(), "Failed to map kernel directory"); + mapDir(kernel_directory, p_start, p_end, v_start, v_end, allocator) catch panic(@errorReturnTrace(), "Failed to map kernel directory"); const tty_addr = tty.getVideoBufferAddress(); // If the previous mappping space didn't cover the tty buffer, do so now if (v_start > tty_addr or v_end <= tty_addr) { const tty_phys = virtToPhys(tty_addr); const tty_buff_size = 32 * 1024; - mapDir(kernel_directory, tty_phys, tty_phys + tty_buff_size, tty_addr, tty_addr + tty_buff_size, allocator) catch panic.panicFmt(@errorReturnTrace(), "Failed to map vga buffer in kernel directory"); + mapDir(kernel_directory, tty_phys, tty_phys + tty_buff_size, tty_addr, tty_addr + tty_buff_size, allocator) catch panic(@errorReturnTrace(), "Failed to map vga buffer in kernel directory"); } const dir_physaddr = @ptrToInt(virtToPhys(kernel_directory)); @@ -338,7 +338,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void : : [addr] "{eax}" (dir_physaddr) ); - isr.registerIsr(14, pageFault) catch panic.panicFmt(@errorReturnTrace(), "Failed to register page fault ISR"); + isr.registerIsr(14, pageFault) catch panic(@errorReturnTrace(), "Failed to register page fault ISR"); } fn checkDirEntry(entry: DirectoryEntry, virt_start: usize, virt_end: usize, phys_start: usize, table: *Table) void { diff --git a/src/kernel/kmain.zig b/src/kernel/kmain.zig index 1aa4421..4dc0600 100644 --- a/src/kernel/kmain.zig +++ b/src/kernel/kmain.zig @@ -22,13 +22,13 @@ export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefi // Need to import this as we need the panic to be in the root source file, or zig will just use the // builtin panic and just loop, which is what we don't want -const panic_root = if (builtin.is_test) @import(build_options.mock_path ++ "panic_mock.zig") else @import("panic.zig"); +const panic_root = @import("panic.zig").panic; // 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); arch.disableInterrupts(); - panic_root.panicFmt(error_return_trace, "{}", msg); + panic_root(error_return_trace, "{}", msg); } export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void { diff --git a/src/kernel/panic.zig b/src/kernel/panic.zig index 8ba90fa..f384186 100644 --- a/src/kernel/panic.zig +++ b/src/kernel/panic.zig @@ -1,13 +1,12 @@ -// Zig version: 0.4.0 - const builtin = @import("builtin"); const tty = @import("tty.zig"); const arch = @import("arch.zig").internals; +const log = @import("log.zig"); -pub fn panicFmt(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn { +pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn { @setCold(true); - tty.print("KERNEL PANIC\n"); - tty.print(format, args); - tty.print("\nHALTING\n"); + log.logInfo("KERNEL PANIC\n"); + log.logInfo(format, args); + log.logInfo("HALTING\n"); arch.haltNoInterrupts(); } diff --git a/src/kernel/vga.zig b/src/kernel/vga.zig index d2da678..d7065c1 100644 --- a/src/kernel/vga.zig +++ b/src/kernel/vga.zig @@ -1,6 +1,12 @@ const std = @import("std"); +const builtin = @import("builtin"); +const is_test = builtin.is_test; const expectEqual = std.testing.expectEqual; +const build_options = @import("build_options"); +const mock_path = build_options.mock_path; const arch = @import("arch.zig").internals; +const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("log.zig"); +const panic = @import("panic.zig").panic; /// The port address for the VGA register selection. const PORT_ADDRESS: u16 = 0x03D4; @@ -8,39 +14,21 @@ const PORT_ADDRESS: u16 = 0x03D4; /// The port address for the VGA data. const PORT_DATA: u16 = 0x03D5; -/// The indexes that is passed to the address port to select the register for the data to be -/// read or written to. -const REG_HORIZONTAL_TOTAL: u8 = 0x00; -const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01; -const REG_START_HORIZONTAL_BLINKING: u8 = 0x02; -const REG_END_HORIZONTAL_BLINKING: u8 = 0x03; -const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04; -const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05; -const REG_VERTICAL_TOTAL: u8 = 0x06; -const REG_OVERFLOW: u8 = 0x07; -const REG_PRESET_ROW_SCAN: u8 = 0x08; +/// The indexes that is passed to the address port to select the maximum scan line register for +/// the data to be read or written to. const REG_MAXIMUM_SCAN_LINE: u8 = 0x09; -/// The register select for setting the cursor scan lines. +/// The register select for setting the cursor start scan lines. const REG_CURSOR_START: u8 = 0x0A; + +/// The register select for setting the cursor end scan lines. const REG_CURSOR_END: u8 = 0x0B; -const REG_START_ADDRESS_HIGH: u8 = 0x0C; -const REG_START_ADDRESS_LOW: u8 = 0x0D; -/// The command for setting the cursor's linear location. +/// The command for setting the cursor's linear location (Upper 8 bits). const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E; -const REG_CURSOR_LOCATION_LOW: u8 = 0x0F; -/// Other VGA registers. -const REG_VERTICAL_RETRACE_START: u8 = 0x10; -const REG_VERTICAL_RETRACE_END: u8 = 0x11; -const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12; -const REG_OFFSET: u8 = 0x13; -const REG_UNDERLINE_LOCATION: u8 = 0x14; -const REG_START_VERTICAL_BLINKING: u8 = 0x15; -const REG_END_VERTICAL_BLINKING: u8 = 0x16; -const REG_CRT_MODE_CONTROL: u8 = 0x17; -const REG_LINE_COMPARE: u8 = 0x18; +/// The command for setting the cursor's linear location (Lower 8 bits). +const REG_CURSOR_LOCATION_LOW: u8 = 0x0F; /// The start of the cursor scan line, the very beginning. const CURSOR_SCANLINE_START: u8 = 0x0; @@ -60,22 +48,56 @@ pub const WIDTH: u16 = 80; /// The number of characters heigh the screen is. pub const HEIGHT: u16 = 25; -/// The set of colours that VGA supports and can display for the foreground and background. +// ---------- +// The set of colours that VGA supports and can display for the foreground and background. +// ---------- + +/// Foreground/background VGA colour black. pub const COLOUR_BLACK: u4 = 0x00; + +/// Foreground/background VGA colour blue. pub const COLOUR_BLUE: u4 = 0x01; + +/// Foreground/background VGA colour green. pub const COLOUR_GREEN: u4 = 0x02; + +/// Foreground/background VGA colour cyan. pub const COLOUR_CYAN: u4 = 0x03; + +/// Foreground/background VGA colour red. pub const COLOUR_RED: u4 = 0x04; + +/// Foreground/background VGA colour magenta. pub const COLOUR_MAGENTA: u4 = 0x05; + +/// Foreground/background VGA colour brown. pub const COLOUR_BROWN: u4 = 0x06; + +/// Foreground/background VGA colour light grey. pub const COLOUR_LIGHT_GREY: u4 = 0x07; + +/// Foreground/background VGA colour dark grey. pub const COLOUR_DARK_GREY: u4 = 0x08; + +/// Foreground/background VGA colour light blue. pub const COLOUR_LIGHT_BLUE: u4 = 0x09; + +/// Foreground/background VGA colour light green. pub const COLOUR_LIGHT_GREEN: u4 = 0x0A; + +/// Foreground/background VGA colour light cyan. pub const COLOUR_LIGHT_CYAN: u4 = 0x0B; + +/// Foreground/background VGA colour light red. pub const COLOUR_LIGHT_RED: u4 = 0x0C; + +/// Foreground/background VGA colour light magenta. pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D; + +/// Foreground/background VGA colour light brown. pub const COLOUR_LIGHT_BROWN: u4 = 0x0E; + +/// Foreground/background VGA colour white. pub const COLOUR_WHITE: u4 = 0x0F; /// The set of shapes that can be displayed. @@ -93,26 +115,63 @@ var cursor_scanline_start: u8 = undefined; /// The cursor scan line end so to know whether is in block or underline mode. var cursor_scanline_end: u8 = undefined; -/// A inline function for setting the VGA register port to read from or write to. -inline fn sendPort(port: u8) void { - arch.outb(PORT_ADDRESS, port); +/// +/// Set the VGA register port to read from or write to. +/// +/// Arguments: +/// IN index: u8 - The index to send to the port address to select the register to write data +/// to. +/// +inline fn sendPort(index: u8) void { + arch.outb(PORT_ADDRESS, index); } -/// A inline function for sending data to the set VGA register port. +/// +/// Send data to the set VGA register port. +/// +/// Arguments: +/// IN data: u8 - The data to send to the selected register. +/// inline fn sendData(data: u8) void { arch.outb(PORT_DATA, data); } -/// A inline function for setting the VGA register port to read from or write toa and sending data -/// to the set VGA register port. -inline fn sendPortData(port: u8, data: u8) void { - sendPort(port); +/// +/// Get data from a set VGA register port. +/// +/// Return: u8 +/// The data in the selected register. +/// +inline fn getData() u8 { + return arch.inb(PORT_DATA); +} + +/// +/// Set the VGA register port to write to and sending data to that VGA register port. +/// +/// Arguments: +/// IN index: u8 - The index to send to the port address to select the register to write the +// data to. +/// IN data: u8 - The data to send to the selected register. +/// +inline fn sendPortData(index: u8, data: u8) void { + sendPort(index); sendData(data); } -/// A inline function for getting data from a set VGA register port. -inline fn getData() u8 { - return arch.inb(PORT_DATA); +/// +/// Set the VGA register port to read from and get the data from that VGA register port. +/// +/// Arguments: +/// IN index: u8 - The index to send to the port address to select the register to read the +/// data from. +/// +/// Return: u8 +/// The data in the selected register. +/// +inline fn getPortData(index: u8) u8 { + sendPort(index); + return getData(); } /// @@ -178,19 +237,16 @@ pub fn updateCursor(x: u16, y: u16) void { /// The linear cursor position. /// pub fn getCursor() u16 { - var cursor: u16 = 0; + var cursor = u16(0); - sendPort(REG_CURSOR_LOCATION_LOW); - cursor |= u16(getData()); - - sendPort(REG_CURSOR_LOCATION_HIGH); - cursor |= u16(getData()) << 8; + cursor |= u16(getPortData(REG_CURSOR_LOCATION_LOW)); + cursor |= u16(getPortData(REG_CURSOR_LOCATION_HIGH)) << 8; return cursor; } /// -/// Enables the blinking cursor to that is is visible. +/// Enables the blinking cursor so that is is visible. /// pub fn enableCursor() void { sendPortData(REG_CURSOR_START, cursor_scanline_start); @@ -198,7 +254,7 @@ pub fn enableCursor() void { } /// -/// Disables the blinking cursor to that is is visible. +/// Disables the blinking cursor so that is is invisible. /// pub fn disableCursor() void { sendPortData(REG_CURSOR_START, CURSOR_DISABLE); @@ -230,17 +286,23 @@ pub fn setCursorShape(shape: CursorShape) void { /// Initialise the VGA text mode. This sets the cursor and underline shape. /// pub fn init() void { + log.logInfo("Init vga\n"); + // Set the maximum scan line to 0x0F sendPortData(REG_MAXIMUM_SCAN_LINE, CURSOR_SCANLINE_END); // Set by default the underline cursor setCursorShape(CursorShape.UNDERLINE); + + log.logInfo("Done\n"); + + if (build_options.rt_test) runtimeTests(); } test "entryColour" { - var fg: u4 = COLOUR_BLACK; - var bg: u4 = COLOUR_BLACK; - var res: u8 = entryColour(fg, bg); + var fg = COLOUR_BLACK; + var bg = COLOUR_BLACK; + var res = entryColour(fg, bg); expectEqual(u8(0x00), res); fg = COLOUR_LIGHT_GREEN; @@ -260,11 +322,11 @@ test "entryColour" { } test "entry" { - var colour: u8 = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN); + const colour = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN); expectEqual(u8(0xA6), colour); // Character '0' is 0x30 - var video_entry: u16 = entry('0', colour); + var video_entry = entry('0', colour); expectEqual(u16(0xA630), video_entry); video_entry = entry(0x55, colour); @@ -272,12 +334,12 @@ test "entry" { } test "updateCursor width out of bounds" { - const x: u16 = WIDTH; - const y: u16 = 0; + const x = WIDTH; + const y = 0; - const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); - const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); - const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); + const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1); + const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF); + const expected_lower = @truncate(u8, max_cursor & 0x00FF); arch.initTest(); defer arch.freeTest(); @@ -289,12 +351,12 @@ test "updateCursor width out of bounds" { } test "updateCursor height out of bounds" { - const x: u16 = 0; - const y: u16 = HEIGHT; + const x = 0; + const y = HEIGHT; - const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); - const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); - const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); + const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1); + const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF); + const expected_lower = @truncate(u8, max_cursor & 0x00FF); arch.initTest(); defer arch.freeTest(); @@ -306,12 +368,12 @@ test "updateCursor height out of bounds" { } test "updateCursor width and height out of bounds" { - const x: u16 = WIDTH; - const y: u16 = HEIGHT; + const x = WIDTH; + const y = HEIGHT; - const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); - const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); - const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); + const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1); + const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF); + const expected_lower = @truncate(u8, max_cursor & 0x00FF); arch.initTest(); defer arch.freeTest(); @@ -323,12 +385,12 @@ test "updateCursor width and height out of bounds" { } test "updateCursor width-1 and height out of bounds" { - const x: u16 = WIDTH - 1; - const y: u16 = HEIGHT; + const x = WIDTH - 1; + const y = HEIGHT; - const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); - const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); - const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); + const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1); + const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF); + const expected_lower = @truncate(u8, max_cursor & 0x00FF); arch.initTest(); defer arch.freeTest(); @@ -340,12 +402,12 @@ test "updateCursor width-1 and height out of bounds" { } test "updateCursor width and height-1 out of bounds" { - const x: u16 = WIDTH; - const y: u16 = HEIGHT - 1; + const x = WIDTH; + const y = HEIGHT - 1; - const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); - const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); - const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); + const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1); + const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF); + const expected_lower = @truncate(u8, max_cursor & 0x00FF); arch.initTest(); defer arch.freeTest(); @@ -357,12 +419,12 @@ test "updateCursor width and height-1 out of bounds" { } test "updateCursor in bounds" { - var x: u16 = 0x000A; - var y: u16 = 0x000A; - const expected: u16 = y * WIDTH + x; + var x = u8(0x0A); + var y = u8(0x0A); + const expected = y * WIDTH + x; - var expected_upper: u8 = @truncate(u8, (expected >> 8) & 0x00FF); - var expected_lower: u8 = @truncate(u8, expected & 0x00FF); + var expected_upper = @truncate(u8, (expected >> 8) & 0x00FF); + var expected_lower = @truncate(u8, expected & 0x00FF); arch.initTest(); defer arch.freeTest(); @@ -373,44 +435,38 @@ test "updateCursor in bounds" { } test "getCursor 1: 10" { - const expect: u16 = u16(10); + const expect = u16(10); // Mocking out the arch.outb and arch.inb calls for getting the hardware cursor: arch.initTest(); defer arch.freeTest(); arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); - arch.addTestParams("inb", PORT_DATA, u8(10)); - arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH); - arch.addTestParams("inb", PORT_DATA, u8(0)); - const actual: u16 = getCursor(); + const actual = getCursor(); expectEqual(expect, actual); } test "getCursor 2: 0xBEEF" { - const expect: u16 = u16(0xBEEF); + const expect = u16(0xBEEF); // Mocking out the arch.outb and arch.inb calls for getting the hardware cursor: arch.initTest(); defer arch.freeTest(); arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); - arch.addTestParams("inb", PORT_DATA, u8(0xEF)); - arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH); - arch.addTestParams("inb", PORT_DATA, u8(0xBE)); - const actual: u16 = getCursor(); + const actual = getCursor(); expectEqual(expect, actual); } -test "enableCursor all" { +test "enableCursor" { arch.initTest(); defer arch.freeTest(); @@ -424,7 +480,7 @@ test "enableCursor all" { enableCursor(); } -test "disableCursor all" { +test "disableCursor" { arch.initTest(); defer arch.freeTest(); @@ -457,7 +513,7 @@ test "setCursorShape BLOCK" { setCursorShape(CursorShape.BLOCK); } -test "init all" { +test "init" { arch.initTest(); defer arch.freeTest(); @@ -468,3 +524,76 @@ test "init all" { init(); } + +/// +/// Check that the maximum scan line is CURSOR_SCANLINE_END (0xF) when VGA is initialised. +/// +fn rt_correctMaxScanLine() void { + const max_scan_line = getPortData(REG_MAXIMUM_SCAN_LINE); + + if (max_scan_line != CURSOR_SCANLINE_END) { + panic(@errorReturnTrace(), "Max scan line not {}, found {}\n", CURSOR_SCANLINE_END, max_scan_line); + } + + log.logInfo("VGA: Tested max scan line\n"); +} + +/// +/// Check that the cursor is an underline when the VGA initialises. +/// +fn rt_correctCursorShape() void { + // Check the global variables are correct + if (cursor_scanline_start != CURSOR_SCANLINE_MIDDLE or cursor_scanline_end != CURSOR_SCANLINE_END) { + panic(@errorReturnTrace(), "Global cursor scanline incorrect. Start: {}, end: {}\n", cursor_scanline_start, cursor_scanline_end); + } + + const cursor_start = getPortData(REG_CURSOR_START); + const cursor_end = getPortData(REG_CURSOR_END); + + if (cursor_start != CURSOR_SCANLINE_MIDDLE or cursor_end != CURSOR_SCANLINE_END) { + panic(@errorReturnTrace(), "Cursor scanline are incorrect. Start: {}, end: {}\n", cursor_start, cursor_end); + } + + log.logInfo("VGA: Tested cursor shape\n"); +} + +/// +/// Update the cursor to a known value. Then get the cursor and check they match. This will also +/// save the previous cursor position and restore is to the original position. +/// +fn rt_setCursorGetCursor() void { + // The known locations + const x = u16(10); + const y = u16(20); + + // Save the previous location + const prev_linear_loc = getCursor(); + const prev_x_loc = @truncate(u8, prev_linear_loc % WIDTH); + const prev_y_loc = @truncate(u8, prev_linear_loc / WIDTH); + + // Set the known location + updateCursor(x, y); + + // Get the cursor + const actual_linear_loc = getCursor(); + const actual_x_loc = @truncate(u8, actual_linear_loc % WIDTH); + const actual_y_loc = @truncate(u8, actual_linear_loc / WIDTH); + + if (x != actual_x_loc or y != actual_y_loc) { + panic(@errorReturnTrace(), "VGA cursor not the same: a_x: {}, a_y: {}, e_x: {}, e_y: {}\n", x, y, actual_x_loc, actual_y_loc); + } + + // Restore the previous x and y + updateCursor(prev_x_loc, prev_y_loc); + + log.logInfo("VGA: Tested updating cursor\n"); +} + +/// +/// Run all the runtime tests. +/// +fn runtimeTests() void { + rt_correctMaxScanLine(); + rt_correctCursorShape(); + rt_setCursorGetCursor(); +} diff --git a/test/kernel/arch/x86/rt-test.py b/test/kernel/arch/x86/rt-test.py index 3fee314..37e6770 100644 --- a/test/kernel/arch/x86/rt-test.py +++ b/test/kernel/arch/x86/rt-test.py @@ -1,4 +1,4 @@ -def getTestCases(TestCase): +def get_test_cases(TestCase): return [ TestCase("GDT init", [r"Init gdt", r"Done"]), TestCase("GDT tests", [r"GDT: Tested loading GDT"]), diff --git a/test/mock/kernel/log_mock.zig b/test/mock/kernel/log_mock.zig index 7c58ab4..a6c54a3 100644 --- a/test/mock/kernel/log_mock.zig +++ b/test/mock/kernel/log_mock.zig @@ -1,14 +1,17 @@ const mock_framework = @import("mock_framework.zig"); +pub const initTest = mock_framework.initTest; +pub const freeTest = mock_framework.freeTest; +pub const addTestParams = mock_framework.addTestParams; +pub const addConsumeFunction = mock_framework.addConsumeFunction; +pub const addRepeatFunction = mock_framework.addRepeatFunction; pub const Level = enum { INFO, DEBUG, WARNING, - ERROR + ERROR, }; -fn logCallback(context: void, str: []const u8) anyerror!void {} - pub fn log(comptime level: Level, comptime format: []const u8, args: ...) void { //return mock_framework.performAction("log", void, level, format, args); } @@ -28,23 +31,3 @@ pub fn logWarning(comptime format: []const u8, args: ...) void { pub fn logError(comptime format: []const u8, args: ...) void { //return mock_framework.performAction("logError", void, format, args); } - -pub fn addRepeatFunction(comptime fun_name: []const u8, function: var) void { - mock_framework.addRepeatFunction(fun_name, function); -} - -pub fn addTestFunction(comptime fun_name: []const u8, function: var) void { - mock_framework.addRepeatFunction(fun_name, function); -} - -pub fn addTestParams(comptime fun_name: []const u8, params: ...) void { - mock_framework.addTestParams(fun_name, params); -} - -pub fn initTest() void { - mock_framework.initTest(); -} - -pub fn freeTest() void { - mock_framework.freeTest(); -} \ No newline at end of file diff --git a/test/rt-test.py b/test/rt-test.py index f38a79c..f35bf28 100644 --- a/test/rt-test.py +++ b/test/rt-test.py @@ -18,6 +18,20 @@ def test_failure(case, exp, expected_idx, found): def test_pass(case, exp, expected_idx, found): print("PASS: %s #%d, expected '%s', found '%s'" %(case.name, expected_idx + 1, exp, found)) +def get_pre_archinit_cases(): + return [ + TestCase("Arch init starts", [r"Init arch \w+"]) + ] + +def get_post_archinit_cases(): + return [ + TestCase("Arch init finishes", [r"Arch init 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("Init finishes", [r"Init done"]) + ] + if __name__ == "__main__": arch = sys.argv[1] zig_path = sys.argv[2] @@ -25,18 +39,10 @@ if __name__ == "__main__": arch_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(arch_module) - # The list of log statements to look for before arch init is called - pre_archinit_cases = [ - TestCase("Arch init starts", [r"Init arch \w+"]) - ] + # The list of log statements to look for before arch init is called + + # All log statements to look for, including the arch-specific ones + # The list of log statements to look for after arch init is called - post_archinit_cases = [ - TestCase("Arch init finishes", [r"Arch init done"]), - TestCase("TTY init", [r"Init tty", r"Done"]), - TestCase("Init finishes", [r"Init done"]) - ] - # All log statements to look for, including the arch-specific ones - cases = pre_archinit_cases + arch_module.getTestCases(TestCase) + post_archinit_cases + cases = get_pre_archinit_cases() + arch_module.get_test_cases(TestCase) + get_post_archinit_cases() if len(cases) > 0: proc = subprocess.Popen(zig_path + " build run -Drt-test=true", stdout=subprocess.PIPE, shell=True) @@ -47,7 +53,7 @@ if __name__ == "__main__": expected_idx = 0 # Go through the expected log messages while expected_idx < len(case.expected): - e = "\[INFO\] " + case.expected[expected_idx] + e = r"\[INFO\] " + case.expected[expected_idx] line = proc.stdout.readline().decode("utf-8") if not line: break