Remove the old and in with the new

Added the new testing to the OS files

Some spelling
INOUT => IN/OUT
Added some doc comments to log

Added the new runtime to the build + added the None test mode

Moved some stuff around
None test mode is the default to run/build the OS normally with no runtime tests.

Add the new runtime testing to the CI


Updated README and CI


Increased timeout


Print the log message


Spelling


Move runtime to test folder


Add new RT to tty


Add a log to use the unmapped memory to cause page fault in release


Ensure the integer overflow happens even in release builds
This commit is contained in:
DrDeano 2020-06-23 12:43:52 +01:00
parent d6d99ef667
commit 2c91e6f9d0
No known key found for this signature in database
GPG key ID: 96188600582B9ED7
28 changed files with 667 additions and 405 deletions

View file

@ -3,7 +3,7 @@ const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const builtin = @import("builtin");
const is_test = builtin.is_test;
const panic = @import("../../panic.zig").panic;
const build_options = @import("build_options");
const mock_path = build_options.arch_mock_path;
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
@ -438,7 +438,10 @@ pub fn init() void {
// Load the TSS
arch.ltr(TSS_OFFSET);
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
fn mock_lgdt(ptr: *const GdtPtr) void {
@ -671,8 +674,13 @@ test "init" {
///
fn rt_loadedGDTSuccess() void {
const loaded_gdt = arch.sgdt();
expect(gdt_ptr.limit == loaded_gdt.limit);
expect(gdt_ptr.base == loaded_gdt.base);
if (gdt_ptr.limit != loaded_gdt.limit) {
panic(@errorReturnTrace(), "FAILURE: GDT not loaded properly: 0x{X} != 0x{X}\n", .{ gdt_ptr.limit, loaded_gdt.limit });
}
if (gdt_ptr.base != loaded_gdt.base) {
panic(@errorReturnTrace(), "FAILURE: GDT not loaded properly: 0x{X} != {X}\n", .{ gdt_ptr.base, loaded_gdt.base });
}
log.logInfo("GDT: Tested loading GDT\n", .{});
}
///
@ -680,5 +688,4 @@ fn rt_loadedGDTSuccess() void {
///
fn runtimeTests() void {
rt_loadedGDTSuccess();
log.logInfo("GDT: Tested loading GDT\n", .{});
}

View file

@ -4,7 +4,7 @@ const expectEqual = std.testing.expectEqual;
const expectError = std.testing.expectError;
const builtin = @import("builtin");
const is_test = builtin.is_test;
const panic = @import("../../panic.zig").panic;
const build_options = @import("build_options");
const mock_path = build_options.arch_mock_path;
const gdt = if (is_test) @import(mock_path ++ "gdt_mock.zig") else @import("gdt.zig");
@ -101,7 +101,7 @@ var idt_ptr: IdtPtr = IdtPtr{
.base = 0,
};
/// The IDT entry table of NUMBER_OF_ENTRIES entries. Initially all zero'ed.
/// The IDT entry table of NUMBER_OF_ENTRIES entries. Initially all zeroed.
var idt_entries: [NUMBER_OF_ENTRIES]IdtEntry = [_]IdtEntry{IdtEntry{
.base_low = 0,
.selector = 0,
@ -186,7 +186,10 @@ pub fn init() void {
arch.lidt(&idt_ptr);
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
fn testHandler0() callconv(.Naked) void {}
@ -325,8 +328,12 @@ test "init" {
///
fn rt_loadedIDTSuccess() void {
const loaded_idt = arch.sidt();
expect(idt_ptr.limit == loaded_idt.limit);
expect(idt_ptr.base == loaded_idt.base);
if (idt_ptr.limit != loaded_idt.limit) {
panic(@errorReturnTrace(), "FAILURE: IDT not loaded properly: 0x{X} != 0x{X}\n", .{ idt_ptr.limit, loaded_idt.limit });
}
if (idt_ptr.base != loaded_idt.base) {
panic(@errorReturnTrace(), "FAILURE: IDT not loaded properly: 0x{X} != {X}\n", .{ idt_ptr.base, loaded_idt.base });
}
log.logInfo("IDT: Tested loading IDT\n", .{});
}

View file

@ -88,7 +88,7 @@ fn openIrq(index: u8, handler: idt.InterruptHandler) void {
/// IN irq_num: u8 - The IRQ index to test.
///
/// Return: bool
/// Whether the IRQ index if valid.
/// Whether the IRQ index is valid.
///
pub fn isValidIrq(irq_num: u32) bool {
return irq_num < NUMBER_OF_ENTRIES;
@ -136,7 +136,10 @@ pub fn init() void {
openIrq(i, interrupts.getInterruptStub(i));
}
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
fn testFunction0() callconv(.Naked) void {}
@ -227,13 +230,13 @@ test "registerIrq invalid irq index" {
}
///
/// Test that all handers are null at initialisation.
/// Test that all handlers are null at initialisation.
///
fn rt_unregisteredHandlers() void {
// Ensure all ISR are not registered yet
for (irq_handlers) |h, i| {
if (h) |_| {
panic(@errorReturnTrace(), "Handler found for IRQ: {}-{}\n", .{ i, h });
panic(@errorReturnTrace(), "FAILURE: Handler found for IRQ: {}-{}\n", .{ i, h });
}
}
@ -250,7 +253,7 @@ fn rt_openedIdtEntries() void {
for (idt_entries) |entry, i| {
if (i >= IRQ_OFFSET and isValidIrq(i - IRQ_OFFSET)) {
if (!idt.isIdtOpen(entry)) {
panic(@errorReturnTrace(), "IDT entry for {} is not open\n", .{i});
panic(@errorReturnTrace(), "FAILURE: IDT entry for {} is not open\n", .{i});
}
}
}

View file

@ -130,7 +130,7 @@ pub const SECURITY: u8 = 30;
/// The of exception handlers initialised to null. Need to open a ISR for these to be valid.
var isr_handlers: [NUMBER_OF_ENTRIES]?IsrHandler = [_]?IsrHandler{null} ** NUMBER_OF_ENTRIES;
/// The syscall hander.
/// The syscall handler.
var syscall_handler: ?IsrHandler = null;
///
@ -188,7 +188,7 @@ fn openIsr(index: u8, handler: idt.InterruptHandler) void {
/// IN isr_num: u16 - The isr number to check
///
/// Return: bool
/// Whether a ISR hander index if valid.
/// Whether a ISR handler index is valid.
///
pub fn isValidIsr(isr_num: u32) bool {
return isr_num < NUMBER_OF_ENTRIES or isr_num == syscalls.INTERRUPT;
@ -244,7 +244,10 @@ pub fn init() void {
openIsr(syscalls.INTERRUPT, interrupts.getInterruptStub(syscalls.INTERRUPT));
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
fn testFunction0() callconv(.Naked) void {}
@ -356,18 +359,18 @@ test "registerIsr invalid isr index" {
}
///
/// Test that all handers are null at initialisation.
/// Test that all handlers are null at initialisation.
///
fn rt_unregisteredHandlers() void {
// Ensure all ISR are not registered yet
for (isr_handlers) |h, i| {
if (h) |_| {
panic(@errorReturnTrace(), "Handler found for ISR: {}-{}\n", .{ i, h });
panic(@errorReturnTrace(), "FAILURE: Handler found for ISR: {}-{}\n", .{ i, h });
}
}
if (syscall_handler) |h| {
panic(@errorReturnTrace(), "Pre-testing failed for syscall: {}\n", .{h});
panic(@errorReturnTrace(), "FAILURE: Pre-testing failed for syscall: {}\n", .{h});
}
log.logInfo("ISR: Tested registered handlers\n", .{});
@ -383,7 +386,7 @@ fn rt_openedIdtEntries() void {
for (idt_entries) |entry, i| {
if (isValidIsr(i)) {
if (!idt.isIdtOpen(entry)) {
panic(@errorReturnTrace(), "IDT entry for {} is not open\n", .{i});
panic(@errorReturnTrace(), "FAILURE: IDT entry for {} is not open\n", .{i});
}
}
}

View file

@ -11,7 +11,7 @@ const log = @import("../../log.zig");
const mem = @import("../../mem.zig");
const vmm = @import("../../vmm.zig");
const multiboot = @import("multiboot.zig");
const options = @import("build_options");
const build_options = @import("build_options");
const testing = std.testing;
/// An array of directory entries and page tables. Forms the first level of paging and covers the entire 4GB memory space.
@ -138,7 +138,7 @@ inline fn virtToTableEntryIdx(virt: usize) usize {
///
/// Arguments:
/// val: *align(1) u32 - The entry to modify
/// attr: u32 - The bits corresponding to the atttribute to set
/// attr: u32 - The bits corresponding to the attribute to set
///
inline fn setAttribute(val: *align(1) u32, attr: u32) void {
val.* |= attr;
@ -149,7 +149,7 @@ inline fn setAttribute(val: *align(1) u32, attr: u32) void {
///
/// Arguments:
/// val: *align(1) u32 - The entry to modify
/// attr: u32 - The bits corresponding to the atttribute to clear
/// attr: u32 - The bits corresponding to the attribute to clear
///
inline fn clearAttribute(val: *align(1) u32, attr: u32) void {
val.* &= ~attr;
@ -157,7 +157,7 @@ inline fn clearAttribute(val: *align(1) u32, attr: u32) void {
///
/// Map a page directory entry, setting the present, size, writable, write-through and physical address bits.
/// Clears the user and cache disabled bits. Entry should be zero'ed.
/// Clears the user and cache disabled bits. Entry should be zeroed.
///
/// Arguments:
/// IN virt_addr: usize - The start of the virtual space to map
@ -297,11 +297,11 @@ fn mapTableEntry(entry: *align(1) TableEntry, phys_addr: usize, attrs: vmm.Attri
/// Arguments:
/// IN virtual_start: usize - The start of the virtual region to map
/// IN virtual_end: usize - The end (exclusive) of the virtual region to map
/// IN physical_start: usize - The start of the physical region to mape to
/// IN physical_start: usize - The start of the physical region to map to
/// IN physical_end: usize - The end (exclusive) of the physical region to map to
/// IN attrs: vmm.Attributes - The attributes to apply to this mapping
/// INOUT allocator: *std.mem.Allocator - The allocator to use to allocate any intermediate data structures required to map this region
/// INOUT dir: *Directory - The page directory to map within
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use to allocate any intermediate data structures required to map this region
/// IN/OUT dir: *Directory - The page directory to map within
///
/// Error: vmm.MapperError || std.mem.Allocator.Error
/// * - See mapDirEntry
@ -326,7 +326,7 @@ pub fn map(virt_start: usize, virt_end: usize, phys_start: usize, phys_end: usiz
/// Arguments:
/// IN virtual_start: usize - The start of the virtual region to unmap
/// IN virtual_end: usize - The end (exclusive) of the virtual region to unmap
/// INOUT dir: *Directory - The page directory to unmap within
/// IN/OUT dir: *Directory - The page directory to unmap within
///
/// Error: std.mem.Allocator.Error || vmm.MapperError
/// vmm.MapperError.NotMapped - If the region being unmapped wasn't mapped in the first place
@ -396,7 +396,7 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile
log.logInfo("Init paging\n", .{});
defer log.logInfo("Done paging\n", .{});
isr.registerIsr(isr.PAGE_FAULT, if (options.rt_test) rt_pageFault else pageFault) catch |e| {
isr.registerIsr(isr.PAGE_FAULT, if (build_options.test_mode == .Initialisation) rt_pageFault else pageFault) catch |e| {
panic(@errorReturnTrace(), "Failed to register page fault ISR: {}\n", .{e});
};
const dir_physaddr = @ptrToInt(mem.virtToPhys(&kernel_directory));
@ -405,7 +405,10 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile
: [addr] "{eax}" (dir_physaddr)
);
const v_end = std.mem.alignForward(@ptrToInt(mem_profile.vaddr_end) + mem.FIXED_ALLOC_SIZE, PAGE_SIZE_4KB);
if (options.rt_test) runtimeTests(v_end);
switch (build_options.test_mode) {
.Initialisation => runtimeTests(v_end),
else => {},
}
}
fn checkDirEntry(entry: DirectoryEntry, virt_start: usize, virt_end: usize, phys_start: usize, attrs: vmm.Attributes, table: *Table, present: bool) void {
@ -541,7 +544,7 @@ test "map and unmap" {
}
// The labels to jump to after attempting to cause a page fault. This is needed as we don't want to cause an
// infinite loop by jummping to the same instruction that caused the fault.
// infinite loop by jumping to the same instruction that caused the fault.
extern var rt_fault_callback: *u32;
extern var rt_fault_callback2: *u32;
@ -560,26 +563,32 @@ fn rt_accessUnmappedMem(v_end: u32) void {
// Accessing unmapped mem causes a page fault
var ptr = @intToPtr(*u8, v_end);
var value = ptr.*;
// Need this as in release builds the above is optimised out so it needs to be use
log.logError("FAILURE: Value: {}\n", .{value});
// This is the label that we return to after processing the page fault
asm volatile (
\\.global rt_fault_callback
\\rt_fault_callback:
);
testing.expect(faulted);
if (!faulted) {
panic(@errorReturnTrace(), "FAILURE: Paging should have faulted\n", .{});
}
log.logInfo("Paging: Tested accessing unmapped memory\n", .{});
}
fn rt_accessMappedMem(v_end: u32) void {
use_callback2 = true;
faulted = false;
// Accessing mapped memory does't cause a page fault
// Accessing mapped memory doesn't cause a page fault
var ptr = @intToPtr(*u8, v_end - PAGE_SIZE_4KB);
var value = ptr.*;
asm volatile (
\\.global rt_fault_callback2
\\rt_fault_callback2:
);
testing.expect(!faulted);
if (faulted) {
panic(@errorReturnTrace(), "FAILURE: Paging shouldn't have faulted\n", .{});
}
log.logInfo("Paging: Tested accessing mapped memory\n", .{});
}

View file

@ -468,7 +468,10 @@ pub fn init() void {
// Clear the IRQ for the slave
clearMask(IRQ_CASCADE_FOR_SLAVE);
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
test "sendCommandMaster" {
@ -814,11 +817,11 @@ test "init" {
fn rt_picAllMasked() void {
// The master will have interrupt 2 clear because this is the link to the slave (third bit)
if (readDataMaster() != 0xFB) {
panic(@errorReturnTrace(), "Master masks are not set, found: {}\n", .{readDataMaster()});
panic(@errorReturnTrace(), "FAILURE: Master masks are not set, found: {}\n", .{readDataMaster()});
}
if (readDataSlave() != 0xFF) {
panic(@errorReturnTrace(), "Slave masks are not set, found: {}\n", .{readDataSlave()});
panic(@errorReturnTrace(), "FAILURE: Slave masks are not set, found: {}\n", .{readDataSlave()});
}
log.logInfo("PIC: Tested masking\n", .{});

View file

@ -391,7 +391,10 @@ pub fn init() void {
},
};
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
test "sendCommand" {
@ -551,7 +554,7 @@ fn rt_waitTicks() void {
const difference = getTicks() - waiting;
if (previous_count + epsilon < difference or previous_count > difference + epsilon) {
panic(@errorReturnTrace(), "Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", .{ difference, previous_count, epsilon });
panic(@errorReturnTrace(), "FAILURE: Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", .{ difference, previous_count, epsilon });
}
log.logInfo("PIT: Tested wait ticks\n", .{});
@ -575,13 +578,13 @@ fn rt_waitTicks2() void {
const difference = getTicks() + 15 - waiting;
if (previous_count + epsilon < difference or previous_count > difference + epsilon) {
panic(@errorReturnTrace(), "Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", .{ difference, previous_count, epsilon });
panic(@errorReturnTrace(), "FAILURE: Waiting failed. difference: {}, previous_count: {}. Epsilon: {}\n", .{ difference, previous_count, epsilon });
}
log.logInfo("PIT: Tested wait ticks 2\n", .{});
// Reset ticks
ticks = 0;
log.logInfo("PIT: Tested wait ticks 2\n", .{});
}
///
@ -593,7 +596,7 @@ fn rt_initCounter_0() void {
const expected_hz: u32 = 10027;
if (time_ns != expected_ns or time_under_1_ns != expected_ps or getFrequency() != expected_hz) {
panic(@errorReturnTrace(), "Frequency not set properly. Hz: {}!={}, ns: {}!={}, ps: {}!= {}\n", .{
panic(@errorReturnTrace(), "FAILURE: Frequency not set properly. Hz: {}!={}, ns: {}!={}, ps: {}!= {}\n", .{
getFrequency(),
expected_hz,
time_ns,
@ -611,19 +614,19 @@ fn rt_initCounter_0() void {
irq_exists = true;
},
error.InvalidIrq => {
panic(@errorReturnTrace(), "IRQ for PIT, IRQ number: {} is invalid", .{pic.IRQ_PIT});
panic(@errorReturnTrace(), "FAILURE: IRQ for PIT, IRQ number: {} is invalid", .{pic.IRQ_PIT});
},
};
if (!irq_exists) {
panic(@errorReturnTrace(), "IRQ for PIT doesn't exists\n", .{});
panic(@errorReturnTrace(), "FAILURE: IRQ for PIT doesn't exists\n", .{});
}
const expected_mode = OCW_READ_LOAD_DATA | OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_SELECT_COUNTER_0 | OCW_BINARY_COUNT_BINARY;
const actual_mode = readBackCommand(CounterSelect.Counter0);
if (expected_mode != actual_mode) {
panic(@errorReturnTrace(), "Operating mode don't not set properly. Found: {}, expecting: {}\n", .{ actual_mode, expected_mode });
panic(@errorReturnTrace(), "FAILURE: Operating mode don't not set properly. Found: {}, expecting: {}\n", .{ actual_mode, expected_mode });
}
log.logInfo("PIT: Tested init\n", .{});

View file

@ -49,9 +49,9 @@ var ticks: u32 = 0;
/// registers so don't get inconsistent values.
///
/// Return: bool
/// Whether the CMOS chip is buzzy and a update is in progress.
/// Whether the CMOS chip is busy and a update is in progress.
///
fn isBuzzy() bool {
fn isBusy() bool {
return (cmos.readStatusRegister(cmos.StatusRegister.A, false) & 0x80) != 0;
}
@ -122,7 +122,7 @@ fn bcdToBinary(bcd: u32) u32 {
///
fn readRtcRegisters() DateTime {
// Make sure there isn't a update in progress
while (isBuzzy()) {}
while (isBusy()) {}
var date_time = DateTime{
.second = cmos.readRtcRegister(cmos.RtcRegister.SECOND),
@ -283,10 +283,13 @@ pub fn init() void {
// Read status register C to clear any interrupts that may have happened during set up
const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
test "isBuzzy not buzzy" {
test "isBusy not busy" {
cmos.initTest();
defer cmos.freeTest();
@ -295,10 +298,10 @@ test "isBuzzy not buzzy" {
.{ cmos.StatusRegister.A, false, @as(u8, 0x60) },
);
expect(!isBuzzy());
expect(!isBusy());
}
test "isBuzzy buzzy" {
test "isBusy busy" {
cmos.initTest();
defer cmos.freeTest();
@ -307,7 +310,7 @@ test "isBuzzy buzzy" {
.{ cmos.StatusRegister.A, false, @as(u8, 0x80) },
);
expect(isBuzzy());
expect(isBusy());
}
test "calcDayOfWeek" {
@ -446,7 +449,7 @@ test "readRtcRegisters" {
cmos.initTest();
defer cmos.freeTest();
// Have 2 buzzy loops
// Have 2 busy loops
cmos.addTestParams(
"readStatusRegister",
.{ cmos.StatusRegister.A, false, @as(u8, 0x80), cmos.StatusRegister.A, false, @as(u8, 0x80), cmos.StatusRegister.A, false, @as(u8, 0x00) },
@ -481,7 +484,7 @@ test "readRtc unstable read" {
cmos.initTest();
defer cmos.freeTest();
// No buzzy loop
// No busy loop
cmos.addTestParams(
"readStatusRegister",
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
@ -504,7 +507,7 @@ test "readRtc unstable read" {
});
// Will try again, and now stable
// No buzzy loop
// No busy loop
cmos.addTestParams(
"readStatusRegister",
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
@ -555,7 +558,7 @@ test "readRtc is BCD" {
cmos.initTest();
defer cmos.freeTest();
// No buzzy loop
// No busy loop
cmos.addTestParams(
"readStatusRegister",
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
@ -608,7 +611,7 @@ test "readRtc is 12 hours" {
cmos.initTest();
defer cmos.freeTest();
// No buzzy loop
// No busy loop
cmos.addTestParams(
"readStatusRegister",
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
@ -695,24 +698,24 @@ fn rt_init() void {
irq_exists = true;
},
error.InvalidIrq => {
panic(@errorReturnTrace(), "IRQ for RTC, IRQ number: {} is invalid", .{pic.IRQ_REAL_TIME_CLOCK});
panic(@errorReturnTrace(), "FAILURE: IRQ for RTC, IRQ number: {} is invalid\n", .{pic.IRQ_REAL_TIME_CLOCK});
},
};
if (!irq_exists) {
panic(@errorReturnTrace(), "IRQ for RTC doesn't exists\n", .{});
panic(@errorReturnTrace(), "FAILURE: IRQ for RTC doesn't exists\n", .{});
}
// Check the rate
const status_a = cmos.readStatusRegister(cmos.StatusRegister.A, false);
if (status_a & @as(u8, 0x0F) != 7) {
panic(@errorReturnTrace(), "Rate not set properly, got: {}\n", .{status_a & @as(u8, 0x0F)});
panic(@errorReturnTrace(), "FAILURE: Rate not set properly, got: {}\n", .{status_a & @as(u8, 0x0F)});
}
// Check if interrupts are enabled
const status_b = cmos.readStatusRegister(cmos.StatusRegister.B, true);
if (status_b & ~@as(u8, 0x40) == 0) {
panic(@errorReturnTrace(), "Interrupts not enabled\n", .{});
panic(@errorReturnTrace(), "FAILURE: Interrupts not enabled\n", .{});
}
log.logInfo("RTC: Tested init\n", .{});
@ -727,7 +730,7 @@ fn rt_interrupts() void {
pit.waitTicks(100);
if (prev_ticks == ticks) {
panic(@errorReturnTrace(), "No interrupt happened\n", .{});
panic(@errorReturnTrace(), "FAILURE: No interrupt happened\n", .{});
}
log.logInfo("RTC: Tested interrupts\n", .{});

View file

@ -65,7 +65,7 @@ fn lcrValue(char_len: u8, stop_bit: bool, parity_bit: bool, msb: u1) SerialError
}
///
/// The serial controller accepts a divisor rather than a raw badrate, as that is more space efficient.
/// The serial controller accepts a divisor rather than a raw baudrate, as that is more space efficient.
/// This function computes the divisor for a desired baudrate. Note that multiple baudrates can have the same divisor.
///
/// Arguments:

View file

@ -3,7 +3,8 @@ const testing = @import("std").testing;
const assert = @import("std").debug.assert;
const isr = @import("isr.zig");
const log = @import("../../log.zig");
const options = @import("build_options");
const build_options = @import("build_options");
const panic = @import("../../panic.zig").panic;
/// The isr number associated with syscalls
pub const INTERRUPT: u16 = 0x80;
@ -241,39 +242,43 @@ pub fn init() void {
defer log.logInfo("Done syscalls\n", .{});
isr.registerIsr(INTERRUPT, handle) catch unreachable;
if (options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
/// Tests
var testInt: u32 = 0;
var test_int: u32 = 0;
fn testHandler0(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
testInt += 1;
test_int += 1;
return 0;
}
fn testHandler1(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
testInt += arg1;
test_int += arg1;
return 1;
}
fn testHandler2(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
testInt += arg1 + arg2;
test_int += arg1 + arg2;
return 2;
}
fn testHandler3(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
testInt += arg1 + arg2 + arg3;
test_int += arg1 + arg2 + arg3;
return 3;
}
fn testHandler4(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
testInt += arg1 + arg2 + arg3 + arg4;
test_int += arg1 + arg2 + arg3 + arg4;
return 4;
}
fn testHandler5(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
testInt += arg1 + arg2 + arg3 + arg4 + arg5;
test_int += arg1 + arg2 + arg3 + arg4 + arg5;
return 5;
}
@ -286,29 +291,40 @@ test "registerSyscall returns SyscallExists" {
}
fn runtimeTests() void {
registerSyscall(123, testHandler0) catch unreachable;
registerSyscall(124, testHandler1) catch unreachable;
registerSyscall(125, testHandler2) catch unreachable;
registerSyscall(126, testHandler3) catch unreachable;
registerSyscall(127, testHandler4) catch unreachable;
registerSyscall(128, testHandler5) catch unreachable;
assert(testInt == 0);
registerSyscall(123, testHandler0) catch panic(@errorReturnTrace(), "FAILURE registering handler 0\n", .{});
registerSyscall(124, testHandler1) catch panic(@errorReturnTrace(), "FAILURE registering handler 1\n", .{});
registerSyscall(125, testHandler2) catch panic(@errorReturnTrace(), "FAILURE registering handler 2\n", .{});
registerSyscall(126, testHandler3) catch panic(@errorReturnTrace(), "FAILURE registering handler 3\n", .{});
registerSyscall(127, testHandler4) catch panic(@errorReturnTrace(), "FAILURE registering handler 4\n", .{});
registerSyscall(128, testHandler5) catch panic(@errorReturnTrace(), "FAILURE registering handler 5\n", .{});
if (syscall0(123) == 0 and testInt == 1)
log.logInfo("Syscalls: Tested no args\n", .{});
if (test_int != 0) {
panic(@errorReturnTrace(), "FAILURE initial test_int not 0: {}\n", .{test_int});
}
if (syscall1(124, 2) == 1 and testInt == 3)
log.logInfo("Syscalls: Tested 1 arg\n", .{});
if (syscall0(123) != 0 or test_int != 1) {
panic(@errorReturnTrace(), "FAILURE syscall0\n", .{});
}
if (syscall2(125, 2, 3) == 2 and testInt == 8)
log.logInfo("Syscalls: Tested 2 args\n", .{});
if (syscall1(124, 2) != 1 or test_int != 3) {
panic(@errorReturnTrace(), "FAILURE syscall2\n", .{});
}
if (syscall3(126, 2, 3, 4) == 3 and testInt == 17)
log.logInfo("Syscalls: Tested 3 args\n", .{});
if (syscall2(125, 2, 3) != 2 or test_int != 8) {
panic(@errorReturnTrace(), "FAILURE syscall2\n", .{});
}
if (syscall4(127, 2, 3, 4, 5) == 4 and testInt == 31)
log.logInfo("Syscalls: Tested 4 args\n", .{});
if (syscall3(126, 2, 3, 4) != 3 or test_int != 17) {
panic(@errorReturnTrace(), "FAILURE syscall3\n", .{});
}
if (syscall5(128, 2, 3, 4, 5, 6) == 5 and testInt == 51)
log.logInfo("Syscalls: Tested 5 args\n", .{});
if (syscall4(127, 2, 3, 4, 5) != 4 or test_int != 31) {
panic(@errorReturnTrace(), "FAILURE syscall4\n", .{});
}
if (syscall5(128, 2, 3, 4, 5, 6) != 5 or test_int != 51) {
panic(@errorReturnTrace(), "FAILURE syscall5\n", .{});
}
log.logInfo("Syscall: Tested all args\n", .{});
}

View file

@ -548,7 +548,10 @@ pub fn init() void {
row = ROW_MIN;
}
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
const test_colour: u8 = vga.orig_entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK);

View file

@ -293,7 +293,10 @@ pub fn init() void {
// Set by default the underline cursor
setCursorShape(CursorShape.UNDERLINE);
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
test "entryColour" {
@ -531,7 +534,7 @@ 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 });
panic(@errorReturnTrace(), "FAILURE: Max scan line not {}, found {}\n", .{ CURSOR_SCANLINE_END, max_scan_line });
}
log.logInfo("VGA: Tested max scan line\n", .{});
@ -543,14 +546,14 @@ fn rt_correctMaxScanLine() void {
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 });
panic(@errorReturnTrace(), "FAILURE: 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 });
panic(@errorReturnTrace(), "FAILURE: Cursor scanline are incorrect. Start: {}, end: {}\n", .{ cursor_start, cursor_end });
}
log.logInfo("VGA: Tested cursor shape\n", .{});
@ -579,7 +582,7 @@ fn rt_setCursorGetCursor() void {
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 });
panic(@errorReturnTrace(), "FAILURE: 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