Skip to content

Commit

Permalink
Display contextual information on page fault
Browse files Browse the repository at this point in the history
  • Loading branch information
SamTebbs33 committed Jan 3, 2024
1 parent ff58ced commit 6b85519
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ jobs:
run: zig*/zig build rt-test -Ddisable-display -Dtest-mode=Panic ${{ matrix.build_mode }}
- name: Run runtime test - Scheduler
run: zig*/zig build rt-test -Ddisable-display -Dtest-mode=Scheduler ${{ matrix.build_mode }}
- name: Run runtime test - Memory
run: zig*/zig build rt-test -Ddisable-display -Dtest-mode=Memory ${{ matrix.build_mode }}
15 changes: 15 additions & 0 deletions src/kernel/arch/x86/arch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,21 @@ pub fn runtimeTestCheckUserTaskState(ctx: *const CpuState) bool {
return ctx.eax == 0xCAFE and ctx.ebx == 0xBEEF;
}

///
/// Trigger a page fault to test paging and its diagnostics
///
/// Arguments:
/// IN the_vmm: The VMM to get an unallocated test address from
///
pub fn runtimeTestChecksMem(the_vmm: *const vmm.VirtualMemoryManager(VmmPayload)) void {
var addr = the_vmm.start;
while (addr < the_vmm.end and (the_vmm.isSet(addr) catch unreachable)) {
addr += vmm.BLOCK_SIZE;
}
const should_fault = @intToPtr(*usize, addr).*;
_ = should_fault;
}

test "" {
std.testing.refAllDecls(@This());
}
14 changes: 11 additions & 3 deletions src/kernel/arch/x86/paging.zig
Original file line number Diff line number Diff line change
Expand Up @@ -416,13 +416,20 @@ pub fn unmap(virtual_start: usize, virtual_end: usize, allocator: Allocator, dir
}

///
/// Called when a page fault occurs. This will log the CPU state and control registers.
/// Called when a page fault occurs.
/// This will log the CPU state and control registers as well as some human-readable information.
///
/// Arguments:
/// IN state: *arch.CpuState - The CPU's state when the fault occurred.
///
fn pageFault(state: *arch.CpuState) u32 {
log.info("State: {X}\n", .{state});
const err = state.error_code;
const diag_present = if (err & 0b1 != 0) "present" else "non-present";
const diag_rw = if (err & 0b10 != 0) "writing to" else "reading from";
const diag_ring = if (err & 0b100 != 0) "user" else "kernel";
const diag_reserved = if (err & 0b1000 != 0) " with reserved bit set" else "";
const diag_fetch = if (err & 0b10000 != 0) "instruction" else "data";
log.info("Page fault: {s} process {s} a {s} page during {s} fetch{s}\n", .{ diag_ring, diag_rw, diag_present, diag_fetch, diag_reserved });
var cr0 = asm volatile ("mov %%cr0, %[cr0]"
: [cr0] "=r" (-> u32),
);
Expand All @@ -435,7 +442,8 @@ fn pageFault(state: *arch.CpuState) u32 {
var cr4 = asm volatile ("mov %%cr4, %[cr4]"
: [cr4] "=r" (-> u32),
);
log.info("CR0: 0x{X}, CR2: 0x{X}, CR3: 0x{X}, CR4: 0x{X}\n", .{ cr0, cr2, cr3, cr4 });
log.info("CR0: 0x{X}, CR2/address: 0x{X}, CR3: 0x{X}, CR4: 0x{X}, EIP: 0x{X}\n", .{ cr0, cr2, cr3, cr4, state.eip });
log.info("State: {X}\n", .{state});
@panic("Page fault");
}

Expand Down
3 changes: 2 additions & 1 deletion src/kernel/kmain.zig
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ export fn kmain(boot_payload: arch.BootPayload) void {
panic_root.panic(@errorReturnTrace(), "Failed to initialise panic symbols: {}\n", .{e});
};

// The VMM runtime tests can't happen until the architecture has initialised itself
// The VMM and mem runtime tests can't happen until the architecture has initialised itself
switch (build_options.test_mode) {
.Initialisation => vmm.runtimeTests(arch.VmmPayload, kernel_vmm, &mem_profile),
.Memory => arch.runtimeTestChecksMem(kernel_vmm),
else => {},
}

Expand Down
27 changes: 27 additions & 0 deletions test/runtime_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub const TestMode = enum {
/// Run the scheduler runtime test.
Scheduler,

/// Run the memory runtime test.
Memory,

///
/// Return a string description for the test mode provided.
///
Expand All @@ -45,6 +48,7 @@ pub const TestMode = enum {
.Initialisation => "Initialisation runtime tests",
.Panic => "Panic runtime tests",
.Scheduler => "Scheduler runtime tests",
.Memory => "Memory runtime tests",
};
}
};
Expand Down Expand Up @@ -178,6 +182,28 @@ pub const RuntimeStep = struct {
}
}

///
/// This tests the kernel's memory system and makes sure the expected diagnostics are printed.
///
/// Arguments:
/// IN/OUT self: *RuntimeStep - Self.
///
/// Return: bool
/// Whether the test has passed or failed.
///
fn test_mem(self: *RuntimeStep) bool {
while (true) {
const msg = self.get_msg() catch return false;
defer self.builder.allocator.free(msg);

std.debug.print("{s}\n", .{msg});

if (std.mem.eql(u8, msg, "[info] (x86_paging): Page fault: kernel process reading from a non-present page during data fetch")) {
return true;
}
}
}

///
/// The make function that is called by the builder. This will create a qemu process with the
/// stdout as a Pipe. Then create the read thread to read the logs from the qemu stdout. Then
Expand Down Expand Up @@ -309,6 +335,7 @@ pub const RuntimeStep = struct {
.Initialisation => test_init,
.Panic => test_panic,
.Scheduler => test_scheduler,
.Memory => test_mem,
},
};
return runtime_step;
Expand Down

0 comments on commit 6b85519

Please sign in to comment.