Added unit and runtime tests
Also fixed some typos Also changed the panic a bit Removed changed to unnecessary files Fixed merge Feedback Fixed tests
This commit is contained in:
parent
d306078c62
commit
42bdbf6b7f
10 changed files with 438 additions and 159 deletions
|
@ -238,6 +238,7 @@ test "" {
|
||||||
_ = @import("gdt.zig");
|
_ = @import("gdt.zig");
|
||||||
_ = @import("idt.zig");
|
_ = @import("idt.zig");
|
||||||
_ = @import("pic.zig");
|
_ = @import("pic.zig");
|
||||||
|
_ = @import("isr.zig");
|
||||||
_ = @import("syscalls.zig");
|
_ = @import("syscalls.zig");
|
||||||
_ = @import("paging.zig");
|
_ = @import("paging.zig");
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("ar
|
||||||
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
||||||
|
|
||||||
/// The structure that contains all the information that each IDT entry needs.
|
/// The structure that contains all the information that each IDT entry needs.
|
||||||
const IdtEntry = packed struct {
|
pub const IdtEntry = packed struct {
|
||||||
/// The lower 16 bits of the base address of the interrupt handler offset.
|
/// The lower 16 bits of the base address of the interrupt handler offset.
|
||||||
base_low: u16,
|
base_low: u16,
|
||||||
|
|
||||||
|
@ -88,12 +88,12 @@ const PRIVILEGE_RING_2: u2 = 0x2;
|
||||||
/// Privilege level 3. User land. The privilege level the calling descriptor minimum will have.
|
/// Privilege level 3. User land. The privilege level the calling descriptor minimum will have.
|
||||||
const PRIVILEGE_RING_3: u2 = 0x3;
|
const PRIVILEGE_RING_3: u2 = 0x3;
|
||||||
|
|
||||||
/// The total number of entries the IDT can have (2^8).
|
|
||||||
const NUMBER_OF_ENTRIES: u16 = 256;
|
|
||||||
|
|
||||||
/// The total size of all the IDT entries (minus 1).
|
/// The total size of all the IDT entries (minus 1).
|
||||||
const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1;
|
const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1;
|
||||||
|
|
||||||
|
/// The total number of entries the IDT can have (2^8).
|
||||||
|
pub const NUMBER_OF_ENTRIES: u16 = 256;
|
||||||
|
|
||||||
/// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the
|
/// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the
|
||||||
/// size.
|
/// size.
|
||||||
var idt_ptr: IdtPtr = IdtPtr{
|
var idt_ptr: IdtPtr = IdtPtr{
|
||||||
|
@ -150,7 +150,7 @@ fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2) IdtEntry {
|
||||||
/// Return: bool
|
/// Return: bool
|
||||||
/// Whether the provided IDT entry is open or not.
|
/// Whether the provided IDT entry is open or not.
|
||||||
///
|
///
|
||||||
fn isIdtOpen(entry: IdtEntry) bool {
|
pub fn isIdtOpen(entry: IdtEntry) bool {
|
||||||
return entry.present == 1;
|
return entry.present == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,31 @@
|
||||||
// Zig version: 0.4.0
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
const panic = @import("../../panic.zig").panic;
|
const is_test = builtin.is_test;
|
||||||
const idt = @import("idt.zig");
|
const expect = std.testing.expect;
|
||||||
const arch = @import("arch.zig");
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
const expectError = std.testing.expectError;
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const mock_path = build_options.arch_mock_path;
|
||||||
const syscalls = @import("syscalls.zig");
|
const syscalls = @import("syscalls.zig");
|
||||||
|
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic;
|
||||||
|
const idt = if (is_test) @import(mock_path ++ "idt_mock.zig") else @import("idt.zig");
|
||||||
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
||||||
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
||||||
|
|
||||||
const NUMBER_OF_ENTRIES: u16 = 32;
|
/// The error set for the ISR. This will be from installing a ISR handler.
|
||||||
|
pub const IsrError = error{
|
||||||
|
/// The ISR index is invalid.
|
||||||
|
InvalidIsr,
|
||||||
|
|
||||||
// The external assembly that is fist called to set up the exception handler.
|
/// A ISR handler already exists.
|
||||||
extern fn isr0() void;
|
IsrExists,
|
||||||
extern fn isr1() void;
|
};
|
||||||
extern fn isr2() void;
|
|
||||||
extern fn isr3() void;
|
/// The type of a ISR handler. A function that takes a interrupt context and returns void.
|
||||||
extern fn isr4() void;
|
const IsrHandler = fn (*arch.InterruptContext) void;
|
||||||
extern fn isr5() void;
|
|
||||||
extern fn isr6() void;
|
/// The number of ISR entries.
|
||||||
extern fn isr7() void;
|
const NUMBER_OF_ENTRIES: u8 = 32;
|
||||||
extern fn isr8() void;
|
|
||||||
extern fn isr9() void;
|
|
||||||
extern fn isr10() void;
|
|
||||||
extern fn isr11() void;
|
|
||||||
extern fn isr12() void;
|
|
||||||
extern fn isr13() void;
|
|
||||||
extern fn isr14() void;
|
|
||||||
extern fn isr15() void;
|
|
||||||
extern fn isr16() void;
|
|
||||||
extern fn isr17() void;
|
|
||||||
extern fn isr18() void;
|
|
||||||
extern fn isr19() void;
|
|
||||||
extern fn isr20() void;
|
|
||||||
extern fn isr21() void;
|
|
||||||
extern fn isr22() void;
|
|
||||||
extern fn isr23() void;
|
|
||||||
extern fn isr24() void;
|
|
||||||
extern fn isr25() void;
|
|
||||||
extern fn isr26() void;
|
|
||||||
extern fn isr27() void;
|
|
||||||
extern fn isr28() void;
|
|
||||||
extern fn isr29() void;
|
|
||||||
extern fn isr30() void;
|
|
||||||
extern fn isr31() void;
|
|
||||||
extern fn isr128() void;
|
|
||||||
|
|
||||||
/// The exception messaged that is printed when a exception happens
|
/// The exception messaged that is printed when a exception happens
|
||||||
const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const u8{
|
const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const u8{
|
||||||
|
@ -78,55 +63,139 @@ const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const
|
||||||
"Reserved",
|
"Reserved",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Errors that an isr function can return
|
/// Divide By Zero exception.
|
||||||
pub const IsrError = error{UnrecognisedIsr};
|
pub const DIVIDE_BY_ZERO: u8 = 0;
|
||||||
|
|
||||||
/// An isr handler. Takes an interrupt context and returns void.
|
/// Single Step (Debugger) exception.
|
||||||
/// Should finish quickly to avoid delaying further interrupts and the previously running code
|
pub const SINGLE_STEP_DEBUG: u8 = 1;
|
||||||
pub const IsrHandler = fn (*arch.InterruptContext) void;
|
|
||||||
|
|
||||||
// The of exception handlers initialised to unhandled.
|
/// Non Maskable Interrupt exception.
|
||||||
var isr_handlers: [NUMBER_OF_ENTRIES]IsrHandler = [_]IsrHandler{unhandled} ** NUMBER_OF_ENTRIES;
|
pub const NON_MASKABLE_INTERRUPT: u8 = 2;
|
||||||
var syscall_handler: IsrHandler = unhandled;
|
|
||||||
|
|
||||||
///
|
/// Breakpoint (Debugger) exception.
|
||||||
/// A dummy handler that will make a call to panic as it is a unhandled exception.
|
pub const BREAKPOINT_DEBUG: u8 = 3;
|
||||||
///
|
|
||||||
/// Arguments:
|
|
||||||
/// IN context: *arch.InterruptContext - Pointer to the exception context containing the
|
|
||||||
/// contents of the register at the time of the exception.
|
|
||||||
///
|
|
||||||
fn unhandled(context: *arch.InterruptContext) void {
|
|
||||||
const interrupt_num = context.int_num;
|
|
||||||
panic(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
/// Overflow exception.
|
||||||
/// Checks if the isr is valid and returns true if it is, else false.
|
pub const OVERFLOW: u8 = 4;
|
||||||
/// To be valid it must be greater than or equal to 0 and less than NUMBER_OF_ENTRIES.
|
|
||||||
///
|
/// Bound Range Exceeded exception.
|
||||||
/// Arguments:
|
pub const BOUND_RANGE_EXCEEDED: u8 = 5;
|
||||||
/// IN isr_num: u16 - The isr number to check
|
|
||||||
///
|
/// Invalid Opcode exception.
|
||||||
pub fn isValidIsr(isr_num: u32) bool {
|
pub const INVALID_OPCODE: u8 = 6;
|
||||||
return isr_num >= 0 and isr_num < NUMBER_OF_ENTRIES;
|
|
||||||
}
|
/// No Coprocessor, Device Not Available exception.
|
||||||
|
pub const DEVICE_NOT_AVAILABLE: u8 = 7;
|
||||||
|
|
||||||
|
/// Double Fault exception.
|
||||||
|
pub const DOUBLE_FAULT: u8 = 8;
|
||||||
|
|
||||||
|
/// Coprocessor Segment Overrun exception.
|
||||||
|
pub const COPROCESSOR_SEGMENT_OVERRUN: u8 = 9;
|
||||||
|
|
||||||
|
/// Invalid Task State Segment (TSS) exception.
|
||||||
|
pub const INVALID_TASK_STATE_SEGMENT: u8 = 10;
|
||||||
|
|
||||||
|
/// Segment Not Present exception.
|
||||||
|
pub const SEGMENT_NOT_PRESENT: u8 = 11;
|
||||||
|
|
||||||
|
/// Stack Segment Overrun exception.
|
||||||
|
pub const STACK_SEGMENT_FAULT: u8 = 12;
|
||||||
|
|
||||||
|
/// General Protection Fault exception.
|
||||||
|
pub const GENERAL_PROTECTION_FAULT: u8 = 13;
|
||||||
|
|
||||||
|
/// Page Fault exception.
|
||||||
|
pub const PAGE_FAULT: u8 = 14;
|
||||||
|
|
||||||
|
/// x87 FPU Floating Point Error exception.
|
||||||
|
pub const X87_FLOAT_POINT: u8 = 16;
|
||||||
|
|
||||||
|
/// Alignment Check exception.
|
||||||
|
pub const ALIGNMENT_CHECK: u8 = 17;
|
||||||
|
|
||||||
|
/// Machine Check exception.
|
||||||
|
pub const MACHINE_CHECK: u8 = 18;
|
||||||
|
|
||||||
|
/// SIMD Floating Point exception.
|
||||||
|
pub const SIMD_FLOAT_POINT: u8 = 19;
|
||||||
|
|
||||||
|
/// Virtualisation exception.
|
||||||
|
pub const VIRTUALISATION: u8 = 20;
|
||||||
|
|
||||||
|
/// Security exception.
|
||||||
|
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.
|
||||||
|
var syscall_handler: ?IsrHandler = null;
|
||||||
|
|
||||||
|
// The external assembly that is fist called to set up the exception handler.
|
||||||
|
extern fn isr0() void;
|
||||||
|
extern fn isr1() void;
|
||||||
|
extern fn isr2() void;
|
||||||
|
extern fn isr3() void;
|
||||||
|
extern fn isr4() void;
|
||||||
|
extern fn isr5() void;
|
||||||
|
extern fn isr6() void;
|
||||||
|
extern fn isr7() void;
|
||||||
|
extern fn isr8() void;
|
||||||
|
extern fn isr9() void;
|
||||||
|
extern fn isr10() void;
|
||||||
|
extern fn isr11() void;
|
||||||
|
extern fn isr12() void;
|
||||||
|
extern fn isr13() void;
|
||||||
|
extern fn isr14() void;
|
||||||
|
extern fn isr15() void;
|
||||||
|
extern fn isr16() void;
|
||||||
|
extern fn isr17() void;
|
||||||
|
extern fn isr18() void;
|
||||||
|
extern fn isr19() void;
|
||||||
|
extern fn isr20() void;
|
||||||
|
extern fn isr21() void;
|
||||||
|
extern fn isr22() void;
|
||||||
|
extern fn isr23() void;
|
||||||
|
extern fn isr24() void;
|
||||||
|
extern fn isr25() void;
|
||||||
|
extern fn isr26() void;
|
||||||
|
extern fn isr27() void;
|
||||||
|
extern fn isr28() void;
|
||||||
|
extern fn isr29() void;
|
||||||
|
extern fn isr30() void;
|
||||||
|
extern fn isr31() void;
|
||||||
|
extern fn isr128() void;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The exception handler that each of the exceptions will call when a exception happens.
|
/// The exception handler that each of the exceptions will call when a exception happens.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN context: *arch.InterruptContext - Pointer to the exception context containing the
|
/// IN ctx: *arch.InterruptContext - Pointer to the exception context containing the contents
|
||||||
/// contents of the register at the time of the exception.
|
/// of the register at the time of the exception.
|
||||||
///
|
///
|
||||||
export fn isrHandler(context: *arch.InterruptContext) void {
|
export fn isrHandler(ctx: *arch.InterruptContext) void {
|
||||||
const isr_num = context.int_num;
|
// Get the interrupt number
|
||||||
if (isr_num == syscalls.INTERRUPT) {
|
const isr_num = ctx.int_num;
|
||||||
syscall_handler(context);
|
|
||||||
} else if (isValidIsr(isr_num)) {
|
if (isValidIsr(isr_num)) {
|
||||||
isr_handlers[isr_num](context);
|
if (isr_num == syscalls.INTERRUPT) {
|
||||||
|
// A syscall, so use the syscall handler
|
||||||
|
if (syscall_handler) |handler| {
|
||||||
|
handler(ctx);
|
||||||
|
} else {
|
||||||
|
panic(@errorReturnTrace(), "Syscall handler not registered\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isr_handlers[isr_num]) |handler| {
|
||||||
|
// Regular ISR exception, if there is one registered.
|
||||||
|
handler(ctx);
|
||||||
|
} else {
|
||||||
|
panic(@errorReturnTrace(), "ISR not registered to: {}-{}\n", isr_num, exception_msg[isr_num]);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
panic(null, "Unrecognised isr: {}\n", isr_num);
|
panic(@errorReturnTrace(), "Invalid ISR index: {}\n", isr_num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,45 +209,67 @@ export fn isrHandler(context: *arch.InterruptContext) void {
|
||||||
fn openIsr(index: u8, handler: idt.InterruptHandler) void {
|
fn openIsr(index: u8, handler: idt.InterruptHandler) void {
|
||||||
idt.openInterruptGate(index, handler) catch |err| switch (err) {
|
idt.openInterruptGate(index, handler) catch |err| switch (err) {
|
||||||
error.IdtEntryExists => {
|
error.IdtEntryExists => {
|
||||||
panic(@errorReturnTrace(), "Error opening ISR number: {} exists", index);
|
panic(@errorReturnTrace(), "Error opening ISR number: {} exists\n", index);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Checks if the isr is valid and returns true if it is, else false.
|
||||||
|
/// To be valid it must be greater than or equal to 0 and less than NUMBER_OF_ENTRIES.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN isr_num: u16 - The isr number to check
|
||||||
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// Whether a ISR hander index if valid.
|
||||||
|
///
|
||||||
|
pub fn isValidIsr(isr_num: u32) bool {
|
||||||
|
return isr_num < NUMBER_OF_ENTRIES or isr_num == syscalls.INTERRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Register an exception by setting its exception handler to the given function.
|
/// Register an exception by setting its exception handler to the given function.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN irq_num: u16 - The exception number to register.
|
/// IN irq_num: u16 - The exception number to register.
|
||||||
///
|
///
|
||||||
/// Errors:
|
/// Errors: IsrError
|
||||||
/// IsrError.UnrecognisedIsr - If `isr_num` is invalid (see isValidIsr)
|
/// IsrError.InvalidIsr - If the ISR index is invalid (see isValidIsr).
|
||||||
|
/// IsrError.IsrExists - If the ISR handler has already been registered.
|
||||||
///
|
///
|
||||||
pub fn registerIsr(isr_num: u16, handler: fn (*arch.InterruptContext) void) !void {
|
pub fn registerIsr(isr_num: u16, handler: IsrHandler) IsrError!void {
|
||||||
if (isr_num == syscalls.INTERRUPT) {
|
// Check if a valid ISR index
|
||||||
syscall_handler = handler;
|
if (isValidIsr(isr_num)) {
|
||||||
} else if (isValidIsr(isr_num)) {
|
if (isr_num == syscalls.INTERRUPT) {
|
||||||
isr_handlers[isr_num] = handler;
|
// Syscall handler
|
||||||
|
if (syscall_handler) |_| {
|
||||||
|
// One already registered
|
||||||
|
return IsrError.IsrExists;
|
||||||
|
} else {
|
||||||
|
// Register a handler
|
||||||
|
syscall_handler = handler;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isr_handlers[isr_num]) |_| {
|
||||||
|
// One already registered
|
||||||
|
return IsrError.IsrExists;
|
||||||
|
} else {
|
||||||
|
// Register a handler
|
||||||
|
isr_handlers[isr_num] = handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return IsrError.UnrecognisedIsr;
|
return IsrError.InvalidIsr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Unregister an exception by setting its exception handler to the unhandled function call to
|
|
||||||
/// panic.
|
|
||||||
///
|
|
||||||
/// Arguments:
|
|
||||||
/// IN irq_num: u16 - The exception number to unregister.
|
|
||||||
///
|
|
||||||
pub fn unregisterIsr(isr_num: u16) void {
|
|
||||||
isr_handlers[isr_num] = unhandled;
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Initialise the exception and opening up all the IDT interrupt gates for each exception.
|
/// Initialise the exception and opening up all the IDT interrupt gates for each exception.
|
||||||
///
|
///
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
|
log.logInfo("Init isr\n");
|
||||||
|
|
||||||
openIsr(0, isr0);
|
openIsr(0, isr0);
|
||||||
openIsr(1, isr1);
|
openIsr(1, isr1);
|
||||||
openIsr(2, isr2);
|
openIsr(2, isr2);
|
||||||
|
@ -212,4 +303,160 @@ pub fn init() void {
|
||||||
openIsr(30, isr30);
|
openIsr(30, isr30);
|
||||||
openIsr(31, isr31);
|
openIsr(31, isr31);
|
||||||
openIsr(syscalls.INTERRUPT, isr128);
|
openIsr(syscalls.INTERRUPT, isr128);
|
||||||
|
|
||||||
|
log.logInfo("Done\n");
|
||||||
|
|
||||||
|
if (build_options.rt_test) runtimeTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn testFunction0() void {}
|
||||||
|
fn testFunction1(ctx: *arch.InterruptContext) void {}
|
||||||
|
fn testFunction2(ctx: *arch.InterruptContext) void {}
|
||||||
|
fn testFunction3(ctx: *arch.InterruptContext) void {}
|
||||||
|
fn testFunction4(ctx: *arch.InterruptContext) void {}
|
||||||
|
|
||||||
|
test "openIsr" {
|
||||||
|
idt.initTest();
|
||||||
|
defer idt.freeTest();
|
||||||
|
|
||||||
|
const index = u8(0);
|
||||||
|
const handler = testFunction0;
|
||||||
|
const ret: idt.IdtError!void = {};
|
||||||
|
|
||||||
|
idt.addTestParams("openInterruptGate", index, handler, ret);
|
||||||
|
|
||||||
|
openIsr(index, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "isValidIsr" {
|
||||||
|
comptime var i = 0;
|
||||||
|
inline while (i < NUMBER_OF_ENTRIES) : (i += 1) {
|
||||||
|
expectEqual(true, isValidIsr(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
expectEqual(true, isValidIsr(syscalls.INTERRUPT));
|
||||||
|
|
||||||
|
expectEqual(false, isValidIsr(200));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "registerIsr re-register syscall handler" {
|
||||||
|
// Pre testing
|
||||||
|
expect(null == syscall_handler);
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
try registerIsr(syscalls.INTERRUPT, testFunction3);
|
||||||
|
expectError(IsrError.IsrExists, registerIsr(syscalls.INTERRUPT, testFunction4));
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
expectEqual(testFunction3, syscall_handler.?);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
syscall_handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "registerIsr register syscall handler" {
|
||||||
|
// Pre testing
|
||||||
|
expect(null == syscall_handler);
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
try registerIsr(syscalls.INTERRUPT, testFunction3);
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
expectEqual(testFunction3, syscall_handler.?);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
syscall_handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "registerIsr re-register isr handler" {
|
||||||
|
// Pre testing
|
||||||
|
for (isr_handlers) |h| {
|
||||||
|
expect(null == h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
try registerIsr(0, testFunction1);
|
||||||
|
expectError(IsrError.IsrExists, registerIsr(0, testFunction2));
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
for (isr_handlers) |h, i| {
|
||||||
|
if (i != 0) {
|
||||||
|
expect(null == h);
|
||||||
|
} else {
|
||||||
|
expectEqual(testFunction1, h.?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
isr_handlers[0] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "registerIsr register isr handler" {
|
||||||
|
// Pre testing
|
||||||
|
for (isr_handlers) |h| {
|
||||||
|
expect(null == h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
try registerIsr(0, testFunction1);
|
||||||
|
|
||||||
|
// Post testing
|
||||||
|
for (isr_handlers) |h, i| {
|
||||||
|
if (i != 0) {
|
||||||
|
expect(null == h);
|
||||||
|
} else {
|
||||||
|
expectEqual(testFunction1, h.?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
isr_handlers[0] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "registerIsr invalid isr index" {
|
||||||
|
expectError(IsrError.InvalidIsr, registerIsr(200, testFunction1));
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Test that all handers 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syscall_handler) |h| {
|
||||||
|
panic(@errorReturnTrace(), "Pre-testing failed for syscall: {}\n", h);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.logInfo("ISR: Tested registered handlers\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Test that all IDT entries for the ISRs are open.
|
||||||
|
///
|
||||||
|
fn rt_openedIdtEntries() void {
|
||||||
|
const loaded_idt = arch.sidt();
|
||||||
|
const idt_entries = @intToPtr([*]idt.IdtEntry, loaded_idt.base)[0..idt.NUMBER_OF_ENTRIES];
|
||||||
|
|
||||||
|
for (idt_entries) |entry, i| {
|
||||||
|
if (isValidIsr(i)) {
|
||||||
|
if (!idt.isIdtOpen(entry)) {
|
||||||
|
panic(@errorReturnTrace(), "IDT entry for {} is not open\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.logInfo("ISR: Tested opened IDT entries\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run all the runtime tests.
|
||||||
|
///
|
||||||
|
fn runtimeTests() void {
|
||||||
|
rt_unregisteredHandlers();
|
||||||
|
rt_openedIdtEntries();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,10 @@ const PagingError = error{
|
||||||
/// Physical and virtual addresses don't cover spaces of the same size.
|
/// Physical and virtual addresses don't cover spaces of the same size.
|
||||||
PhysicalVirtualMismatch,
|
PhysicalVirtualMismatch,
|
||||||
|
|
||||||
/// Physical addressses aren't aligned by page size.
|
/// Physical addresses aren't aligned by page size.
|
||||||
UnalignedPhysAddresses,
|
UnalignedPhysAddresses,
|
||||||
|
|
||||||
/// Virtual addressses aren't aligned by page size.
|
/// Virtual addresses aren't aligned by page size.
|
||||||
UnalignedVirtAddresses,
|
UnalignedVirtAddresses,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -301,7 +301,7 @@ fn mapDir(dir: *Directory, virt_start: usize, virt_end: usize, phys_start: usize
|
||||||
/// Called when a page fault occurs.
|
/// Called when a page fault occurs.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN state: *arch.InterruptContext - The CPU's state when the fault occured.
|
/// IN state: *arch.InterruptContext - The CPU's state when the fault occurred.
|
||||||
///
|
///
|
||||||
fn pageFault(state: *arch.InterruptContext) void {
|
fn pageFault(state: *arch.InterruptContext) void {
|
||||||
@panic("Page fault");
|
@panic("Page fault");
|
||||||
|
@ -334,7 +334,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void
|
||||||
panic(@errorReturnTrace(), "Failed to map kernel directory: {}\n", e);
|
panic(@errorReturnTrace(), "Failed to map kernel directory: {}\n", e);
|
||||||
};
|
};
|
||||||
const tty_addr = tty.getVideoBufferAddress();
|
const tty_addr = tty.getVideoBufferAddress();
|
||||||
// If the previous mappping space didn't cover the tty buffer, do so now
|
// If the previous mapping space didn't cover the tty buffer, do so now
|
||||||
if (v_start > tty_addr or v_end <= tty_addr) {
|
if (v_start > tty_addr or v_end <= tty_addr) {
|
||||||
const tty_phys = virtToPhys(tty_addr);
|
const tty_phys = virtToPhys(tty_addr);
|
||||||
const tty_buff_size = 32 * 1024;
|
const tty_buff_size = 32 * 1024;
|
||||||
|
@ -348,7 +348,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void
|
||||||
:
|
:
|
||||||
: [addr] "{eax}" (dir_physaddr)
|
: [addr] "{eax}" (dir_physaddr)
|
||||||
);
|
);
|
||||||
isr.registerIsr(14, if (options.rt_test) rt_pageFault else pageFault) catch |e| {
|
isr.registerIsr(isr.PAGE_FAULT, if (options.rt_test) rt_pageFault else pageFault) catch |e| {
|
||||||
panic(@errorReturnTrace(), "Failed to register page fault ISR: {}\n", e);
|
panic(@errorReturnTrace(), "Failed to register page fault ISR: {}\n", e);
|
||||||
};
|
};
|
||||||
log.logInfo("Done\n");
|
log.logInfo("Done\n");
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
// Zig version: 0.4.0
|
|
||||||
|
|
||||||
const arch = @import("arch.zig");
|
const arch = @import("arch.zig");
|
||||||
const assert = @import("std").debug.assert;
|
const assert = @import("std").debug.assert;
|
||||||
const irq = @import("irq.zig");
|
const irq = @import("irq.zig");
|
||||||
const pic = @import("pic.zig");
|
const pic = @import("pic.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
|
const panic = @import("../../panic.zig").panic;
|
||||||
|
|
||||||
// The port addresses of the PIT registers
|
// The port addresses of the PIT registers
|
||||||
|
|
||||||
/// The port address for the PIT data register for counter 0. This is going to be used as the
|
/// The port address for the PIT data register for counter 0. This is going to be used as the
|
||||||
/// system clock.
|
/// system clock.
|
||||||
const COUNTER_0_REGISTER: u16 = 0x40;
|
const COUNTER_0_REGISTER: u16 = 0x40;
|
||||||
|
|
||||||
/// The port address for the PIT data register for counter 1. This was used for refreshing the DRAM
|
/// The port address for the PIT data register for counter 1. This was used for refreshing the DRAM
|
||||||
/// chips. But now is unused and unknown use, so won't use.
|
/// chips. But now is unused and unknown use, so won't use.
|
||||||
const COUNTER_1_REGISTER: u16 = 0x41;
|
const COUNTER_1_REGISTER: u16 = 0x41;
|
||||||
|
|
||||||
/// The port address for the PIT data register for counter 2. Connected to the PC speakers, we'll
|
/// The port address for the PIT data register for counter 2. Connected to the PC speakers, we'll
|
||||||
/// use this for the speakers.
|
/// use this for the speakers.
|
||||||
const COUNTER_2_REGISTER: u16 = 0x42;
|
const COUNTER_2_REGISTER: u16 = 0x42;
|
||||||
|
|
||||||
/// The port address for the PIT control word register. Used to tell the PIT controller what is
|
/// The port address for the PIT control word register. Used to tell the PIT controller what is
|
||||||
/// about to happen. Tell what data is going where, lower or upper part of it's registers.
|
/// about to happen. Tell what data is going where, lower or upper part of it's registers.
|
||||||
const COMMAND_REGISTER: u16 = 0x43;
|
const COMMAND_REGISTER: u16 = 0x43;
|
||||||
|
|
||||||
// The operational command word marks for the different modes.
|
// The operational command word marks for the different modes.
|
||||||
//
|
//
|
||||||
|
@ -49,62 +49,62 @@ const COMMAND_REGISTER: u16 = 0x43;
|
||||||
// 11: Illegal value.
|
// 11: Illegal value.
|
||||||
|
|
||||||
/// Have the counter count in binary (internally?).
|
/// Have the counter count in binary (internally?).
|
||||||
const OCW_BINARY_COUNT_BINARY: u8 = 0x00; // xxxxxxx0
|
const OCW_BINARY_COUNT_BINARY: u8 = 0x00; // xxxxxxx0
|
||||||
|
|
||||||
/// Have the counter count in BCD (internally?).
|
/// Have the counter count in BCD (internally?).
|
||||||
const OCW_BINARY_COUNT_BCD: u8 = 0x01; // xxxxxxx1
|
const OCW_BINARY_COUNT_BCD: u8 = 0x01; // xxxxxxx1
|
||||||
|
|
||||||
/// The PIT counter will be programmed with an initial COUNT value that counts down at a rate of
|
/// The PIT counter will be programmed with an initial COUNT value that counts down at a rate of
|
||||||
/// the input clock frequency. When the COUNT reaches 0, and after the control word is written,
|
/// the input clock frequency. When the COUNT reaches 0, and after the control word is written,
|
||||||
/// then its OUT pit is set high (1). Count down starts then the COUNT is set. The OUT pin remains
|
/// then its OUT pit is set high (1). Count down starts then the COUNT is set. The OUT pin remains
|
||||||
/// high until the counter is reloaded with a new COUNT value or a new control work is written.
|
/// high until the counter is reloaded with a new COUNT value or a new control work is written.
|
||||||
const OCW_MODE_TERMINAL_COUNT: u8 = 0x00; // xxxx000x
|
const OCW_MODE_TERMINAL_COUNT: u8 = 0x00; // xxxx000x
|
||||||
|
|
||||||
/// The counter is programmed to output a pulse every curtain number of clock pulses. The OUT pin
|
/// The counter is programmed to output a pulse every curtain number of clock pulses. The OUT pin
|
||||||
/// remains high as soon as a control word is written. When COUNT is written, the counter waits
|
/// remains high as soon as a control word is written. When COUNT is written, the counter waits
|
||||||
/// until the rising edge of the GATE pin to start. One clock pulse after the GATE pin, the OUT
|
/// until the rising edge of the GATE pin to start. One clock pulse after the GATE pin, the OUT
|
||||||
/// pin will remain low until COUNT reaches 0.
|
/// pin will remain low until COUNT reaches 0.
|
||||||
const OCW_MODE_ONE_SHOT: u8 = 0x02; // xxxx001x
|
const OCW_MODE_ONE_SHOT: u8 = 0x02; // xxxx001x
|
||||||
|
|
||||||
/// The counter is initiated with a COUNT value. Counting starts next clock pulse. OUT pin remains
|
/// The counter is initiated with a COUNT value. Counting starts next clock pulse. OUT pin remains
|
||||||
/// high until COUNT reaches 1, then is set low for one clock pulse. Then COUNT is reset back to
|
/// high until COUNT reaches 1, then is set low for one clock pulse. Then COUNT is reset back to
|
||||||
/// initial value and OUT pin is set high again.
|
/// initial value and OUT pin is set high again.
|
||||||
const OCW_MODE_RATE_GENERATOR: u8 = 0x04; // xxxx010x
|
const OCW_MODE_RATE_GENERATOR: u8 = 0x04; // xxxx010x
|
||||||
|
|
||||||
/// Similar to PIT_OCW_MODE_RATE_GENERATOR, but OUT pin will be high for half the time and low for
|
/// Similar to PIT_OCW_MODE_RATE_GENERATOR, but OUT pin will be high for half the time and low for
|
||||||
/// half the time. Good for the speaker when setting a tone.
|
/// half the time. Good for the speaker when setting a tone.
|
||||||
const OCW_MODE_SQUARE_WAVE_GENERATOR: u8 = 0x06; // xxxx011x
|
const OCW_MODE_SQUARE_WAVE_GENERATOR: u8 = 0x06; // xxxx011x
|
||||||
|
|
||||||
/// The counter is initiated with a COUNT value. Counting starts on next clock pulse. OUT pin remains
|
/// The counter is initiated with a COUNT value. Counting starts on next clock pulse. OUT pin remains
|
||||||
/// high until count is 0. Then OUT pin is low for one clock pulse. Then resets to high again.
|
/// high until count is 0. Then OUT pin is low for one clock pulse. Then resets to high again.
|
||||||
const OCW_MODE_SOFTWARE_TRIGGER: u8 = 0x08; // xxxx100x
|
const OCW_MODE_SOFTWARE_TRIGGER: u8 = 0x08; // xxxx100x
|
||||||
|
|
||||||
/// The counter is initiated with a COUNT value. OUT pin remains high until the rising edge of the
|
/// The counter is initiated with a COUNT value. OUT pin remains high until the rising edge of the
|
||||||
/// GATE pin. Then the counting begins. When COUNT reaches 0, OUT pin goes low for one clock pulse.
|
/// GATE pin. Then the counting begins. When COUNT reaches 0, OUT pin goes low for one clock pulse.
|
||||||
/// Then COUNT is reset and OUT pin goes high. This cycles for each rising edge of the GATE pin.
|
/// Then COUNT is reset and OUT pin goes high. This cycles for each rising edge of the GATE pin.
|
||||||
const OCW_MODE_HARDWARE_TRIGGER: u8 = 0x0A; // xxxx101x
|
const OCW_MODE_HARDWARE_TRIGGER: u8 = 0x0A; // xxxx101x
|
||||||
|
|
||||||
/// The counter value is latched into an internal control register at the time of the I/O write
|
/// The counter value is latched into an internal control register at the time of the I/O write
|
||||||
/// operations.
|
/// operations.
|
||||||
const OCW_READ_LOAD_LATCH: u8 = 0x00; // xx00xxxx
|
const OCW_READ_LOAD_LATCH: u8 = 0x00; // xx00xxxx
|
||||||
|
|
||||||
/// Read or load the most significant bit only.
|
/// Read or load the most significant bit only.
|
||||||
const OCW_READ_LOAD_LSB_ONLY: u8 = 0x10; // xx01xxxx
|
const OCW_READ_LOAD_LSB_ONLY: u8 = 0x10; // xx01xxxx
|
||||||
|
|
||||||
/// Read or load the least significant bit only.
|
/// Read or load the least significant bit only.
|
||||||
const OCW_READ_LOAD_MSB_ONLY: u8 = 0x20; // xx10xxxx
|
const OCW_READ_LOAD_MSB_ONLY: u8 = 0x20; // xx10xxxx
|
||||||
|
|
||||||
/// Read or load the least significant bit first then the most significant bit.
|
/// Read or load the least significant bit first then the most significant bit.
|
||||||
const OCW_READ_LOAD_DATA: u8 = 0x30; // xx11xxxx
|
const OCW_READ_LOAD_DATA: u8 = 0x30; // xx11xxxx
|
||||||
|
|
||||||
/// The OCW bits for selecting counter 0. Used for the system clock.
|
/// The OCW bits for selecting counter 0. Used for the system clock.
|
||||||
const OCW_SELECT_COUNTER_0: u8 = 0x00; // 00xxxxxx
|
const OCW_SELECT_COUNTER_0: u8 = 0x00; // 00xxxxxx
|
||||||
|
|
||||||
/// The OCW bits for selecting counter 1. Was for the memory refreshing.
|
/// The OCW bits for selecting counter 1. Was for the memory refreshing.
|
||||||
const OCW_SELECT_COUNTER_1: u8 = 0x40; // 01xxxxxx
|
const OCW_SELECT_COUNTER_1: u8 = 0x40; // 01xxxxxx
|
||||||
|
|
||||||
/// The OCW bits for selecting counter 2. Channel for the speaker.
|
/// The OCW bits for selecting counter 2. Channel for the speaker.
|
||||||
const OCW_SELECT_COUNTER_2: u8 = 0x80; // 10xxxxxx
|
const OCW_SELECT_COUNTER_2: u8 = 0x80; // 10xxxxxx
|
||||||
|
|
||||||
// The divisor constant
|
// The divisor constant
|
||||||
const MAX_FREQUENCY: u32 = 1193180;
|
const MAX_FREQUENCY: u32 = 1193180;
|
||||||
|
|
|
@ -29,17 +29,21 @@ var handlers: [NUM_HANDLERS]?SyscallHandler = [_]?SyscallHandler{null} ** NUM_HA
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to check
|
/// IN syscall: u32 - The syscall to check
|
||||||
///
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// Whether the syscall number is valid.
|
||||||
|
///
|
||||||
pub fn isValidSyscall(syscall: u32) bool {
|
pub fn isValidSyscall(syscall: u32) bool {
|
||||||
return syscall < NUM_HANDLERS;
|
return syscall < NUM_HANDLERS;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Handle a syscall. Gets the syscall number from eax within the context and calls the registered handler.
|
/// Handle a syscall. Gets the syscall number from eax within the context and calls the registered
|
||||||
/// If there isn't a registered handler or the syscall is invalid (>= NUM_HANDLERS) then a warning is logged.
|
/// handler. If there isn't a registered handler or the syscall is invalid (>= NUM_HANDLERS) then a
|
||||||
|
/// warning is logged.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - The cpu context when the syscall was triggered. The syscall number is
|
/// IN ctx: *arch.InterruptContext - The cpu context when the syscall was triggered. The
|
||||||
/// stored in eax.
|
/// syscall number is stored in eax.
|
||||||
///
|
///
|
||||||
fn handle(ctx: *arch.InterruptContext) void {
|
fn handle(ctx: *arch.InterruptContext) void {
|
||||||
// The syscall number is put in eax
|
// The syscall number is put in eax
|
||||||
|
@ -80,6 +84,9 @@ pub fn registerSyscall(syscall: u8, handler: SyscallHandler) SyscallError!void {
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
||||||
///
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The return value from the syscall.
|
||||||
|
///
|
||||||
inline fn syscall0(syscall: u32) u32 {
|
inline fn syscall0(syscall: u32) u32 {
|
||||||
return asm volatile (
|
return asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
|
@ -95,6 +102,9 @@ inline fn syscall0(syscall: u32) u32 {
|
||||||
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
/// IN syscall: u32 - The syscall to trigger, put in eax.
|
||||||
/// IN arg: u32 - The argument to pass. Put in ebx.
|
/// IN arg: u32 - The argument to pass. Put in ebx.
|
||||||
///
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The return value from the syscall.
|
||||||
|
///
|
||||||
inline fn syscall1(syscall: u32, arg: u32) u32 {
|
inline fn syscall1(syscall: u32, arg: u32) u32 {
|
||||||
return asm volatile (
|
return asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
|
@ -112,6 +122,9 @@ inline fn syscall1(syscall: u32, arg: u32) u32 {
|
||||||
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
/// IN arg1: u32 - The first argument to pass. Put in ebx.
|
||||||
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
||||||
///
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The return value from the syscall.
|
||||||
|
///
|
||||||
inline fn syscall2(syscall: u32, arg1: u32, arg2: u32) u32 {
|
inline fn syscall2(syscall: u32, arg1: u32, arg2: u32) u32 {
|
||||||
return asm volatile (
|
return asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
|
@ -131,6 +144,9 @@ inline fn syscall2(syscall: u32, arg1: u32, arg2: u32) u32 {
|
||||||
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
/// IN arg2: u32 - The second argument to pass. Put in ecx.
|
||||||
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
||||||
///
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The return value from the syscall.
|
||||||
|
///
|
||||||
inline fn syscall3(syscall: u32, arg1: u32, arg2: u32, arg3: u32) u32 {
|
inline fn syscall3(syscall: u32, arg1: u32, arg2: u32, arg3: u32) u32 {
|
||||||
return asm volatile (
|
return asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
|
@ -152,6 +168,9 @@ inline fn syscall3(syscall: u32, arg1: u32, arg2: u32, arg3: u32) u32 {
|
||||||
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
/// IN arg3: u32 - The third argument to pass. Put in edx.
|
||||||
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
||||||
///
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The return value from the syscall.
|
||||||
|
///
|
||||||
inline fn syscall4(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32 {
|
inline fn syscall4(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32 {
|
||||||
return asm volatile (
|
return asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
|
@ -175,6 +194,9 @@ inline fn syscall4(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32
|
||||||
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
/// IN arg4: u32 - The fourth argument to pass. Put in esi.
|
||||||
/// IN arg5: u32 - The fifth argument to pass. Put in edi.
|
/// IN arg5: u32 - The fifth argument to pass. Put in edi.
|
||||||
///
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The return value from the syscall.
|
||||||
|
///
|
||||||
inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
||||||
return asm volatile (
|
return asm volatile (
|
||||||
\\int $0x80
|
\\int $0x80
|
||||||
|
@ -189,12 +211,16 @@ inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Gets the syscall argument according to the given index. 0 => ebx, 1 => ecx, 2 => edx, 3 => esi and 4 => edi.
|
/// Gets the syscall argument according to the given index. 0 => ebx, 1 => ecx, 2 => edx,
|
||||||
|
/// 3 => esi and 4 => edi.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - The interrupt context from which to get the argument
|
/// IN ctx: *arch.InterruptContext - The interrupt context from which to get the argument
|
||||||
/// IN arg_idx: comptime u32 - The argument index to get. Between 0 and 4.
|
/// IN arg_idx: comptime u32 - The argument index to get. Between 0 and 4.
|
||||||
///
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The syscall argument from the given index.
|
||||||
|
///
|
||||||
inline fn syscallArg(ctx: *arch.InterruptContext, comptime arg_idx: u32) u32 {
|
inline fn syscallArg(ctx: *arch.InterruptContext, comptime arg_idx: u32) u32 {
|
||||||
return switch (arg_idx) {
|
return switch (arg_idx) {
|
||||||
0 => ctx.ebx,
|
0 => ctx.ebx,
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const is_test = builtin.is_test;
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
|
const mock_path = build_options.mock_path;
|
||||||
const arch = @import("arch.zig").internals;
|
const arch = @import("arch.zig").internals;
|
||||||
const multiboot = @import("multiboot.zig");
|
const multiboot = @import("multiboot.zig");
|
||||||
const tty = @import("tty.zig");
|
const tty = @import("tty.zig");
|
||||||
const vga = @import("vga.zig");
|
const vga = @import("vga.zig");
|
||||||
const log = @import("log.zig");
|
const log = @import("log.zig");
|
||||||
const serial = @import("serial.zig");
|
const serial = @import("serial.zig");
|
||||||
const mem = if (builtin.is_test) @import(build_options.mock_path ++ "mem_mock.zig") else @import("mem.zig");
|
const mem = if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.zig");
|
||||||
|
const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic;
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
switch (builtin.arch) {
|
switch (builtin.arch) {
|
||||||
|
@ -20,10 +23,6 @@ comptime {
|
||||||
// from the linker script
|
// from the linker script
|
||||||
export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefined;
|
export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefined;
|
||||||
|
|
||||||
// Need to import this as we need the panic to be in the root source file, or zig will just use the
|
|
||||||
// builtin panic and just loop, which is what we don't want
|
|
||||||
const panic_root = @import("panic.zig").panic;
|
|
||||||
|
|
||||||
// Just call the panic function, as this need to be in the root source file
|
// Just call the panic function, as this need to be in the root source file
|
||||||
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
|
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
|
||||||
@setCold(true);
|
@setCold(true);
|
||||||
|
|
|
@ -6,6 +6,8 @@ def get_test_cases(TestCase):
|
||||||
TestCase("IDT tests", [r"IDT: Tested loading IDT"]),
|
TestCase("IDT tests", [r"IDT: Tested loading IDT"]),
|
||||||
TestCase("PIC init", [r"Init pic", r"Done"]),
|
TestCase("PIC init", [r"Init pic", r"Done"]),
|
||||||
TestCase("PIC tests", [r"PIC: Tested masking"]),
|
TestCase("PIC tests", [r"PIC: Tested masking"]),
|
||||||
|
TestCase("ISR init", [r"Init isr", r"Done"]),
|
||||||
|
TestCase("ISR tests", [r"ISR: Tested registered handlers", r"ISR: Tested opened IDT entries"]),
|
||||||
TestCase("PIT init", [r"Init pit", r".+", r"Done"]),
|
TestCase("PIT init", [r"Init pit", r".+", r"Done"]),
|
||||||
TestCase("Paging init", [r"Init paging", r"Done"]),
|
TestCase("Paging init", [r"Init paging", r"Done"]),
|
||||||
TestCase("Paging tests", [r"Paging: Tested accessing unmapped memory", r"Paging: Tested accessing mapped memory"]),
|
TestCase("Paging tests", [r"Paging: Tested accessing unmapped memory", r"Paging: Tested accessing mapped memory"]),
|
||||||
|
|
|
@ -21,9 +21,9 @@ const IdtEntry = packed struct {
|
||||||
// Need to use the type from the source file so that types match
|
// Need to use the type from the source file so that types match
|
||||||
pub const IdtPtr = src_idt.IdtPtr;
|
pub const IdtPtr = src_idt.IdtPtr;
|
||||||
|
|
||||||
pub const InterruptHandler = extern fn () void;
|
pub const InterruptHandler = src_idt.InterruptHandler;
|
||||||
|
|
||||||
pub const IdtError = error{IdtEntryExists};
|
pub const IdtError = src_idt.IdtError;
|
||||||
|
|
||||||
const TASK_GATE: u4 = 0x5;
|
const TASK_GATE: u4 = 0x5;
|
||||||
const INTERRUPT_GATE: u4 = 0xE;
|
const INTERRUPT_GATE: u4 = 0xE;
|
||||||
|
@ -39,7 +39,7 @@ const NUMBER_OF_ENTRIES: u16 = 256;
|
||||||
const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1;
|
const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1;
|
||||||
|
|
||||||
pub fn openInterruptGate(index: u8, handler: InterruptHandler) IdtError!void {
|
pub fn openInterruptGate(index: u8, handler: InterruptHandler) IdtError!void {
|
||||||
return mock_framework.performAction("openInterruptGate", IdtError!void, port);
|
return mock_framework.performAction("openInterruptGate", IdtError!void, index, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
|
|
|
@ -19,6 +19,8 @@ const DataElementType = enum {
|
||||||
U32,
|
U32,
|
||||||
PTR_CONST_GdtPtr,
|
PTR_CONST_GdtPtr,
|
||||||
PTR_CONST_IdtPtr,
|
PTR_CONST_IdtPtr,
|
||||||
|
ERROR_IDTERROR_VOID,
|
||||||
|
EFN_OVOID,
|
||||||
FN_OVOID,
|
FN_OVOID,
|
||||||
FN_OUSIZE,
|
FN_OUSIZE,
|
||||||
FN_OU16,
|
FN_OU16,
|
||||||
|
@ -28,6 +30,7 @@ const DataElementType = enum {
|
||||||
FN_IU8_IU8_OU16,
|
FN_IU8_IU8_OU16,
|
||||||
FN_IU16_IU8_OVOID,
|
FN_IU16_IU8_OVOID,
|
||||||
FN_IU16_IU16_OVOID,
|
FN_IU16_IU16_OVOID,
|
||||||
|
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
FN_IPTRCONSTGDTPTR_OVOID,
|
FN_IPTRCONSTGDTPTR_OVOID,
|
||||||
FN_IPTRCONSTIDTPTR_OVOID,
|
FN_IPTRCONSTIDTPTR_OVOID,
|
||||||
};
|
};
|
||||||
|
@ -44,6 +47,8 @@ const DataElement = union(DataElementType) {
|
||||||
U32: u32,
|
U32: u32,
|
||||||
PTR_CONST_GdtPtr: *const gdt.GdtPtr,
|
PTR_CONST_GdtPtr: *const gdt.GdtPtr,
|
||||||
PTR_CONST_IdtPtr: *const idt.IdtPtr,
|
PTR_CONST_IdtPtr: *const idt.IdtPtr,
|
||||||
|
ERROR_IDTERROR_VOID: idt.IdtError!void,
|
||||||
|
EFN_OVOID: extern fn () void,
|
||||||
FN_OVOID: fn () void,
|
FN_OVOID: fn () void,
|
||||||
FN_OUSIZE: fn () usize,
|
FN_OUSIZE: fn () usize,
|
||||||
FN_OU16: fn () u16,
|
FN_OU16: fn () u16,
|
||||||
|
@ -53,6 +58,7 @@ const DataElement = union(DataElementType) {
|
||||||
FN_IU8_IU8_OU16: fn (u8, u8) u16,
|
FN_IU8_IU8_OU16: fn (u8, u8) u16,
|
||||||
FN_IU16_IU8_OVOID: fn (u16, u8) void,
|
FN_IU16_IU8_OVOID: fn (u16, u8) void,
|
||||||
FN_IU16_IU16_OVOID: fn (u16, u16) void,
|
FN_IU16_IU16_OVOID: fn (u16, u16) void,
|
||||||
|
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID: fn (u8, extern fn () void) idt.IdtError!void,
|
||||||
FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void,
|
FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void,
|
||||||
FN_IPTRCONSTIDTPTR_OVOID: fn (*const idt.IdtPtr) void,
|
FN_IPTRCONSTIDTPTR_OVOID: fn (*const idt.IdtPtr) void,
|
||||||
};
|
};
|
||||||
|
@ -136,6 +142,8 @@ fn Mock() type {
|
||||||
u32 => DataElement{ .U32 = arg },
|
u32 => DataElement{ .U32 = arg },
|
||||||
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
|
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
|
||||||
*const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg },
|
*const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg },
|
||||||
|
idt.IdtError!void => DataElement{ .ERROR_IDTERROR_VOID = arg },
|
||||||
|
extern fn () void => DataElement{ .EFN_OVOID = arg },
|
||||||
fn () void => DataElement{ .FN_OVOID = arg },
|
fn () void => DataElement{ .FN_OVOID = arg },
|
||||||
fn () usize => DataElement{ .FN_OUSIZE = arg },
|
fn () usize => DataElement{ .FN_OUSIZE = arg },
|
||||||
fn () u16 => DataElement{ .FN_OU16 = arg },
|
fn () u16 => DataElement{ .FN_OU16 = arg },
|
||||||
|
@ -147,6 +155,7 @@ fn Mock() type {
|
||||||
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
|
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
|
||||||
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
|
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
|
||||||
fn (*const idt.IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg },
|
fn (*const idt.IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg },
|
||||||
|
fn (u8, extern fn () void) idt.IdtError!void => DataElement{ .FN_IU8_IEFNOVOID_OERRORIDTERRORVOID = arg },
|
||||||
else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))),
|
else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -168,6 +177,8 @@ fn Mock() type {
|
||||||
u32 => DataElementType.U32,
|
u32 => DataElementType.U32,
|
||||||
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
|
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
|
||||||
*const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr,
|
*const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr,
|
||||||
|
idt.IdtError!void => DataElement.ERROR_IDTERROR_VOID,
|
||||||
|
extern fn () void => DataElementType.EFN_OVOID,
|
||||||
fn () void => DataElementType.FN_OVOID,
|
fn () void => DataElementType.FN_OVOID,
|
||||||
fn () u16 => DataElementType.FN_OU16,
|
fn () u16 => DataElementType.FN_OU16,
|
||||||
fn (u16) void => DataElementType.FN_IU16_OVOID,
|
fn (u16) void => DataElementType.FN_IU16_OVOID,
|
||||||
|
@ -178,6 +189,7 @@ fn Mock() type {
|
||||||
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
|
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
|
||||||
fn (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_OVOID,
|
fn (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_OVOID,
|
||||||
fn (*const idt.IdtPtr) void => DataElementType.FN_IPTRCONSTIDTPTR_OVOID,
|
fn (*const idt.IdtPtr) void => DataElementType.FN_IPTRCONSTIDTPTR_OVOID,
|
||||||
|
fn (u8, extern fn () void) idt.IdtError!void => DataElementType.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
else => @compileError("Type not supported: " ++ @typeName(T)),
|
else => @compileError("Type not supported: " ++ @typeName(T)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -201,6 +213,8 @@ fn Mock() type {
|
||||||
u32 => element.U32,
|
u32 => element.U32,
|
||||||
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
|
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
|
||||||
*const idt.IdtPtr => element.PTR_CONST_IdtPtr,
|
*const idt.IdtPtr => element.PTR_CONST_IdtPtr,
|
||||||
|
idt.IdtError!void => element.ERROR_IDTERROR_VOID,
|
||||||
|
extern fn () void => element.EFN_OVOID,
|
||||||
fn () void => element.FN_OVOID,
|
fn () void => element.FN_OVOID,
|
||||||
fn () u16 => element.FN_OU16,
|
fn () u16 => element.FN_OU16,
|
||||||
fn (u16) void => element.FN_IU16_OVOID,
|
fn (u16) void => element.FN_IU16_OVOID,
|
||||||
|
@ -211,6 +225,7 @@ fn Mock() type {
|
||||||
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
|
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
|
||||||
fn (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_OVOID,
|
fn (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_OVOID,
|
||||||
fn (*const idt.IdtPtr) void => element.FN_IPTRCONSTIDTPTR_OVOID,
|
fn (*const idt.IdtPtr) void => element.FN_IPTRCONSTIDTPTR_OVOID,
|
||||||
|
fn (u8, extern fn () void) idt.IdtError!void => element.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
else => @compileError("Type not supported: " ++ @typeName(T)),
|
else => @compileError("Type not supported: " ++ @typeName(T)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -248,8 +263,7 @@ fn Mock() type {
|
||||||
fn expectTest(comptime ExpectedType: type, expected_value: ExpectedType, elem: DataElement) void {
|
fn expectTest(comptime ExpectedType: type, expected_value: ExpectedType, elem: DataElement) void {
|
||||||
if (ExpectedType == void) {
|
if (ExpectedType == void) {
|
||||||
// Can't test void as it has no value
|
// Can't test void as it has no value
|
||||||
warn("Can not test a value for void\n");
|
std.debug.panic("Can not test a value for void\n");
|
||||||
expect(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the types match
|
// Test that the types match
|
||||||
|
@ -291,9 +305,7 @@ fn Mock() type {
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
warn("No more test values for the return of function: " ++ fun_name ++ "\n");
|
std.debug.panic("No more test values for the return of function: " ++ fun_name ++ "\n");
|
||||||
expect(false);
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,9 +340,7 @@ fn Mock() type {
|
||||||
} else {
|
} else {
|
||||||
// Shouldn't get here as we would have just added a new mapping
|
// Shouldn't get here as we would have just added a new mapping
|
||||||
// But just in case ;)
|
// But just in case ;)
|
||||||
warn("No function name: " ++ fun_name ++ "\n");
|
std.debug.panic("No function name: " ++ fun_name ++ "\n");
|
||||||
expect(false);
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,14 +458,10 @@ fn Mock() type {
|
||||||
kv_actions_list.value = action_list;
|
kv_actions_list.value = action_list;
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
warn("No action list elements for function: " ++ fun_name ++ "\n");
|
std.debug.panic("No action list elements for function: " ++ fun_name ++ "\n");
|
||||||
expect(false);
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn("No function name: " ++ fun_name ++ "\n");
|
std.debug.panic("No function name: " ++ fun_name ++ "\n");
|
||||||
expect(false);
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,9 +495,7 @@ fn Mock() type {
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
ActionType.TestValue, ActionType.ConsumeFunctionCall => {
|
ActionType.TestValue, ActionType.ConsumeFunctionCall => {
|
||||||
// These need to be all consumed
|
// These need to be all consumed
|
||||||
warn("Unused testing value: Type: {}, value: {} for function '{}'\n", action.action, DataElementType(action.data), next.key);
|
std.debug.panic("Unused testing value: Type: {}, value: {} for function '{}'\n", action.action, DataElementType(action.data), next.key);
|
||||||
expect(false);
|
|
||||||
unreachable;
|
|
||||||
},
|
},
|
||||||
ActionType.RepeatFunctionCall => {
|
ActionType.RepeatFunctionCall => {
|
||||||
// As this is a repeat action, the function will still be here
|
// As this is a repeat action, the function will still be here
|
||||||
|
|
Loading…
Reference in a new issue