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

View file

@ -67,7 +67,7 @@ pub fn Bitmap(comptime BitmapType: type) type {
/// Set an entry within a bitmap as occupied.
///
/// Arguments:
/// INOUT self: *Self - The bitmap to modify.
/// IN/OUT self: *Self - The bitmap to modify.
/// IN idx: usize - The index within the bitmap to set.
///
/// Error: BitmapError.
@ -86,7 +86,7 @@ pub fn Bitmap(comptime BitmapType: type) type {
/// Set an entry within a bitmap as unoccupied.
///
/// Arguments:
/// INOUT self: *Self - The bitmap to modify.
/// IN/OUT self: *Self - The bitmap to modify.
/// IN idx: usize - The index within the bitmap to clear.
///
/// Error: BitmapError.
@ -106,7 +106,7 @@ pub fn Bitmap(comptime BitmapType: type) type {
///
/// Arguments:
/// IN self: *const Self - The bitmap to use.
/// IN idx: usize - The index into all of the bitmap's entries.
/// IN idx: usize - The index into all of the bitmaps entries.
///
/// Return: BitmapType.
/// The bit corresponding to that index but within a single BitmapType.
@ -119,7 +119,7 @@ pub fn Bitmap(comptime BitmapType: type) type {
/// Find a number of contiguous free entries and set them.
///
/// Arguments:
/// INOUT self: *Self - The bitmap to modify.
/// IN/OUT self: *Self - The bitmap to modify.
/// IN num: usize - The number of entries to set.
///
/// Return: ?usize

View file

@ -158,7 +158,7 @@ const Heap = struct {
/// Attempt to allocate a portion of memory within a heap. It is recommended to not call this directly and instead use the Allocator interface.
///
/// Arguments:
/// INOUT self: *Heap - The heap to allocate within
/// IN/OUT self: *Heap - The heap to allocate within
/// IN size: usize - The size of the allocation
/// IN alignment: ?u29 - The alignment that the returned address should have, else null if no alignment is required
///
@ -187,7 +187,7 @@ const Heap = struct {
/// Free previously allocated memory. It is recommended to not call this directly and instead use the Allocator interface.
///
/// Arguments:
/// INOUT self: *Heap - The heap to free within
/// IN/OUT self: *Heap - The heap to free within
/// IN ptr: usize - The address of the allocation to free. Should have been returned from a prior call to alloc.
/// IN len: usize - The size of the allocated region.
///

View file

@ -81,6 +81,12 @@ export fn kmain(boot_payload: arch.BootPayload) void {
tty.print("Hello Pluto from kernel :)\n", .{});
// The panic runtime tests must run last as they never return
if (build_options.rt_test) panic_root.runtimeTests();
switch (build_options.test_mode) {
.Initialisation => {
log.logInfo("SUCCESS\n", .{});
},
else => {},
}
arch.spinWait();
}

View file

@ -9,6 +9,7 @@ const LoggingError = error{};
/// The OutStream for the format function
const OutStream = std.io.OutStream(void, LoggingError, logCallback);
/// The different levels of logging that can be outputted.
pub const Level = enum {
INFO,
DEBUG,
@ -18,6 +19,20 @@ pub const Level = enum {
var serial: Serial = undefined;
///
/// The call back function for the std library format function.
///
/// Arguments:
/// context: void - The context of the printing. There isn't a need for a context for this
/// so is void.
/// str: []const u8 - The string to print to the serial terminal.
///
/// Return: usize
/// The number of bytes written. This will always be the length of the string to print.
///
/// Error: LoggingError
/// {} - No error as LoggingError is empty.
///
fn logCallback(context: void, str: []const u8) LoggingError!usize {
serial.writeBytes(str);
return str.len;
@ -27,8 +42,10 @@ fn logCallback(context: void, str: []const u8) LoggingError!usize {
/// Write a message to the log output stream with a certain logging level.
///
/// Arguments:
/// IN comptime level: Level - The logging level to use. Determines the message prefix and whether it is filtered.
/// IN comptime format: []const u8 - The message format. Uses the standard format specification options.
/// IN comptime level: Level - The logging level to use. Determines the message prefix and
/// whether it is filtered.
/// IN comptime format: []const u8 - The message format. Uses the standard format specification
/// options.
/// IN args: var - A struct of the parameters for the format string.
///
pub fn log(comptime level: Level, comptime format: []const u8, args: var) void {
@ -39,7 +56,8 @@ pub fn log(comptime level: Level, comptime format: []const u8, args: var) void {
/// Write a message to the log output stream with the INFO level.
///
/// Arguments:
/// IN comptime format: []const u8 - The message format. Uses the standard format specification options.
/// IN comptime format: []const u8 - The message format. Uses the standard format specification
/// options.
/// IN args: var - A struct of the parameters for the format string.
///
pub fn logInfo(comptime format: []const u8, args: var) void {
@ -50,7 +68,8 @@ pub fn logInfo(comptime format: []const u8, args: var) void {
/// Write a message to the log output stream with the DEBUG level.
///
/// Arguments:
/// IN comptime format: []const u8 - The message format. Uses the standard format specification options.
/// IN comptime format: []const u8 - The message format. Uses the standard format specification
/// options.
/// IN args: var - A struct of the parameters for the format string.
///
pub fn logDebug(comptime format: []const u8, args: var) void {
@ -61,7 +80,8 @@ pub fn logDebug(comptime format: []const u8, args: var) void {
/// Write a message to the log output stream with the WARNING level.
///
/// Arguments:
/// IN comptime format: []const u8 - The message format. Uses the standard format specification options.
/// IN comptime format: []const u8 - The message format. Uses the standard format specification
/// options.
/// IN args: var - A struct of the parameters for the format string.
///
pub fn logWarning(comptime format: []const u8, args: var) void {
@ -72,7 +92,8 @@ pub fn logWarning(comptime format: []const u8, args: var) void {
/// Write a message to the log output stream with the ERROR level.
///
/// Arguments:
/// IN comptime format: []const u8 - The message format. Uses the standard format specification options.
/// IN comptime format: []const u8 - The message format. Uses the standard format specification
/// options.
/// IN args: var - A struct of the parameters for the format string.
///
pub fn logError(comptime format: []const u8, args: var) void {
@ -88,9 +109,15 @@ pub fn logError(comptime format: []const u8, args: var) void {
pub fn init(ser: Serial) void {
serial = ser;
if (build_options.rt_test) runtimeTests();
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
///
/// The logging runtime tests that will test all logging levels.
///
pub fn runtimeTests() void {
inline for (@typeInfo(Level).Enum.fields) |field| {
const level = @field(Level, field.name);

View file

@ -46,7 +46,7 @@ pub const MemProfile = struct {
/// The virtual regions of reserved memory. Should not include what is tracked by the vaddr_* fields but should include the regions occupied by the modules. These are reserved and mapped by the VMM
virtual_reserved: []Map,
/// The phsyical regions of reserved memory. Should not include what is tracked by the physaddr_* fields but should include the regions occupied by the modules. These are reserved by the PMM
/// The physical regions of reserved memory. Should not include what is tracked by the physaddr_* fields but should include the regions occupied by the modules. These are reserved by the PMM
physical_reserved: []Range,
/// The allocator to use before a heap can be set up.

View file

@ -5,6 +5,7 @@ const arch = @import("arch.zig").internals;
const log = @import("log.zig");
const multiboot = @import("multiboot.zig");
const mem = @import("mem.zig");
const build_options = @import("build_options");
const ArrayList = std.ArrayList;
const testing = std.testing;
@ -15,7 +16,7 @@ const PanicError = error{
InvalidSymbolFile,
};
/// An entry within a symbol map. Corresponds to one entry in a symbole file
/// An entry within a symbol map. Corresponds to one entry in a symbol file
const MapEntry = struct {
/// The address that the entry corresponds to
addr: usize,
@ -59,7 +60,7 @@ const SymbolMap = struct {
/// Error: std.mem.Allocator.Error
/// * - See ArrayList.append
///
pub fn add(self: *SymbolMap, name: []const u8, addr: u32) !void {
pub fn add(self: *SymbolMap, name: []const u8, addr: usize) !void {
try self.addEntry(MapEntry{ .addr = addr, .func_name = name });
}
@ -130,7 +131,7 @@ pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: var
/// whitespace character.
///
/// Arguments:
/// INOUT ptr: *[*]const u8 - The address at which to start looking, updated after all
/// IN/OUT 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.
@ -166,7 +167,9 @@ fn parseAddr(ptr: *[*]const u8, end: *const u8) !usize {
/// 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;
if (@ptrToInt(ptr) >= @ptrToInt(end)) {
return PanicError.InvalidSymbolFile;
}
return ptr[0];
}
@ -219,7 +222,7 @@ fn parseNonWhitespace(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 {
/// character.
///
/// Arguments:
/// INOUT ptr: *[*]const u8 - The address at which to start looking, updated after all
/// IN/OUT 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.
@ -242,7 +245,7 @@ fn parseName(ptr: *[*]const u8, end: *const u8) PanicError![]const u8 {
/// 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
/// IN/OUT 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.
@ -281,8 +284,9 @@ pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) !
defer log.logInfo("Done panic\n", .{});
// Exit if we haven't loaded all debug modules
if (mem_profile.modules.len < 1)
if (mem_profile.modules.len < 1) {
return;
}
var kmap_start: usize = 0;
var kmap_end: usize = 0;
for (mem_profile.modules) |module| {
@ -296,8 +300,9 @@ pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) !
}
// 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)
if (kmap_start == 0 or kmap_end == 0) {
return;
}
var syms = SymbolMap.init(allocator);
errdefer syms.deinit();
@ -307,6 +312,11 @@ pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) !
try syms.addEntry(entry);
}
symbol_map = syms;
switch (build_options.test_mode) {
.Panic => runtimeTests(),
else => {},
}
}
test "parseChar" {
@ -441,7 +451,13 @@ test "SymbolMap" {
testing.expectEqual(map.search(2345), "jkl");
}
///
/// Runtime test for panic. This will trigger a integer overflow.
///
pub fn runtimeTests() void {
@setRuntimeSafety(true);
var x: u8 = 255;
x += 1;
// If we get here, then a panic was not triggered so fail
panic(@errorReturnTrace(), "FAILURE: No integer overflow\n", .{});
}

View file

@ -120,43 +120,12 @@ pub fn init(mem: *const MemProfile, allocator: *std.mem.Allocator) void {
}
}
if (build_options.rt_test) {
runtimeTests(mem, allocator);
switch (build_options.test_mode) {
.Initialisation => runtimeTests(mem, allocator),
else => {},
}
}
///
/// Allocate all blocks and make sure they don't overlap with any reserved addresses.
///
/// Arguments:
/// IN mem: *const MemProfile - The memory profile to check for reserved memory regions.
/// INOUT allocator: *std.mem.Allocator - The allocator to use when needing to create intermediate structures used for testing
///
fn runtimeTests(mem: *const MemProfile, allocator: *std.mem.Allocator) void {
// Make sure that occupied memory can't be allocated
var prev_alloc: usize = std.math.maxInt(usize);
var alloc_list = std.ArrayList(usize).init(allocator);
defer alloc_list.deinit();
while (alloc()) |alloced| {
if (prev_alloc == alloced) {
panic(null, "PMM allocated the same address twice: 0x{x}", .{alloced});
}
prev_alloc = alloced;
for (mem.physical_reserved) |entry| {
var addr = std.mem.alignBackward(entry.start, BLOCK_SIZE);
if (addr == alloced) {
panic(null, "PMM allocated an address that should be reserved by the memory map: 0x{x}", .{addr});
}
}
alloc_list.append(alloced) catch |e| panic(@errorReturnTrace(), "Failed to add PMM allocation to list: {}", .{e});
}
// Clean up
for (alloc_list.items) |alloced| {
free(alloced) catch |e| panic(@errorReturnTrace(), "Failed freeing allocation in PMM rt test: {}", .{e});
}
log.logInfo("PMM: Tested allocation\n", .{});
}
test "alloc" {
bitmap = try Bitmap(u32).init(32, std.heap.page_allocator);
comptime var addr = 0;
@ -227,3 +196,35 @@ test "setAddr and isSet" {
}
}
}
///
/// Allocate all blocks and make sure they don't overlap with any reserved addresses.
///
/// Arguments:
/// IN mem: *const MemProfile - The memory profile to check for reserved memory regions.
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use when needing to create intermediate structures used for testing
///
fn runtimeTests(mem: *const MemProfile, allocator: *std.mem.Allocator) void {
// Make sure that occupied memory can't be allocated
var prev_alloc: usize = std.math.maxInt(usize);
var alloc_list = std.ArrayList(usize).init(allocator);
defer alloc_list.deinit();
while (alloc()) |alloced| {
if (prev_alloc == alloced) {
panic(null, "FAILURE: PMM allocated the same address twice: 0x{x}", .{alloced});
}
prev_alloc = alloced;
for (mem.physical_reserved) |entry| {
var addr = std.mem.alignBackward(@intCast(usize, entry.start), BLOCK_SIZE);
if (addr == alloced) {
panic(null, "FAILURE: PMM allocated an address that should be reserved by the memory map: 0x{x}", .{addr});
}
}
alloc_list.append(alloced) catch |e| panic(@errorReturnTrace(), "FAILURE: Failed to add PMM allocation to list: {}", .{e});
}
// Clean up
for (alloc_list.items) |alloced| {
free(alloced) catch |e| panic(@errorReturnTrace(), "FAILURE: Failed freeing allocation in PMM rt test: {}", .{e});
}
log.logInfo("PMM: Tested allocation\n", .{});
}

View file

@ -31,7 +31,10 @@ pub const Serial = struct {
///
pub fn init(boot_payload: arch.BootPayload) Serial {
const serial = arch.initSerial(boot_payload);
if (build_options.rt_test) runtimeTests(serial);
switch (build_options.test_mode) {
.Initialisation => runtimeTests(serial),
else => {},
}
return serial;
}

View file

@ -64,7 +64,7 @@ pub fn Mapper(comptime Payload: type) type {
/// IN physical_start: usize - The start of the physical memory to map to
/// IN physical_end: usize - The end of the physical memory to map to
/// IN attrs: Attributes - The attributes to apply to this region of memory
/// INOUT allocator: std.mem.Allocator - The allocator to use when mapping, if required
/// IN/OUT allocator: std.mem.Allocator - The allocator to use when mapping, if required
/// IN spec: Payload - The payload to pass to the mapper
///
/// Error: std.mem.AllocatorError || MapperError
@ -73,7 +73,7 @@ pub fn Mapper(comptime Payload: type) type {
mapFn: fn (virtual_start: usize, virtual_end: usize, physical_start: usize, physical_end: usize, attrs: Attributes, allocator: *std.mem.Allocator, spec: Payload) (std.mem.Allocator.Error || MapperError)!void,
///
/// Unmap a region (can span more than one block) of virtual memory from its physical memory. After a call to this function, the memory should not be accesible without error.
/// Unmap a region (can span more than one block) of virtual memory from its physical memory. After a call to this function, the memory should not be accessible without error.
///
/// Arguments:
/// IN virtual_start: usize - The start of the virtual region to unmap
@ -148,7 +148,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Arguments:
/// IN start: usize - The start of the memory region to manage
/// IN end: usize - The end of the memory region to manage. Must be greater than the start
/// INOUT allocator: *std.mem.Allocator - The allocator to use when allocating and freeing regions
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use when allocating and freeing regions
/// IN mapper: Mapper - The mapper to use when allocating and freeing regions
/// IN payload: Payload - The payload data to be passed to the mapper
///
@ -193,13 +193,13 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Map a region (can span more than one block) of virtual memory to a specific region of memory
///
/// Arguments:
/// INOUT self: *Self - The manager to modify
/// IN/OUT self: *Self - The manager to modify
/// IN virtual: mem.Range - The virtual region to set
/// IN physical: ?mem.Range - The physical region to map to or null if only the virtual region is to be set
/// IN attrs: Attributes - The attributes to apply to the memory regions
///
/// Error: VmmError || Bitmap(u32).BitmapError || std.mem.Allocator.Error || MapperError
/// VmmError.AlreadyAllocated - The virtual address has arlready been allocated
/// VmmError.AlreadyAllocated - The virtual address has already been allocated
/// VmmError.PhysicalAlreadyAllocated - The physical address has already been allocated
/// VmmError.PhysicalVirtualMismatch - The physical region and virtual region are of different sizes
/// VmmError.InvalidVirtAddresses - The start virtual address is greater than the end address
@ -256,7 +256,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Allocate a number of contiguous blocks of virtual memory
///
/// Arguments:
/// INOUT self: *Self - The manager to allocate for
/// IN/OUT self: *Self - The manager to allocate for
/// IN num: usize - The number of blocks to allocate
/// IN attrs: Attributes - The attributes to apply to the mapped memory
///
@ -298,7 +298,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Free a previous allocation
///
/// Arguments:
/// INOUT self: *Self - The manager to free within
/// IN/OUT self: *Self - The manager to free within
/// IN vaddr: usize - The start of the allocation to free. This should be the address returned from a prior `alloc` call
///
/// Error: Bitmap.BitmapError || VmmError
@ -316,12 +316,12 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
for (physical.items) |block, i| {
// Clear the address space entry, unmap the virtual memory and free the physical memory
try self.bmp.clearEntry(entry + i);
pmm.free(block) catch |e| panic(@errorReturnTrace(), "Failed to free PMM reserved memory at {x}: {}\n", .{ block * BLOCK_SIZE, e });
pmm.free(block) catch |e| panic(@errorReturnTrace(), "Failed to free PMM reserved memory at 0x{X}: {}\n", .{ block * BLOCK_SIZE, e });
}
// Unmap the entire range
const region_start = entry * BLOCK_SIZE;
const region_end = (entry + num_physical_allocations) * BLOCK_SIZE;
self.mapper.unmapFn(region_start, region_end, self.payload) catch |e| panic(@errorReturnTrace(), "Failed to unmap VMM reserved memory from {x} to {x}: {}\n", .{ region_start, region_end, e });
self.mapper.unmapFn(region_start, region_end, self.payload) catch |e| panic(@errorReturnTrace(), "Failed to unmap VMM reserved memory from 0x{X} to 0x{X}: {}\n", .{ region_start, region_end, e });
// The allocation is freed so remove from the map
self.allocations.removeAssertDiscard(vaddr);
} else {
@ -336,7 +336,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
///
/// Arguments:
/// IN mem_profile: *const mem.MemProfile - The system's memory profile. This is used to find the kernel code region and boot modules
/// INOUT allocator: *std.mem.Allocator - The allocator to use when needing to allocate memory
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use when needing to allocate memory
///
/// Return: VirtualMemoryManager
/// The virtual memory manager created with all reserved virtual regions allocated
@ -367,7 +367,10 @@ pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) s
};
}
if (build_options.rt_test) runtimeTests(arch.VmmPayload, vmm, mem_profile);
switch (build_options.test_mode) {
.Initialisation => runtimeTests(arch.VmmPayload, vmm, mem_profile),
else => {},
}
return vmm;
}
@ -391,7 +394,7 @@ test "alloc and free" {
std.testing.expectEqual(@as(?usize, null), result);
should_be_set = false;
} else {
// Else it should have succedded and allocated the correct address
// Else it should have succeeded and allocated the correct address
std.testing.expectEqual(@as(?usize, vmm.start + entry * BLOCK_SIZE), result);
try virtual_allocations.append(result orelse unreachable);
}
@ -528,7 +531,7 @@ fn testInit(num_entries: u32) std.mem.Allocator.Error!VirtualMemoryManager(u8) {
/// IN pstart: usize - The start of the physical region to map
/// IN pend: usize - The end of the physical region to map
/// IN attrs: Attributes - The attributes to map with
/// INOUT allocator: *std.mem.Allocator - The allocator to use. Ignored
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use. Ignored
/// IN payload: u8 - The payload value. Expected to be 39
///
fn testMap(vstart: usize, vend: usize, pstart: usize, pend: usize, attrs: Attributes, allocator: *std.mem.Allocator, payload: u8) (std.mem.Allocator.Error || MapperError)!void {