Merge pull request #138 from SamTebbs33/feature/initial_multitasking
Initial scheduler
This commit is contained in:
commit
7e0c1fd589
30 changed files with 1127 additions and 395 deletions
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
|
@ -37,3 +37,5 @@ jobs:
|
||||||
run: zig*/zig build rt-test -Ddisable-display -Dtest-mode=Initialisation ${{ matrix.build_mode }}
|
run: zig*/zig build rt-test -Ddisable-display -Dtest-mode=Initialisation ${{ matrix.build_mode }}
|
||||||
- name: Run runtime test - Panic
|
- name: Run runtime test - Panic
|
||||||
run: zig*/zig build rt-test -Ddisable-display -Dtest-mode=Panic ${{ matrix.build_mode }}
|
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 }}
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -38,6 +38,9 @@
|
||||||
# Intellij
|
# Intellij
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# Zig
|
# Zig
|
||||||
zig-cache
|
zig-cache
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ const builtin = @import("builtin");
|
||||||
const rt = @import("test/runtime_test.zig");
|
const rt = @import("test/runtime_test.zig");
|
||||||
const RuntimeStep = rt.RuntimeStep;
|
const RuntimeStep = rt.RuntimeStep;
|
||||||
const Builder = std.build.Builder;
|
const Builder = std.build.Builder;
|
||||||
const LibExeObjStep = std.build.LibExeObjStep;
|
|
||||||
const Step = std.build.Step;
|
|
||||||
const Target = std.Target;
|
const Target = std.Target;
|
||||||
const CrossTarget = std.zig.CrossTarget;
|
const CrossTarget = std.zig.CrossTarget;
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const is_test = builtin.is_test;
|
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;
|
||||||
|
|
||||||
pub const internals = if (is_test) @import(build_options.mock_path ++ "arch_mock.zig") else switch (builtin.arch) {
|
pub const internals = if (is_test) @import(mock_path ++ "arch_mock.zig") else switch (builtin.arch) {
|
||||||
.i386 => @import("arch/x86/arch.zig"),
|
.i386 => @import("arch/x86/arch.zig"),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,47 +1,54 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const cmos = @import("cmos.zig");
|
||||||
const gdt = @import("gdt.zig");
|
const gdt = @import("gdt.zig");
|
||||||
const idt = @import("idt.zig");
|
const idt = @import("idt.zig");
|
||||||
const pic = @import("pic.zig");
|
|
||||||
const irq = @import("irq.zig");
|
const irq = @import("irq.zig");
|
||||||
const isr = @import("isr.zig");
|
const isr = @import("isr.zig");
|
||||||
|
const paging = @import("paging.zig");
|
||||||
|
const pic = @import("pic.zig");
|
||||||
const pit = @import("pit.zig");
|
const pit = @import("pit.zig");
|
||||||
const rtc = @import("rtc.zig");
|
const rtc = @import("rtc.zig");
|
||||||
const serial = @import("serial.zig");
|
const serial = @import("serial.zig");
|
||||||
const paging = @import("paging.zig");
|
|
||||||
const syscalls = @import("syscalls.zig");
|
const syscalls = @import("syscalls.zig");
|
||||||
const mem = @import("../../mem.zig");
|
|
||||||
const multiboot = @import("multiboot.zig");
|
|
||||||
const pmm = @import("pmm.zig");
|
|
||||||
const vmm = @import("../../vmm.zig");
|
|
||||||
const log = @import("../../log.zig");
|
|
||||||
const tty = @import("tty.zig");
|
const tty = @import("tty.zig");
|
||||||
const vga = @import("vga.zig");
|
const vga = @import("vga.zig");
|
||||||
|
const mem = @import("../../mem.zig");
|
||||||
|
const multiboot = @import("multiboot.zig");
|
||||||
|
const vmm = @import("../../vmm.zig");
|
||||||
|
const log = @import("../../log.zig");
|
||||||
const Serial = @import("../../serial.zig").Serial;
|
const Serial = @import("../../serial.zig").Serial;
|
||||||
const panic = @import("../../panic.zig").panic;
|
const panic = @import("../../panic.zig").panic;
|
||||||
const TTY = @import("../../tty.zig").TTY;
|
const TTY = @import("../../tty.zig").TTY;
|
||||||
const MemProfile = mem.MemProfile;
|
const MemProfile = mem.MemProfile;
|
||||||
|
|
||||||
/// The virtual end of the kernel code
|
/// The virtual end of the kernel code.
|
||||||
extern var KERNEL_VADDR_END: *u32;
|
extern var KERNEL_VADDR_END: *u32;
|
||||||
|
|
||||||
/// The virtual start of the kernel code
|
/// The virtual start of the kernel code.
|
||||||
extern var KERNEL_VADDR_START: *u32;
|
extern var KERNEL_VADDR_START: *u32;
|
||||||
|
|
||||||
/// The physical end of the kernel code
|
/// The physical end of the kernel code.
|
||||||
extern var KERNEL_PHYSADDR_END: *u32;
|
extern var KERNEL_PHYSADDR_END: *u32;
|
||||||
|
|
||||||
/// The physical start of the kernel code
|
/// The physical start of the kernel code.
|
||||||
extern var KERNEL_PHYSADDR_START: *u32;
|
extern var KERNEL_PHYSADDR_START: *u32;
|
||||||
|
|
||||||
/// The boot-time offset that the virtual addresses are from the physical addresses
|
/// The boot-time offset that the virtual addresses are from the physical addresses.
|
||||||
extern var KERNEL_ADDR_OFFSET: *u32;
|
extern var KERNEL_ADDR_OFFSET: *u32;
|
||||||
|
|
||||||
|
/// The virtual address of the top limit of the stack.
|
||||||
|
extern var KERNEL_STACK_START: *u32;
|
||||||
|
|
||||||
|
/// The virtual address of the base of the stack.
|
||||||
|
extern var KERNEL_STACK_END: *u32;
|
||||||
|
|
||||||
/// The interrupt context that is given to a interrupt handler. It contains most of the registers
|
/// The interrupt context that is given to a interrupt handler. It contains most of the registers
|
||||||
/// and the interrupt number and error code (if there is one).
|
/// and the interrupt number and error code (if there is one).
|
||||||
pub const InterruptContext = struct {
|
pub const CpuState = packed struct {
|
||||||
// Extra segments
|
// Extra segments
|
||||||
|
ss: u32,
|
||||||
gs: u32,
|
gs: u32,
|
||||||
fs: u32,
|
fs: u32,
|
||||||
es: u32,
|
es: u32,
|
||||||
|
@ -68,7 +75,7 @@ pub const InterruptContext = struct {
|
||||||
cs: u32,
|
cs: u32,
|
||||||
eflags: u32,
|
eflags: u32,
|
||||||
user_esp: u32,
|
user_esp: u32,
|
||||||
ss: u32,
|
user_ss: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// x86's boot payload is the multiboot info passed by grub
|
/// x86's boot payload is the multiboot info passed by grub
|
||||||
|
@ -89,6 +96,9 @@ pub const VMM_MAPPER: vmm.Mapper(VmmPayload) = vmm.Mapper(VmmPayload){ .mapFn =
|
||||||
/// The size of each allocatable block of memory, normally set to the page size.
|
/// The size of each allocatable block of memory, normally set to the page size.
|
||||||
pub const MEMORY_BLOCK_SIZE: usize = paging.PAGE_SIZE_4KB;
|
pub const MEMORY_BLOCK_SIZE: usize = paging.PAGE_SIZE_4KB;
|
||||||
|
|
||||||
|
/// The default stack size of a task. Currently this is set to a page size.
|
||||||
|
pub const STACK_SIZE: u32 = MEMORY_BLOCK_SIZE / @sizeOf(u32);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Assembly to write to a given port with a byte of data.
|
/// Assembly to write to a given port with a byte of data.
|
||||||
///
|
///
|
||||||
|
@ -239,10 +249,9 @@ pub fn halt() void {
|
||||||
/// Wait the kernel but still can handle interrupts.
|
/// Wait the kernel but still can handle interrupts.
|
||||||
///
|
///
|
||||||
pub fn spinWait() noreturn {
|
pub fn spinWait() noreturn {
|
||||||
while (true) {
|
|
||||||
enableInterrupts();
|
enableInterrupts();
|
||||||
|
while (true) {
|
||||||
halt();
|
halt();
|
||||||
disableInterrupts();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,13 +321,21 @@ pub fn initTTY(boot_payload: BootPayload) TTY {
|
||||||
/// Return: mem.MemProfile
|
/// Return: mem.MemProfile
|
||||||
/// The constructed memory profile
|
/// The constructed memory profile
|
||||||
///
|
///
|
||||||
/// Error: std.mem.Allocator.Error
|
/// Error: Allocator.Error
|
||||||
/// std.mem.Allocator.Error.OutOfMemory - There wasn't enough memory in the allocated created to populate the memory profile, consider increasing mem.FIXED_ALLOC_SIZE
|
/// Allocator.Error.OutOfMemory - There wasn't enough memory in the allocated created to populate the memory profile, consider increasing mem.FIXED_ALLOC_SIZE
|
||||||
///
|
///
|
||||||
pub fn initMem(mb_info: BootPayload) std.mem.Allocator.Error!MemProfile {
|
pub fn initMem(mb_info: BootPayload) Allocator.Error!MemProfile {
|
||||||
log.logInfo("Init mem\n", .{});
|
log.logInfo("Init mem\n", .{});
|
||||||
defer log.logInfo("Done mem\n", .{});
|
defer log.logInfo("Done mem\n", .{});
|
||||||
|
|
||||||
|
log.logDebug("KERNEL_ADDR_OFFSET: 0x{X}\n", .{@ptrToInt(&KERNEL_ADDR_OFFSET)});
|
||||||
|
log.logDebug("KERNEL_STACK_START: 0x{X}\n", .{@ptrToInt(&KERNEL_STACK_START)});
|
||||||
|
log.logDebug("KERNEL_STACK_END: 0x{X}\n", .{@ptrToInt(&KERNEL_STACK_END)});
|
||||||
|
log.logDebug("KERNEL_VADDR_START: 0x{X}\n", .{@ptrToInt(&KERNEL_VADDR_START)});
|
||||||
|
log.logDebug("KERNEL_VADDR_END: 0x{X}\n", .{@ptrToInt(&KERNEL_VADDR_END)});
|
||||||
|
log.logDebug("KERNEL_PHYSADDR_START: 0x{X}\n", .{@ptrToInt(&KERNEL_PHYSADDR_START)});
|
||||||
|
log.logDebug("KERNEL_PHYSADDR_END: 0x{X}\n", .{@ptrToInt(&KERNEL_PHYSADDR_END)});
|
||||||
|
|
||||||
const mods_count = mb_info.mods_count;
|
const mods_count = mb_info.mods_count;
|
||||||
mem.ADDR_OFFSET = @ptrToInt(&KERNEL_ADDR_OFFSET);
|
mem.ADDR_OFFSET = @ptrToInt(&KERNEL_ADDR_OFFSET);
|
||||||
const mmap_addr = mb_info.mmap_addr;
|
const mmap_addr = mb_info.mmap_addr;
|
||||||
|
@ -338,6 +355,7 @@ pub fn initMem(mb_info: BootPayload) std.mem.Allocator.Error!MemProfile {
|
||||||
try reserved_physical_mem.append(.{ .start = @intCast(usize, entry.addr), .end = end });
|
try reserved_physical_mem.append(.{ .start = @intCast(usize, entry.addr), .end = end });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map the multiboot info struct itself
|
// Map the multiboot info struct itself
|
||||||
const mb_region = mem.Range{
|
const mb_region = mem.Range{
|
||||||
.start = @ptrToInt(mb_info),
|
.start = @ptrToInt(mb_info),
|
||||||
|
@ -384,19 +402,63 @@ pub fn initMem(mb_info: BootPayload) std.mem.Allocator.Error!MemProfile {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialise a 32bit kernel stack used for creating a task.
|
||||||
|
/// Currently only support fn () noreturn functions for the entry point.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN entry_point: usize - The pointer to the entry point of the function. Functions only
|
||||||
|
/// supported is fn () noreturn
|
||||||
|
/// IN allocator: *Allocator - The allocator use for allocating a stack.
|
||||||
|
///
|
||||||
|
/// Return: struct { stack: []u32, pointer: usize }
|
||||||
|
/// The stack and stack pointer with the stack initialised as a 32bit kernel stack.
|
||||||
|
///
|
||||||
|
/// Error: Allocator.Error
|
||||||
|
/// OutOfMemory - Unable to allocate space for the stack.
|
||||||
|
///
|
||||||
|
pub fn initTaskStack(entry_point: usize, allocator: *Allocator) Allocator.Error!struct { stack: []u32, pointer: usize } {
|
||||||
|
// TODO Will need to add the exit point
|
||||||
|
// Set up everything as a kernel task
|
||||||
|
var stack = try allocator.alloc(u32, STACK_SIZE);
|
||||||
|
stack[STACK_SIZE - 18] = gdt.KERNEL_DATA_OFFSET; // ss
|
||||||
|
stack[STACK_SIZE - 17] = gdt.KERNEL_DATA_OFFSET; // gs
|
||||||
|
stack[STACK_SIZE - 16] = gdt.KERNEL_DATA_OFFSET; // fs
|
||||||
|
stack[STACK_SIZE - 15] = gdt.KERNEL_DATA_OFFSET; // es
|
||||||
|
stack[STACK_SIZE - 14] = gdt.KERNEL_DATA_OFFSET; // ds
|
||||||
|
|
||||||
|
stack[STACK_SIZE - 13] = 0; // edi
|
||||||
|
stack[STACK_SIZE - 12] = 0; // esi
|
||||||
|
// End of the stack
|
||||||
|
stack[STACK_SIZE - 11] = @ptrToInt(&stack[STACK_SIZE - 1]); // ebp
|
||||||
|
stack[STACK_SIZE - 10] = 0; // esp (temp) this won't be popped by popa bc intel is dump XD
|
||||||
|
|
||||||
|
stack[STACK_SIZE - 9] = 0; // ebx
|
||||||
|
stack[STACK_SIZE - 8] = 0; // edx
|
||||||
|
stack[STACK_SIZE - 7] = 0; // ecx
|
||||||
|
stack[STACK_SIZE - 6] = 0; // eax
|
||||||
|
|
||||||
|
stack[STACK_SIZE - 5] = 0; // int_num
|
||||||
|
stack[STACK_SIZE - 4] = 0; // error_code
|
||||||
|
|
||||||
|
stack[STACK_SIZE - 3] = entry_point; // eip
|
||||||
|
stack[STACK_SIZE - 2] = gdt.KERNEL_CODE_OFFSET; // cs
|
||||||
|
stack[STACK_SIZE - 1] = 0x202; // eflags
|
||||||
|
|
||||||
|
const ret = .{ .stack = stack, .pointer = @ptrToInt(&stack[STACK_SIZE - 18]) };
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Initialise the architecture
|
/// Initialise the architecture
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
|
/// IN boot_payload: BootPayload - The multiboot information from the GRUB bootloader.
|
||||||
/// IN mem_profile: *const MemProfile - The memory profile of the computer. Used to set up
|
/// IN mem_profile: *const MemProfile - The memory profile of the computer. Used to set up
|
||||||
/// paging.
|
/// paging.
|
||||||
/// IN allocator: *Allocator - The allocator use to handle memory.
|
/// IN allocator: *Allocator - The allocator use to handle memory.
|
||||||
/// IN comptime options: type - The build options that is passed to the kernel to be
|
|
||||||
/// used for run time testing.
|
|
||||||
///
|
///
|
||||||
pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile, allocator: *Allocator) void {
|
pub fn init(boot_payload: BootPayload, mem_profile: *const MemProfile, allocator: *Allocator) void {
|
||||||
disableInterrupts();
|
|
||||||
|
|
||||||
gdt.init();
|
gdt.init();
|
||||||
idt.init();
|
idt.init();
|
||||||
|
|
||||||
|
@ -404,15 +466,13 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile
|
||||||
isr.init();
|
isr.init();
|
||||||
irq.init();
|
irq.init();
|
||||||
|
|
||||||
paging.init(mb_info, mem_profile, allocator);
|
paging.init(boot_payload, mem_profile, allocator);
|
||||||
|
|
||||||
pit.init();
|
pit.init();
|
||||||
rtc.init();
|
rtc.init();
|
||||||
|
|
||||||
syscalls.init();
|
syscalls.init();
|
||||||
|
|
||||||
enableInterrupts();
|
|
||||||
|
|
||||||
// Initialise the VGA and TTY here since their tests belong the architecture and so should be a part of the
|
// Initialise the VGA and TTY here since their tests belong the architecture and so should be a part of the
|
||||||
// arch init test messages
|
// arch init test messages
|
||||||
vga.init();
|
vga.init();
|
||||||
|
@ -420,17 +480,5 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
test "" {
|
test "" {
|
||||||
_ = @import("gdt.zig");
|
std.meta.refAllDecls(@This());
|
||||||
_ = @import("idt.zig");
|
|
||||||
_ = @import("pic.zig");
|
|
||||||
_ = @import("isr.zig");
|
|
||||||
_ = @import("irq.zig");
|
|
||||||
_ = @import("pit.zig");
|
|
||||||
_ = @import("cmos.zig");
|
|
||||||
_ = @import("rtc.zig");
|
|
||||||
_ = @import("syscalls.zig");
|
|
||||||
_ = @import("paging.zig");
|
|
||||||
_ = @import("serial.zig");
|
|
||||||
_ = @import("tty.zig");
|
|
||||||
_ = @import("vga.zig");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const constants = @import("constants");
|
const constants = @import("constants");
|
||||||
const multiboot_info = @import("multiboot.zig").multiboot_info_t;
|
const arch = @import("arch.zig");
|
||||||
|
|
||||||
/// The multiboot header
|
/// The multiboot header
|
||||||
const MultiBoot = packed struct {
|
const MultiBoot = packed struct {
|
||||||
|
@ -66,7 +66,7 @@ export var boot_page_directory: [1024]u32 align(4096) linksection(".rodata.boot"
|
||||||
export var kernel_stack: [16 * 1024]u8 align(16) linksection(".bss.stack") = undefined;
|
export var kernel_stack: [16 * 1024]u8 align(16) linksection(".bss.stack") = undefined;
|
||||||
extern var KERNEL_ADDR_OFFSET: *u32;
|
extern var KERNEL_ADDR_OFFSET: *u32;
|
||||||
|
|
||||||
extern fn kmain(mb_info: *multiboot_info) void;
|
extern fn kmain(mb_info: arch.BootPayload) void;
|
||||||
|
|
||||||
export fn _start() align(16) linksection(".text.boot") callconv(.Naked) noreturn {
|
export fn _start() align(16) linksection(".text.boot") callconv(.Naked) noreturn {
|
||||||
// Set the page directory to the boot directory
|
// Set the page directory to the boot directory
|
||||||
|
@ -109,6 +109,6 @@ export fn start_higher_half() callconv(.Naked) noreturn {
|
||||||
\\mov %%ebx, %[res]
|
\\mov %%ebx, %[res]
|
||||||
: [res] "=r" (-> usize)
|
: [res] "=r" (-> usize)
|
||||||
) + @ptrToInt(&KERNEL_ADDR_OFFSET);
|
) + @ptrToInt(&KERNEL_ADDR_OFFSET);
|
||||||
kmain(@intToPtr(*multiboot_info, mb_info_addr));
|
kmain(@intToPtr(arch.BootPayload, mb_info_addr));
|
||||||
while (true) {}
|
while (true) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,27 +84,31 @@ const GdtEntry = packed struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The TSS entry structure
|
/// The TSS entry structure
|
||||||
const TtsEntry = packed struct {
|
const Tss = packed struct {
|
||||||
/// Pointer to the previous TSS entry
|
/// Pointer to the previous TSS entry
|
||||||
prev_tss: u32,
|
prev_tss: u16,
|
||||||
|
reserved1: u16,
|
||||||
|
|
||||||
/// Ring 0 32 bit stack pointer.
|
/// Ring 0 32 bit stack pointer.
|
||||||
esp0: u32,
|
esp0: u32,
|
||||||
|
|
||||||
/// Ring 0 32 bit stack pointer.
|
/// Ring 0 32 bit stack pointer.
|
||||||
ss0: u32,
|
ss0: u16,
|
||||||
|
reserved2: u16,
|
||||||
|
|
||||||
/// Ring 1 32 bit stack pointer.
|
/// Ring 1 32 bit stack pointer.
|
||||||
esp1: u32,
|
esp1: u32,
|
||||||
|
|
||||||
/// Ring 1 32 bit stack pointer.
|
/// Ring 1 32 bit stack pointer.
|
||||||
ss1: u32,
|
ss1: u16,
|
||||||
|
reserved3: u16,
|
||||||
|
|
||||||
/// Ring 2 32 bit stack pointer.
|
/// Ring 2 32 bit stack pointer.
|
||||||
esp2: u32,
|
esp2: u32,
|
||||||
|
|
||||||
/// Ring 2 32 bit stack pointer.
|
/// Ring 2 32 bit stack pointer.
|
||||||
ss2: u32,
|
ss2: u16,
|
||||||
|
reserved4: u16,
|
||||||
|
|
||||||
/// The CR3 control register 3.
|
/// The CR3 control register 3.
|
||||||
cr3: u32,
|
cr3: u32,
|
||||||
|
@ -140,25 +144,32 @@ const TtsEntry = packed struct {
|
||||||
edi: u32,
|
edi: u32,
|
||||||
|
|
||||||
/// The extra segment.
|
/// The extra segment.
|
||||||
es: u32,
|
es: u16,
|
||||||
|
reserved5: u16,
|
||||||
|
|
||||||
/// The code segment.
|
/// The code segment.
|
||||||
cs: u32,
|
cs: u16,
|
||||||
|
reserved6: u16,
|
||||||
|
|
||||||
/// The stack segment.
|
/// The stack segment.
|
||||||
ss: u32,
|
ss: u16,
|
||||||
|
reserved7: u16,
|
||||||
|
|
||||||
/// The data segment.
|
/// The data segment.
|
||||||
ds: u32,
|
ds: u16,
|
||||||
|
reserved8: u16,
|
||||||
|
|
||||||
/// A extra segment FS.
|
/// A extra segment FS.
|
||||||
fs: u32,
|
fs: u16,
|
||||||
|
reserved9: u16,
|
||||||
|
|
||||||
/// A extra segment GS.
|
/// A extra segment GS.
|
||||||
gs: u32,
|
gs: u16,
|
||||||
|
reserved10: u16,
|
||||||
|
|
||||||
/// The local descriptor table register.
|
/// The local descriptor table register.
|
||||||
ldtr: u32,
|
ldtr: u16,
|
||||||
|
reserved11: u16,
|
||||||
|
|
||||||
/// ?
|
/// ?
|
||||||
trap: u16,
|
trap: u16,
|
||||||
|
@ -177,8 +188,8 @@ pub const GdtPtr = packed struct {
|
||||||
base: u32,
|
base: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The total number of entries in the GTD: null, kernel code, kernel data, user code, user data
|
/// The total number of entries in the GDT including: null, kernel code, kernel data, user code,
|
||||||
/// and TSS
|
/// user data and the TSS.
|
||||||
const NUMBER_OF_ENTRIES: u16 = 0x06;
|
const NUMBER_OF_ENTRIES: u16 = 0x06;
|
||||||
|
|
||||||
/// The size of the GTD in bytes (minus 1).
|
/// The size of the GTD in bytes (minus 1).
|
||||||
|
@ -315,24 +326,28 @@ pub const USER_DATA_OFFSET: u16 = 0x20;
|
||||||
pub const TSS_OFFSET: u16 = 0x28;
|
pub const TSS_OFFSET: u16 = 0x28;
|
||||||
|
|
||||||
/// The GDT entry table of NUMBER_OF_ENTRIES entries.
|
/// The GDT entry table of NUMBER_OF_ENTRIES entries.
|
||||||
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = [_]GdtEntry{
|
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = init: {
|
||||||
|
var gdt_entries_temp: [NUMBER_OF_ENTRIES]GdtEntry = undefined;
|
||||||
|
|
||||||
// Null descriptor
|
// Null descriptor
|
||||||
makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS),
|
gdt_entries_temp[0] = makeGdtEntry(0, 0, NULL_SEGMENT, NULL_FLAGS);
|
||||||
|
|
||||||
// Kernel Code
|
// Kernel code descriptor
|
||||||
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT_CODE, PAGING_32_BIT),
|
gdt_entries_temp[1] = makeGdtEntry(0, 0xFFFFF, KERNEL_SEGMENT_CODE, PAGING_32_BIT);
|
||||||
|
|
||||||
// Kernel Data
|
// Kernel data descriptor
|
||||||
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT_DATA, PAGING_32_BIT),
|
gdt_entries_temp[2] = makeGdtEntry(0, 0xFFFFF, KERNEL_SEGMENT_DATA, PAGING_32_BIT);
|
||||||
|
|
||||||
// User Code
|
// User code descriptor
|
||||||
makeEntry(0, 0xFFFFF, USER_SEGMENT_CODE, PAGING_32_BIT),
|
gdt_entries_temp[3] = makeGdtEntry(0, 0xFFFFF, USER_SEGMENT_CODE, PAGING_32_BIT);
|
||||||
|
|
||||||
// User Data
|
// User data descriptor
|
||||||
makeEntry(0, 0xFFFFF, USER_SEGMENT_DATA, PAGING_32_BIT),
|
gdt_entries_temp[4] = makeGdtEntry(0, 0xFFFFF, USER_SEGMENT_DATA, PAGING_32_BIT);
|
||||||
|
|
||||||
// Fill in TSS at runtime
|
// TSS descriptor, one each for each processor
|
||||||
makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS),
|
// Will initialise the TSS at runtime
|
||||||
|
gdt_entries_temp[5] = makeGdtEntry(0, 0, NULL_SEGMENT, NULL_FLAGS);
|
||||||
|
break :init gdt_entries_temp;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the
|
/// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the
|
||||||
|
@ -342,35 +357,12 @@ var gdt_ptr: GdtPtr = GdtPtr{
|
||||||
.base = undefined,
|
.base = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The task state segment entry.
|
/// The main task state segment entry.
|
||||||
var tss: TtsEntry = TtsEntry{
|
var main_tss_entry: Tss = init: {
|
||||||
.prev_tss = 0,
|
var tss_temp = std.mem.zeroes(Tss);
|
||||||
.esp0 = 0,
|
tss_temp.ss0 = KERNEL_DATA_OFFSET;
|
||||||
.ss0 = KERNEL_DATA_OFFSET,
|
tss_temp.io_permissions_base_offset = @sizeOf(Tss);
|
||||||
.esp1 = 0,
|
break :init tss_temp;
|
||||||
.ss1 = 0,
|
|
||||||
.esp2 = 0,
|
|
||||||
.ss2 = 0,
|
|
||||||
.cr3 = 0,
|
|
||||||
.eip = 0,
|
|
||||||
.eflags = 0,
|
|
||||||
.eax = 0,
|
|
||||||
.ecx = 0,
|
|
||||||
.edx = 0,
|
|
||||||
.ebx = 0,
|
|
||||||
.esp = 0,
|
|
||||||
.ebp = 0,
|
|
||||||
.esi = 0,
|
|
||||||
.edi = 0,
|
|
||||||
.es = 0,
|
|
||||||
.cs = 0,
|
|
||||||
.ss = 0,
|
|
||||||
.ds = 0,
|
|
||||||
.fs = 0,
|
|
||||||
.gs = 0,
|
|
||||||
.ldtr = 0,
|
|
||||||
.trap = 0,
|
|
||||||
.io_permissions_base_offset = @sizeOf(TtsEntry),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -386,11 +378,11 @@ var tss: TtsEntry = TtsEntry{
|
||||||
/// A new GDT entry with the give access and flag bits set with the base at 0x00000000 and
|
/// A new GDT entry with the give access and flag bits set with the base at 0x00000000 and
|
||||||
/// limit at 0xFFFFF.
|
/// limit at 0xFFFFF.
|
||||||
///
|
///
|
||||||
fn makeEntry(base: u32, limit: u20, access: AccessBits, flags: FlagBits) GdtEntry {
|
fn makeGdtEntry(base: u32, limit: u20, access: AccessBits, flags: FlagBits) GdtEntry {
|
||||||
return GdtEntry{
|
return .{
|
||||||
.limit_low = @truncate(u16, limit),
|
.limit_low = @truncate(u16, limit),
|
||||||
.base_low = @truncate(u24, base),
|
.base_low = @truncate(u24, base),
|
||||||
.access = AccessBits{
|
.access = .{
|
||||||
.accessed = access.accessed,
|
.accessed = access.accessed,
|
||||||
.read_write = access.read_write,
|
.read_write = access.read_write,
|
||||||
.direction_conforming = access.direction_conforming,
|
.direction_conforming = access.direction_conforming,
|
||||||
|
@ -400,7 +392,7 @@ fn makeEntry(base: u32, limit: u20, access: AccessBits, flags: FlagBits) GdtEntr
|
||||||
.present = access.present,
|
.present = access.present,
|
||||||
},
|
},
|
||||||
.limit_high = @truncate(u4, limit >> 16),
|
.limit_high = @truncate(u4, limit >> 16),
|
||||||
.flags = FlagBits{
|
.flags = .{
|
||||||
.reserved_zero = flags.reserved_zero,
|
.reserved_zero = flags.reserved_zero,
|
||||||
.is_64_bit = flags.is_64_bit,
|
.is_64_bit = flags.is_64_bit,
|
||||||
.is_32_bit = flags.is_32_bit,
|
.is_32_bit = flags.is_32_bit,
|
||||||
|
@ -410,16 +402,6 @@ fn makeEntry(base: u32, limit: u20, access: AccessBits, flags: FlagBits) GdtEntr
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
/// Set the stack pointer in the TSS entry.
|
|
||||||
///
|
|
||||||
/// Arguments:
|
|
||||||
/// IN esp0: u32 - The stack pointer.
|
|
||||||
///
|
|
||||||
pub fn setTssStack(esp0: u32) void {
|
|
||||||
tss.esp0 = esp0;
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Initialise the Global Descriptor table.
|
/// Initialise the Global Descriptor table.
|
||||||
///
|
///
|
||||||
|
@ -427,7 +409,7 @@ pub fn init() void {
|
||||||
log.logInfo("Init gdt\n", .{});
|
log.logInfo("Init gdt\n", .{});
|
||||||
defer log.logInfo("Done gdt\n", .{});
|
defer log.logInfo("Done gdt\n", .{});
|
||||||
// Initiate TSS
|
// Initiate TSS
|
||||||
gdt_entries[TSS_INDEX] = makeEntry(@ptrToInt(&tss), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, NULL_FLAGS);
|
gdt_entries[TSS_INDEX] = makeGdtEntry(@ptrToInt(&main_tss_entry), @sizeOf(Tss) - 1, TSS_SEGMENT, NULL_FLAGS);
|
||||||
|
|
||||||
// Set the base address where all the GDT entries are.
|
// Set the base address where all the GDT entries are.
|
||||||
gdt_ptr.base = @ptrToInt(&gdt_entries[0]);
|
gdt_ptr.base = @ptrToInt(&gdt_entries[0]);
|
||||||
|
@ -453,7 +435,7 @@ test "GDT entries" {
|
||||||
expectEqual(@as(u32, 1), @sizeOf(AccessBits));
|
expectEqual(@as(u32, 1), @sizeOf(AccessBits));
|
||||||
expectEqual(@as(u32, 1), @sizeOf(FlagBits));
|
expectEqual(@as(u32, 1), @sizeOf(FlagBits));
|
||||||
expectEqual(@as(u32, 8), @sizeOf(GdtEntry));
|
expectEqual(@as(u32, 8), @sizeOf(GdtEntry));
|
||||||
expectEqual(@as(u32, 104), @sizeOf(TtsEntry));
|
expectEqual(@as(u32, 104), @sizeOf(Tss));
|
||||||
expectEqual(@as(u32, 6), @sizeOf(GdtPtr));
|
expectEqual(@as(u32, 6), @sizeOf(GdtPtr));
|
||||||
|
|
||||||
const null_entry = gdt_entries[NULL_INDEX];
|
const null_entry = gdt_entries[NULL_INDEX];
|
||||||
|
@ -476,45 +458,45 @@ test "GDT entries" {
|
||||||
|
|
||||||
expectEqual(TABLE_SIZE, gdt_ptr.limit);
|
expectEqual(TABLE_SIZE, gdt_ptr.limit);
|
||||||
|
|
||||||
expectEqual(@as(u32, 0), tss.prev_tss);
|
expectEqual(@as(u32, 0), main_tss_entry.prev_tss);
|
||||||
expectEqual(@as(u32, 0), tss.esp0);
|
expectEqual(@as(u32, 0), main_tss_entry.esp0);
|
||||||
expectEqual(@as(u32, KERNEL_DATA_OFFSET), tss.ss0);
|
expectEqual(@as(u32, KERNEL_DATA_OFFSET), main_tss_entry.ss0);
|
||||||
expectEqual(@as(u32, 0), tss.esp1);
|
expectEqual(@as(u32, 0), main_tss_entry.esp1);
|
||||||
expectEqual(@as(u32, 0), tss.ss1);
|
expectEqual(@as(u32, 0), main_tss_entry.ss1);
|
||||||
expectEqual(@as(u32, 0), tss.esp2);
|
expectEqual(@as(u32, 0), main_tss_entry.esp2);
|
||||||
expectEqual(@as(u32, 0), tss.ss2);
|
expectEqual(@as(u32, 0), main_tss_entry.ss2);
|
||||||
expectEqual(@as(u32, 0), tss.cr3);
|
expectEqual(@as(u32, 0), main_tss_entry.cr3);
|
||||||
expectEqual(@as(u32, 0), tss.eip);
|
expectEqual(@as(u32, 0), main_tss_entry.eip);
|
||||||
expectEqual(@as(u32, 0), tss.eflags);
|
expectEqual(@as(u32, 0), main_tss_entry.eflags);
|
||||||
expectEqual(@as(u32, 0), tss.eax);
|
expectEqual(@as(u32, 0), main_tss_entry.eax);
|
||||||
expectEqual(@as(u32, 0), tss.ecx);
|
expectEqual(@as(u32, 0), main_tss_entry.ecx);
|
||||||
expectEqual(@as(u32, 0), tss.edx);
|
expectEqual(@as(u32, 0), main_tss_entry.edx);
|
||||||
expectEqual(@as(u32, 0), tss.ebx);
|
expectEqual(@as(u32, 0), main_tss_entry.ebx);
|
||||||
expectEqual(@as(u32, 0), tss.esp);
|
expectEqual(@as(u32, 0), main_tss_entry.esp);
|
||||||
expectEqual(@as(u32, 0), tss.ebp);
|
expectEqual(@as(u32, 0), main_tss_entry.ebp);
|
||||||
expectEqual(@as(u32, 0), tss.esi);
|
expectEqual(@as(u32, 0), main_tss_entry.esi);
|
||||||
expectEqual(@as(u32, 0), tss.edi);
|
expectEqual(@as(u32, 0), main_tss_entry.edi);
|
||||||
expectEqual(@as(u32, 0), tss.es);
|
expectEqual(@as(u32, 0), main_tss_entry.es);
|
||||||
expectEqual(@as(u32, 0), tss.cs);
|
expectEqual(@as(u32, 0), main_tss_entry.cs);
|
||||||
expectEqual(@as(u32, 0), tss.ss);
|
expectEqual(@as(u32, 0), main_tss_entry.ss);
|
||||||
expectEqual(@as(u32, 0), tss.ds);
|
expectEqual(@as(u32, 0), main_tss_entry.ds);
|
||||||
expectEqual(@as(u32, 0), tss.fs);
|
expectEqual(@as(u32, 0), main_tss_entry.fs);
|
||||||
expectEqual(@as(u32, 0), tss.gs);
|
expectEqual(@as(u32, 0), main_tss_entry.gs);
|
||||||
expectEqual(@as(u32, 0), tss.ldtr);
|
expectEqual(@as(u32, 0), main_tss_entry.ldtr);
|
||||||
expectEqual(@as(u16, 0), tss.trap);
|
expectEqual(@as(u16, 0), main_tss_entry.trap);
|
||||||
|
|
||||||
// Size of TtsEntry will fit in a u16 as 104 < 65535 (2^16)
|
// Size of Tss will fit in a u16 as 104 < 65535 (2^16)
|
||||||
expectEqual(@as(u16, @sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
expectEqual(@as(u16, @sizeOf(Tss)), main_tss_entry.io_permissions_base_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "makeEntry NULL" {
|
test "makeGdtEntry NULL" {
|
||||||
const actual = makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS);
|
const actual = makeGdtEntry(0, 0, NULL_SEGMENT, NULL_FLAGS);
|
||||||
|
|
||||||
const expected: u64 = 0;
|
const expected: u64 = 0;
|
||||||
expectEqual(expected, @bitCast(u64, actual));
|
expectEqual(expected, @bitCast(u64, actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "makeEntry alternating bit pattern" {
|
test "makeGdtEntry alternating bit pattern" {
|
||||||
const alt_access = AccessBits{
|
const alt_access = AccessBits{
|
||||||
.accessed = 1,
|
.accessed = 1,
|
||||||
.read_write = 0,
|
.read_write = 0,
|
||||||
|
@ -536,106 +518,12 @@ test "makeEntry alternating bit pattern" {
|
||||||
|
|
||||||
expectEqual(@as(u4, 0b0101), @bitCast(u4, alt_flag));
|
expectEqual(@as(u4, 0b0101), @bitCast(u4, alt_flag));
|
||||||
|
|
||||||
const actual = makeEntry(0b01010101010101010101010101010101, 0b01010101010101010101, alt_access, alt_flag);
|
const actual = makeGdtEntry(0b01010101010101010101010101010101, 0b01010101010101010101, alt_access, alt_flag);
|
||||||
|
|
||||||
const expected: u64 = 0b0101010101010101010101010101010101010101010101010101010101010101;
|
const expected: u64 = 0b0101010101010101010101010101010101010101010101010101010101010101;
|
||||||
expectEqual(expected, @bitCast(u64, actual));
|
expectEqual(expected, @bitCast(u64, actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "setTssStack" {
|
|
||||||
// Pre-testing
|
|
||||||
expectEqual(@as(u32, 0), tss.prev_tss);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp0);
|
|
||||||
expectEqual(@as(u32, KERNEL_DATA_OFFSET), tss.ss0);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp1);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss1);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp2);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss2);
|
|
||||||
expectEqual(@as(u32, 0), tss.cr3);
|
|
||||||
expectEqual(@as(u32, 0), tss.eip);
|
|
||||||
expectEqual(@as(u32, 0), tss.eflags);
|
|
||||||
expectEqual(@as(u32, 0), tss.eax);
|
|
||||||
expectEqual(@as(u32, 0), tss.ecx);
|
|
||||||
expectEqual(@as(u32, 0), tss.edx);
|
|
||||||
expectEqual(@as(u32, 0), tss.ebx);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp);
|
|
||||||
expectEqual(@as(u32, 0), tss.ebp);
|
|
||||||
expectEqual(@as(u32, 0), tss.esi);
|
|
||||||
expectEqual(@as(u32, 0), tss.edi);
|
|
||||||
expectEqual(@as(u32, 0), tss.es);
|
|
||||||
expectEqual(@as(u32, 0), tss.cs);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss);
|
|
||||||
expectEqual(@as(u32, 0), tss.ds);
|
|
||||||
expectEqual(@as(u32, 0), tss.fs);
|
|
||||||
expectEqual(@as(u32, 0), tss.gs);
|
|
||||||
expectEqual(@as(u32, 0), tss.ldtr);
|
|
||||||
expectEqual(@as(u16, 0), tss.trap);
|
|
||||||
expectEqual(@as(u16, @sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
|
||||||
|
|
||||||
// Call function
|
|
||||||
setTssStack(100);
|
|
||||||
|
|
||||||
// Post-testing
|
|
||||||
expectEqual(@as(u32, 0), tss.prev_tss);
|
|
||||||
expectEqual(@as(u32, 100), tss.esp0);
|
|
||||||
expectEqual(@as(u32, KERNEL_DATA_OFFSET), tss.ss0);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp1);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss1);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp2);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss2);
|
|
||||||
expectEqual(@as(u32, 0), tss.cr3);
|
|
||||||
expectEqual(@as(u32, 0), tss.eip);
|
|
||||||
expectEqual(@as(u32, 0), tss.eflags);
|
|
||||||
expectEqual(@as(u32, 0), tss.eax);
|
|
||||||
expectEqual(@as(u32, 0), tss.ecx);
|
|
||||||
expectEqual(@as(u32, 0), tss.edx);
|
|
||||||
expectEqual(@as(u32, 0), tss.ebx);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp);
|
|
||||||
expectEqual(@as(u32, 0), tss.ebp);
|
|
||||||
expectEqual(@as(u32, 0), tss.esi);
|
|
||||||
expectEqual(@as(u32, 0), tss.edi);
|
|
||||||
expectEqual(@as(u32, 0), tss.es);
|
|
||||||
expectEqual(@as(u32, 0), tss.cs);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss);
|
|
||||||
expectEqual(@as(u32, 0), tss.ds);
|
|
||||||
expectEqual(@as(u32, 0), tss.fs);
|
|
||||||
expectEqual(@as(u32, 0), tss.gs);
|
|
||||||
expectEqual(@as(u32, 0), tss.ldtr);
|
|
||||||
expectEqual(@as(u16, 0), tss.trap);
|
|
||||||
expectEqual(@as(u16, @sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
setTssStack(0);
|
|
||||||
|
|
||||||
expectEqual(@as(u32, 0), tss.prev_tss);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp0);
|
|
||||||
expectEqual(@as(u32, KERNEL_DATA_OFFSET), tss.ss0);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp1);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss1);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp2);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss2);
|
|
||||||
expectEqual(@as(u32, 0), tss.cr3);
|
|
||||||
expectEqual(@as(u32, 0), tss.eip);
|
|
||||||
expectEqual(@as(u32, 0), tss.eflags);
|
|
||||||
expectEqual(@as(u32, 0), tss.eax);
|
|
||||||
expectEqual(@as(u32, 0), tss.ecx);
|
|
||||||
expectEqual(@as(u32, 0), tss.edx);
|
|
||||||
expectEqual(@as(u32, 0), tss.ebx);
|
|
||||||
expectEqual(@as(u32, 0), tss.esp);
|
|
||||||
expectEqual(@as(u32, 0), tss.ebp);
|
|
||||||
expectEqual(@as(u32, 0), tss.esi);
|
|
||||||
expectEqual(@as(u32, 0), tss.edi);
|
|
||||||
expectEqual(@as(u32, 0), tss.es);
|
|
||||||
expectEqual(@as(u32, 0), tss.cs);
|
|
||||||
expectEqual(@as(u32, 0), tss.ss);
|
|
||||||
expectEqual(@as(u32, 0), tss.ds);
|
|
||||||
expectEqual(@as(u32, 0), tss.fs);
|
|
||||||
expectEqual(@as(u32, 0), tss.gs);
|
|
||||||
expectEqual(@as(u32, 0), tss.ldtr);
|
|
||||||
expectEqual(@as(u16, 0), tss.trap);
|
|
||||||
expectEqual(@as(u16, @sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "init" {
|
test "init" {
|
||||||
// Set up
|
// Set up
|
||||||
arch.initTest();
|
arch.initTest();
|
||||||
|
@ -650,8 +538,8 @@ test "init" {
|
||||||
|
|
||||||
// Post testing
|
// Post testing
|
||||||
const tss_entry = gdt_entries[TSS_INDEX];
|
const tss_entry = gdt_entries[TSS_INDEX];
|
||||||
const tss_limit = @sizeOf(TtsEntry) - 1;
|
const tss_limit = @sizeOf(Tss) - 1;
|
||||||
const tss_addr = @ptrToInt(&tss);
|
const tss_addr = @ptrToInt(&main_tss_entry);
|
||||||
|
|
||||||
var expected: u64 = 0;
|
var expected: u64 = 0;
|
||||||
expected |= @as(u64, @truncate(u16, tss_limit));
|
expected |= @as(u64, @truncate(u16, tss_limit));
|
||||||
|
@ -665,7 +553,7 @@ test "init" {
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
gdt_ptr.base = 0;
|
gdt_ptr.base = 0;
|
||||||
gdt_entries[TSS_INDEX] = makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS);
|
gdt_entries[TSS_INDEX] = makeGdtEntry(0, 0, NULL_SEGMENT, NULL_FLAGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -686,6 +574,6 @@ fn rt_loadedGDTSuccess() void {
|
||||||
///
|
///
|
||||||
/// Run all the runtime tests.
|
/// Run all the runtime tests.
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
pub fn runtimeTests() void {
|
||||||
rt_loadedGDTSuccess();
|
rt_loadedGDTSuccess();
|
||||||
}
|
}
|
||||||
|
|
|
@ -340,6 +340,6 @@ fn rt_loadedIDTSuccess() void {
|
||||||
///
|
///
|
||||||
/// Run all the runtime tests.
|
/// Run all the runtime tests.
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
pub fn runtimeTests() void {
|
||||||
rt_loadedIDTSuccess();
|
rt_loadedIDTSuccess();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,22 +3,22 @@ const syscalls = @import("syscalls.zig");
|
||||||
const irq = @import("irq.zig");
|
const irq = @import("irq.zig");
|
||||||
const idt = @import("idt.zig");
|
const idt = @import("idt.zig");
|
||||||
|
|
||||||
extern fn irqHandler(ctx: *arch.InterruptContext) void;
|
extern fn irqHandler(ctx: *arch.CpuState) usize;
|
||||||
extern fn isrHandler(ctx: *arch.InterruptContext) void;
|
extern fn isrHandler(ctx: *arch.CpuState) usize;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The main handler for all exceptions and interrupts. This will then go and call the correct
|
/// The main handler for all exceptions and interrupts. This will then go and call the correct
|
||||||
/// handler for an ISR or IRQ.
|
/// handler for an ISR or IRQ.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - Pointer to the exception context containing the contents
|
/// IN ctx: *arch.CpuState - Pointer to the exception context containing the contents
|
||||||
/// of the registers at the time of a exception.
|
/// of the registers at the time of a exception.
|
||||||
///
|
///
|
||||||
export fn handler(ctx: *arch.InterruptContext) void {
|
export fn handler(ctx: *arch.CpuState) usize {
|
||||||
if (ctx.int_num < irq.IRQ_OFFSET or ctx.int_num == syscalls.INTERRUPT) {
|
if (ctx.int_num < irq.IRQ_OFFSET or ctx.int_num == syscalls.INTERRUPT) {
|
||||||
isrHandler(ctx);
|
return isrHandler(ctx);
|
||||||
} else {
|
} else {
|
||||||
irqHandler(ctx);
|
return irqHandler(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ export fn commonStub() callconv(.Naked) void {
|
||||||
\\push %%es
|
\\push %%es
|
||||||
\\push %%fs
|
\\push %%fs
|
||||||
\\push %%gs
|
\\push %%gs
|
||||||
|
\\push %%ss
|
||||||
\\mov $0x10, %%ax
|
\\mov $0x10, %%ax
|
||||||
\\mov %%ax, %%ds
|
\\mov %%ax, %%ds
|
||||||
\\mov %%ax, %%es
|
\\mov %%ax, %%es
|
||||||
|
@ -40,7 +41,8 @@ export fn commonStub() callconv(.Naked) void {
|
||||||
\\mov %%esp, %%eax
|
\\mov %%esp, %%eax
|
||||||
\\push %%eax
|
\\push %%eax
|
||||||
\\call handler
|
\\call handler
|
||||||
\\pop %%eax
|
\\mov %%eax, %%esp
|
||||||
|
\\pop %%ss
|
||||||
\\pop %%gs
|
\\pop %%gs
|
||||||
\\pop %%fs
|
\\pop %%fs
|
||||||
\\pop %%es
|
\\pop %%es
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub const IrqError = error{
|
||||||
const NUMBER_OF_ENTRIES: u16 = 16;
|
const NUMBER_OF_ENTRIES: u16 = 16;
|
||||||
|
|
||||||
/// The type of a IRQ handler. A function that takes a interrupt context and returns void.
|
/// The type of a IRQ handler. A function that takes a interrupt context and returns void.
|
||||||
const IrqHandler = fn (*arch.InterruptContext) void;
|
const IrqHandler = fn (*arch.CpuState) usize;
|
||||||
|
|
||||||
// The offset from the interrupt number where the IRQs are.
|
// The offset from the interrupt number where the IRQs are.
|
||||||
pub const IRQ_OFFSET: u16 = 32;
|
pub const IRQ_OFFSET: u16 = 32;
|
||||||
|
@ -38,15 +38,17 @@ var irq_handlers: [NUMBER_OF_ENTRIES]?IrqHandler = [_]?IrqHandler{null} ** NUMBE
|
||||||
/// The IRQ handler that each of the IRQs will call when a interrupt happens.
|
/// The IRQ handler that each of the IRQs will call when a interrupt happens.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - Pointer to the interrupt context containing the contents
|
/// IN ctx: *arch.CpuState - Pointer to the interrupt context containing the contents
|
||||||
/// of the register at the time of the interrupt.
|
/// of the register at the time of the interrupt.
|
||||||
///
|
///
|
||||||
export fn irqHandler(ctx: *arch.InterruptContext) void {
|
export fn irqHandler(ctx: *arch.CpuState) usize {
|
||||||
// Get the IRQ index, by getting the interrupt number and subtracting the offset.
|
// Get the IRQ index, by getting the interrupt number and subtracting the offset.
|
||||||
if (ctx.int_num < IRQ_OFFSET) {
|
if (ctx.int_num < IRQ_OFFSET) {
|
||||||
panic(@errorReturnTrace(), "Not an IRQ number: {}\n", .{ctx.int_num});
|
panic(@errorReturnTrace(), "Not an IRQ number: {}\n", .{ctx.int_num});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ret_esp = @ptrToInt(ctx);
|
||||||
|
|
||||||
const irq_offset = ctx.int_num - IRQ_OFFSET;
|
const irq_offset = ctx.int_num - IRQ_OFFSET;
|
||||||
if (isValidIrq(irq_offset)) {
|
if (isValidIrq(irq_offset)) {
|
||||||
// IRQ index is valid so can truncate
|
// IRQ index is valid so can truncate
|
||||||
|
@ -54,7 +56,7 @@ export fn irqHandler(ctx: *arch.InterruptContext) void {
|
||||||
if (irq_handlers[irq_num]) |handler| {
|
if (irq_handlers[irq_num]) |handler| {
|
||||||
// Make sure it isn't a spurious irq
|
// Make sure it isn't a spurious irq
|
||||||
if (!pic.spuriousIrq(irq_num)) {
|
if (!pic.spuriousIrq(irq_num)) {
|
||||||
handler(ctx);
|
ret_esp = handler(ctx);
|
||||||
// Send the end of interrupt command
|
// Send the end of interrupt command
|
||||||
pic.sendEndOfInterrupt(irq_num);
|
pic.sendEndOfInterrupt(irq_num);
|
||||||
}
|
}
|
||||||
|
@ -64,6 +66,7 @@ export fn irqHandler(ctx: *arch.InterruptContext) void {
|
||||||
} else {
|
} else {
|
||||||
panic(@errorReturnTrace(), "Invalid IRQ index: {}", .{irq_offset});
|
panic(@errorReturnTrace(), "Invalid IRQ index: {}", .{irq_offset});
|
||||||
}
|
}
|
||||||
|
return ret_esp;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -143,8 +146,12 @@ pub fn init() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testFunction0() callconv(.Naked) void {}
|
fn testFunction0() callconv(.Naked) void {}
|
||||||
fn testFunction1(ctx: *arch.InterruptContext) void {}
|
fn testFunction1(ctx: *arch.CpuState) u32 {
|
||||||
fn testFunction2(ctx: *arch.InterruptContext) void {}
|
return 0;
|
||||||
|
}
|
||||||
|
fn testFunction2(ctx: *arch.CpuState) u32 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
test "openIrq" {
|
test "openIrq" {
|
||||||
idt.initTest();
|
idt.initTest();
|
||||||
|
@ -264,7 +271,7 @@ fn rt_openedIdtEntries() void {
|
||||||
///
|
///
|
||||||
/// Run all the runtime tests.
|
/// Run all the runtime tests.
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
pub fn runtimeTests() void {
|
||||||
rt_unregisteredHandlers();
|
rt_unregisteredHandlers();
|
||||||
rt_openedIdtEntries();
|
rt_openedIdtEntries();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub const IsrError = error{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The type of a ISR handler. A function that takes a interrupt context and returns void.
|
/// The type of a ISR handler. A function that takes a interrupt context and returns void.
|
||||||
const IsrHandler = fn (*arch.InterruptContext) void;
|
const IsrHandler = fn (*arch.CpuState) usize;
|
||||||
|
|
||||||
/// The number of ISR entries.
|
/// The number of ISR entries.
|
||||||
const NUMBER_OF_ENTRIES: u8 = 32;
|
const NUMBER_OF_ENTRIES: u8 = 32;
|
||||||
|
@ -137,32 +137,36 @@ var syscall_handler: ?IsrHandler = null;
|
||||||
/// 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 ctx: *arch.InterruptContext - Pointer to the exception context containing the contents
|
/// IN ctx: *arch.CpuState - Pointer to the exception context containing the contents
|
||||||
/// of the register at the time of the exception.
|
/// of the register at the time of the exception.
|
||||||
///
|
///
|
||||||
export fn isrHandler(ctx: *arch.InterruptContext) void {
|
export fn isrHandler(ctx: *arch.CpuState) usize {
|
||||||
// Get the interrupt number
|
// Get the interrupt number
|
||||||
const isr_num = ctx.int_num;
|
const isr_num = ctx.int_num;
|
||||||
|
|
||||||
|
var ret_esp = @ptrToInt(ctx);
|
||||||
|
|
||||||
if (isValidIsr(isr_num)) {
|
if (isValidIsr(isr_num)) {
|
||||||
if (isr_num == syscalls.INTERRUPT) {
|
if (isr_num == syscalls.INTERRUPT) {
|
||||||
// A syscall, so use the syscall handler
|
// A syscall, so use the syscall handler
|
||||||
if (syscall_handler) |handler| {
|
if (syscall_handler) |handler| {
|
||||||
handler(ctx);
|
ret_esp = handler(ctx);
|
||||||
} else {
|
} else {
|
||||||
panic(@errorReturnTrace(), "Syscall handler not registered\n", .{});
|
panic(@errorReturnTrace(), "Syscall handler not registered\n", .{});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isr_handlers[isr_num]) |handler| {
|
if (isr_handlers[isr_num]) |handler| {
|
||||||
// Regular ISR exception, if there is one registered.
|
// Regular ISR exception, if there is one registered.
|
||||||
handler(ctx);
|
ret_esp = handler(ctx);
|
||||||
} else {
|
} else {
|
||||||
panic(@errorReturnTrace(), "ISR not registered to: {}-{}\n", .{ isr_num, exception_msg[isr_num] });
|
log.logInfo("State: {X}\n", .{ctx});
|
||||||
|
panic(@errorReturnTrace(), "ISR {} ({}) triggered with error code 0x{X} but not registered\n", .{ exception_msg[isr_num], isr_num, ctx.error_code });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic(@errorReturnTrace(), "Invalid ISR index: {}\n", .{isr_num});
|
panic(@errorReturnTrace(), "Invalid ISR index: {}\n", .{isr_num});
|
||||||
}
|
}
|
||||||
|
return ret_esp;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -251,10 +255,18 @@ pub fn init() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testFunction0() callconv(.Naked) void {}
|
fn testFunction0() callconv(.Naked) void {}
|
||||||
fn testFunction1(ctx: *arch.InterruptContext) void {}
|
fn testFunction1(ctx: *arch.CpuState) u32 {
|
||||||
fn testFunction2(ctx: *arch.InterruptContext) void {}
|
return 0;
|
||||||
fn testFunction3(ctx: *arch.InterruptContext) void {}
|
}
|
||||||
fn testFunction4(ctx: *arch.InterruptContext) void {}
|
fn testFunction2(ctx: *arch.CpuState) u32 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fn testFunction3(ctx: *arch.CpuState) u32 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fn testFunction4(ctx: *arch.CpuState) u32 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
test "openIsr" {
|
test "openIsr" {
|
||||||
idt.initTest();
|
idt.initTest();
|
||||||
|
@ -397,7 +409,7 @@ fn rt_openedIdtEntries() void {
|
||||||
///
|
///
|
||||||
/// Run all the runtime tests.
|
/// Run all the runtime tests.
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
pub fn runtimeTests() void {
|
||||||
rt_unregisteredHandlers();
|
rt_unregisteredHandlers();
|
||||||
rt_openedIdtEntries();
|
rt_openedIdtEntries();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ SECTIONS {
|
||||||
}
|
}
|
||||||
|
|
||||||
.bss.stack ALIGN(4K) : AT (ADDR(.bss.stack) - KERNEL_ADDR_OFFSET) {
|
.bss.stack ALIGN(4K) : AT (ADDR(.bss.stack) - KERNEL_ADDR_OFFSET) {
|
||||||
|
KERNEL_STACK_START = .;
|
||||||
KEEP(*(.bss.stack))
|
KEEP(*(.bss.stack))
|
||||||
KERNEL_STACK_END = .;
|
KERNEL_STACK_END = .;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const expectEqual = std.testing.expectEqual;
|
const testing = std.testing;
|
||||||
const expect = std.testing.expect;
|
const expectEqual = testing.expectEqual;
|
||||||
|
const expect = testing.expect;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const is_test = builtin.is_test;
|
||||||
const panic = @import("../../panic.zig").panic;
|
const panic = @import("../../panic.zig").panic;
|
||||||
const arch = @import("arch.zig");
|
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");
|
||||||
const isr = @import("isr.zig");
|
const isr = @import("isr.zig");
|
||||||
const MemProfile = @import("../../mem.zig").MemProfile;
|
const MemProfile = @import("../../mem.zig").MemProfile;
|
||||||
const tty = @import("../../tty.zig");
|
const tty = @import("../../tty.zig");
|
||||||
|
@ -11,8 +15,6 @@ const log = @import("../../log.zig");
|
||||||
const mem = @import("../../mem.zig");
|
const mem = @import("../../mem.zig");
|
||||||
const vmm = @import("../../vmm.zig");
|
const vmm = @import("../../vmm.zig");
|
||||||
const multiboot = @import("multiboot.zig");
|
const multiboot = @import("multiboot.zig");
|
||||||
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.
|
/// An array of directory entries and page tables. Forms the first level of paging and covers the entire 4GB memory space.
|
||||||
pub const Directory = packed struct {
|
pub const Directory = packed struct {
|
||||||
|
@ -361,27 +363,23 @@ pub fn unmap(virtual_start: usize, virtual_end: usize, dir: *Directory) (std.mem
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN state: *arch.InterruptContext - The CPU's state when the fault occurred.
|
/// IN state: *arch.CpuState - The CPU's state when the fault occurred.
|
||||||
///
|
///
|
||||||
fn pageFault(state: *arch.InterruptContext) void {
|
fn pageFault(state: *arch.CpuState) u32 {
|
||||||
log.logInfo("State: {X}\n", .{state});
|
log.logInfo("State: {X}\n", .{state});
|
||||||
var cr0: u32 = 0;
|
var cr0 = asm volatile ("mov %%cr0, %[cr0]"
|
||||||
var cr2: u32 = 0;
|
: [cr0] "=r" (-> u32)
|
||||||
var cr3: u32 = 0;
|
|
||||||
var cr4: u32 = 0;
|
|
||||||
asm volatile ("mov %%cr0, %[cr0]"
|
|
||||||
: [cr0] "=r" (cr0)
|
|
||||||
);
|
);
|
||||||
asm volatile ("mov %%cr2, %[cr2]"
|
var cr2 = asm volatile ("mov %%cr2, %[cr2]"
|
||||||
: [cr2] "=r" (cr2)
|
: [cr2] "=r" (-> u32)
|
||||||
);
|
);
|
||||||
asm volatile ("mov %%cr3, %[cr3]"
|
var cr3 = asm volatile ("mov %%cr3, %[cr3]"
|
||||||
: [cr3] "=r" (cr3)
|
: [cr3] "=r" (-> u32)
|
||||||
);
|
);
|
||||||
asm volatile ("mov %%cr4, %[cr4]"
|
var cr4 = asm volatile ("mov %%cr4, %[cr4]"
|
||||||
: [cr4] "=r" (cr4)
|
: [cr4] "=r" (-> u32)
|
||||||
);
|
);
|
||||||
log.logInfo("CR0: {X}, CR2: {X}, CR3: {X}, CR4: {X}\n\n", .{ cr0, cr2, cr3, cr4 });
|
log.logInfo("CR0: 0x{X}, CR2: 0x{X}, CR3: 0x{X}, CR4: 0x{X}\n", .{ cr0, cr2, cr3, cr4 });
|
||||||
@panic("Page fault");
|
@panic("Page fault");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,10 +549,12 @@ extern var rt_fault_callback2: *u32;
|
||||||
var faulted = false;
|
var faulted = false;
|
||||||
var use_callback2 = false;
|
var use_callback2 = false;
|
||||||
|
|
||||||
fn rt_pageFault(ctx: *arch.InterruptContext) void {
|
fn rt_pageFault(ctx: *arch.CpuState) u32 {
|
||||||
faulted = true;
|
faulted = true;
|
||||||
// Return to the fault callback
|
// Return to the fault callback
|
||||||
ctx.eip = @ptrToInt(&if (use_callback2) rt_fault_callback2 else rt_fault_callback);
|
ctx.eip = @ptrToInt(&if (use_callback2) rt_fault_callback2 else rt_fault_callback);
|
||||||
|
|
||||||
|
return @ptrToInt(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rt_accessUnmappedMem(v_end: u32) void {
|
fn rt_accessUnmappedMem(v_end: u32) void {
|
||||||
|
@ -592,7 +592,7 @@ fn rt_accessMappedMem(v_end: u32) void {
|
||||||
log.logInfo("Paging: Tested accessing mapped memory\n", .{});
|
log.logInfo("Paging: Tested accessing mapped memory\n", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtimeTests(v_end: u32) void {
|
pub fn runtimeTests(v_end: u32) void {
|
||||||
rt_accessUnmappedMem(v_end);
|
rt_accessUnmappedMem(v_end);
|
||||||
rt_accessMappedMem(v_end);
|
rt_accessMappedMem(v_end);
|
||||||
}
|
}
|
||||||
|
|
|
@ -830,6 +830,6 @@ fn rt_picAllMasked() void {
|
||||||
///
|
///
|
||||||
/// Run all the runtime tests.
|
/// Run all the runtime tests.
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
pub fn runtimeTests() void {
|
||||||
rt_picAllMasked();
|
rt_picAllMasked();
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,11 +231,12 @@ inline fn sendDataToCounter(counter: CounterSelect, data: u8) void {
|
||||||
/// The interrupt handler for the PIT. This will increment a counter for now.
|
/// The interrupt handler for the PIT. This will increment a counter for now.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - Pointer to the interrupt context containing the contents
|
/// IN ctx: *arch.CpuState - Pointer to the interrupt context containing the contents
|
||||||
/// of the register at the time of the interrupt.
|
/// of the register at the time of the interrupt.
|
||||||
///
|
///
|
||||||
fn pitHandler(ctx: *arch.InterruptContext) void {
|
fn pitHandler(ctx: *arch.CpuState) usize {
|
||||||
ticks +%= 1;
|
ticks +%= 1;
|
||||||
|
return @ptrToInt(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -324,25 +325,17 @@ pub fn waitTicks(ticks_to_wait: u32) void {
|
||||||
const wait_ticks2 = ticks_to_wait - wait_ticks1;
|
const wait_ticks2 = ticks_to_wait - wait_ticks1;
|
||||||
|
|
||||||
while (ticks > wait_ticks1) {
|
while (ticks > wait_ticks1) {
|
||||||
arch.enableInterrupts();
|
|
||||||
arch.halt();
|
arch.halt();
|
||||||
arch.disableInterrupts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ticks < wait_ticks2) {
|
while (ticks < wait_ticks2) {
|
||||||
arch.enableInterrupts();
|
|
||||||
arch.halt();
|
arch.halt();
|
||||||
arch.disableInterrupts();
|
|
||||||
}
|
}
|
||||||
arch.enableInterrupts();
|
|
||||||
} else {
|
} else {
|
||||||
const wait_ticks = ticks + ticks_to_wait;
|
const wait_ticks = ticks + ticks_to_wait;
|
||||||
while (ticks < wait_ticks) {
|
while (ticks < wait_ticks) {
|
||||||
arch.enableInterrupts();
|
|
||||||
arch.halt();
|
arch.halt();
|
||||||
arch.disableInterrupts();
|
|
||||||
}
|
}
|
||||||
arch.enableInterrupts();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +628,11 @@ fn rt_initCounter_0() void {
|
||||||
///
|
///
|
||||||
/// Run all the runtime tests.
|
/// Run all the runtime tests.
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
pub fn runtimeTests() void {
|
||||||
|
// Interrupts aren't enabled yet, so for the runtime tests, enable it temporary
|
||||||
|
arch.enableInterrupts();
|
||||||
|
defer arch.disableInterrupts();
|
||||||
|
|
||||||
rt_initCounter_0();
|
rt_initCounter_0();
|
||||||
rt_waitTicks();
|
rt_waitTicks();
|
||||||
rt_waitTicks2();
|
rt_waitTicks2();
|
||||||
|
|
|
@ -6,13 +6,14 @@ const expectEqual = std.testing.expectEqual;
|
||||||
const expectError = std.testing.expectError;
|
const expectError = std.testing.expectError;
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
const mock_path = build_options.arch_mock_path;
|
const mock_path = build_options.arch_mock_path;
|
||||||
const arch = @import("arch.zig");
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const pic = @import("pic.zig");
|
const pic = @import("pic.zig");
|
||||||
const pit = @import("pit.zig");
|
const pit = @import("pit.zig");
|
||||||
const irq = @import("irq.zig");
|
const irq = @import("irq.zig");
|
||||||
const cmos = if (is_test) @import(mock_path ++ "cmos_mock.zig") else @import("cmos.zig");
|
const cmos = if (is_test) @import(mock_path ++ "cmos_mock.zig") else @import("cmos.zig");
|
||||||
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic;
|
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic;
|
||||||
|
const scheduler = @import("../../scheduler.zig");
|
||||||
|
|
||||||
/// The Century register is unreliable. We need a APIC interface to infer if we have a century
|
/// The Century register is unreliable. We need a APIC interface to infer if we have a century
|
||||||
/// register. So this is a current TODO.
|
/// register. So this is a current TODO.
|
||||||
|
@ -44,6 +45,8 @@ const RtcError = error{
|
||||||
/// The number of ticks that has passed when RTC was initially set up.
|
/// The number of ticks that has passed when RTC was initially set up.
|
||||||
var ticks: u32 = 0;
|
var ticks: u32 = 0;
|
||||||
|
|
||||||
|
var schedule: bool = true;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Checks if the CMOS chip isn't updating the RTC registers. Call this before reading any RTC
|
/// Checks if the CMOS chip isn't updating the RTC registers. Call this before reading any RTC
|
||||||
/// registers so don't get inconsistent values.
|
/// registers so don't get inconsistent values.
|
||||||
|
@ -206,14 +209,26 @@ fn readRtc() DateTime {
|
||||||
/// The interrupt handler for the RTC.
|
/// The interrupt handler for the RTC.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - Pointer to the interrupt context containing the contents
|
/// IN ctx: *arch.CpuState - Pointer to the interrupt context containing the contents
|
||||||
/// of the register at the time of the interrupt.
|
/// of the register at the time of the interrupt.
|
||||||
///
|
///
|
||||||
fn rtcHandler(ctx: *arch.InterruptContext) void {
|
fn rtcHandler(ctx: *arch.CpuState) usize {
|
||||||
ticks +%= 1;
|
ticks +%= 1;
|
||||||
|
|
||||||
|
var ret_esp: usize = undefined;
|
||||||
|
|
||||||
|
// Call the scheduler
|
||||||
|
if (schedule) {
|
||||||
|
ret_esp = scheduler.pickNextTask(ctx);
|
||||||
|
} else {
|
||||||
|
ret_esp = @ptrToInt(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// Need to read status register C
|
// Need to read status register C
|
||||||
|
// Might need to disable the NMI bit, set to true
|
||||||
const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
|
const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
|
||||||
|
|
||||||
|
return ret_esp;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -264,9 +279,6 @@ pub fn init() void {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Need to disable interrupts went setting up the RTC
|
|
||||||
arch.disableInterrupts();
|
|
||||||
|
|
||||||
// Set the interrupt rate to 512Hz
|
// Set the interrupt rate to 512Hz
|
||||||
setRate(7) catch |err| switch (err) {
|
setRate(7) catch |err| switch (err) {
|
||||||
error.RateError => {
|
error.RateError => {
|
||||||
|
@ -277,9 +289,6 @@ pub fn init() void {
|
||||||
// Enable RTC interrupts
|
// Enable RTC interrupts
|
||||||
enableInterrupts();
|
enableInterrupts();
|
||||||
|
|
||||||
// Can now enable interrupts
|
|
||||||
arch.enableInterrupts();
|
|
||||||
|
|
||||||
// Read status register C to clear any interrupts that may have happened during set up
|
// Read status register C to clear any interrupts that may have happened during set up
|
||||||
const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
|
const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
|
||||||
|
|
||||||
|
@ -739,7 +748,15 @@ fn rt_interrupts() void {
|
||||||
///
|
///
|
||||||
/// Run all the runtime tests.
|
/// Run all the runtime tests.
|
||||||
///
|
///
|
||||||
fn runtimeTests() void {
|
pub fn runtimeTests() void {
|
||||||
rt_init();
|
rt_init();
|
||||||
|
|
||||||
|
// Disable the scheduler temporary
|
||||||
|
schedule = false;
|
||||||
|
// Interrupts aren't enabled yet, so for the runtime tests, enable it temporary
|
||||||
|
arch.enableInterrupts();
|
||||||
rt_interrupts();
|
rt_interrupts();
|
||||||
|
arch.disableInterrupts();
|
||||||
|
// Can enable it back
|
||||||
|
schedule = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
const arch = @import("arch.zig");
|
const std = @import("std");
|
||||||
const testing = @import("std").testing;
|
const builtin = @import("builtin");
|
||||||
const assert = @import("std").debug.assert;
|
const is_test = builtin.is_test;
|
||||||
|
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");
|
||||||
|
const testing = std.testing;
|
||||||
|
const expect = std.testing.expect;
|
||||||
const isr = @import("isr.zig");
|
const isr = @import("isr.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const build_options = @import("build_options");
|
|
||||||
const panic = @import("../../panic.zig").panic;
|
const panic = @import("../../panic.zig").panic;
|
||||||
|
|
||||||
/// The isr number associated with syscalls
|
/// The isr number associated with syscalls
|
||||||
|
@ -13,7 +17,7 @@ pub const INTERRUPT: u16 = 0x80;
|
||||||
pub const NUM_HANDLERS: u16 = 256;
|
pub const NUM_HANDLERS: u16 = 256;
|
||||||
|
|
||||||
/// A syscall handler
|
/// A syscall handler
|
||||||
pub const SyscallHandler = fn (ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32;
|
pub const SyscallHandler = fn (ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32;
|
||||||
|
|
||||||
/// Errors that syscall utility functions can throw
|
/// Errors that syscall utility functions can throw
|
||||||
pub const SyscallError = error{
|
pub const SyscallError = error{
|
||||||
|
@ -44,10 +48,10 @@ pub fn isValidSyscall(syscall: u32) bool {
|
||||||
/// warning is logged.
|
/// warning is logged.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - The cpu context when the syscall was triggered. The
|
/// IN ctx: *arch.CpuState - The cpu context when the syscall was triggered. The
|
||||||
/// syscall number is stored in eax.
|
/// syscall number is stored in eax.
|
||||||
///
|
///
|
||||||
fn handle(ctx: *arch.InterruptContext) void {
|
fn handle(ctx: *arch.CpuState) u32 {
|
||||||
// The syscall number is put in eax
|
// The syscall number is put in eax
|
||||||
const syscall = ctx.eax;
|
const syscall = ctx.eax;
|
||||||
if (isValidSyscall(syscall)) {
|
if (isValidSyscall(syscall)) {
|
||||||
|
@ -59,6 +63,7 @@ fn handle(ctx: *arch.InterruptContext) void {
|
||||||
} else {
|
} else {
|
||||||
log.logWarning("Syscall {} is invalid\n", .{syscall});
|
log.logWarning("Syscall {} is invalid\n", .{syscall});
|
||||||
}
|
}
|
||||||
|
return @ptrToInt(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -217,13 +222,13 @@ inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg
|
||||||
/// 3 => esi and 4 => edi.
|
/// 3 => esi and 4 => edi.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN ctx: *arch.InterruptContext - The interrupt context from which to get the argument
|
/// IN ctx: *arch.CpuState - 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
|
/// Return: u32
|
||||||
/// The syscall argument from the given index.
|
/// The syscall argument from the given index.
|
||||||
///
|
///
|
||||||
inline fn syscallArg(ctx: *arch.InterruptContext, comptime arg_idx: u32) u32 {
|
inline fn syscallArg(ctx: *arch.CpuState, comptime arg_idx: u32) u32 {
|
||||||
return switch (arg_idx) {
|
return switch (arg_idx) {
|
||||||
0 => ctx.ebx,
|
0 => ctx.ebx,
|
||||||
1 => ctx.ecx,
|
1 => ctx.ecx,
|
||||||
|
@ -252,32 +257,32 @@ pub fn init() void {
|
||||||
/// Tests
|
/// Tests
|
||||||
var test_int: u32 = 0;
|
var test_int: u32 = 0;
|
||||||
|
|
||||||
fn testHandler0(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler0(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
||||||
test_int += 1;
|
test_int += 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler1(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler1(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
||||||
test_int += arg1;
|
test_int += arg1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler2(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler2(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
||||||
test_int += arg1 + arg2;
|
test_int += arg1 + arg2;
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler3(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler3(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
||||||
test_int += arg1 + arg2 + arg3;
|
test_int += arg1 + arg2 + arg3;
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler4(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler4(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
||||||
test_int += arg1 + arg2 + arg3 + arg4;
|
test_int += arg1 + arg2 + arg3 + arg4;
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHandler5(ctx: *arch.InterruptContext, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
fn testHandler5(ctx: *arch.CpuState, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
|
||||||
test_int += arg1 + arg2 + arg3 + arg4 + arg5;
|
test_int += arg1 + arg2 + arg3 + arg4 + arg5;
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
@ -287,7 +292,7 @@ test "registerSyscall returns SyscallExists" {
|
||||||
registerSyscall(123, testHandler0) catch |err| {
|
registerSyscall(123, testHandler0) catch |err| {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
assert(false);
|
expect(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtimeTests() void {
|
fn runtimeTests() void {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// A comptime bitmap that uses a specific type to store the entries. No allocators needed.
|
/// A comptime bitmap that uses a specific type to store the entries. No allocators needed.
|
||||||
|
@ -613,6 +614,7 @@ test "setFirstFree multiple bitmaps" {
|
||||||
testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL);
|
testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL);
|
||||||
testing.expectEqual(bmp.bitmaps[1], 1);
|
testing.expectEqual(bmp.bitmaps[1], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "setFirstFree" {
|
test "setFirstFree" {
|
||||||
var bmp = try Bitmap(u32).init(32, std.heap.page_allocator);
|
var bmp = try Bitmap(u32).init(32, std.heap.page_allocator);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ 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 mock_path = build_options.mock_path;
|
||||||
const vmm = if (is_test) @import(mock_path ++ "vmm_mock.zig") else @import("vmm.zig");
|
const vmm = if (is_test) @import(mock_path ++ "vmm_mock.zig") else @import("vmm.zig");
|
||||||
const log = @import("log.zig");
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("log.zig");
|
||||||
const panic = @import("panic.zig").panic;
|
const panic = @import("panic.zig").panic;
|
||||||
|
|
||||||
const FreeListAllocator = struct {
|
const FreeListAllocator = struct {
|
||||||
|
|
|
@ -12,7 +12,9 @@ const serial = @import("serial.zig");
|
||||||
const vmm = if (is_test) @import(mock_path ++ "vmm_mock.zig") else @import("vmm.zig");
|
const vmm = if (is_test) @import(mock_path ++ "vmm_mock.zig") else @import("vmm.zig");
|
||||||
const mem = if (is_test) @import(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") else @import("panic.zig");
|
const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig") else @import("panic.zig");
|
||||||
|
const task = if (is_test) @import(mock_path ++ "task_mock.zig") else @import("task.zig");
|
||||||
const heap = @import("heap.zig");
|
const heap = @import("heap.zig");
|
||||||
|
const scheduler = @import("scheduler.zig");
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
if (!is_test) {
|
if (!is_test) {
|
||||||
|
@ -28,7 +30,14 @@ var kernel_vmm: vmm.VirtualMemoryManager(arch.VmmPayload) = undefined;
|
||||||
|
|
||||||
// This is for unit testing as we need to export KERNEL_ADDR_OFFSET as it is no longer available
|
// This is for unit testing as we need to export KERNEL_ADDR_OFFSET as it is no longer available
|
||||||
// from the linker script
|
// from the linker script
|
||||||
|
// These will need to be kept up to date with the debug logs in the mem init.
|
||||||
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;
|
||||||
|
export var KERNEL_STACK_START: u32 = if (builtin.is_test) 0xC014A000 else undefined;
|
||||||
|
export var KERNEL_STACK_END: u32 = if (builtin.is_test) 0xC014E000 else undefined;
|
||||||
|
export var KERNEL_VADDR_START: u32 = if (builtin.is_test) 0xC0100000 else undefined;
|
||||||
|
export var KERNEL_VADDR_END: u32 = if (builtin.is_test) 0xC014E000 else undefined;
|
||||||
|
export var KERNEL_PHYSADDR_START: u32 = if (builtin.is_test) 0x100000 else undefined;
|
||||||
|
export var KERNEL_PHYSADDR_END: u32 = if (builtin.is_test) 0x14E000 else undefined;
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -64,10 +73,39 @@ export fn kmain(boot_payload: arch.BootPayload) void {
|
||||||
var kernel_heap = heap.init(arch.VmmPayload, &kernel_vmm, vmm.Attributes{ .kernel = true, .writable = true, .cachable = true }, heap_size) catch |e| {
|
var kernel_heap = heap.init(arch.VmmPayload, &kernel_vmm, vmm.Attributes{ .kernel = true, .writable = true, .cachable = true }, heap_size) catch |e| {
|
||||||
panic_root.panic(@errorReturnTrace(), "Failed to initialise kernel heap: {}\n", .{e});
|
panic_root.panic(@errorReturnTrace(), "Failed to initialise kernel heap: {}\n", .{e});
|
||||||
};
|
};
|
||||||
|
|
||||||
tty.init(&kernel_heap.allocator, boot_payload);
|
tty.init(&kernel_heap.allocator, boot_payload);
|
||||||
|
|
||||||
|
scheduler.init(&kernel_heap.allocator) catch |e| {
|
||||||
|
panic_root.panic(@errorReturnTrace(), "Failed to initialise scheduler: {}", .{e});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialisation is finished, now does other stuff
|
||||||
log.logInfo("Init done\n", .{});
|
log.logInfo("Init done\n", .{});
|
||||||
|
|
||||||
|
// Main initialisation finished so can enable interrupts
|
||||||
|
arch.enableInterrupts();
|
||||||
|
|
||||||
|
log.logInfo("Creating init2\n", .{});
|
||||||
|
|
||||||
|
// Create a init2 task
|
||||||
|
var idle_task = task.Task.create(initStage2, &kernel_heap.allocator) catch |e| {
|
||||||
|
panic_root.panic(@errorReturnTrace(), "Failed to create init stage 2 task: {}", .{e});
|
||||||
|
};
|
||||||
|
scheduler.scheduleTask(idle_task, &kernel_heap.allocator) catch |e| {
|
||||||
|
panic_root.panic(@errorReturnTrace(), "Failed to schedule init stage 2 task: {}", .{e});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Can't return for now, later this can return maybe
|
||||||
|
// TODO: Maybe make this the idle task
|
||||||
|
arch.spinWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Stage 2 initialisation. This will initialise main kernel features after the architecture
|
||||||
|
/// initialisation.
|
||||||
|
///
|
||||||
|
fn initStage2() noreturn {
|
||||||
tty.clear();
|
tty.clear();
|
||||||
const logo =
|
const logo =
|
||||||
\\ _____ _ _ _ _______ ____
|
\\ _____ _ _ _ _______ ____
|
||||||
|
@ -88,5 +126,6 @@ export fn kmain(boot_payload: arch.BootPayload) void {
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can't return for now, later this can return maybe
|
||||||
arch.spinWait();
|
arch.spinWait();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Serial = @import("serial.zig").Serial;
|
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
|
const Serial = @import("serial.zig").Serial;
|
||||||
|
const scheduler = @import("scheduler.zig");
|
||||||
|
|
||||||
/// The errors that can occur when logging
|
/// The errors that can occur when logging
|
||||||
const LoggingError = error{};
|
const LoggingError = error{};
|
||||||
|
@ -49,7 +50,9 @@ fn logCallback(context: void, str: []const u8) LoggingError!usize {
|
||||||
/// IN args: anytype - A struct of the parameters for the format string.
|
/// IN args: anytype - A struct of the parameters for the format string.
|
||||||
///
|
///
|
||||||
pub fn log(comptime level: Level, comptime format: []const u8, args: anytype) void {
|
pub fn log(comptime level: Level, comptime format: []const u8, args: anytype) void {
|
||||||
|
scheduler.taskSwitching(false);
|
||||||
fmt.format(OutStream{ .context = {} }, "[" ++ @tagName(level) ++ "] " ++ format, args) catch unreachable;
|
fmt.format(OutStream{ .context = {} }, "[" ++ @tagName(level) ++ "] " ++ format, args) catch unreachable;
|
||||||
|
scheduler.taskSwitching(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -118,7 +121,7 @@ pub fn init(ser: Serial) void {
|
||||||
///
|
///
|
||||||
/// The logging runtime tests that will test all logging levels.
|
/// The logging runtime tests that will test all logging levels.
|
||||||
///
|
///
|
||||||
pub fn runtimeTests() void {
|
fn runtimeTests() void {
|
||||||
inline for (@typeInfo(Level).Enum.fields) |field| {
|
inline for (@typeInfo(Level).Enum.fields) |field| {
|
||||||
const level = @field(Level, field.name);
|
const level = @field(Level, field.name);
|
||||||
log(level, "Test " ++ field.name ++ " level\n", .{});
|
log(level, "Test " ++ field.name ++ " level\n", .{});
|
||||||
|
|
|
@ -2,7 +2,7 @@ const is_test = @import("builtin").is_test;
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
const mock_path = build_options.mock_path;
|
const mock_path = build_options.mock_path;
|
||||||
const arch = @import("arch.zig").internals;
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig").internals;
|
||||||
const MemProfile = (if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.zig")).MemProfile;
|
const MemProfile = (if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.zig")).MemProfile;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const panic = @import("panic.zig").panic;
|
const panic = @import("panic.zig").panic;
|
||||||
|
|
360
src/kernel/scheduler.zig
Normal file
360
src/kernel/scheduler.zig
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
const expectError = std.testing.expectError;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const is_test = builtin.is_test;
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const mock_path = build_options.mock_path;
|
||||||
|
const arch = @import("arch.zig").internals;
|
||||||
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("log.zig");
|
||||||
|
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic;
|
||||||
|
const task = if (is_test) @import(mock_path ++ "task_mock.zig") else @import("task.zig");
|
||||||
|
const Task = task.Task;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const TailQueue = std.TailQueue;
|
||||||
|
|
||||||
|
/// The function type for the entry point.
|
||||||
|
const EntryPointFn = fn () void;
|
||||||
|
|
||||||
|
/// The default stack size of a task. Currently this is set to a page size.
|
||||||
|
const STACK_SIZE: u32 = arch.MEMORY_BLOCK_SIZE / @sizeOf(usize);
|
||||||
|
|
||||||
|
/// Pointer to the start of the main kernel stack
|
||||||
|
extern var KERNEL_STACK_START: []u32;
|
||||||
|
|
||||||
|
/// The current task running
|
||||||
|
var current_task: *Task = undefined;
|
||||||
|
|
||||||
|
/// Array list of all runnable tasks
|
||||||
|
var tasks: TailQueue(*Task) = undefined;
|
||||||
|
|
||||||
|
/// Whether the scheduler is allowed to switch tasks.
|
||||||
|
var can_switch: bool = true;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The idle task that just halts the CPU but the CPU can still handle interrupts.
|
||||||
|
///
|
||||||
|
fn idle() noreturn {
|
||||||
|
arch.spinWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn taskSwitching(enabled: bool) void {
|
||||||
|
can_switch = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Round robin. This will first save the the current tasks stack pointer, then will pick the next
|
||||||
|
/// task to be run from the queue. It will add the current task to the end of the queue and pop the
|
||||||
|
/// next task from the front as set this as the current task. Then will return the stack pointer
|
||||||
|
/// of the next task to be loaded into the stack register to load the next task stack to pop off
|
||||||
|
/// its state. Interrupts are assumed disabled.
|
||||||
|
///
|
||||||
|
/// Argument:
|
||||||
|
/// IN ctx: *arch.CpuState - Pointer to the exception context containing the contents
|
||||||
|
/// of the registers at the time of a exception.
|
||||||
|
///
|
||||||
|
/// Return: usize
|
||||||
|
/// The new stack pointer to the next stack of the next task.
|
||||||
|
///
|
||||||
|
pub fn pickNextTask(ctx: *arch.CpuState) usize {
|
||||||
|
// Save the stack pointer from old task
|
||||||
|
current_task.stack_pointer = @ptrToInt(ctx);
|
||||||
|
|
||||||
|
// If we can't switch, then continue with the current task
|
||||||
|
if (!can_switch) {
|
||||||
|
return current_task.stack_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick the next task
|
||||||
|
// If there isn't one, then just return the same task
|
||||||
|
if (tasks.pop()) |new_task_node| {
|
||||||
|
// Get the next task
|
||||||
|
const next_task = new_task_node.data;
|
||||||
|
|
||||||
|
// Move some pointers to don't need to allocate memory, speeds things up
|
||||||
|
new_task_node.data = current_task;
|
||||||
|
new_task_node.prev = null;
|
||||||
|
new_task_node.next = null;
|
||||||
|
|
||||||
|
// Add the 'current_task' node to the end of the queue
|
||||||
|
tasks.prepend(new_task_node);
|
||||||
|
|
||||||
|
current_task = next_task;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context switch in the interrupt stub handler which will pop the next task state off the
|
||||||
|
// stack
|
||||||
|
return current_task.stack_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Create a new task and add it to the scheduling queue. No locking.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN entry_point: EntryPointFn - The entry point into the task. This must be a function.
|
||||||
|
///
|
||||||
|
/// Error: Allocator.Error
|
||||||
|
/// OutOfMemory - If there isn't enough memory for the a task/stack. Any memory allocated will
|
||||||
|
/// be freed on return.
|
||||||
|
///
|
||||||
|
pub fn scheduleTask(new_task: *Task, allocator: *Allocator) Allocator.Error!void {
|
||||||
|
var task_node = try tasks.createNode(new_task, allocator);
|
||||||
|
tasks.prepend(task_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialise the scheduler. This will set up the current task to the code that is currently
|
||||||
|
/// running. So if there is a task switch before kmain can finish, can continue when switched back.
|
||||||
|
/// This will set the stack to KERNEL_STACK_START from the linker stript. This will also create the
|
||||||
|
/// idle task for when there is no more tasks to run.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN allocator: *Allocator - The allocator to use when needing to allocate memory.
|
||||||
|
///
|
||||||
|
/// Error: Allocator.Error
|
||||||
|
/// OutOfMemory - There is no more memory. Any memory allocated will be freed on return.
|
||||||
|
///
|
||||||
|
pub fn init(allocator: *Allocator) Allocator.Error!void {
|
||||||
|
// TODO: Maybe move the task init here?
|
||||||
|
log.logInfo("Init scheduler\n", .{});
|
||||||
|
defer log.logInfo("Done scheduler\n", .{});
|
||||||
|
|
||||||
|
// Init the task list for round robin
|
||||||
|
tasks = TailQueue(*Task).init();
|
||||||
|
|
||||||
|
// Set up the init task to continue execution
|
||||||
|
current_task = try allocator.create(Task);
|
||||||
|
errdefer allocator.destroy(current_task);
|
||||||
|
// PID 0
|
||||||
|
current_task.pid = 0;
|
||||||
|
current_task.stack = @intToPtr([*]u32, @ptrToInt(&KERNEL_STACK_START))[0..4096];
|
||||||
|
// ESP will be saved on next schedule
|
||||||
|
|
||||||
|
// Run the runtime tests here
|
||||||
|
switch (build_options.test_mode) {
|
||||||
|
.Scheduler => runtimeTests(allocator),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the idle task when there are no more tasks left
|
||||||
|
var idle_task = try Task.create(idle, allocator);
|
||||||
|
errdefer idle_task.destroy(allocator);
|
||||||
|
|
||||||
|
try scheduleTask(idle_task, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing the errdefer
|
||||||
|
const FailingAllocator = std.testing.FailingAllocator;
|
||||||
|
const testing_allocator = &std.testing.base_allocator_instance.allocator;
|
||||||
|
|
||||||
|
fn test_fn1() void {}
|
||||||
|
fn test_fn2() void {}
|
||||||
|
|
||||||
|
var test_pid_counter: u7 = 1;
|
||||||
|
|
||||||
|
fn task_create(entry_point: EntryPointFn, allocator: *Allocator) Allocator.Error!*Task {
|
||||||
|
var t = try allocator.create(Task);
|
||||||
|
errdefer allocator.destroy(t);
|
||||||
|
t.pid = test_pid_counter;
|
||||||
|
// Just alloc something
|
||||||
|
t.stack = try allocator.alloc(u32, 1);
|
||||||
|
t.stack_pointer = 0;
|
||||||
|
test_pid_counter += 1;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task_destroy(self: *Task, allocator: *Allocator) void {
|
||||||
|
if (@ptrToInt(self.stack.ptr) != @ptrToInt(&KERNEL_STACK_START)) {
|
||||||
|
allocator.free(self.stack);
|
||||||
|
}
|
||||||
|
allocator.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "pickNextTask" {
|
||||||
|
task.initTest();
|
||||||
|
defer task.freeTest();
|
||||||
|
|
||||||
|
task.addConsumeFunction("Task.create", task_create);
|
||||||
|
task.addConsumeFunction("Task.create", task_create);
|
||||||
|
task.addRepeatFunction("Task.destroy", task_destroy);
|
||||||
|
|
||||||
|
var ctx: arch.CpuState = std.mem.zeroes(arch.CpuState);
|
||||||
|
|
||||||
|
var allocator = std.testing.allocator;
|
||||||
|
tasks = TailQueue(*Task).init();
|
||||||
|
|
||||||
|
// Set up a current task
|
||||||
|
current_task = try allocator.create(Task);
|
||||||
|
defer allocator.destroy(current_task);
|
||||||
|
current_task.pid = 0;
|
||||||
|
current_task.stack = @intToPtr([*]u32, @ptrToInt(&KERNEL_STACK_START))[0..4096];
|
||||||
|
current_task.stack_pointer = @ptrToInt(&KERNEL_STACK_START);
|
||||||
|
|
||||||
|
// Create two tasks and schedule them
|
||||||
|
var test_fn1_task = try Task.create(test_fn1, allocator);
|
||||||
|
defer test_fn1_task.destroy(allocator);
|
||||||
|
try scheduleTask(test_fn1_task, allocator);
|
||||||
|
|
||||||
|
var test_fn2_task = try Task.create(test_fn2, allocator);
|
||||||
|
defer test_fn2_task.destroy(allocator);
|
||||||
|
try scheduleTask(test_fn2_task, allocator);
|
||||||
|
|
||||||
|
// Get the stack pointers of the created tasks
|
||||||
|
const fn1_stack_pointer = tasks.first.?.data.stack_pointer;
|
||||||
|
const fn2_stack_pointer = tasks.first.?.next.?.data.stack_pointer;
|
||||||
|
|
||||||
|
expectEqual(pickNextTask(&ctx), fn1_stack_pointer);
|
||||||
|
// The stack pointer of the re-added task should point to the context
|
||||||
|
expectEqual(tasks.first.?.data.stack_pointer, @ptrToInt(&ctx));
|
||||||
|
|
||||||
|
// Should be the PID of the next task
|
||||||
|
expectEqual(current_task.pid, 1);
|
||||||
|
|
||||||
|
expectEqual(pickNextTask(&ctx), fn2_stack_pointer);
|
||||||
|
// The stack pointer of the re-added task should point to the context
|
||||||
|
expectEqual(tasks.first.?.data.stack_pointer, @ptrToInt(&ctx));
|
||||||
|
|
||||||
|
// Should be the PID of the next task
|
||||||
|
expectEqual(current_task.pid, 2);
|
||||||
|
|
||||||
|
expectEqual(pickNextTask(&ctx), @ptrToInt(&ctx));
|
||||||
|
// The stack pointer of the re-added task should point to the context
|
||||||
|
expectEqual(tasks.first.?.data.stack_pointer, @ptrToInt(&ctx));
|
||||||
|
|
||||||
|
// Should be back tot he beginning
|
||||||
|
expectEqual(current_task.pid, 0);
|
||||||
|
|
||||||
|
// Reset the test pid
|
||||||
|
test_pid_counter = 1;
|
||||||
|
|
||||||
|
// Free the queue
|
||||||
|
while (tasks.pop()) |elem| {
|
||||||
|
tasks.destroyNode(elem, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "createNewTask add new task" {
|
||||||
|
task.initTest();
|
||||||
|
defer task.freeTest();
|
||||||
|
|
||||||
|
task.addConsumeFunction("Task.create", task_create);
|
||||||
|
task.addConsumeFunction("Task.destroy", task_destroy);
|
||||||
|
|
||||||
|
// Set the global allocator
|
||||||
|
var allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
// Init the task list
|
||||||
|
tasks = TailQueue(*Task).init();
|
||||||
|
|
||||||
|
var test_fn1_task = try Task.create(test_fn1, allocator);
|
||||||
|
defer test_fn1_task.destroy(allocator);
|
||||||
|
try scheduleTask(test_fn1_task, allocator);
|
||||||
|
|
||||||
|
expectEqual(tasks.len, 1);
|
||||||
|
|
||||||
|
// Free the memory
|
||||||
|
tasks.destroyNode(tasks.first.?, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "init" {
|
||||||
|
task.initTest();
|
||||||
|
defer task.freeTest();
|
||||||
|
|
||||||
|
task.addConsumeFunction("Task.create", task_create);
|
||||||
|
task.addRepeatFunction("Task.destroy", task_destroy);
|
||||||
|
|
||||||
|
var allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
try init(allocator);
|
||||||
|
|
||||||
|
expectEqual(current_task.pid, 0);
|
||||||
|
expectEqual(current_task.stack, @intToPtr([*]u32, @ptrToInt(&KERNEL_STACK_START))[0..4096]);
|
||||||
|
|
||||||
|
expectEqual(tasks.len, 1);
|
||||||
|
|
||||||
|
// Free the tasks created
|
||||||
|
current_task.destroy(allocator);
|
||||||
|
while (tasks.pop()) |elem| {
|
||||||
|
elem.data.destroy(allocator);
|
||||||
|
tasks.destroyNode(elem, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A volatile pointer used to control a loop outside the task. This is so to ensure a task switch
|
||||||
|
/// ocurred.
|
||||||
|
var is_set: *volatile bool = undefined;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The test task function.
|
||||||
|
///
|
||||||
|
fn task_function() noreturn {
|
||||||
|
log.logInfo("Switched\n", .{});
|
||||||
|
is_set.* = false;
|
||||||
|
while (true) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// This tests that variables in registers and on the stack are preserved when a task switch
|
||||||
|
/// occurs. Also tests that a global volatile can be test in one task and be reacted to in another.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN allocator: *Allocator - The allocator to use when needing to allocate memory.
|
||||||
|
///
|
||||||
|
fn rt_variable_preserved(allocator: *Allocator) void {
|
||||||
|
// Create the memory for the boolean
|
||||||
|
is_set = allocator.create(bool) catch unreachable;
|
||||||
|
defer allocator.destroy(is_set);
|
||||||
|
is_set.* = true;
|
||||||
|
|
||||||
|
var test_task = Task.create(task_function, allocator) catch unreachable;
|
||||||
|
scheduleTask(test_task, allocator) catch unreachable;
|
||||||
|
// TODO: Need to add the ability to remove tasks
|
||||||
|
|
||||||
|
var w: u32 = 0;
|
||||||
|
var x: u32 = 1;
|
||||||
|
var y: u32 = 2;
|
||||||
|
var z: u32 = 3;
|
||||||
|
|
||||||
|
while (is_set.*) {
|
||||||
|
if (w != 0) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: w not 0, but: {}\n", .{w});
|
||||||
|
}
|
||||||
|
if (x != 1) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: x not 1, but: {}\n", .{x});
|
||||||
|
}
|
||||||
|
if (y != 2) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: y not 2, but: {}\n", .{y});
|
||||||
|
}
|
||||||
|
if (z != 3) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: z not 3, but: {}\n", .{z});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure these are the same values
|
||||||
|
if (w != 0) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: w not 0, but: {}\n", .{w});
|
||||||
|
}
|
||||||
|
if (x != 1) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: x not 1, but: {}\n", .{x});
|
||||||
|
}
|
||||||
|
if (y != 2) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: y not 2, but: {}\n", .{y});
|
||||||
|
}
|
||||||
|
if (z != 3) {
|
||||||
|
panic(@errorReturnTrace(), "FAILED: z not 3, but: {}\n", .{z});
|
||||||
|
}
|
||||||
|
|
||||||
|
log.logInfo("SUCCESS: Scheduler variables preserved\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The scheduler runtime tests that will test the scheduling functionality.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN allocator: *Allocator - The allocator to use when needing to allocate memory.
|
||||||
|
///
|
||||||
|
fn runtimeTests(allocator: *Allocator) void {
|
||||||
|
arch.enableInterrupts();
|
||||||
|
rt_variable_preserved(allocator);
|
||||||
|
while (true) {}
|
||||||
|
}
|
209
src/kernel/task.zig
Normal file
209
src/kernel/task.zig
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
const expectError = std.testing.expectError;
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const is_test = builtin.is_test;
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const mock_path = build_options.mock_path;
|
||||||
|
const arch = @import("arch.zig").internals;
|
||||||
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("log.zig");
|
||||||
|
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic;
|
||||||
|
const ComptimeBitmap = @import("bitmap.zig").ComptimeBitmap;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
/// The kernels main stack start as this is used to check for if the task being destroyed is this stack
|
||||||
|
/// as we cannot deallocate this.
|
||||||
|
extern var KERNEL_STACK_START: *u32;
|
||||||
|
|
||||||
|
/// The function type for the entry point.
|
||||||
|
const EntryPointFn = fn () void;
|
||||||
|
|
||||||
|
/// The bitmap type for the PIDs
|
||||||
|
const PidBitmap = if (is_test) ComptimeBitmap(u128) else ComptimeBitmap(u1024);
|
||||||
|
|
||||||
|
/// The list of PIDs that have been allocated.
|
||||||
|
var all_pids: PidBitmap = brk: {
|
||||||
|
var pids = PidBitmap.init();
|
||||||
|
// Set the first PID as this is for the current task running, init 0
|
||||||
|
_ = pids.setFirstFree() orelse unreachable;
|
||||||
|
break :brk pids;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The task control block for storing all the information needed to save and restore a task.
|
||||||
|
pub const Task = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// The unique task identifier
|
||||||
|
pid: PidBitmap.IndexType,
|
||||||
|
|
||||||
|
/// Pointer to the stack for the task. This will be allocated on initialisation.
|
||||||
|
stack: []u32,
|
||||||
|
|
||||||
|
/// The current stack pointer into the stack.
|
||||||
|
stack_pointer: usize,
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Create a task. This will allocate a PID and the stack. The stack will be set up as a
|
||||||
|
/// kernel task. As this is a new task, the stack will need to be initialised with the CPU
|
||||||
|
/// state as described in arch.CpuState struct.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN entry_point: EntryPointFn - The entry point into the task. This must be a function.
|
||||||
|
/// IN allocator: *Allocator - The allocator for allocating memory for a task.
|
||||||
|
///
|
||||||
|
/// Return: *Task
|
||||||
|
/// Pointer to an allocated task. This will then need to be added to the task queue.
|
||||||
|
///
|
||||||
|
/// Error: Allocator.Error
|
||||||
|
/// OutOfMemory - If there is no more memory to allocate. Any memory or PID allocated will
|
||||||
|
/// be freed on return.
|
||||||
|
///
|
||||||
|
pub fn create(entry_point: EntryPointFn, allocator: *Allocator) Allocator.Error!*Task {
|
||||||
|
var task = try allocator.create(Task);
|
||||||
|
errdefer allocator.destroy(task);
|
||||||
|
|
||||||
|
task.pid = allocatePid();
|
||||||
|
errdefer freePid(task.pid);
|
||||||
|
|
||||||
|
const task_stack = try arch.initTaskStack(@ptrToInt(entry_point), allocator);
|
||||||
|
task.stack = task_stack.stack;
|
||||||
|
task.stack_pointer = task_stack.pointer;
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Destroy the task. This will release the allocated PID and free the stack and self.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN/OUT self: *Self - The pointer to self.
|
||||||
|
///
|
||||||
|
pub fn destroy(self: *Self, allocator: *Allocator) void {
|
||||||
|
freePid(self.pid);
|
||||||
|
// We need to check that the the stack has been allocated as task 0 (init) won't have a
|
||||||
|
// stack allocated as this in the linker script
|
||||||
|
if (@ptrToInt(self.stack.ptr) != @ptrToInt(&KERNEL_STACK_START)) {
|
||||||
|
allocator.free(self.stack);
|
||||||
|
}
|
||||||
|
allocator.destroy(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Allocate a process identifier. If out of PIDs, then will panic. Is this occurs, will need to
|
||||||
|
/// increase the bitmap.
|
||||||
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// A new PID.
|
||||||
|
///
|
||||||
|
fn allocatePid() PidBitmap.IndexType {
|
||||||
|
return all_pids.setFirstFree() orelse panic(@errorReturnTrace(), "Out of PIDs\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Free an allocated PID. One must be allocated to be freed. If one wasn't allocated will panic.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN pid: u32 - The PID to free.
|
||||||
|
///
|
||||||
|
fn freePid(pid: PidBitmap.IndexType) void {
|
||||||
|
if (!all_pids.isSet(pid)) {
|
||||||
|
panic(@errorReturnTrace(), "PID {} not allocated\n", .{pid});
|
||||||
|
}
|
||||||
|
all_pids.clearEntry(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing the errdefer
|
||||||
|
const FailingAllocator = std.testing.FailingAllocator;
|
||||||
|
const testing_allocator = &std.testing.base_allocator_instance.allocator;
|
||||||
|
|
||||||
|
fn test_fn1() void {}
|
||||||
|
|
||||||
|
test "create out of memory for task" {
|
||||||
|
// Set the global allocator
|
||||||
|
var fa = FailingAllocator.init(testing_allocator, 0);
|
||||||
|
|
||||||
|
expectError(error.OutOfMemory, Task.create(test_fn1, &fa.allocator));
|
||||||
|
|
||||||
|
// Make sure any memory allocated is freed
|
||||||
|
expectEqual(fa.allocated_bytes, fa.freed_bytes);
|
||||||
|
|
||||||
|
// Make sure no PIDs were allocated
|
||||||
|
expectEqual(all_pids.bitmap, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "create out of memory for stack" {
|
||||||
|
// Set the global allocator
|
||||||
|
var fa = FailingAllocator.init(testing_allocator, 1);
|
||||||
|
|
||||||
|
expectError(error.OutOfMemory, Task.create(test_fn1, &fa.allocator));
|
||||||
|
|
||||||
|
// Make sure any memory allocated is freed
|
||||||
|
expectEqual(fa.allocated_bytes, fa.freed_bytes);
|
||||||
|
|
||||||
|
// Make sure no PIDs were allocated
|
||||||
|
expectEqual(all_pids.bitmap, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "create expected setup" {
|
||||||
|
var task = try Task.create(test_fn1, std.testing.allocator);
|
||||||
|
defer task.destroy(std.testing.allocator);
|
||||||
|
|
||||||
|
// Will allocate the first PID 1, 0 will always be allocated
|
||||||
|
expectEqual(task.pid, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "destroy cleans up" {
|
||||||
|
// This used the leak detector allocator in testing
|
||||||
|
// So if any alloc were not freed, this will fail the test
|
||||||
|
var fa = FailingAllocator.init(testing_allocator, 2);
|
||||||
|
|
||||||
|
var task = try Task.create(test_fn1, &fa.allocator);
|
||||||
|
|
||||||
|
task.destroy(&fa.allocator);
|
||||||
|
|
||||||
|
// Make sure any memory allocated is freed
|
||||||
|
expectEqual(fa.allocated_bytes, fa.freed_bytes);
|
||||||
|
|
||||||
|
// All PIDs were freed
|
||||||
|
expectEqual(all_pids.bitmap, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Multiple create" {
|
||||||
|
var task1 = try Task.create(test_fn1, std.testing.allocator);
|
||||||
|
var task2 = try Task.create(test_fn1, std.testing.allocator);
|
||||||
|
|
||||||
|
expectEqual(task1.pid, 1);
|
||||||
|
expectEqual(task2.pid, 2);
|
||||||
|
expectEqual(all_pids.bitmap, 7);
|
||||||
|
|
||||||
|
task1.destroy(std.testing.allocator);
|
||||||
|
|
||||||
|
expectEqual(all_pids.bitmap, 5);
|
||||||
|
|
||||||
|
var task3 = try Task.create(test_fn1, std.testing.allocator);
|
||||||
|
|
||||||
|
expectEqual(task3.pid, 1);
|
||||||
|
expectEqual(all_pids.bitmap, 7);
|
||||||
|
|
||||||
|
task2.destroy(std.testing.allocator);
|
||||||
|
task3.destroy(std.testing.allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "allocatePid and freePid" {
|
||||||
|
expectEqual(all_pids.bitmap, 1);
|
||||||
|
|
||||||
|
var i: usize = 1;
|
||||||
|
while (i < PidBitmap.NUM_ENTRIES) : (i += 1) {
|
||||||
|
expectEqual(i, allocatePid());
|
||||||
|
}
|
||||||
|
|
||||||
|
expectEqual(all_pids.bitmap, PidBitmap.BITMAP_FULL);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < PidBitmap.NUM_ENTRIES) : (i += 1) {
|
||||||
|
freePid(@truncate(PidBitmap.IndexType, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
expectEqual(all_pids.bitmap, 0);
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ const paging = @import("paging_mock.zig");
|
||||||
const Serial = @import("../../../src/kernel/serial.zig").Serial;
|
const Serial = @import("../../../src/kernel/serial.zig").Serial;
|
||||||
const TTY = @import("../../../src/kernel/tty.zig").TTY;
|
const TTY = @import("../../../src/kernel/tty.zig").TTY;
|
||||||
|
|
||||||
|
pub const task = @import("task_mock.zig");
|
||||||
|
|
||||||
const mock_framework = @import("mock_framework.zig");
|
const mock_framework = @import("mock_framework.zig");
|
||||||
pub const initTest = mock_framework.initTest;
|
pub const initTest = mock_framework.initTest;
|
||||||
pub const freeTest = mock_framework.freeTest;
|
pub const freeTest = mock_framework.freeTest;
|
||||||
|
@ -16,7 +18,8 @@ pub const addTestParams = mock_framework.addTestParams;
|
||||||
pub const addConsumeFunction = mock_framework.addConsumeFunction;
|
pub const addConsumeFunction = mock_framework.addConsumeFunction;
|
||||||
pub const addRepeatFunction = mock_framework.addRepeatFunction;
|
pub const addRepeatFunction = mock_framework.addRepeatFunction;
|
||||||
|
|
||||||
pub const InterruptContext = struct {
|
pub const CpuState = struct {
|
||||||
|
ss: u32,
|
||||||
gs: u32,
|
gs: u32,
|
||||||
fs: u32,
|
fs: u32,
|
||||||
es: u32,
|
es: u32,
|
||||||
|
@ -35,14 +38,16 @@ pub const InterruptContext = struct {
|
||||||
cs: u32,
|
cs: u32,
|
||||||
eflags: u32,
|
eflags: u32,
|
||||||
user_esp: u32,
|
user_esp: u32,
|
||||||
ss: u32,
|
user_ss: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const VmmPayload = u8;
|
pub const VmmPayload = u8;
|
||||||
pub const KERNEL_VMM_PAYLOAD: usize = 0;
|
pub const KERNEL_VMM_PAYLOAD: usize = 0;
|
||||||
pub const MEMORY_BLOCK_SIZE: u32 = paging.PAGE_SIZE_4KB;
|
pub const MEMORY_BLOCK_SIZE: u32 = paging.PAGE_SIZE_4KB;
|
||||||
|
pub const STACK_SIZE: u32 = MEMORY_BLOCK_SIZE / @sizeOf(u32);
|
||||||
pub const VMM_MAPPER: vmm.Mapper(VmmPayload) = undefined;
|
pub const VMM_MAPPER: vmm.Mapper(VmmPayload) = undefined;
|
||||||
pub const BootPayload = u8;
|
pub const BootPayload = u8;
|
||||||
|
pub const Task = task.Task;
|
||||||
|
|
||||||
// The virtual/physical start/end of the kernel code
|
// The virtual/physical start/end of the kernel code
|
||||||
var KERNEL_PHYSADDR_START: u32 = 0x00100000;
|
var KERNEL_PHYSADDR_START: u32 = 0x00100000;
|
||||||
|
@ -132,8 +137,13 @@ pub fn initMem(payload: BootPayload) std.mem.Allocator.Error!mem.MemProfile {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initTaskStack(entry_point: usize, allocator: *Allocator) Allocator.Error!struct { stack: []u32, pointer: usize } {
|
||||||
|
const ret = .{ .stack = &([_]u32{}), .pointer = 0 };
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(payload: BootPayload, mem_profile: *const MemProfile, allocator: *Allocator) void {
|
pub fn init(payload: BootPayload, mem_profile: *const MemProfile, allocator: *Allocator) void {
|
||||||
// I'll get back to this as this doesn't effect the GDT testing.
|
// I'll get back to this as this doesn't effect the current testing.
|
||||||
// When I come on to the mem.zig testing, I'll fix :)
|
// When I come on to the mem.zig testing, I'll fix :)
|
||||||
//return mock_framework.performAction("init", void, mem_profile, allocator);
|
//return mock_framework.performAction("init", void, mem_profile, allocator);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ const GdtEntry = packed struct {
|
||||||
base_high: u8,
|
base_high: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TtsEntry = packed struct {
|
const Tss = packed struct {
|
||||||
prev_tss: u32,
|
prev_tss: u32,
|
||||||
esp0: u32,
|
esp0: u32,
|
||||||
ss0: u32,
|
ss0: u32,
|
||||||
|
@ -160,10 +160,6 @@ pub const USER_CODE_OFFSET: u16 = 0x18;
|
||||||
pub const USER_DATA_OFFSET: u16 = 0x20;
|
pub const USER_DATA_OFFSET: u16 = 0x20;
|
||||||
pub const TSS_OFFSET: u16 = 0x28;
|
pub const TSS_OFFSET: u16 = 0x28;
|
||||||
|
|
||||||
pub fn setTssStack(esp0: u32) void {
|
|
||||||
return mock_framework.performAction("setTssStack", void, esp0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
return mock_framework.performAction("init", void);
|
return mock_framework.performAction("init", void);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub const addTestParams = mock_framework.addTestParams;
|
||||||
pub const addConsumeFunction = mock_framework.addConsumeFunction;
|
pub const addConsumeFunction = mock_framework.addConsumeFunction;
|
||||||
pub const addRepeatFunction = mock_framework.addRepeatFunction;
|
pub const addRepeatFunction = mock_framework.addRepeatFunction;
|
||||||
|
|
||||||
const IdtEntry = packed struct {
|
pub const IdtEntry = packed struct {
|
||||||
base_low: u16,
|
base_low: u16,
|
||||||
selector: u16,
|
selector: u16,
|
||||||
zero: u8,
|
zero: u8,
|
||||||
|
@ -34,10 +34,14 @@ const PRIVILEGE_RING_1: u2 = 0x1;
|
||||||
const PRIVILEGE_RING_2: u2 = 0x2;
|
const PRIVILEGE_RING_2: u2 = 0x2;
|
||||||
const PRIVILEGE_RING_3: u2 = 0x3;
|
const PRIVILEGE_RING_3: u2 = 0x3;
|
||||||
|
|
||||||
const NUMBER_OF_ENTRIES: u16 = 256;
|
pub 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 isIdtOpen(entry: IdtEntry) bool {
|
||||||
|
return mock_framework.performAction("isIdtOpen", bool, .{entry});
|
||||||
|
}
|
||||||
|
|
||||||
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, .{ index, handler });
|
return mock_framework.performAction("openInterruptGate", IdtError!void, .{ index, handler });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
const StringHashMap = std.StringHashMap;
|
const StringHashMap = std.StringHashMap;
|
||||||
const expect = std.testing.expect;
|
const expect = std.testing.expect;
|
||||||
const expectEqual = std.testing.expectEqual;
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
@ -8,6 +9,7 @@ const warn = std.debug.warn;
|
||||||
const gdt = @import("gdt_mock.zig");
|
const gdt = @import("gdt_mock.zig");
|
||||||
const idt = @import("idt_mock.zig");
|
const idt = @import("idt_mock.zig");
|
||||||
const cmos = @import("cmos_mock.zig");
|
const cmos = @import("cmos_mock.zig");
|
||||||
|
const task = @import("task_mock.zig");
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The enumeration of types that the mocking framework supports. These include basic types like u8
|
/// The enumeration of types that the mocking framework supports. These include basic types like u8
|
||||||
|
@ -19,13 +21,18 @@ const DataElementType = enum {
|
||||||
U8,
|
U8,
|
||||||
U16,
|
U16,
|
||||||
U32,
|
U32,
|
||||||
ECmosStatusRegister,
|
USIZE,
|
||||||
ECmosRtcRegister,
|
PTR_ALLOCATOR,
|
||||||
PTR_CONST_GdtPtr,
|
ECMOSSTATUSREGISTER,
|
||||||
PTR_CONST_IdtPtr,
|
ECMOSRTCREGISTER,
|
||||||
GdtPtr,
|
GDTPTR,
|
||||||
IdtPtr,
|
IDTPTR,
|
||||||
|
IDTENTRY,
|
||||||
|
PTR_CONST_GDTPTR,
|
||||||
|
PTR_CONST_IDTPTR,
|
||||||
ERROR_IDTERROR_VOID,
|
ERROR_IDTERROR_VOID,
|
||||||
|
ERROR_MEM_PTRTASK,
|
||||||
|
PTR_TASK,
|
||||||
EFN_OVOID,
|
EFN_OVOID,
|
||||||
NFN_OVOID,
|
NFN_OVOID,
|
||||||
FN_OVOID,
|
FN_OVOID,
|
||||||
|
@ -34,20 +41,26 @@ const DataElementType = enum {
|
||||||
FN_IU8_OBOOL,
|
FN_IU8_OBOOL,
|
||||||
FN_IU8_OVOID,
|
FN_IU8_OVOID,
|
||||||
FN_IU16_OVOID,
|
FN_IU16_OVOID,
|
||||||
|
FN_IUSIZE_OVOID,
|
||||||
FN_IU16_OU8,
|
FN_IU16_OU8,
|
||||||
FN_IU4_IU4_OU8,
|
FN_IU4_IU4_OU8,
|
||||||
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_IECmosStatusRegister_IBOOL_OU8,
|
FN_IECMOSSTATUSREGISTER_IBOOL_OU8,
|
||||||
FN_IECmosStatusRegister_IU8_IBOOL_OVOID,
|
FN_IECMOSSTATUSREGISTER_IU8_IBOOL_OVOID,
|
||||||
FN_IECmosRtcRegister_OU8,
|
FN_IECMOSRTCREGISTER_OU8,
|
||||||
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
||||||
FN_IPTRCONSTGDTPTR_OVOID,
|
FN_IPTRCONSTGDTPTR_OVOID,
|
||||||
FN_IPTRCONSTIDTPTR_OVOID,
|
FN_IPTRCONSTIDTPTR_OVOID,
|
||||||
FN_OGDTPTR,
|
FN_OGDTPTR,
|
||||||
FN_OIDTPTR,
|
FN_OIDTPTR,
|
||||||
|
FN_IIDTENTRY_OBOOL,
|
||||||
|
FN_IPTRTask_IUSIZE_OVOID,
|
||||||
|
FN_IPTRTASK_IPTRALLOCATOR_OVOID,
|
||||||
|
FN_IFNOVOID_OMEMERRORPTRTASK,
|
||||||
|
FN_IFNOVOID_IPTRALLOCATOR_OMEMERRORPTRTASK,
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -64,13 +77,18 @@ const DataElement = union(DataElementType) {
|
||||||
U8: u8,
|
U8: u8,
|
||||||
U16: u16,
|
U16: u16,
|
||||||
U32: u32,
|
U32: u32,
|
||||||
ECmosStatusRegister: cmos.StatusRegister,
|
USIZE: usize,
|
||||||
ECmosRtcRegister: cmos.RtcRegister,
|
PTR_ALLOCATOR: *std.mem.Allocator,
|
||||||
PTR_CONST_GdtPtr: *const gdt.GdtPtr,
|
ECMOSSTATUSREGISTER: cmos.StatusRegister,
|
||||||
IdtPtr: idt.IdtPtr,
|
ECMOSRTCREGISTER: cmos.RtcRegister,
|
||||||
GdtPtr: gdt.GdtPtr,
|
GDTPTR: gdt.GdtPtr,
|
||||||
PTR_CONST_IdtPtr: *const idt.IdtPtr,
|
IDTPTR: idt.IdtPtr,
|
||||||
|
IDTENTRY: idt.IdtEntry,
|
||||||
|
PTR_CONST_GDTPTR: *const gdt.GdtPtr,
|
||||||
|
PTR_CONST_IDTPTR: *const idt.IdtPtr,
|
||||||
ERROR_IDTERROR_VOID: idt.IdtError!void,
|
ERROR_IDTERROR_VOID: idt.IdtError!void,
|
||||||
|
ERROR_MEM_PTRTASK: std.mem.Allocator.Error!*task.Task,
|
||||||
|
PTR_TASK: *task.Task,
|
||||||
EFN_OVOID: fn () callconv(.C) void,
|
EFN_OVOID: fn () callconv(.C) void,
|
||||||
NFN_OVOID: fn () callconv(.Naked) void,
|
NFN_OVOID: fn () callconv(.Naked) void,
|
||||||
FN_OVOID: fn () void,
|
FN_OVOID: fn () void,
|
||||||
|
@ -78,21 +96,27 @@ const DataElement = union(DataElementType) {
|
||||||
FN_OU16: fn () u16,
|
FN_OU16: fn () u16,
|
||||||
FN_IU8_OBOOL: fn (u8) bool,
|
FN_IU8_OBOOL: fn (u8) bool,
|
||||||
FN_IU8_OVOID: fn (u8) void,
|
FN_IU8_OVOID: fn (u8) void,
|
||||||
|
FN_IUSIZE_OVOID: fn (usize) void,
|
||||||
FN_IU16_OVOID: fn (u16) void,
|
FN_IU16_OVOID: fn (u16) void,
|
||||||
FN_IU16_OU8: fn (u16) u8,
|
FN_IU16_OU8: fn (u16) u8,
|
||||||
FN_IU4_IU4_OU8: fn (u4, u4) u8,
|
FN_IU4_IU4_OU8: fn (u4, u4) u8,
|
||||||
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_IECmosStatusRegister_IBOOL_OU8: fn (cmos.StatusRegister, bool) u8,
|
FN_IECMOSSTATUSREGISTER_IBOOL_OU8: fn (cmos.StatusRegister, bool) u8,
|
||||||
FN_IECmosStatusRegister_IU8_IBOOL_OVOID: fn (cmos.StatusRegister, u8, bool) void,
|
FN_IECMOSSTATUSREGISTER_IU8_IBOOL_OVOID: fn (cmos.StatusRegister, u8, bool) void,
|
||||||
FN_IECmosRtcRegister_OU8: fn (cmos.RtcRegister) u8,
|
FN_IECMOSRTCREGISTER_OU8: fn (cmos.RtcRegister) u8,
|
||||||
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID: fn (u8, fn () callconv(.C) void) idt.IdtError!void,
|
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID: fn (u8, fn () callconv(.C) void) idt.IdtError!void,
|
||||||
FN_IU8_INFNOVOID_OERRORIDTERRORVOID: fn (u8, fn () callconv(.Naked) void) idt.IdtError!void,
|
FN_IU8_INFNOVOID_OERRORIDTERRORVOID: fn (u8, fn () callconv(.Naked) 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,
|
||||||
FN_OGDTPTR: fn () gdt.GdtPtr,
|
FN_OGDTPTR: fn () gdt.GdtPtr,
|
||||||
FN_OIDTPTR: fn () idt.IdtPtr,
|
FN_OIDTPTR: fn () idt.IdtPtr,
|
||||||
|
FN_IIDTENTRY_OBOOL: fn (idt.IdtEntry) bool,
|
||||||
|
FN_IPTRTask_IUSIZE_OVOID: fn (*task.Task, usize) void,
|
||||||
|
FN_IPTRTASK_IPTRALLOCATOR_OVOID: fn (*task.Task, *std.mem.Allocator) void,
|
||||||
|
FN_IFNOVOID_OMEMERRORPTRTASK: fn (fn () void) std.mem.Allocator.Error!*task.Task,
|
||||||
|
FN_IFNOVOID_IPTRALLOCATOR_OMEMERRORPTRTASK: fn (fn () void, *std.mem.Allocator) std.mem.Allocator.Error!*task.Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -173,11 +197,18 @@ fn Mock() type {
|
||||||
u8 => DataElement{ .U8 = arg },
|
u8 => DataElement{ .U8 = arg },
|
||||||
u16 => DataElement{ .U16 = arg },
|
u16 => DataElement{ .U16 = arg },
|
||||||
u32 => DataElement{ .U32 = arg },
|
u32 => DataElement{ .U32 = arg },
|
||||||
cmos.StatusRegister => DataElement{ .ECmosStatusRegister = arg },
|
usize => DataElement{ .USIZE = arg },
|
||||||
cmos.RtcRegister => DataElement{ .ECmosRtcRegister = arg },
|
*std.mem.Allocator => DataElement{ .PTR_ALLOCATOR = arg },
|
||||||
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
|
cmos.StatusRegister => DataElement{ .ECMOSSTATUSREGISTER = arg },
|
||||||
*const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg },
|
cmos.RtcRegister => DataElement{ .ECMOSRTCREGISTER = arg },
|
||||||
|
gdt.GdtPtr => DataElement{ .GDTPTR = arg },
|
||||||
|
idt.IdtPtr => DataElement{ .IDTPTR = arg },
|
||||||
|
idt.IdtEntry => DataElement{ .IDTENTRY = arg },
|
||||||
|
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GDTPTR = arg },
|
||||||
|
*const idt.IdtPtr => DataElement{ .PTR_CONST_IDTPTR = arg },
|
||||||
idt.IdtError!void => DataElement{ .ERROR_IDTERROR_VOID = arg },
|
idt.IdtError!void => DataElement{ .ERROR_IDTERROR_VOID = arg },
|
||||||
|
std.mem.Allocator.Error!*task.Task => DataElement{ .ERROR_MEM_PTRTASK = arg },
|
||||||
|
*task.Task => DataElement{ .PTR_TASK = arg },
|
||||||
fn () callconv(.C) void => DataElement{ .EFN_OVOID = arg },
|
fn () callconv(.C) void => DataElement{ .EFN_OVOID = arg },
|
||||||
fn () callconv(.Naked) void => DataElement{ .NFN_OVOID = arg },
|
fn () callconv(.Naked) void => DataElement{ .NFN_OVOID = arg },
|
||||||
fn () void => DataElement{ .FN_OVOID = arg },
|
fn () void => DataElement{ .FN_OVOID = arg },
|
||||||
|
@ -185,19 +216,27 @@ fn Mock() type {
|
||||||
fn () u16 => DataElement{ .FN_OU16 = arg },
|
fn () u16 => DataElement{ .FN_OU16 = arg },
|
||||||
fn (u8) bool => DataElement{ .FN_IU8_OBOOL = arg },
|
fn (u8) bool => DataElement{ .FN_IU8_OBOOL = arg },
|
||||||
fn (u8) void => DataElement{ .FN_IU8_OVOID = arg },
|
fn (u8) void => DataElement{ .FN_IU8_OVOID = arg },
|
||||||
|
fn (usize) void => DataElement{ .FN_IUSIZE_OVOID = arg },
|
||||||
fn (u16) void => DataElement{ .FN_IU16_OVOID = arg },
|
fn (u16) void => DataElement{ .FN_IU16_OVOID = arg },
|
||||||
fn (u16) u8 => DataElement{ .FN_IU16_OU8 = arg },
|
fn (u16) u8 => DataElement{ .FN_IU16_OU8 = arg },
|
||||||
fn (u4, u4) u8 => DataElement{ .FN_IU4_IU4_OU8 = arg },
|
fn (u4, u4) u8 => DataElement{ .FN_IU4_IU4_OU8 = arg },
|
||||||
fn (u8, u8) u16 => DataElement{ .FN_IU8_IU8_OU16 = arg },
|
fn (u8, u8) u16 => DataElement{ .FN_IU8_IU8_OU16 = arg },
|
||||||
fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg },
|
fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg },
|
||||||
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
|
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
|
||||||
fn (cmos.StatusRegister, bool) u8 => DataElement{ .FN_IECmosStatusRegister_IBOOL_OU8 = arg },
|
fn (cmos.StatusRegister, bool) u8 => DataElement{ .FN_IECMOSSTATUSREGISTER_IBOOL_OU8 = arg },
|
||||||
fn (cmos.StatusRegister, u8, bool) void => DataElement{ .FN_IECmosStatusRegister_IU8_IBOOL_OVOID = arg },
|
fn (cmos.StatusRegister, u8, bool) void => DataElement{ .FN_IECMOSSTATUSREGISTER_IU8_IBOOL_OVOID = arg },
|
||||||
fn (cmos.RtcRegister) u8 => DataElement{ .FN_IECmosRtcRegister_OU8 = arg },
|
fn (cmos.RtcRegister) u8 => DataElement{ .FN_IECMOSRTCREGISTER_OU8 = arg },
|
||||||
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
|
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
|
||||||
|
fn () gdt.GdtPtr => DataElement{ .FN_OGDTPTR = arg },
|
||||||
fn (*const idt.IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg },
|
fn (*const idt.IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg },
|
||||||
|
fn () idt.IdtPtr => DataElement{ .FN_OIDTPTR = arg },
|
||||||
fn (u8, fn () callconv(.C) void) idt.IdtError!void => DataElement{ .FN_IU8_IEFNOVOID_OERRORIDTERRORVOID = arg },
|
fn (u8, fn () callconv(.C) void) idt.IdtError!void => DataElement{ .FN_IU8_IEFNOVOID_OERRORIDTERRORVOID = arg },
|
||||||
fn (u8, fn () callconv(.Naked) void) idt.IdtError!void => DataElement{ .FN_IU8_INFNOVOID_OERRORIDTERRORVOID = arg },
|
fn (u8, fn () callconv(.Naked) void) idt.IdtError!void => DataElement{ .FN_IU8_INFNOVOID_OERRORIDTERRORVOID = arg },
|
||||||
|
fn (idt.IdtEntry) bool => DataElement{ .FN_IIDTENTRY_OBOOL = arg },
|
||||||
|
fn (*task.Task, usize) void => DataElement{ .FN_IPTRTask_IUSIZE_OVOID = arg },
|
||||||
|
fn (*task.Task, *std.mem.Allocator) void => DataElement{ .FN_IPTRTASK_IPTRALLOCATOR_OVOID = arg },
|
||||||
|
fn (fn () void) std.mem.Allocator.Error!*task.Task => DataElement{ .FN_IFNOVOID_OMEMERRORPTRTASK = arg },
|
||||||
|
fn (fn () void, *std.mem.Allocator) std.mem.Allocator.Error!*task.Task => DataElement{ .FN_IFNOVOID_IPTRALLOCATOR_OMEMERRORPTRTASK = arg },
|
||||||
else => @compileError("Type not supported: " ++ @typeName(@TypeOf(arg))),
|
else => @compileError("Type not supported: " ++ @typeName(@TypeOf(arg))),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -218,34 +257,46 @@ fn Mock() type {
|
||||||
u8 => DataElementType.U8,
|
u8 => DataElementType.U8,
|
||||||
u16 => DataElementType.U16,
|
u16 => DataElementType.U16,
|
||||||
u32 => DataElementType.U32,
|
u32 => DataElementType.U32,
|
||||||
cmos.StatusRegister => DataElementType.ECmosStatusRegister,
|
usize => DataElementType.USIZE,
|
||||||
cmos.RtcRegister => DataElementType.ECmosRtcRegister,
|
*std.mem.Allocator => DataElementType.PTR_ALLOCATOR,
|
||||||
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
|
cmos.StatusRegister => DataElementType.ECMOSSTATUSREGISTER,
|
||||||
*const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr,
|
cmos.RtcRegister => DataElementType.ECMOSRTCREGISTER,
|
||||||
gdt.GdtPtr => DataElement.GdtPtr,
|
gdt.GdtPtr => DataElementType.GDTPTR,
|
||||||
idt.IdtPtr => DataElement.IdtPtr,
|
idt.IdtPtr => DataElementType.IDTPTR,
|
||||||
idt.IdtError!void => DataElement.ERROR_IDTERROR_VOID,
|
idt.IdtEntry => DataElementType.IDTENTRY,
|
||||||
|
*const gdt.GdtPtr => DataElementType.PTR_CONST_GDTPTR,
|
||||||
|
*const idt.IdtPtr => DataElementType.PTR_CONST_IDTPTR,
|
||||||
|
idt.IdtError!void => DataElementType.ERROR_IDTERROR_VOID,
|
||||||
|
std.mem.Allocator.Error!*task.Task => DataElementType.ERROR_MEM_PTRTASK,
|
||||||
|
*task.Task => DataElementType.PTR_TASK,
|
||||||
fn () callconv(.C) void => DataElementType.EFN_OVOID,
|
fn () callconv(.C) void => DataElementType.EFN_OVOID,
|
||||||
fn () callconv(.Naked) void => DataElementType.NFN_OVOID,
|
fn () callconv(.Naked) void => DataElementType.NFN_OVOID,
|
||||||
fn () void => DataElementType.FN_OVOID,
|
fn () void => DataElementType.FN_OVOID,
|
||||||
|
fn () usize => DataElementType.FN_OUSIZE,
|
||||||
fn () u16 => DataElementType.FN_OU16,
|
fn () u16 => DataElementType.FN_OU16,
|
||||||
fn (u8) bool => DataElementType.FN_IU8_OBOOL,
|
fn (u8) bool => DataElementType.FN_IU8_OBOOL,
|
||||||
fn (u8) void => DataElementType.FN_IU8_OVOID,
|
fn (u8) void => DataElementType.FN_IU8_OVOID,
|
||||||
fn (u16) void => DataElementType.FN_IU16_OVOID,
|
fn (u16) void => DataElementType.FN_IU16_OVOID,
|
||||||
|
fn (usize) void => DataElementType.FN_IUSIZE_OVOID,
|
||||||
fn (u16) u8 => DataElementType.FN_IU16_OU8,
|
fn (u16) u8 => DataElementType.FN_IU16_OU8,
|
||||||
fn (u4, u4) u8 => DataElementType.FN_IU4_IU4_OU8,
|
fn (u4, u4) u8 => DataElementType.FN_IU4_IU4_OU8,
|
||||||
fn (u8, u8) u16 => DataElementType.FN_IU8_IU8_OU16,
|
fn (u8, u8) u16 => DataElementType.FN_IU8_IU8_OU16,
|
||||||
fn (u16, u8) void => DataElementType.FN_IU16_IU8_OVOID,
|
fn (u16, u8) void => DataElementType.FN_IU16_IU8_OVOID,
|
||||||
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
|
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
|
||||||
fn (cmos.StatusRegister, bool) u8 => DataElementType.FN_IECmosStatusRegister_IBOOL_OU8,
|
fn (cmos.StatusRegister, bool) u8 => DataElementType.FN_IECMOSSTATUSREGISTER_IBOOL_OU8,
|
||||||
fn (cmos.StatusRegister, u8, bool) void => DataElementType.FN_IECmosStatusRegister_IU8_IBOOL_OVOID,
|
fn (cmos.StatusRegister, u8, bool) void => DataElementType.FN_IECMOSSTATUSREGISTER_IU8_IBOOL_OVOID,
|
||||||
fn (cmos.RtcRegister) u8 => DataElementType.FN_IECmosRtcRegister_OU8,
|
fn (cmos.RtcRegister) u8 => DataElementType.FN_IECMOSRTCREGISTER_OU8,
|
||||||
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 () gdt.GdtPtr => DataElementType.FN_OGDTPTR,
|
fn () gdt.GdtPtr => DataElementType.FN_OGDTPTR,
|
||||||
fn () idt.IdtPtr => DataElementType.FN_OIDTPTR,
|
fn () idt.IdtPtr => DataElementType.FN_OIDTPTR,
|
||||||
fn (u8, fn () callconv(.C) void) idt.IdtError!void => DataElementType.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
fn (u8, fn () callconv(.C) void) idt.IdtError!void => DataElementType.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
fn (u8, fn () callconv(.Naked) void) idt.IdtError!void => DataElementType.FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
fn (u8, fn () callconv(.Naked) void) idt.IdtError!void => DataElementType.FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
||||||
|
fn (idt.IdtEntry) bool => DataElementType.FN_IIDTENTRY_OBOOL,
|
||||||
|
fn (*task.Task, usize) void => DataElementType.FN_IPTRTask_IUSIZE_OVOID,
|
||||||
|
fn (*task.Task, *std.mem.Allocator) void => DataElementType.FN_IPTRTASK_IPTRALLOCATOR_OVOID,
|
||||||
|
fn (fn () void) std.mem.Allocator.Error!*task.Task => DataElementType.FN_IFNOVOID_OMEMERRORPTRTASK,
|
||||||
|
fn (fn () void, *std.mem.Allocator) std.mem.Allocator.Error!*task.Task => DataElementType.FN_IFNOVOID_IPTRALLOCATOR_OMEMERRORPTRTASK,
|
||||||
else => @compileError("Type not supported: " ++ @typeName(T)),
|
else => @compileError("Type not supported: " ++ @typeName(T)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -268,34 +319,46 @@ fn Mock() type {
|
||||||
u8 => element.U8,
|
u8 => element.U8,
|
||||||
u16 => element.U16,
|
u16 => element.U16,
|
||||||
u32 => element.U32,
|
u32 => element.U32,
|
||||||
cmos.StatusRegister => element.ECmosStatusRegister,
|
usize => element.USIZE,
|
||||||
cmos.RtcRegister => element.ECmosRtcRegister,
|
*std.mem.Allocator => element.PTR_ALLOCATOR,
|
||||||
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
|
cmos.StatusRegister => element.ECMOSSTATUSREGISTER,
|
||||||
*const idt.IdtPtr => element.PTR_CONST_IdtPtr,
|
gdt.GdtPtr => element.GDTPTR,
|
||||||
gdt.GdtPtr => element.GdtPtr,
|
idt.IdtPtr => element.IDTPTR,
|
||||||
idt.IdtPtr => element.IdtPtr,
|
idt.IdtEntry => element.IDTENTRY,
|
||||||
|
cmos.RtcRegister => element.ECMOSRTCREGISTER,
|
||||||
|
*const gdt.GdtPtr => element.PTR_CONST_GDTPTR,
|
||||||
|
*const idt.IdtPtr => element.PTR_CONST_IDTPTR,
|
||||||
idt.IdtError!void => element.ERROR_IDTERROR_VOID,
|
idt.IdtError!void => element.ERROR_IDTERROR_VOID,
|
||||||
|
std.mem.Allocator.Error!*task.Task => element.ERROR_MEM_PTRTASK,
|
||||||
|
*task.Task => element.PTR_TASK,
|
||||||
fn () callconv(.C) void => element.EFN_OVOID,
|
fn () callconv(.C) void => element.EFN_OVOID,
|
||||||
fn () callconv(.Naked) void => element.NFN_OVOID,
|
fn () callconv(.Naked) void => element.NFN_OVOID,
|
||||||
fn () void => element.FN_OVOID,
|
fn () void => element.FN_OVOID,
|
||||||
|
fn () usize => element.FN_OUSIZE,
|
||||||
fn () u16 => element.FN_OU16,
|
fn () u16 => element.FN_OU16,
|
||||||
fn (u8) bool => element.FN_IU8_OBOOL,
|
fn (u8) bool => element.FN_IU8_OBOOL,
|
||||||
fn (u8) void => element.FN_IU8_OVOID,
|
fn (u8) void => element.FN_IU8_OVOID,
|
||||||
fn (u16) void => element.FN_IU16_OVOID,
|
fn (u16) void => element.FN_IU16_OVOID,
|
||||||
|
fn (usize) void => element.FN_IUSIZE_OVOID,
|
||||||
fn (u16) u8 => element.FN_IU16_OU8,
|
fn (u16) u8 => element.FN_IU16_OU8,
|
||||||
fn (u4, u4) u8 => element.FN_IU4_IU4_OU8,
|
fn (u4, u4) u8 => element.FN_IU4_IU4_OU8,
|
||||||
fn (u8, u8) u16 => element.FN_IU8_IU8_OU16,
|
fn (u8, u8) u16 => element.FN_IU8_IU8_OU16,
|
||||||
fn (u16, u8) void => element.FN_IU16_IU8_OVOID,
|
fn (u16, u8) void => element.FN_IU16_IU8_OVOID,
|
||||||
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
|
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
|
||||||
fn (cmos.StatusRegister, bool) u8 => element.FN_IECmosStatusRegister_IBOOL_OU8,
|
fn (cmos.StatusRegister, bool) u8 => element.FN_IECMOSSTATUSREGISTER_IBOOL_OU8,
|
||||||
fn (cmos.StatusRegister, u8, bool) void => element.FN_IECmosStatusRegister_IU8_IBOOL_OVOID,
|
fn (cmos.StatusRegister, u8, bool) void => element.FN_IECMOSSTATUSREGISTER_IU8_IBOOL_OVOID,
|
||||||
fn (cmos.RtcRegister) u8 => element.FN_IECmosRtcRegister_OU8,
|
fn (cmos.RtcRegister) u8 => element.FN_IECMOSRTCREGISTER_OU8,
|
||||||
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, fn () callconv(.C) void) idt.IdtError!void => element.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
fn (u8, fn () callconv(.C) void) idt.IdtError!void => element.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
fn (u8, fn () callconv(.Naked) void) idt.IdtError!void => element.FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
fn (u8, fn () callconv(.Naked) void) idt.IdtError!void => element.FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
||||||
fn () gdt.GdtPtr => element.FN_OGDTPTR,
|
fn () gdt.GdtPtr => element.FN_OGDTPTR,
|
||||||
fn () idt.IdtPtr => element.FN_OIDTPTR,
|
fn () idt.IdtPtr => element.FN_OIDTPTR,
|
||||||
|
fn (idt.IdtEntry) bool => element.FN_IIDTENTRY_OBOOL,
|
||||||
|
fn (*task.Task, usize) void => element.FN_IPTRTask_IUSIZE_OVOID,
|
||||||
|
fn (*task.Task, *std.mem.Allocator) void => element.FN_IPTRTASK_IPTRALLOCATOR_OVOID,
|
||||||
|
fn (fn () void) std.mem.Allocator.Error!*task.Task => element.FN_IFNOVOID_OMEMERRORPTRTASK,
|
||||||
|
fn (fn () void, *std.mem.Allocator) std.mem.Allocator.Error!*task.Task => element.FN_IFNOVOID_IPTRALLOCATOR_OMEMERRORPTRTASK,
|
||||||
else => @compileError("Type not supported: " ++ @typeName(T)),
|
else => @compileError("Type not supported: " ++ @typeName(T)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -614,7 +677,7 @@ fn getMockObject() *Mock() {
|
||||||
if (mock) |*m| {
|
if (mock) |*m| {
|
||||||
return m;
|
return m;
|
||||||
} else {
|
} else {
|
||||||
warn("MOCK object doesn't exists, please initiate this test\n", .{});
|
warn("MOCK object doesn't exists, please initialise this test\n", .{});
|
||||||
expect(false);
|
expect(false);
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
27
test/mock/kernel/task_mock.zig
Normal file
27
test/mock/kernel/task_mock.zig
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const mock_framework = @import("mock_framework.zig");
|
||||||
|
pub const initTest = mock_framework.initTest;
|
||||||
|
pub const freeTest = mock_framework.freeTest;
|
||||||
|
pub const addTestParams = mock_framework.addTestParams;
|
||||||
|
pub const addConsumeFunction = mock_framework.addConsumeFunction;
|
||||||
|
pub const addRepeatFunction = mock_framework.addRepeatFunction;
|
||||||
|
|
||||||
|
const EntryPointFn = fn () void;
|
||||||
|
|
||||||
|
pub const Task = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pid: u32,
|
||||||
|
stack: []u32,
|
||||||
|
stack_pointer: usize,
|
||||||
|
|
||||||
|
pub fn create(entry_point: EntryPointFn, allocator: *Allocator) Allocator.Error!*Task {
|
||||||
|
return mock_framework.performAction("Task.create", Allocator.Error!*Task, .{ entry_point, allocator });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self: *Self, allocator: *Allocator) void {
|
||||||
|
return mock_framework.performAction("Task.destroy", void, .{ self, allocator });
|
||||||
|
}
|
||||||
|
};
|
|
@ -27,6 +27,9 @@ pub const TestMode = enum {
|
||||||
/// Run the panic runtime test.
|
/// Run the panic runtime test.
|
||||||
Panic,
|
Panic,
|
||||||
|
|
||||||
|
/// Run the scheduler runtime test.
|
||||||
|
Scheduler,
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Return a string description for the test mode provided.
|
/// Return a string description for the test mode provided.
|
||||||
///
|
///
|
||||||
|
@ -41,6 +44,7 @@ pub const TestMode = enum {
|
||||||
.None => "Runs the OS normally (Default)",
|
.None => "Runs the OS normally (Default)",
|
||||||
.Initialisation => "Initialisation runtime tests",
|
.Initialisation => "Initialisation runtime tests",
|
||||||
.Panic => "Panic runtime tests",
|
.Panic => "Panic runtime tests",
|
||||||
|
.Scheduler => "Scheduler runtime tests",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -143,6 +147,35 @@ pub const RuntimeStep = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// This tests the OS's scheduling by checking that we schedule a task that prints the success.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN/OUT self: *RuntimeStep - Self.
|
||||||
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// Whether the test has passed or failed.
|
||||||
|
///
|
||||||
|
fn test_scheduler(self: *RuntimeStep) bool {
|
||||||
|
var state: usize = 0;
|
||||||
|
while (true) {
|
||||||
|
const msg = self.get_msg() catch return false;
|
||||||
|
defer self.builder.allocator.free(msg);
|
||||||
|
|
||||||
|
std.debug.warn("{}\n", .{msg});
|
||||||
|
|
||||||
|
// Make sure `[INFO] Switched` then `[INFO] SUCCESS: Scheduler variables preserved` are logged in this order
|
||||||
|
if (std.mem.eql(u8, msg, "[INFO] Switched") and state == 0) {
|
||||||
|
state = 1;
|
||||||
|
} else if (std.mem.eql(u8, msg, "[INFO] SUCCESS: Scheduler variables preserved") and state == 1) {
|
||||||
|
state = 2;
|
||||||
|
}
|
||||||
|
if (state == 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The make function that is called by the builder. This will create the qemu process with the
|
/// The make function that is called by the builder. This will create the qemu process with the
|
||||||
/// stdout as a Pipe. Then create the read thread to read the logs from the qemu stdout. Then
|
/// stdout as a Pipe. Then create the read thread to read the logs from the qemu stdout. Then
|
||||||
|
@ -204,7 +237,7 @@ pub const RuntimeStep = struct {
|
||||||
fn read_logs(self: *RuntimeStep) void {
|
fn read_logs(self: *RuntimeStep) void {
|
||||||
const stream = self.os_proc.stdout.?.reader();
|
const stream = self.os_proc.stdout.?.reader();
|
||||||
// Line shouldn't be longer than this
|
// Line shouldn't be longer than this
|
||||||
const max_line_length: usize = 128;
|
const max_line_length: usize = 1024;
|
||||||
while (true) {
|
while (true) {
|
||||||
const line = stream.readUntilDelimiterAlloc(self.builder.allocator, '\n', max_line_length) catch |e| switch (e) {
|
const line = stream.readUntilDelimiterAlloc(self.builder.allocator, '\n', max_line_length) catch |e| switch (e) {
|
||||||
error.EndOfStream => {
|
error.EndOfStream => {
|
||||||
|
@ -212,7 +245,10 @@ pub const RuntimeStep = struct {
|
||||||
// join the thread to exit nicely :)
|
// join the thread to exit nicely :)
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => {
|
||||||
|
std.debug.warn("Unexpected error: {}\n", .{e});
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// put line in the queue
|
// put line in the queue
|
||||||
|
@ -270,6 +306,7 @@ pub const RuntimeStep = struct {
|
||||||
.None => print_logs,
|
.None => print_logs,
|
||||||
.Initialisation => test_init,
|
.Initialisation => test_init,
|
||||||
.Panic => test_panic,
|
.Panic => test_panic,
|
||||||
|
.Scheduler => test_scheduler,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return runtime_step;
|
return runtime_step;
|
||||||
|
|
Loading…
Reference in a new issue