Merge pull request #78 from SamTebbs33/feature/adding-tests-for-gdt
Feature/adding tests for gdt
This commit is contained in:
commit
9d9c5d6a39
18 changed files with 1141 additions and 590 deletions
60
build.zig
60
build.zig
|
@ -1,6 +1,7 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Builder = std.build.Builder;
|
||||
const LibExeObjStep = std.build.LibExeObjStep;
|
||||
const Step = std.build.Step;
|
||||
const Target = std.build.Target;
|
||||
const fs = std.fs;
|
||||
|
@ -16,17 +17,18 @@ pub fn build(b: *Builder) !void {
|
|||
};
|
||||
|
||||
const target_str = switch (target.getArch()) {
|
||||
builtin.Arch.i386 => "x86",
|
||||
.i386 => "x86",
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const debug = b.option(bool, "debug", "build with debug symbols / make qemu wait for a debug connection") orelse false;
|
||||
const rt_test = b.option(bool, "rt-test", "enable/disable runtime testing") orelse false;
|
||||
|
||||
const main_src = "src/kernel/kmain.zig";
|
||||
|
||||
const exec = b.addExecutable("pluto", main_src);
|
||||
exec.setMainPkgPath(".");
|
||||
const const_path = try fs.path.join(b.allocator, [_][]const u8{ "src/kernel/arch/", target_str, "/constants.zig" });
|
||||
exec.addPackagePath("constants", const_path);
|
||||
const constants_path = try fs.path.join(b.allocator, [_][]const u8{ "src/kernel/arch", target_str, "constants.zig" });
|
||||
exec.addPackagePath("constants", constants_path);
|
||||
exec.addBuildOption(bool, "rt_test", rt_test);
|
||||
exec.setLinkerScriptPath("link.ld");
|
||||
exec.setTheTarget(target);
|
||||
|
@ -35,12 +37,12 @@ pub fn build(b: *Builder) !void {
|
|||
exec.addAssemblyFile("src/kernel/arch/x86/irq_asm.s");
|
||||
exec.addAssemblyFile("src/kernel/arch/x86/isr_asm.s");
|
||||
},
|
||||
else => {},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const iso_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "pluto.iso" }) catch unreachable;
|
||||
const grub_build_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso", "boot" }) catch unreachable;
|
||||
const iso_dir_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso" }) catch unreachable;
|
||||
const iso_path = try fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "pluto.iso" });
|
||||
const grub_build_path = try fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso", "boot" });
|
||||
const iso_dir_path = try fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso" });
|
||||
|
||||
const mkdir_cmd = b.addSystemCommand([_][]const u8{ "mkdir", "-p", fs.path.dirname(grub_build_path).? });
|
||||
|
||||
|
@ -59,7 +61,10 @@ pub fn build(b: *Builder) !void {
|
|||
b.default_step.dependOn(&iso_cmd.step);
|
||||
|
||||
const run_step = b.step("run", "Run with qemu");
|
||||
const qemu_bin = if (target.getArch() == builtin.Arch.i386) "qemu-system-i386" else unreachable;
|
||||
const qemu_bin = switch (target.getArch()) {
|
||||
.i386 => "qemu-system-i386",
|
||||
else => unreachable,
|
||||
};
|
||||
const qemu_cmd = b.addSystemCommand([_][]const u8{
|
||||
qemu_bin,
|
||||
"-cdrom",
|
||||
|
@ -69,10 +74,15 @@ pub fn build(b: *Builder) !void {
|
|||
"-serial",
|
||||
"stdio",
|
||||
});
|
||||
if (debug)
|
||||
|
||||
if (debug) {
|
||||
qemu_cmd.addArgs([_][]const u8{ "-s", "-S" });
|
||||
if (rt_test)
|
||||
}
|
||||
|
||||
if (rt_test) {
|
||||
qemu_cmd.addArgs([_][]const u8{ "-display", "none" });
|
||||
}
|
||||
|
||||
run_step.dependOn(&qemu_cmd.step);
|
||||
qemu_cmd.step.dependOn(&iso_cmd.step);
|
||||
|
||||
|
@ -81,15 +91,24 @@ pub fn build(b: *Builder) !void {
|
|||
const script = b.addSystemCommand([_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe });
|
||||
test_step.dependOn(&script.step);
|
||||
} else {
|
||||
inline for ([_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }) |test_mode| {
|
||||
const mode_str = comptime modeToString(test_mode);
|
||||
const unit_tests = b.addTest("test/unittests/test_all.zig");
|
||||
const mock_path = "\"" ++ "../../test/mock/kernel/" ++ "\"";
|
||||
const arch_mock_path = "\"" ++ "../../../../test/mock/kernel/" ++ "\"";
|
||||
const modes = [_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall };
|
||||
inline for (modes) |test_mode| {
|
||||
const mode_str = switch (test_mode) {
|
||||
Mode.Debug => "debug",
|
||||
Mode.ReleaseFast => "release-fast",
|
||||
Mode.ReleaseSafe => "release-safe",
|
||||
Mode.ReleaseSmall => "release-small",
|
||||
};
|
||||
const unit_tests = b.addTest(main_src);
|
||||
unit_tests.setBuildMode(test_mode);
|
||||
unit_tests.setMainPkgPath(".");
|
||||
unit_tests.setNamePrefix(mode_str ++ " - ");
|
||||
unit_tests.addPackagePath("mocking", "test/mock/kernel/mocking.zig");
|
||||
unit_tests.addPackagePath("constants", const_path);
|
||||
unit_tests.addPackagePath("constants", constants_path);
|
||||
unit_tests.addBuildOption(bool, "rt_test", rt_test);
|
||||
unit_tests.addBuildOption([]const u8, "mock_path", mock_path);
|
||||
unit_tests.addBuildOption([]const u8, "arch_mock_path", arch_mock_path);
|
||||
test_step.dependOn(&unit_tests.step);
|
||||
}
|
||||
}
|
||||
|
@ -107,12 +126,3 @@ pub fn build(b: *Builder) !void {
|
|||
});
|
||||
debug_step.dependOn(&debug_cmd.step);
|
||||
}
|
||||
|
||||
fn modeToString(comptime mode: Mode) []const u8 {
|
||||
return switch (mode) {
|
||||
Mode.Debug => "debug",
|
||||
Mode.ReleaseFast => "release-fast",
|
||||
Mode.ReleaseSafe => "release-safe",
|
||||
Mode.ReleaseSmall => "release-small",
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const builtin = @import("builtin");
|
||||
const is_test = builtin.is_test;
|
||||
const build_options = @import("build_options");
|
||||
|
||||
pub const internals = if (builtin.is_test) @import("mocking").arch else switch (builtin.arch) {
|
||||
builtin.Arch.i386 => @import("arch/x86/arch.zig"),
|
||||
pub const internals = if (is_test) @import(build_options.mock_path ++ "arch_mock.zig") else switch (builtin.arch) {
|
||||
.i386 => @import("arch/x86/arch.zig"),
|
||||
else => unreachable,
|
||||
};
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
// Zig version: 0.4.0
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const builtin = @import("builtin");
|
||||
const gdt = @import("gdt.zig");
|
||||
const idt = @import("idt.zig");
|
||||
const irq = @import("irq.zig");
|
||||
const isr = @import("isr.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const pit = @import("pit.zig");
|
||||
const paging = @import("paging.zig");
|
||||
const MemProfile = @import("../../mem.zig").MemProfile;
|
||||
const syscalls = @import("syscalls.zig");
|
||||
const mem = @import("../../mem.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const MemProfile = mem.MemProfile;
|
||||
|
||||
/// 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).
|
||||
pub const InterruptContext = struct {
|
||||
// Extra segments
|
||||
gs: u32,
|
||||
|
@ -44,29 +46,7 @@ pub const InterruptContext = struct {
|
|||
};
|
||||
|
||||
///
|
||||
/// Initialise the architecture
|
||||
///
|
||||
pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator, comptime options: type) void {
|
||||
disableInterrupts();
|
||||
|
||||
gdt.init();
|
||||
idt.init();
|
||||
|
||||
isr.init();
|
||||
irq.init();
|
||||
|
||||
pit.init();
|
||||
|
||||
paging.init(mem_profile, allocator);
|
||||
|
||||
syscalls.init(options);
|
||||
|
||||
// Enable interrupts
|
||||
enableInterrupts();
|
||||
}
|
||||
|
||||
///
|
||||
/// Inline assembly to write to a given port with a byte of data.
|
||||
/// Assembly to write to a given port with a byte of data.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN port: u16 - The port to write to.
|
||||
|
@ -81,12 +61,12 @@ pub fn outb(port: u16, data: u8) void {
|
|||
}
|
||||
|
||||
///
|
||||
/// Inline assembly that reads data from a given port and returns its value.
|
||||
/// Assembly that reads data from a given port and returns its value.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN port: u16 - The port to read data from.
|
||||
///
|
||||
/// Return:
|
||||
/// Return: u8
|
||||
/// The data that the port returns.
|
||||
///
|
||||
pub fn inb(port: u16) u8 {
|
||||
|
@ -101,6 +81,7 @@ pub fn inb(port: u16) u8 {
|
|||
/// event being waited.
|
||||
///
|
||||
pub fn ioWait() void {
|
||||
// Port 0x80 is free to use
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
|
@ -114,13 +95,22 @@ pub fn ioWait() void {
|
|||
///
|
||||
pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void {
|
||||
// Load the GDT into the CPU
|
||||
asm volatile ("lgdt (%%eax)" : : [gdt_ptr] "{eax}" (gdt_ptr));
|
||||
asm volatile ("lgdt (%%eax)"
|
||||
:
|
||||
: [gdt_ptr] "{eax}" (gdt_ptr)
|
||||
);
|
||||
|
||||
// Load the kernel data segment, index into the GDT
|
||||
asm volatile ("mov %%bx, %%ds" : : [KERNEL_DATA_OFFSET] "{bx}" (gdt.KERNEL_DATA_OFFSET));
|
||||
asm volatile ("mov %%bx, %%ds"
|
||||
:
|
||||
: [KERNEL_DATA_OFFSET] "{bx}" (gdt.KERNEL_DATA_OFFSET)
|
||||
);
|
||||
|
||||
asm volatile ("mov %%bx, %%es");
|
||||
asm volatile ("mov %%bx, %%fs");
|
||||
asm volatile ("mov %%bx, %%gs");
|
||||
asm volatile ("mov %%bx, %%ss");
|
||||
|
||||
// Load the kernel code segment into the CS register
|
||||
asm volatile (
|
||||
\\ljmp $0x08, $1f
|
||||
|
@ -129,33 +119,61 @@ pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void {
|
|||
}
|
||||
|
||||
///
|
||||
/// Load the TSS into the CPU.
|
||||
/// Get the previously loaded GDT from the CPU.
|
||||
///
|
||||
pub fn ltr() void {
|
||||
asm volatile ("ltr %%ax" : : [TSS_OFFSET] "{ax}" (gdt.TSS_OFFSET));
|
||||
/// Return: gdt.GdtPtr
|
||||
/// The previously loaded GDT from the CPU.
|
||||
///
|
||||
pub fn sgdt() gdt.GdtPtr {
|
||||
var gdt_ptr: gdt.GdtPtr = gdt.GdtPtr{ .limit = 0, .base = 0 };
|
||||
asm volatile ("sgdt %[tab]"
|
||||
: [tab] "=m" (gdt_ptr)
|
||||
);
|
||||
return gdt_ptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// Tell the CPU where the TSS is located in the GDT.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN offset: u16 - The offset in the GDT where the TSS segment is located.
|
||||
///
|
||||
pub fn ltr(offset: u16) void {
|
||||
asm volatile ("ltr %%ax"
|
||||
:
|
||||
: [offset] "{ax}" (offset)
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Load the IDT into the CPU.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN idt_ptr: *const idt.IdtPtr - The address of the iDT.
|
||||
///
|
||||
pub fn lidt(idt_ptr: *const idt.IdtPtr) void {
|
||||
asm volatile ("lidt (%%eax)" : : [idt_ptr] "{eax}" (idt_ptr));
|
||||
asm volatile ("lidt (%%eax)"
|
||||
:
|
||||
: [idt_ptr] "{eax}" (idt_ptr)
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Enable interrupts
|
||||
/// Enable interrupts.
|
||||
///
|
||||
pub fn enableInterrupts() void {
|
||||
asm volatile ("sti");
|
||||
}
|
||||
|
||||
///
|
||||
/// Disable interrupts
|
||||
/// Disable interrupts.
|
||||
///
|
||||
pub fn disableInterrupts() void {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
///
|
||||
/// Halt the CPU, but interrupts will still be called
|
||||
/// Halt the CPU, but interrupts will still be called.
|
||||
///
|
||||
pub fn halt() void {
|
||||
asm volatile ("hlt");
|
||||
|
@ -173,7 +191,7 @@ pub fn spinWait() noreturn {
|
|||
}
|
||||
|
||||
///
|
||||
/// Halt the kernel.
|
||||
/// Halt the kernel. No interrupts will be handled.
|
||||
///
|
||||
pub fn haltNoInterrupts() noreturn {
|
||||
while (true) {
|
||||
|
@ -183,12 +201,29 @@ pub fn haltNoInterrupts() noreturn {
|
|||
}
|
||||
|
||||
///
|
||||
/// Register an interrupt handler. The interrupt number should be the arch-specific number.
|
||||
/// Initialise the architecture
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN int: u16 - The arch-specific interrupt number to register for.
|
||||
/// IN handler: fn (ctx: *InterruptContext) void - The handler to assign to the interrupt.
|
||||
/// IN mem_profile: *const MemProfile - The memory profile of the computer. Used to set up
|
||||
/// paging.
|
||||
/// 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 registerInterruptHandler(int: u16, handler: fn (ctx: *InterruptContext) void) void {
|
||||
irq.registerIrq(int, handler);
|
||||
pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime options: type) void {
|
||||
disableInterrupts();
|
||||
|
||||
gdt.init();
|
||||
idt.init();
|
||||
|
||||
isr.init();
|
||||
irq.init();
|
||||
|
||||
pit.init();
|
||||
|
||||
paging.init(mem_profile, allocator);
|
||||
|
||||
syscalls.init(options);
|
||||
|
||||
enableInterrupts();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
const constants = @import("constants");
|
||||
|
||||
/// The multiboot header
|
||||
const MultiBoot = packed struct {
|
||||
magic: i32,
|
||||
flags: i32,
|
||||
checksum: i32,
|
||||
};
|
||||
|
||||
const ALIGN = 1 << 0;
|
||||
const MEMINFO = 1 << 1;
|
||||
const MAGIC = 0x1BADB002;
|
||||
|
@ -9,14 +16,6 @@ const KERNEL_PAGE_NUMBER = constants.KERNEL_ADDR_OFFSET >> 22;
|
|||
// The number of pages occupied by the kernel, will need to be increased as we add a heap etc.
|
||||
const KERNEL_NUM_PAGES = 1;
|
||||
|
||||
extern fn kmain() void;
|
||||
|
||||
const MultiBoot = packed struct {
|
||||
magic: i32,
|
||||
flags: i32,
|
||||
checksum: i32,
|
||||
};
|
||||
|
||||
export var multiboot align(4) linksection(".rodata.boot") = MultiBoot{
|
||||
.magic = MAGIC,
|
||||
.flags = FLAGS,
|
||||
|
@ -65,19 +64,23 @@ 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;
|
||||
|
||||
extern fn kmain() void;
|
||||
|
||||
export nakedcc fn _start() align(16) linksection(".text.boot") noreturn {
|
||||
// Seth the page directory to the boot directory
|
||||
// Set the page directory to the boot directory
|
||||
asm volatile (
|
||||
\\.extern boot_page_directory
|
||||
\\mov $boot_page_directory, %%ecx
|
||||
\\mov %%ecx, %%cr3
|
||||
);
|
||||
|
||||
// Enable 4 MiB pages
|
||||
asm volatile (
|
||||
\\mov %%cr4, %%ecx
|
||||
\\or $0x00000010, %%ecx
|
||||
\\mov %%ecx, %%cr4
|
||||
);
|
||||
|
||||
// Enable paging
|
||||
asm volatile (
|
||||
\\mov %%cr0, %%ecx
|
||||
|
@ -91,12 +94,14 @@ export nakedcc fn _start() align(16) linksection(".text.boot") noreturn {
|
|||
export nakedcc fn start_higher_half() noreturn {
|
||||
// Invalidate the page for the first 4MiB as it's no longer needed
|
||||
asm volatile ("invlpg (0)");
|
||||
|
||||
// Setup the stack
|
||||
asm volatile (
|
||||
\\.extern KERNEL_STACK_END
|
||||
\\mov $KERNEL_STACK_END, %%esp
|
||||
\\mov %%esp, %%ebp
|
||||
);
|
||||
|
||||
// Push the bootloader magic number and multiboot header address with virtual offset
|
||||
asm volatile (
|
||||
\\.extern KERNEL_ADDR_OFFSET
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
/// The virtual address where the kernel will be loaded. This is at 3GB.
|
||||
pub const KERNEL_ADDR_OFFSET = 0xC0000000;
|
||||
|
|
|
@ -1,77 +1,65 @@
|
|||
// Zig version: 0.4.0
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const builtin = @import("builtin");
|
||||
const is_test = builtin.is_test;
|
||||
|
||||
const arch = @import("arch.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const build_options = @import("build_options");
|
||||
const arch = if (is_test) @import(build_options.arch_mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
||||
const log = if (is_test) @import(build_options.arch_mock_path ++ "log_mock.zig") else @import("../../log.zig");
|
||||
|
||||
const NUMBER_OF_ENTRIES: u16 = 0x06;
|
||||
const TABLE_SIZE: u16 = @sizeOf(GdtEntry) * NUMBER_OF_ENTRIES - 1;
|
||||
/// The access bits for a GDT entry.
|
||||
const AccessBits = packed struct {
|
||||
/// Whether the segment has been access. This shouldn't be set as it is set by the CPU when the
|
||||
/// segment is accessed.
|
||||
accessed: u1,
|
||||
|
||||
// The indexes into the GDT where each segment resides.
|
||||
/// For code segments, when set allows the code segment to be readable. Code segments are
|
||||
/// always executable. For data segments, when set allows the data segment to be writeable.
|
||||
/// Data segments are always readable.
|
||||
read_write: u1,
|
||||
|
||||
/// The index of the NULL GDT entry.
|
||||
const NULL_INDEX: u16 = 0x00;
|
||||
/// For code segments, when set allows this code segments to be executed from a equal or lower
|
||||
/// privilege level. The privilege bits represent the highest privilege level that is allowed
|
||||
/// to execute this segment. If not set, then the code segment can only be executed from the
|
||||
/// same ring level specified in the privilege level bits. For data segments, when set the data
|
||||
/// segment grows downwards. When not set, the data segment grows upwards. So for both code and
|
||||
/// data segments, this shouldn't be set.
|
||||
direction_conforming: u1,
|
||||
|
||||
/// The index of the kernel code GDT entry.
|
||||
const KERNEL_CODE_INDEX: u16 = 0x01;
|
||||
/// When set, the segment can be executed, a code segments. When not set, the segment can't be
|
||||
/// executed, data segment.
|
||||
executable: u1,
|
||||
|
||||
/// The index of the kernel data GDT entry.
|
||||
const KERNEL_DATA_INDEX: u16 = 0x02;
|
||||
/// Should be set for code and data segments, but not set for TSS.
|
||||
descriptor: u1,
|
||||
|
||||
/// The index of the user code GDT entry.
|
||||
const USER_CODE_INDEX: u16 = 0x03;
|
||||
/// Privilege/ring level. The kernel level is level 3, the highest privilege. The user level is
|
||||
/// level 0, the lowest privilege.
|
||||
privilege: u2,
|
||||
|
||||
/// The index of the user data GDT entry.
|
||||
const USER_DATA_INDEX: u16 = 0x04;
|
||||
/// Whether the segment is present. This must be set for all valid selectors, not the null
|
||||
/// segment.
|
||||
present: u1,
|
||||
};
|
||||
|
||||
/// The index of the task state segment GDT entry.
|
||||
const TSS_INDEX: u16 = 0x05;
|
||||
/// The flag bits for a GDT entry.
|
||||
const FlagBits = packed struct {
|
||||
/// The lowest bits must be 0 as this is reserved for future use.
|
||||
reserved_zero: u1,
|
||||
|
||||
// The offsets into the GDT where each segment resides.
|
||||
/// When set indicates the segment is a x86-64 segment. If set, then the IS_32_BIT flag must
|
||||
/// not be set. If both are set, then will throw an exception.
|
||||
is_64_bit: u1,
|
||||
|
||||
/// The offset of the NULL GDT entry.
|
||||
pub const NULL_OFFSET: u16 = 0x00;
|
||||
/// When set indicates the segment is a 32 bit protected mode segment. When not set, indicates
|
||||
/// the segment is a 16 bit protected mode segment.
|
||||
is_32_bit: u1,
|
||||
|
||||
/// The offset of the kernel code GDT entry.
|
||||
pub const KERNEL_CODE_OFFSET: u16 = 0x08;
|
||||
|
||||
/// The offset of the kernel data GDT entry.
|
||||
pub const KERNEL_DATA_OFFSET: u16 = 0x10;
|
||||
|
||||
/// The offset of the user code GDT entry.
|
||||
pub const USER_CODE_OFFSET: u16 = 0x18;
|
||||
|
||||
/// The offset of the user data GDT entry.
|
||||
pub const USER_DATA_OFFSET: u16 = 0x20;
|
||||
|
||||
/// The offset of the TTS GDT entry.
|
||||
pub const TSS_OFFSET: u16 = 0x28;
|
||||
|
||||
// The access bits
|
||||
const ACCESSED_BIT = 0x01; // 00000001
|
||||
const WRITABLE_BIT = 0x02; // 00000010
|
||||
const DIRECTION_CONFORMING_BIT = 0x04; // 00000100
|
||||
const EXECUTABLE_BIT = 0x08; // 00001000
|
||||
const DESCRIPTOR_BIT = 0x10; // 00010000
|
||||
|
||||
const PRIVILEGE_RING_0 = 0x00; // 00000000
|
||||
const PRIVILEGE_RING_1 = 0x20; // 00100000
|
||||
const PRIVILEGE_RING_2 = 0x40; // 01000000
|
||||
const PRIVILEGE_RING_3 = 0x60; // 01100000
|
||||
|
||||
const PRESENT_BIT = 0x80; // 10000000
|
||||
|
||||
const KERNEL_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_0 | DESCRIPTOR_BIT;
|
||||
const USER_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_3 | DESCRIPTOR_BIT;
|
||||
|
||||
const CODE_SEGMENT = EXECUTABLE_BIT | WRITABLE_BIT;
|
||||
const DATA_SEGMENT = WRITABLE_BIT;
|
||||
|
||||
const TSS_SEGMENT = PRESENT_BIT | EXECUTABLE_BIT | ACCESSED_BIT;
|
||||
|
||||
// The flag bits
|
||||
const IS_64_BIT = 0x02; // 0010
|
||||
const IS_32_BIT = 0x04; // 0100
|
||||
const IS_LIMIT_4K_BIT = 0x08; // 1000
|
||||
/// The granularity bit. When set the limit is in 4KB blocks (page granularity). When not set,
|
||||
/// then limit is in 1B blocks (byte granularity). This should be set as we are doing paging.
|
||||
granularity: u1,
|
||||
};
|
||||
|
||||
/// The structure that contains all the information that each GDT entry needs.
|
||||
const GdtEntry = packed struct {
|
||||
|
@ -81,41 +69,20 @@ const GdtEntry = packed struct {
|
|||
/// The lower 24 bits of the base address. Describes the start of memory for the entry.
|
||||
base_low: u24,
|
||||
|
||||
/// Bit 0 : accessed - The CPU will set this when the GDT entry is accessed.
|
||||
/// Bit 1 : writable - The writable bit to say if the memory region is writable. If set, then memory region is readable and writable. If not set, then the memory region is just readable.
|
||||
/// Bit 2 : direction_conforming - For a code segment: if set (1), then the code segment can be executed from a lower ring level. If unset (0), then the code segment can only be executed from the same ring level in the privilege flag. For the data segment: if set (1), then the data segment grows downwards. If unset (0), then the data segment grows upwards.
|
||||
/// Bit 3 : executable - The execution bit to say that the memory region is executable.
|
||||
/// Bit 4 : descriptor_bit - The descriptor bit.
|
||||
/// Bit 5-6 : privilege - The ring level of the memory region.
|
||||
/// Bit 7 : present - The present bit to tell that this GDT entry is present.
|
||||
access: u8,
|
||||
/// The access bits, see AccessBits for all the options. 8 bits.
|
||||
access: AccessBits,
|
||||
|
||||
/// The upper 4 bits of the limit address. Describes the size of memory that can be addressed.
|
||||
limit_high: u4,
|
||||
|
||||
/// Bit 0 : reserved_zero - This must always be zero.
|
||||
/// Bit 1 : is_64bits - Whether this is a 64 bit system.
|
||||
/// Bit 2 : is_32bits - Whether this is a 32 bit system.
|
||||
/// Bit 3 : is_limit_4K - Whether paging is turned on, and each address is addressed as if it is a page number not physical/logical linear address.
|
||||
flags: u4,
|
||||
/// The flag bits, see above for all the options. 4 bits.
|
||||
flags: FlagBits,
|
||||
|
||||
/// The upper 8 bits of the base address. Describes the start of memory for the entry.
|
||||
base_high: u8,
|
||||
};
|
||||
|
||||
/// The GDT pointer structure that contains the pointer to the beginning of the GDT and the number
|
||||
/// of the table (minus 1). Used to load the GDT with LGDT instruction.
|
||||
pub const GdtPtr = packed struct {
|
||||
/// 16bit entry for the number of entries (minus 1).
|
||||
limit: u16,
|
||||
|
||||
/// 32bit entry for the base address for the GDT.
|
||||
base: *GdtEntry,
|
||||
};
|
||||
|
||||
///
|
||||
/// The TSS entry structure
|
||||
///
|
||||
const TtsEntry = packed struct {
|
||||
/// Pointer to the previous TSS entry
|
||||
prev_tss: u32,
|
||||
|
@ -199,60 +166,186 @@ const TtsEntry = packed struct {
|
|||
io_permissions_base_offset: u16,
|
||||
};
|
||||
|
||||
///
|
||||
/// Make a GDT entry.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN access: u8 - The access bits for the descriptor.
|
||||
/// IN flags: u4 - The flag bits for the descriptor.
|
||||
///
|
||||
/// Return:
|
||||
/// A new GDT entry with the give access and flag bits set with the base at 0x00000000 and limit at 0xFFFFF.
|
||||
///
|
||||
fn makeEntry(base: u32, limit: u20, access: u8, flags: u4) GdtEntry {
|
||||
return GdtEntry{
|
||||
.limit_low = @truncate(u16, limit),
|
||||
.base_low = @truncate(u24, base),
|
||||
.access = access,
|
||||
.limit_high = @truncate(u4, limit >> 16),
|
||||
.flags = flags,
|
||||
.base_high = @truncate(u8, base >> 24),
|
||||
};
|
||||
}
|
||||
/// The GDT pointer structure that contains the pointer to the beginning of the GDT and the number
|
||||
/// of the table (minus 1). Used to load the GDT with LGDT instruction.
|
||||
pub const GdtPtr = packed struct {
|
||||
/// 16bit entry for the number of entries (minus 1).
|
||||
limit: u16,
|
||||
|
||||
/// 32bit entry for the base address for the GDT.
|
||||
base: u32,
|
||||
};
|
||||
|
||||
/// The total number of entries in the GTD: null, kernel code, kernel data, user code, user data
|
||||
/// and TSS
|
||||
const NUMBER_OF_ENTRIES: u16 = 0x06;
|
||||
|
||||
/// The size of the GTD in bytes (minus 1).
|
||||
const TABLE_SIZE: u16 = @sizeOf(GdtEntry) * NUMBER_OF_ENTRIES - 1;
|
||||
|
||||
// ----------
|
||||
// The indexes into the GDT where each segment resides.
|
||||
// ----------
|
||||
|
||||
/// The index of the NULL GDT entry.
|
||||
const NULL_INDEX: u16 = 0x00;
|
||||
|
||||
/// The index of the kernel code GDT entry.
|
||||
const KERNEL_CODE_INDEX: u16 = 0x01;
|
||||
|
||||
/// The index of the kernel data GDT entry.
|
||||
const KERNEL_DATA_INDEX: u16 = 0x02;
|
||||
|
||||
/// The index of the user code GDT entry.
|
||||
const USER_CODE_INDEX: u16 = 0x03;
|
||||
|
||||
/// The index of the user data GDT entry.
|
||||
const USER_DATA_INDEX: u16 = 0x04;
|
||||
|
||||
/// The index of the task state segment GDT entry.
|
||||
const TSS_INDEX: u16 = 0x05;
|
||||
|
||||
/// The null segment, everything is set to zero.
|
||||
const NULL_SEGMENT: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 0,
|
||||
.direction_conforming = 0,
|
||||
.executable = 0,
|
||||
.descriptor = 0,
|
||||
.privilege = 0,
|
||||
.present = 0,
|
||||
};
|
||||
|
||||
/// This bit pattern represents a kernel code segment with bits: readable, executable, descriptor,
|
||||
/// privilege 0, and present set.
|
||||
const KERNEL_SEGMENT_CODE: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 1,
|
||||
.descriptor = 1,
|
||||
.privilege = 0,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
/// This bit pattern represents a kernel data segment with bits: writeable, descriptor, privilege 0,
|
||||
/// and present set.
|
||||
const KERNEL_SEGMENT_DATA: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 0,
|
||||
.descriptor = 1,
|
||||
.privilege = 0,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
/// This bit pattern represents a user code segment with bits: readable, executable, descriptor,
|
||||
/// privilege 3, and present set.
|
||||
const USER_SEGMENT_CODE: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 1,
|
||||
.descriptor = 1,
|
||||
.privilege = 3,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
/// This bit pattern represents a user data segment with bits: writeable, descriptor, privilege 3,
|
||||
/// and present set.
|
||||
const USER_SEGMENT_DATA: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 0,
|
||||
.descriptor = 1,
|
||||
.privilege = 3,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
/// This bit pattern represents a TSS segment with bits: accessed, executable and present set.
|
||||
const TSS_SEGMENT: AccessBits = AccessBits{
|
||||
.accessed = 1,
|
||||
.read_write = 0,
|
||||
.direction_conforming = 0,
|
||||
.executable = 1,
|
||||
.descriptor = 0,
|
||||
.privilege = 0,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
/// The bit pattern for all bits set to zero.
|
||||
const NULL_FLAGS: FlagBits = FlagBits{
|
||||
.reserved_zero = 0,
|
||||
.is_64_bit = 0,
|
||||
.is_32_bit = 0,
|
||||
.granularity = 0,
|
||||
};
|
||||
|
||||
/// The bit pattern for all segments where we are in 32 bit protected mode and paging enabled.
|
||||
const PAGING_32_BIT: FlagBits = FlagBits{
|
||||
.reserved_zero = 0,
|
||||
.is_64_bit = 0,
|
||||
.is_32_bit = 1,
|
||||
.granularity = 1,
|
||||
};
|
||||
|
||||
// ----------
|
||||
// The offsets into the GDT where each segment resides.
|
||||
// ----------
|
||||
|
||||
/// The offset of the NULL GDT entry.
|
||||
pub const NULL_OFFSET: u16 = 0x00;
|
||||
|
||||
/// The offset of the kernel code GDT entry.
|
||||
pub const KERNEL_CODE_OFFSET: u16 = 0x08;
|
||||
|
||||
/// The offset of the kernel data GDT entry.
|
||||
pub const KERNEL_DATA_OFFSET: u16 = 0x10;
|
||||
|
||||
/// The offset of the user code GDT entry.
|
||||
pub const USER_CODE_OFFSET: u16 = 0x18;
|
||||
|
||||
/// The offset of the user data GDT entry.
|
||||
pub const USER_DATA_OFFSET: u16 = 0x20;
|
||||
|
||||
/// The offset of the TTS GDT entry.
|
||||
pub const TSS_OFFSET: u16 = 0x28;
|
||||
|
||||
/// The GDT entry table of NUMBER_OF_ENTRIES entries.
|
||||
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = [_]GdtEntry{
|
||||
// Null descriptor
|
||||
makeEntry(0, 0, 0, 0),
|
||||
makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS),
|
||||
|
||||
// Kernel Code
|
||||
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT | CODE_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
|
||||
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT_CODE, PAGING_32_BIT),
|
||||
|
||||
// Kernel Data
|
||||
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT | DATA_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
|
||||
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT_DATA, PAGING_32_BIT),
|
||||
|
||||
// User Code
|
||||
makeEntry(0, 0xFFFFF, USER_SEGMENT | CODE_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
|
||||
makeEntry(0, 0xFFFFF, USER_SEGMENT_CODE, PAGING_32_BIT),
|
||||
|
||||
// User Data
|
||||
makeEntry(0, 0xFFFFF, USER_SEGMENT | DATA_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
|
||||
makeEntry(0, 0xFFFFF, USER_SEGMENT_DATA, PAGING_32_BIT),
|
||||
|
||||
// Fill in TSS at runtime
|
||||
makeEntry(0, 0, 0, 0),
|
||||
makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS),
|
||||
};
|
||||
|
||||
/// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the
|
||||
/// size.
|
||||
const gdt_ptr: GdtPtr = GdtPtr{
|
||||
var gdt_ptr: GdtPtr = GdtPtr{
|
||||
.limit = TABLE_SIZE,
|
||||
.base = &gdt_entries[0],
|
||||
.base = undefined,
|
||||
};
|
||||
|
||||
/// The task state segment entry.
|
||||
var tss: TtsEntry = TtsEntry{
|
||||
.prev_tss = 0,
|
||||
.esp0 = undefined,
|
||||
.ss0 = KERNEL_DATA_OFFSET,
|
||||
.esp0 = 0,
|
||||
.ss0 = u32(KERNEL_DATA_OFFSET),
|
||||
.esp1 = 0,
|
||||
.ss1 = 0,
|
||||
.esp2 = 0,
|
||||
|
@ -276,31 +369,316 @@ var tss: TtsEntry = TtsEntry{
|
|||
.gs = 0,
|
||||
.ldtr = 0,
|
||||
.trap = 0,
|
||||
.io_permissions_base_offset = @sizeOf(TtsEntry),
|
||||
.io_permissions_base_offset = u16(@sizeOf(TtsEntry)),
|
||||
};
|
||||
|
||||
///
|
||||
/// Set the stack pointer in the TSS entry
|
||||
/// Make a GDT entry.
|
||||
///
|
||||
/// Arguments:
|
||||
/// IN esp0: u32 - The stack pointer
|
||||
/// IN base: u32 - The linear address where the segment begins.
|
||||
/// IN limit: u20 - The maximum addressable unit whether it is 1B units or page units.
|
||||
/// IN access: AccessBits - The access bits for the descriptor.
|
||||
/// IN flags: FlagBits - The flag bits for the descriptor.
|
||||
///
|
||||
/// Return: GdtEntry
|
||||
/// A new GDT entry with the give access and flag bits set with the base at 0x00000000 and
|
||||
/// limit at 0xFFFFF.
|
||||
///
|
||||
fn makeEntry(base: u32, limit: u20, access: AccessBits, flags: FlagBits) GdtEntry {
|
||||
return GdtEntry{
|
||||
.limit_low = @truncate(u16, limit),
|
||||
.base_low = @truncate(u24, base),
|
||||
.access = AccessBits{
|
||||
.accessed = access.accessed,
|
||||
.read_write = access.read_write,
|
||||
.direction_conforming = access.direction_conforming,
|
||||
.executable = access.executable,
|
||||
.descriptor = access.descriptor,
|
||||
.privilege = access.privilege,
|
||||
.present = access.present,
|
||||
},
|
||||
.limit_high = @truncate(u4, limit >> 16),
|
||||
.flags = FlagBits{
|
||||
.reserved_zero = flags.reserved_zero,
|
||||
.is_64_bit = flags.is_64_bit,
|
||||
.is_32_bit = flags.is_32_bit,
|
||||
.granularity = flags.granularity,
|
||||
},
|
||||
.base_high = @truncate(u8, base >> 24),
|
||||
};
|
||||
}
|
||||
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
pub fn init() void {
|
||||
log.logInfo("Init gdt\n");
|
||||
// Initiate TSS
|
||||
gdt_entries[TSS_INDEX] = makeEntry(@ptrToInt(&tss), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, 0);
|
||||
gdt_entries[TSS_INDEX] = makeEntry(@intCast(u32, @ptrToInt(&tss)), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, NULL_FLAGS);
|
||||
|
||||
// Set the base address where all the GDT entries are.
|
||||
gdt_ptr.base = @intCast(u32, @ptrToInt(&gdt_entries[0]));
|
||||
|
||||
// Load the GDT
|
||||
arch.lgdt(&gdt_ptr);
|
||||
|
||||
// Load the TSS
|
||||
arch.ltr();
|
||||
arch.ltr(TSS_OFFSET);
|
||||
|
||||
log.logInfo("Done\n");
|
||||
|
||||
if (build_options.rt_test) runtimeTests();
|
||||
}
|
||||
|
||||
fn mock_lgdt(ptr: *const GdtPtr) void {
|
||||
expectEqual(TABLE_SIZE, ptr.limit);
|
||||
expectEqual(@intCast(u32, @ptrToInt(&gdt_entries[0])), ptr.base);
|
||||
}
|
||||
|
||||
test "GDT entries" {
|
||||
expectEqual(u32(1), @sizeOf(AccessBits));
|
||||
expectEqual(u32(1), @sizeOf(FlagBits));
|
||||
expectEqual(u32(8), @sizeOf(GdtEntry));
|
||||
expectEqual(u32(104), @sizeOf(TtsEntry));
|
||||
expectEqual(u32(6), @sizeOf(GdtPtr));
|
||||
|
||||
const null_entry = gdt_entries[NULL_INDEX];
|
||||
expectEqual(u64(0), @bitCast(u64, null_entry));
|
||||
|
||||
const kernel_code_entry = gdt_entries[KERNEL_CODE_INDEX];
|
||||
expectEqual(u64(0xCF9A000000FFFF), @bitCast(u64, kernel_code_entry));
|
||||
|
||||
const kernel_data_entry = gdt_entries[KERNEL_DATA_INDEX];
|
||||
expectEqual(u64(0xCF92000000FFFF), @bitCast(u64, kernel_data_entry));
|
||||
|
||||
const user_code_entry = gdt_entries[USER_CODE_INDEX];
|
||||
expectEqual(u64(0xCFFA000000FFFF), @bitCast(u64, user_code_entry));
|
||||
|
||||
const user_data_entry = gdt_entries[USER_DATA_INDEX];
|
||||
expectEqual(u64(0xCFF2000000FFFF), @bitCast(u64, user_data_entry));
|
||||
|
||||
const tss_entry = gdt_entries[TSS_INDEX];
|
||||
expectEqual(u64(0), @bitCast(u64, tss_entry));
|
||||
|
||||
expectEqual(TABLE_SIZE, gdt_ptr.limit);
|
||||
|
||||
expectEqual(u32(0), tss.prev_tss);
|
||||
expectEqual(u32(0), tss.esp0);
|
||||
expectEqual(u32(KERNEL_DATA_OFFSET), tss.ss0);
|
||||
expectEqual(u32(0), tss.esp1);
|
||||
expectEqual(u32(0), tss.ss1);
|
||||
expectEqual(u32(0), tss.esp2);
|
||||
expectEqual(u32(0), tss.ss2);
|
||||
expectEqual(u32(0), tss.cr3);
|
||||
expectEqual(u32(0), tss.eip);
|
||||
expectEqual(u32(0), tss.eflags);
|
||||
expectEqual(u32(0), tss.eax);
|
||||
expectEqual(u32(0), tss.ecx);
|
||||
expectEqual(u32(0), tss.edx);
|
||||
expectEqual(u32(0), tss.ebx);
|
||||
expectEqual(u32(0), tss.esp);
|
||||
expectEqual(u32(0), tss.ebp);
|
||||
expectEqual(u32(0), tss.esi);
|
||||
expectEqual(u32(0), tss.edi);
|
||||
expectEqual(u32(0), tss.es);
|
||||
expectEqual(u32(0), tss.cs);
|
||||
expectEqual(u32(0), tss.ss);
|
||||
expectEqual(u32(0), tss.ds);
|
||||
expectEqual(u32(0), tss.fs);
|
||||
expectEqual(u32(0), tss.gs);
|
||||
expectEqual(u32(0), tss.ldtr);
|
||||
expectEqual(u16(0), tss.trap);
|
||||
|
||||
// Size of TtsEntry will fit in a u16 as 104 < 65535 (2^16)
|
||||
expectEqual(u16(@sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
||||
}
|
||||
|
||||
test "makeEntry NULL" {
|
||||
const actual = makeEntry(u32(0), u32(0), NULL_SEGMENT, NULL_FLAGS);
|
||||
|
||||
const expected = u64(0);
|
||||
expectEqual(expected, @bitCast(u64, actual));
|
||||
}
|
||||
|
||||
test "makeEntry alternating bit pattern" {
|
||||
const alt_access = AccessBits{
|
||||
.accessed = 1,
|
||||
.read_write = 0,
|
||||
.direction_conforming = 1,
|
||||
.executable = 0,
|
||||
.descriptor = 1,
|
||||
.privilege = 0b10,
|
||||
.present = 0,
|
||||
};
|
||||
|
||||
expectEqual(u8(0b01010101), @bitCast(u8, alt_access));
|
||||
|
||||
const alt_flag = FlagBits{
|
||||
.reserved_zero = 1,
|
||||
.is_64_bit = 0,
|
||||
.is_32_bit = 1,
|
||||
.granularity = 0,
|
||||
};
|
||||
|
||||
expectEqual(u4(0b0101), @bitCast(u4, alt_flag));
|
||||
|
||||
const actual = makeEntry(u32(0b01010101010101010101010101010101), u20(0b01010101010101010101), alt_access, alt_flag);
|
||||
|
||||
const expected = u64(0b0101010101010101010101010101010101010101010101010101010101010101);
|
||||
expectEqual(expected, @bitCast(u64, actual));
|
||||
}
|
||||
|
||||
test "setTssStack" {
|
||||
// Pre-testing
|
||||
expectEqual(u32(0), tss.prev_tss);
|
||||
expectEqual(u32(0), tss.esp0);
|
||||
expectEqual(u32(KERNEL_DATA_OFFSET), tss.ss0);
|
||||
expectEqual(u32(0), tss.esp1);
|
||||
expectEqual(u32(0), tss.ss1);
|
||||
expectEqual(u32(0), tss.esp2);
|
||||
expectEqual(u32(0), tss.ss2);
|
||||
expectEqual(u32(0), tss.cr3);
|
||||
expectEqual(u32(0), tss.eip);
|
||||
expectEqual(u32(0), tss.eflags);
|
||||
expectEqual(u32(0), tss.eax);
|
||||
expectEqual(u32(0), tss.ecx);
|
||||
expectEqual(u32(0), tss.edx);
|
||||
expectEqual(u32(0), tss.ebx);
|
||||
expectEqual(u32(0), tss.esp);
|
||||
expectEqual(u32(0), tss.ebp);
|
||||
expectEqual(u32(0), tss.esi);
|
||||
expectEqual(u32(0), tss.edi);
|
||||
expectEqual(u32(0), tss.es);
|
||||
expectEqual(u32(0), tss.cs);
|
||||
expectEqual(u32(0), tss.ss);
|
||||
expectEqual(u32(0), tss.ds);
|
||||
expectEqual(u32(0), tss.fs);
|
||||
expectEqual(u32(0), tss.gs);
|
||||
expectEqual(u32(0), tss.ldtr);
|
||||
expectEqual(u16(0), tss.trap);
|
||||
expectEqual(u16(@sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
||||
|
||||
// Call function
|
||||
setTssStack(u32(100));
|
||||
|
||||
// Post-testing
|
||||
expectEqual(u32(0), tss.prev_tss);
|
||||
expectEqual(u32(100), tss.esp0);
|
||||
expectEqual(u32(KERNEL_DATA_OFFSET), tss.ss0);
|
||||
expectEqual(u32(0), tss.esp1);
|
||||
expectEqual(u32(0), tss.ss1);
|
||||
expectEqual(u32(0), tss.esp2);
|
||||
expectEqual(u32(0), tss.ss2);
|
||||
expectEqual(u32(0), tss.cr3);
|
||||
expectEqual(u32(0), tss.eip);
|
||||
expectEqual(u32(0), tss.eflags);
|
||||
expectEqual(u32(0), tss.eax);
|
||||
expectEqual(u32(0), tss.ecx);
|
||||
expectEqual(u32(0), tss.edx);
|
||||
expectEqual(u32(0), tss.ebx);
|
||||
expectEqual(u32(0), tss.esp);
|
||||
expectEqual(u32(0), tss.ebp);
|
||||
expectEqual(u32(0), tss.esi);
|
||||
expectEqual(u32(0), tss.edi);
|
||||
expectEqual(u32(0), tss.es);
|
||||
expectEqual(u32(0), tss.cs);
|
||||
expectEqual(u32(0), tss.ss);
|
||||
expectEqual(u32(0), tss.ds);
|
||||
expectEqual(u32(0), tss.fs);
|
||||
expectEqual(u32(0), tss.gs);
|
||||
expectEqual(u32(0), tss.ldtr);
|
||||
expectEqual(u16(0), tss.trap);
|
||||
expectEqual(u16(@sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
||||
|
||||
// Clean up
|
||||
setTssStack(u32(0));
|
||||
|
||||
expectEqual(u32(0), tss.prev_tss);
|
||||
expectEqual(u32(0), tss.esp0);
|
||||
expectEqual(u32(KERNEL_DATA_OFFSET), tss.ss0);
|
||||
expectEqual(u32(0), tss.esp1);
|
||||
expectEqual(u32(0), tss.ss1);
|
||||
expectEqual(u32(0), tss.esp2);
|
||||
expectEqual(u32(0), tss.ss2);
|
||||
expectEqual(u32(0), tss.cr3);
|
||||
expectEqual(u32(0), tss.eip);
|
||||
expectEqual(u32(0), tss.eflags);
|
||||
expectEqual(u32(0), tss.eax);
|
||||
expectEqual(u32(0), tss.ecx);
|
||||
expectEqual(u32(0), tss.edx);
|
||||
expectEqual(u32(0), tss.ebx);
|
||||
expectEqual(u32(0), tss.esp);
|
||||
expectEqual(u32(0), tss.ebp);
|
||||
expectEqual(u32(0), tss.esi);
|
||||
expectEqual(u32(0), tss.edi);
|
||||
expectEqual(u32(0), tss.es);
|
||||
expectEqual(u32(0), tss.cs);
|
||||
expectEqual(u32(0), tss.ss);
|
||||
expectEqual(u32(0), tss.ds);
|
||||
expectEqual(u32(0), tss.fs);
|
||||
expectEqual(u32(0), tss.gs);
|
||||
expectEqual(u32(0), tss.ldtr);
|
||||
expectEqual(u16(0), tss.trap);
|
||||
expectEqual(u16(@sizeOf(TtsEntry)), tss.io_permissions_base_offset);
|
||||
}
|
||||
|
||||
test "init" {
|
||||
// Set up
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
arch.addTestParams("ltr", TSS_OFFSET);
|
||||
|
||||
arch.addConsumeFunction("lgdt", mock_lgdt);
|
||||
|
||||
// Call function
|
||||
init();
|
||||
|
||||
// Post testing
|
||||
const tss_entry = gdt_entries[TSS_INDEX];
|
||||
const tss_limit = @sizeOf(TtsEntry) - 1;
|
||||
const tss_addr = @ptrToInt(&tss);
|
||||
|
||||
var expected = u64(0);
|
||||
expected |= u64(@truncate(u16, tss_limit));
|
||||
expected |= u64(@truncate(u24, tss_addr)) << 16;
|
||||
expected |= u64(0x89) << (16 + 24);
|
||||
expected |= u64(@truncate(u4, tss_limit >> 16)) << (16 + 24 + 8);
|
||||
// Flags are zero
|
||||
expected |= u64(@truncate(u8, tss_addr >> 24)) << (16 + 24 + 8 + 4 + 4);
|
||||
|
||||
expectEqual(expected, @bitCast(u64, tss_entry));
|
||||
|
||||
// Reset
|
||||
gdt_ptr.base = 0;
|
||||
gdt_entries[TSS_INDEX] = makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS);
|
||||
}
|
||||
|
||||
///
|
||||
/// Check that the GDT table was loaded properly by getting the previously loaded table and
|
||||
/// compare the limit and base address.
|
||||
///
|
||||
fn rt_loadedGDTSuccess() void {
|
||||
const loaded_gdt = arch.sgdt();
|
||||
expect(gdt_ptr.limit == loaded_gdt.limit);
|
||||
expect(gdt_ptr.base == loaded_gdt.base);
|
||||
}
|
||||
|
||||
///
|
||||
/// Run all the runtime tests.
|
||||
///
|
||||
fn runtimeTests() void {
|
||||
rt_loadedGDTSuccess();
|
||||
log.logInfo("GDT: Tested loading GDT\n");
|
||||
}
|
||||
|
|
7
src/kernel/kernel.zig
Normal file
7
src/kernel/kernel.zig
Normal file
|
@ -0,0 +1,7 @@
|
|||
pub const arch = @import("arch.zig").internals;
|
||||
pub const log = @import("log.zig");
|
||||
pub const mem = @import("mem.zig");
|
||||
pub const panic = @import("panic.zig");
|
||||
pub const serial = @import("serial.zig");
|
||||
pub const tty = @import("tty.zig");
|
||||
pub const vga = @import("vga.zig");
|
|
@ -1,15 +1,13 @@
|
|||
// Zig version: 0.4.0
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const build_options = @import("build_options");
|
||||
const arch = @import("arch.zig").internals;
|
||||
const multiboot = @import("multiboot.zig");
|
||||
const tty = @import("tty.zig");
|
||||
const vga = @import("vga.zig");
|
||||
const log = @import("log.zig");
|
||||
const serial = @import("serial.zig");
|
||||
const mem = if (builtin.is_test) @import("mocking").mem else @import("mem.zig");
|
||||
const options = @import("build_options");
|
||||
const mem = if (builtin.is_test) @import(build_options.mock_path ++ "mem_mock.zig") else @import("mem.zig");
|
||||
|
||||
comptime {
|
||||
switch (builtin.arch) {
|
||||
|
@ -24,7 +22,7 @@ export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefi
|
|||
|
||||
// Need to import this as we need the panic to be in the root source file, or zig will just use the
|
||||
// builtin panic and just loop, which is what we don't want
|
||||
const panic_root = if (builtin.is_test) @import("mocking").panic else @import("panic.zig");
|
||||
const panic_root = if (builtin.is_test) @import(build_options.mock_path ++ "panic_mock.zig") else @import("panic.zig");
|
||||
|
||||
// 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 {
|
||||
|
@ -33,7 +31,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
|
|||
panic_root.panicFmt(error_return_trace, "{}", msg);
|
||||
}
|
||||
|
||||
pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
|
||||
export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
|
||||
if (mb_magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC) {
|
||||
// Booted with compatible bootloader
|
||||
const mem_profile = mem.init(mb_info);
|
||||
|
@ -43,7 +41,7 @@ pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
|
|||
serial.init(serial.DEFAULT_BAUDRATE, serial.Port.COM1) catch unreachable;
|
||||
|
||||
log.logInfo("Init arch " ++ @tagName(builtin.arch) ++ "\n");
|
||||
arch.init(&mem_profile, &fixed_allocator.allocator, options);
|
||||
arch.init(&mem_profile, &fixed_allocator.allocator, build_options);
|
||||
log.logInfo("Arch init done\n");
|
||||
vga.init();
|
||||
tty.init();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const is_test = @import("builtin").is_test;
|
||||
const builtin = @import("builtin");
|
||||
const is_test = builtin.is_test;
|
||||
|
||||
const std = @import("std");
|
||||
const fmt = std.fmt;
|
||||
|
@ -6,8 +7,9 @@ const expect = std.testing.expect;
|
|||
const expectEqual = std.testing.expectEqual;
|
||||
const expectError = std.testing.expectError;
|
||||
|
||||
const vga = if (is_test) @import("mocking").vga else @import("vga.zig");
|
||||
const log = if (is_test) @import("mocking").log else @import("log.zig");
|
||||
const build_options = @import("build_options");
|
||||
const vga = if (is_test) @import(build_options.mock_path ++ "vga_mock.zig") else @import("vga.zig");
|
||||
const log = if (is_test) @import(build_options.mock_path ++ "log_mock.zig") else @import("log.zig");
|
||||
|
||||
/// The number of rows down from the top (row 0) where the displayable region starts. Above is
|
||||
/// where the logo and time is printed
|
||||
|
|
|
@ -1,80 +1,82 @@
|
|||
const std = @import("std");
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const arch = @import("arch.zig").internals;
|
||||
|
||||
/// The port address for the VGA register selection.
|
||||
pub const PORT_ADDRESS: u16 = 0x03D4;
|
||||
const PORT_ADDRESS: u16 = 0x03D4;
|
||||
|
||||
/// The port address for the VGA data.
|
||||
pub const PORT_DATA: u16 = 0x03D5;
|
||||
const PORT_DATA: u16 = 0x03D5;
|
||||
|
||||
/// The indexes that is passed to the address port to select the register for the data to be
|
||||
/// read or written to.
|
||||
pub const REG_HORIZONTAL_TOTAL: u8 = 0x00;
|
||||
pub const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
|
||||
pub const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
|
||||
pub const REG_END_HORIZONTAL_BLINKING: u8 = 0x03;
|
||||
pub const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04;
|
||||
pub const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05;
|
||||
pub const REG_VERTICAL_TOTAL: u8 = 0x06;
|
||||
pub const REG_OVERFLOW: u8 = 0x07;
|
||||
pub const REG_PRESET_ROW_SCAN: u8 = 0x08;
|
||||
pub const REG_MAXIMUM_SCAN_LINE: u8 = 0x09;
|
||||
const REG_HORIZONTAL_TOTAL: u8 = 0x00;
|
||||
const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
|
||||
const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
|
||||
const REG_END_HORIZONTAL_BLINKING: u8 = 0x03;
|
||||
const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04;
|
||||
const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05;
|
||||
const REG_VERTICAL_TOTAL: u8 = 0x06;
|
||||
const REG_OVERFLOW: u8 = 0x07;
|
||||
const REG_PRESET_ROW_SCAN: u8 = 0x08;
|
||||
const REG_MAXIMUM_SCAN_LINE: u8 = 0x09;
|
||||
|
||||
/// The register select for setting the cursor scan lines.
|
||||
pub const REG_CURSOR_START: u8 = 0x0A;
|
||||
pub const REG_CURSOR_END: u8 = 0x0B;
|
||||
pub const REG_START_ADDRESS_HIGH: u8 = 0x0C;
|
||||
pub const REG_START_ADDRESS_LOW: u8 = 0x0D;
|
||||
const REG_CURSOR_START: u8 = 0x0A;
|
||||
const REG_CURSOR_END: u8 = 0x0B;
|
||||
const REG_START_ADDRESS_HIGH: u8 = 0x0C;
|
||||
const REG_START_ADDRESS_LOW: u8 = 0x0D;
|
||||
|
||||
/// The command for setting the cursor's linear location.
|
||||
pub const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E;
|
||||
pub const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
|
||||
const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E;
|
||||
const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
|
||||
|
||||
/// Other VGA registers.
|
||||
pub const REG_VERTICAL_RETRACE_START: u8 = 0x10;
|
||||
pub const REG_VERTICAL_RETRACE_END: u8 = 0x11;
|
||||
pub const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12;
|
||||
pub const REG_OFFSET: u8 = 0x13;
|
||||
pub const REG_UNDERLINE_LOCATION: u8 = 0x14;
|
||||
pub const REG_START_VERTICAL_BLINKING: u8 = 0x15;
|
||||
pub const REG_END_VERTICAL_BLINKING: u8 = 0x16;
|
||||
pub const REG_CRT_MODE_CONTROL: u8 = 0x17;
|
||||
pub const REG_LINE_COMPARE: u8 = 0x18;
|
||||
const REG_VERTICAL_RETRACE_START: u8 = 0x10;
|
||||
const REG_VERTICAL_RETRACE_END: u8 = 0x11;
|
||||
const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12;
|
||||
const REG_OFFSET: u8 = 0x13;
|
||||
const REG_UNDERLINE_LOCATION: u8 = 0x14;
|
||||
const REG_START_VERTICAL_BLINKING: u8 = 0x15;
|
||||
const REG_END_VERTICAL_BLINKING: u8 = 0x16;
|
||||
const REG_CRT_MODE_CONTROL: u8 = 0x17;
|
||||
const REG_LINE_COMPARE: u8 = 0x18;
|
||||
|
||||
/// The start of the cursor scan line, the very beginning.
|
||||
pub const CURSOR_SCANLINE_START: u8 = 0x0;
|
||||
const CURSOR_SCANLINE_START: u8 = 0x0;
|
||||
|
||||
/// The scan line for use in the underline cursor shape.
|
||||
pub const CURSOR_SCANLINE_MIDDLE: u8 = 0xE;
|
||||
const CURSOR_SCANLINE_MIDDLE: u8 = 0xE;
|
||||
|
||||
/// The end of the cursor scan line, the very end.
|
||||
pub const CURSOR_SCANLINE_END: u8 = 0xF;
|
||||
const CURSOR_SCANLINE_END: u8 = 0xF;
|
||||
|
||||
/// If set, disables the cursor.
|
||||
pub const CURSOR_DISABLE: u8 = 0x20;
|
||||
const CURSOR_DISABLE: u8 = 0x20;
|
||||
|
||||
/// The number of characters wide the screen is.
|
||||
pub const WIDTH: u16 = 80;
|
||||
pub const WIDTH: u16 = 80;
|
||||
|
||||
/// The number of characters heigh the screen is.
|
||||
pub const HEIGHT: u16 = 25;
|
||||
pub const HEIGHT: u16 = 25;
|
||||
|
||||
/// The set of colours that VGA supports and can display for the foreground and background.
|
||||
pub const COLOUR_BLACK: u4 = 0x00;
|
||||
pub const COLOUR_BLUE: u4 = 0x01;
|
||||
pub const COLOUR_GREEN: u4 = 0x02;
|
||||
pub const COLOUR_CYAN: u4 = 0x03;
|
||||
pub const COLOUR_RED: u4 = 0x04;
|
||||
pub const COLOUR_MAGENTA: u4 = 0x05;
|
||||
pub const COLOUR_BROWN: u4 = 0x06;
|
||||
pub const COLOUR_LIGHT_GREY: u4 = 0x07;
|
||||
pub const COLOUR_DARK_GREY: u4 = 0x08;
|
||||
pub const COLOUR_LIGHT_BLUE: u4 = 0x09;
|
||||
pub const COLOUR_LIGHT_GREEN: u4 = 0x0A;
|
||||
pub const COLOUR_LIGHT_CYAN: u4 = 0x0B;
|
||||
pub const COLOUR_LIGHT_RED: u4 = 0x0C;
|
||||
pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D;
|
||||
pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
|
||||
pub const COLOUR_WHITE: u4 = 0x0F;
|
||||
pub const COLOUR_BLACK: u4 = 0x00;
|
||||
pub const COLOUR_BLUE: u4 = 0x01;
|
||||
pub const COLOUR_GREEN: u4 = 0x02;
|
||||
pub const COLOUR_CYAN: u4 = 0x03;
|
||||
pub const COLOUR_RED: u4 = 0x04;
|
||||
pub const COLOUR_MAGENTA: u4 = 0x05;
|
||||
pub const COLOUR_BROWN: u4 = 0x06;
|
||||
pub const COLOUR_LIGHT_GREY: u4 = 0x07;
|
||||
pub const COLOUR_DARK_GREY: u4 = 0x08;
|
||||
pub const COLOUR_LIGHT_BLUE: u4 = 0x09;
|
||||
pub const COLOUR_LIGHT_GREEN: u4 = 0x0A;
|
||||
pub const COLOUR_LIGHT_CYAN: u4 = 0x0B;
|
||||
pub const COLOUR_LIGHT_RED: u4 = 0x0C;
|
||||
pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D;
|
||||
pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
|
||||
pub const COLOUR_WHITE: u4 = 0x0F;
|
||||
|
||||
/// The set of shapes that can be displayed.
|
||||
pub const CursorShape = enum {
|
||||
|
@ -234,3 +236,235 @@ pub fn init() void {
|
|||
// Set by default the underline cursor
|
||||
setCursorShape(CursorShape.UNDERLINE);
|
||||
}
|
||||
|
||||
test "entryColour" {
|
||||
var fg: u4 = COLOUR_BLACK;
|
||||
var bg: u4 = COLOUR_BLACK;
|
||||
var res: u8 = entryColour(fg, bg);
|
||||
expectEqual(u8(0x00), res);
|
||||
|
||||
fg = COLOUR_LIGHT_GREEN;
|
||||
bg = COLOUR_BLACK;
|
||||
res = entryColour(fg, bg);
|
||||
expectEqual(u8(0x0A), res);
|
||||
|
||||
fg = COLOUR_BLACK;
|
||||
bg = COLOUR_LIGHT_GREEN;
|
||||
res = entryColour(fg, bg);
|
||||
expectEqual(u8(0xA0), res);
|
||||
|
||||
fg = COLOUR_BROWN;
|
||||
bg = COLOUR_LIGHT_GREEN;
|
||||
res = entryColour(fg, bg);
|
||||
expectEqual(u8(0xA6), res);
|
||||
}
|
||||
|
||||
test "entry" {
|
||||
var colour: u8 = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN);
|
||||
expectEqual(u8(0xA6), colour);
|
||||
|
||||
// Character '0' is 0x30
|
||||
var video_entry: u16 = entry('0', colour);
|
||||
expectEqual(u16(0xA630), video_entry);
|
||||
|
||||
video_entry = entry(0x55, colour);
|
||||
expectEqual(u16(0xA655), video_entry);
|
||||
}
|
||||
|
||||
test "updateCursor width out of bounds" {
|
||||
const x: u16 = WIDTH;
|
||||
const y: u16 = 0;
|
||||
|
||||
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper);
|
||||
|
||||
updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor height out of bounds" {
|
||||
const x: u16 = 0;
|
||||
const y: u16 = HEIGHT;
|
||||
|
||||
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper);
|
||||
|
||||
updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor width and height out of bounds" {
|
||||
const x: u16 = WIDTH;
|
||||
const y: u16 = HEIGHT;
|
||||
|
||||
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper);
|
||||
|
||||
updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor width-1 and height out of bounds" {
|
||||
const x: u16 = WIDTH - 1;
|
||||
const y: u16 = HEIGHT;
|
||||
|
||||
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper);
|
||||
|
||||
updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor width and height-1 out of bounds" {
|
||||
const x: u16 = WIDTH;
|
||||
const y: u16 = HEIGHT - 1;
|
||||
|
||||
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper);
|
||||
|
||||
updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor in bounds" {
|
||||
var x: u16 = 0x000A;
|
||||
var y: u16 = 0x000A;
|
||||
const expected: u16 = y * WIDTH + x;
|
||||
|
||||
var expected_upper: u8 = @truncate(u8, (expected >> 8) & 0x00FF);
|
||||
var expected_lower: u8 = @truncate(u8, expected & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper);
|
||||
updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "getCursor 1: 10" {
|
||||
const expect: u16 = u16(10);
|
||||
|
||||
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW);
|
||||
|
||||
arch.addTestParams("inb", PORT_DATA, u8(10));
|
||||
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH);
|
||||
|
||||
arch.addTestParams("inb", PORT_DATA, u8(0));
|
||||
|
||||
const actual: u16 = getCursor();
|
||||
expectEqual(expect, actual);
|
||||
}
|
||||
|
||||
test "getCursor 2: 0xBEEF" {
|
||||
const expect: u16 = u16(0xBEEF);
|
||||
|
||||
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW);
|
||||
|
||||
arch.addTestParams("inb", PORT_DATA, u8(0xEF));
|
||||
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH);
|
||||
|
||||
arch.addTestParams("inb", PORT_DATA, u8(0xBE));
|
||||
|
||||
const actual: u16 = getCursor();
|
||||
expectEqual(expect, actual);
|
||||
}
|
||||
|
||||
test "enableCursor all" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Need to init the cursor start and end positions, so call the init() to set this up
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE, PORT_DATA, CURSOR_SCANLINE_END, PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END,
|
||||
// Mocking out the arch.outb calls for enabling the cursor:
|
||||
// These are the default cursor positions from init()
|
||||
PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END);
|
||||
|
||||
init();
|
||||
enableCursor();
|
||||
}
|
||||
|
||||
test "disableCursor all" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for disabling the cursor:
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_DISABLE);
|
||||
disableCursor();
|
||||
}
|
||||
|
||||
test "setCursorShape UNDERLINE" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for setting the cursor shape to underline:
|
||||
// This will also check that the scan line variables were set properly as these are using in
|
||||
// the arch.outb call
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END);
|
||||
|
||||
setCursorShape(CursorShape.UNDERLINE);
|
||||
}
|
||||
|
||||
test "setCursorShape BLOCK" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for setting the cursor shape to block:
|
||||
// This will also check that the scan line variables were set properly as these are using in
|
||||
// the arch.outb call
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_START, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END);
|
||||
|
||||
setCursorShape(CursorShape.BLOCK);
|
||||
}
|
||||
|
||||
test "init all" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for setting the cursor max scan line and the shape to block:
|
||||
// This will also check that the scan line variables were set properly as these are using in
|
||||
// the arch.outb call for setting the cursor shape.
|
||||
arch.addTestParams("outb", PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE, PORT_DATA, CURSOR_SCANLINE_END, PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END);
|
||||
|
||||
init();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
def getTestCases(TestCase):
|
||||
return [
|
||||
TestCase("GDT init", [r"Init gdt", r"Done"]),
|
||||
TestCase("GDT tests", [r"GDT: Tested loading GDT"]),
|
||||
TestCase("IDT init", [r"Init idt", r"Done"]),
|
||||
TestCase("PIT init", [r"Init pit", r".+", "Done"]),
|
||||
TestCase("Syscalls init", [r"Init syscalls", "Done"]),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const std = @import("std");
|
||||
const MemProfile = @import("mem_mock.zig").MemProfile;
|
||||
const expect = std.testing.expect;
|
||||
const warn = std.debug.warn;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const mem = @import("mem_mock.zig");
|
||||
const MemProfile = mem.MemProfile;
|
||||
const gdt = @import("gdt_mock.zig");
|
||||
|
||||
const mock_framework = @import("mock_framework.zig");
|
||||
pub const initTest = mock_framework.initTest;
|
||||
|
@ -11,29 +12,20 @@ pub const addConsumeFunction = mock_framework.addConsumeFunction;
|
|||
pub const addRepeatFunction = mock_framework.addRepeatFunction;
|
||||
|
||||
pub const InterruptContext = struct {
|
||||
// Extra segments
|
||||
gs: u32,
|
||||
fs: u32,
|
||||
es: u32,
|
||||
ds: u32,
|
||||
|
||||
// Destination, source, base pointer
|
||||
edi: u32,
|
||||
esi: u32,
|
||||
ebp: u32,
|
||||
esp: u32,
|
||||
|
||||
// General registers
|
||||
ebx: u32,
|
||||
edx: u32,
|
||||
ecx: u32,
|
||||
eax: u32,
|
||||
|
||||
// Interrupt number and error code
|
||||
int_num: u32,
|
||||
error_code: u32,
|
||||
|
||||
// Instruction pointer, code segment and flags
|
||||
eip: u32,
|
||||
cs: u32,
|
||||
eflags: u32,
|
||||
|
@ -41,10 +33,6 @@ pub const InterruptContext = struct {
|
|||
ss: u32,
|
||||
};
|
||||
|
||||
pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator, comptime options: type) void {
|
||||
//return mock_framework.performAction("init", void, mem_profile, allocator);
|
||||
}
|
||||
|
||||
pub fn outb(port: u16, data: u8) void {
|
||||
return mock_framework.performAction("outb", void, port, data);
|
||||
}
|
||||
|
@ -57,20 +45,20 @@ pub fn ioWait() void {
|
|||
return mock_framework.performAction("ioWait", void);
|
||||
}
|
||||
|
||||
pub fn registerInterruptHandler(int: u16, ctx: fn (ctx: *InterruptContext) void) void {
|
||||
return mock_framework.performAction("registerInterruptHandler", void, int, ctx);
|
||||
}
|
||||
|
||||
pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void {
|
||||
return mock_framework.performAction("lgdt", void, gdt_ptr.*);
|
||||
return mock_framework.performAction("lgdt", void, gdt_ptr);
|
||||
}
|
||||
|
||||
pub fn ltr() void {
|
||||
return mock_framework.performAction("ltr", void);
|
||||
pub fn sgdt() gdt.GdtPtr {
|
||||
return mock_framework.performAction("sgdt", gdt.GdtPtr);
|
||||
}
|
||||
|
||||
pub fn ltr(offset: u16) void {
|
||||
return mock_framework.performAction("ltr", void, offset);
|
||||
}
|
||||
|
||||
pub fn lidt(idt_ptr: *const idt.IdtPtr) void {
|
||||
return mock_framework.performAction("lidt", void, idt_ptr.*);
|
||||
return mock_framework.performAction("lidt", void, idt_ptr);
|
||||
}
|
||||
|
||||
pub fn enableInterrupts() void {
|
||||
|
@ -92,3 +80,9 @@ pub fn spinWait() noreturn {
|
|||
pub fn haltNoInterrupts() noreturn {
|
||||
while (true) {}
|
||||
}
|
||||
|
||||
pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime options: type) void {
|
||||
// I'll get back to this as this doesn't effect the GDT testing.
|
||||
// When I come on to the mem.zig testing, I'll fix :)
|
||||
//return mock_framework.performAction("init", void, mem_profile, allocator);
|
||||
}
|
||||
|
|
169
test/mock/kernel/gdt_mock.zig
Normal file
169
test/mock/kernel/gdt_mock.zig
Normal file
|
@ -0,0 +1,169 @@
|
|||
// Can't do: TODO: https://github.com/SamTebbs33/pluto/issues/77
|
||||
//const src_gdt = @import("arch").gdt;
|
||||
const src_gdt = @import("../../../src/kernel/arch/x86/gdt.zig");
|
||||
|
||||
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 AccessBits = packed struct {
|
||||
accessed: u1,
|
||||
read_write: u1,
|
||||
direction_conforming: u1,
|
||||
executable: u1,
|
||||
descriptor: u1,
|
||||
privilege: u2,
|
||||
present: u1,
|
||||
};
|
||||
|
||||
const FlagBits = packed struct {
|
||||
reserved_zero: u1,
|
||||
is_64_bit: u1,
|
||||
is_32_bit: u1,
|
||||
granularity: u1,
|
||||
};
|
||||
|
||||
const GdtEntry = packed struct {
|
||||
limit_low: u16,
|
||||
base_low: u24,
|
||||
access: AccessBits,
|
||||
limit_high: u4,
|
||||
flags: FlagBits,
|
||||
base_high: u8,
|
||||
};
|
||||
|
||||
const TtsEntry = packed struct {
|
||||
prev_tss: u32,
|
||||
esp0: u32,
|
||||
ss0: u32,
|
||||
esp1: u32,
|
||||
ss1: u32,
|
||||
esp2: u32,
|
||||
ss2: u32,
|
||||
cr3: u32,
|
||||
eip: u32,
|
||||
eflags: u32,
|
||||
eax: u32,
|
||||
ecx: u32,
|
||||
edx: u32,
|
||||
ebx: u32,
|
||||
esp: u32,
|
||||
ebp: u32,
|
||||
esi: u32,
|
||||
edi: u32,
|
||||
es: u32,
|
||||
cs: u32,
|
||||
ss: u32,
|
||||
ds: u32,
|
||||
fs: u32,
|
||||
gs: u32,
|
||||
ldtr: u32,
|
||||
trap: u16,
|
||||
io_permissions_base_offset: u16,
|
||||
};
|
||||
|
||||
// Need to use the type from the source file so that types match
|
||||
pub const GdtPtr = src_gdt.GdtPtr;
|
||||
|
||||
const NUMBER_OF_ENTRIES: u16 = 0x06;
|
||||
|
||||
const TABLE_SIZE: u16 = @sizeOf(GdtEntry) * NUMBER_OF_ENTRIES - 1;
|
||||
|
||||
const NULL_INDEX: u16 = 0x00;
|
||||
const KERNEL_CODE_INDEX: u16 = 0x01;
|
||||
const KERNEL_DATA_INDEX: u16 = 0x02;
|
||||
const USER_CODE_INDEX: u16 = 0x03;
|
||||
const USER_DATA_INDEX: u16 = 0x04;
|
||||
const TSS_INDEX: u16 = 0x05;
|
||||
|
||||
const NULL_SEGMENT: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 0,
|
||||
.direction_conforming = 0,
|
||||
.executable = 0,
|
||||
.descriptor = 0,
|
||||
.privilege = 0,
|
||||
.present = 0,
|
||||
};
|
||||
|
||||
const KERNEL_SEGMENT_CODE: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 1,
|
||||
.descriptor = 1,
|
||||
.privilege = 0,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
const KERNEL_SEGMENT_DATA: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 0,
|
||||
.descriptor = 1,
|
||||
.privilege = 0,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
const USER_SEGMENT_CODE: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 1,
|
||||
.descriptor = 1,
|
||||
.privilege = 3,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
const USER_SEGMENT_DATA: AccessBits = AccessBits{
|
||||
.accessed = 0,
|
||||
.read_write = 1,
|
||||
.direction_conforming = 0,
|
||||
.executable = 0,
|
||||
.descriptor = 1,
|
||||
.privilege = 3,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
const TSS_SEGMENT: AccessBits = AccessBits{
|
||||
.accessed = 1,
|
||||
.read_write = 0,
|
||||
.direction_conforming = 0,
|
||||
.executable = 1,
|
||||
.descriptor = 0,
|
||||
.privilege = 0,
|
||||
.present = 1,
|
||||
};
|
||||
|
||||
const NULL_FLAGS: FlagBits = FlagBits{
|
||||
.reserved_zero = 0,
|
||||
.is_64_bit = 0,
|
||||
.is_32_bit = 0,
|
||||
.granularity = 0,
|
||||
};
|
||||
|
||||
const PAGING_32_BIT: FlagBits = FlagBits{
|
||||
.reserved_zero = 0,
|
||||
.is_64_bit = 0,
|
||||
.is_32_bit = 1,
|
||||
.granularity = 1,
|
||||
};
|
||||
|
||||
pub const NULL_OFFSET: u16 = 0x00;
|
||||
pub const KERNEL_CODE_OFFSET: u16 = 0x08;
|
||||
pub const KERNEL_DATA_OFFSET: u16 = 0x10;
|
||||
pub const USER_CODE_OFFSET: u16 = 0x18;
|
||||
pub const USER_DATA_OFFSET: u16 = 0x20;
|
||||
pub const TSS_OFFSET: u16 = 0x28;
|
||||
|
||||
pub fn setTssStack(esp0: u32) void {
|
||||
return mock_framework.performAction("setTssStack", void, esp0);
|
||||
}
|
||||
|
||||
pub fn init() void {
|
||||
return mock_framework.performAction("init", void);
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const StringHashMap = std.StringHashMap;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const GlobalAllocator = std.debug.global_allocator;
|
||||
const TailQueue = std.TailQueue;
|
||||
const warn = std.debug.warn;
|
||||
const gdt = @import("gdt_mock.zig");
|
||||
|
||||
///
|
||||
/// The enumeration of types that the mocking framework supports. These include basic types like u8
|
||||
|
@ -16,14 +16,17 @@ const DataElementType = enum {
|
|||
U8,
|
||||
U16,
|
||||
U32,
|
||||
PTR_CONST_GdtPtr,
|
||||
FN_OVOID,
|
||||
FN_OUSIZE,
|
||||
FN_OU16,
|
||||
FN_IU16_OVOID,
|
||||
FN_IU16_OU8,
|
||||
FN_IU4_IU4_OU8,
|
||||
FN_IU8_IU8_OU16,
|
||||
FN_IU16_IU8_OVOID,
|
||||
FN_IU16_IU16_OVOID,
|
||||
FN_IPTRCONSTGDTPTR_OVOID,
|
||||
};
|
||||
|
||||
///
|
||||
|
@ -36,14 +39,17 @@ const DataElement = union(DataElementType) {
|
|||
U8: u8,
|
||||
U16: u16,
|
||||
U32: u32,
|
||||
PTR_CONST_GdtPtr: *const gdt.GdtPtr,
|
||||
FN_OVOID: fn () void,
|
||||
FN_OUSIZE: fn () usize,
|
||||
FN_OU16: fn () u16,
|
||||
FN_IU16_OVOID: fn (u16) void,
|
||||
FN_IU16_OU8: fn (u16) u8,
|
||||
FN_IU4_IU4_OU8: fn (u4, u4) u8,
|
||||
FN_IU8_IU8_OU16: fn (u8, u8) u16,
|
||||
FN_IU16_IU8_OVOID: fn (u16, u8) void,
|
||||
FN_IU16_IU16_OVOID: fn (u16, u16) void,
|
||||
FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void,
|
||||
};
|
||||
|
||||
///
|
||||
|
@ -123,14 +129,17 @@ fn Mock() type {
|
|||
u8 => DataElement{ .U8 = arg },
|
||||
u16 => DataElement{ .U16 = arg },
|
||||
u32 => DataElement{ .U32 = arg },
|
||||
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
|
||||
fn () void => DataElement{ .FN_OVOID = arg },
|
||||
fn () usize => DataElement{ .FN_OUSIZE = arg },
|
||||
fn () u16 => DataElement{ .FN_OU16 = arg },
|
||||
fn (u16) void => DataElement{ .FN_IU16_OVOID = arg },
|
||||
fn (u16) u8 => DataElement{ .FN_IU16_OU8 = arg },
|
||||
fn (u4, u4) u8 => DataElement{ .FN_IU4_IU4_OU8 = arg },
|
||||
fn (u8, u8) u16 => DataElement{ .FN_IU8_IU8_OU16 = arg },
|
||||
fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg },
|
||||
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
|
||||
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
|
||||
else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))),
|
||||
};
|
||||
}
|
||||
|
@ -150,13 +159,16 @@ fn Mock() type {
|
|||
u8 => DataElementType.U8,
|
||||
u16 => DataElementType.U16,
|
||||
u32 => DataElementType.U32,
|
||||
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
|
||||
fn () void => DataElementType.FN_OVOID,
|
||||
fn () u16 => DataElementType.FN_OU16,
|
||||
fn (u16) void => DataElementType.FN_IU16_OVOID,
|
||||
fn (u16) u8 => DataElementType.FN_IU16_OU8,
|
||||
fn (u4, u4) u8 => DataElementType.FN_IU4_IU4_OU8,
|
||||
fn (u8, u8) u16 => DataElementType.FN_IU8_IU8_OU16,
|
||||
fn (u16, u8) void => DataElementType.FN_IU16_IU8_OVOID,
|
||||
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
|
||||
fn (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_OVOID,
|
||||
else => @compileError("Type not supported: " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
|
@ -178,13 +190,16 @@ fn Mock() type {
|
|||
u8 => element.U8,
|
||||
u16 => element.U16,
|
||||
u32 => element.U32,
|
||||
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
|
||||
fn () void => element.FN_OVOID,
|
||||
fn () u16 => element.FN_OU16,
|
||||
fn (u16) void => element.FN_IU16_OVOID,
|
||||
fn (u16) u8 => element.FN_IU16_OU8,
|
||||
fn (u4, u4) u8 => element.FN_IU4_IU4_OU8,
|
||||
fn (u8, u8) u16 => element.FN_IU8_IU8_OU16,
|
||||
fn (u16, u8) void => element.FN_IU16_IU8_OVOID,
|
||||
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
|
||||
fn (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_OVOID,
|
||||
else => @compileError("Type not supported: " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
pub const arch = @import("arch_mock.zig");
|
||||
pub const gdt = @import("gdt_mock.zig");
|
||||
pub const log = @import("log_mock.zig");
|
||||
pub const mem = @import("mem_mock.zig");
|
||||
pub const vga = @import("vga_mock.zig");
|
||||
pub const arch = @import("arch_mock.zig");
|
||||
pub const panic = @import("panic_mock.zig");
|
||||
pub const vga = @import("vga_mock.zig");
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// I gave up on trying to get all the tests in a separate file for the tty
|
||||
test "" {
|
||||
_ = @import("../../../src/kernel/tty.zig");
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
const vga = @import("../../../src/kernel/vga.zig");
|
||||
const arch = @import("../../../src/kernel/arch.zig").internals;
|
||||
|
||||
const expectEqual = @import("std").testing.expectEqual;
|
||||
|
||||
test "entryColour" {
|
||||
var fg: u4 = vga.COLOUR_BLACK;
|
||||
var bg: u4 = vga.COLOUR_BLACK;
|
||||
var res: u8 = vga.entryColour(fg, bg);
|
||||
expectEqual(u8(0x00), res);
|
||||
|
||||
fg = vga.COLOUR_LIGHT_GREEN;
|
||||
bg = vga.COLOUR_BLACK;
|
||||
res = vga.entryColour(fg, bg);
|
||||
expectEqual(u8(0x0A), res);
|
||||
|
||||
fg = vga.COLOUR_BLACK;
|
||||
bg = vga.COLOUR_LIGHT_GREEN;
|
||||
res = vga.entryColour(fg, bg);
|
||||
expectEqual(u8(0xA0), res);
|
||||
|
||||
fg = vga.COLOUR_BROWN;
|
||||
bg = vga.COLOUR_LIGHT_GREEN;
|
||||
res = vga.entryColour(fg, bg);
|
||||
expectEqual(u8(0xA6), res);
|
||||
}
|
||||
|
||||
test "entry" {
|
||||
var colour: u8 = vga.entryColour(vga.COLOUR_BROWN, vga.COLOUR_LIGHT_GREEN);
|
||||
expectEqual(u8(0xA6), colour);
|
||||
|
||||
// Character '0' is 0x30
|
||||
var video_entry: u16 = vga.entry('0', colour);
|
||||
expectEqual(u16(0xA630), video_entry);
|
||||
|
||||
video_entry = vga.entry(0x55, colour);
|
||||
expectEqual(u16(0xA655), video_entry);
|
||||
}
|
||||
|
||||
test "updateCursor width out of bounds" {
|
||||
const x: u16 = vga.WIDTH;
|
||||
const y: u16 = 0;
|
||||
|
||||
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
|
||||
vga.PORT_DATA, expected_lower,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
|
||||
vga.PORT_DATA, expected_upper);
|
||||
|
||||
vga.updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor height out of bounds" {
|
||||
const x: u16 = 0;
|
||||
const y: u16 = vga.HEIGHT;
|
||||
|
||||
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
|
||||
vga.PORT_DATA, expected_lower,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
|
||||
vga.PORT_DATA, expected_upper);
|
||||
|
||||
vga.updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor width and height out of bounds" {
|
||||
const x: u16 = vga.WIDTH;
|
||||
const y: u16 = vga.HEIGHT;
|
||||
|
||||
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
|
||||
vga.PORT_DATA, expected_lower,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
|
||||
vga.PORT_DATA, expected_upper);
|
||||
|
||||
vga.updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor width-1 and height out of bounds" {
|
||||
const x: u16 = vga.WIDTH - 1;
|
||||
const y: u16 = vga.HEIGHT;
|
||||
|
||||
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
|
||||
vga.PORT_DATA, expected_lower,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
|
||||
vga.PORT_DATA, expected_upper);
|
||||
|
||||
vga.updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor width and height-1 out of bounds" {
|
||||
const x: u16 = vga.WIDTH;
|
||||
const y: u16 = vga.HEIGHT - 1;
|
||||
|
||||
const max_cursor: u16 = (vga.HEIGHT - 1) * vga.WIDTH + (vga.WIDTH - 1);
|
||||
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
||||
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
|
||||
vga.PORT_DATA, expected_lower,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
|
||||
vga.PORT_DATA, expected_upper);
|
||||
|
||||
vga.updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "updateCursor in bounds" {
|
||||
var x: u16 = 0x000A;
|
||||
var y: u16 = 0x000A;
|
||||
const expected: u16 = y * vga.WIDTH + x;
|
||||
|
||||
var expected_upper: u8 = @truncate(u8, (expected >> 8) & 0x00FF);
|
||||
var expected_lower: u8 = @truncate(u8, expected & 0x00FF);
|
||||
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for changing the hardware cursor:
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW,
|
||||
vga.PORT_DATA, expected_lower,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH,
|
||||
vga.PORT_DATA, expected_upper);
|
||||
vga.updateCursor(x, y);
|
||||
}
|
||||
|
||||
test "getCursor 1: 10" {
|
||||
const expect: u16 = u16(10);
|
||||
|
||||
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW);
|
||||
|
||||
arch.addTestParams("inb",
|
||||
vga.PORT_DATA, u8(10));
|
||||
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH);
|
||||
|
||||
arch.addTestParams("inb",
|
||||
vga.PORT_DATA, u8(0));
|
||||
|
||||
const actual: u16 = vga.getCursor();
|
||||
expectEqual(expect, actual);
|
||||
}
|
||||
|
||||
test "getCursor 2: 0xBEEF" {
|
||||
const expect: u16 = u16(0xBEEF);
|
||||
|
||||
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_LOW);
|
||||
|
||||
arch.addTestParams("inb",
|
||||
vga.PORT_DATA, u8(0xEF));
|
||||
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_LOCATION_HIGH);
|
||||
|
||||
arch.addTestParams("inb",
|
||||
vga.PORT_DATA, u8(0xBE));
|
||||
|
||||
const actual: u16 = vga.getCursor();
|
||||
expectEqual(expect, actual);
|
||||
}
|
||||
|
||||
test "enableCursor all" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Need to init the cursor start and end positions, so call the vga.init() to set this up
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_MAXIMUM_SCAN_LINE,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_END,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_END,
|
||||
// Mocking out the arch.outb calls for enabling the cursor:
|
||||
// These are the default cursor positions from vga.init()
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
|
||||
|
||||
vga.init();
|
||||
vga.enableCursor();
|
||||
}
|
||||
|
||||
test "disableCursor all" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for disabling the cursor:
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
|
||||
vga.PORT_DATA, vga.CURSOR_DISABLE);
|
||||
vga.disableCursor();
|
||||
}
|
||||
|
||||
test "setCursorShape UNDERLINE" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for setting the cursor shape to underline:
|
||||
// This will also check that the scan line variables were set properly as these are using in
|
||||
// the arch.outb call
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
|
||||
|
||||
vga.setCursorShape(vga.CursorShape.UNDERLINE);
|
||||
}
|
||||
|
||||
test "setCursorShape BLOCK" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for setting the cursor shape to block:
|
||||
// This will also check that the scan line variables were set properly as these are using in
|
||||
// the arch.outb call
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_START,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
|
||||
|
||||
vga.setCursorShape(vga.CursorShape.BLOCK);
|
||||
}
|
||||
|
||||
test "init all" {
|
||||
arch.initTest();
|
||||
defer arch.freeTest();
|
||||
|
||||
// Mocking out the arch.outb calls for setting the cursor max scan line and the shape to block:
|
||||
// This will also check that the scan line variables were set properly as these are using in
|
||||
// the arch.outb call for setting the cursor shape.
|
||||
arch.addTestParams("outb",
|
||||
vga.PORT_ADDRESS, vga.REG_MAXIMUM_SCAN_LINE,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_END,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_START,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_MIDDLE,
|
||||
vga.PORT_ADDRESS, vga.REG_CURSOR_END,
|
||||
vga.PORT_DATA, vga.CURSOR_SCANLINE_END);
|
||||
|
||||
vga.init();
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
test "all" {
|
||||
_ = @import("kernel/test_vga.zig");
|
||||
_ = @import("kernel/test_tty.zig");
|
||||
}
|
Loading…
Reference in a new issue