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:
parent
d6d99ef667
commit
2c91e6f9d0
28 changed files with 667 additions and 405 deletions
|
@ -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", .{});
|
||||
}
|
||||
|
|
|
@ -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", .{});
|
||||
}
|
||||
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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", .{});
|
||||
}
|
||||
|
||||
|
|
|
@ -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", .{});
|
||||
|
|
|
@ -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", .{});
|
||||
|
|
|
@ -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", .{});
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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", .{});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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", .{});
|
||||
}
|
||||
|
|
|
@ -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", .{});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue