Added gdt unit and runtime tests

Updated runtime tests


Added doc comments for runtime tests


PR review


WIP


Fixed testing


Import GDT to run the unit tests


Removed redundant arch tests


Removed whitespace
This commit is contained in:
ED 2019-09-16 22:19:21 +01:00
parent 9c35de8673
commit 07cc1ae89b
18 changed files with 1141 additions and 590 deletions

View file

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const Builder = std.build.Builder; const Builder = std.build.Builder;
const LibExeObjStep = std.build.LibExeObjStep;
const Step = std.build.Step; const Step = std.build.Step;
const Target = std.build.Target; const Target = std.build.Target;
const fs = std.fs; const fs = std.fs;
@ -16,17 +17,18 @@ pub fn build(b: *Builder) !void {
}; };
const target_str = switch (target.getArch()) { const target_str = switch (target.getArch()) {
builtin.Arch.i386 => "x86", .i386 => "x86",
else => unreachable, else => unreachable,
}; };
const debug = b.option(bool, "debug", "build with debug symbols / make qemu wait for a debug connection") orelse false; 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 rt_test = b.option(bool, "rt-test", "enable/disable runtime testing") orelse false;
const main_src = "src/kernel/kmain.zig"; const main_src = "src/kernel/kmain.zig";
const exec = b.addExecutable("pluto", main_src); const exec = b.addExecutable("pluto", main_src);
exec.setMainPkgPath("."); const constants_path = try fs.path.join(b.allocator, [_][]const u8{ "src/kernel/arch", target_str, "constants.zig" });
const const_path = try fs.path.join(b.allocator, [_][]const u8{ "src/kernel/arch/", target_str, "/constants.zig" }); exec.addPackagePath("constants", constants_path);
exec.addPackagePath("constants", const_path);
exec.addBuildOption(bool, "rt_test", rt_test); exec.addBuildOption(bool, "rt_test", rt_test);
exec.setLinkerScriptPath("link.ld"); exec.setLinkerScriptPath("link.ld");
exec.setTheTarget(target); 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/irq_asm.s");
exec.addAssemblyFile("src/kernel/arch/x86/isr_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 iso_path = try fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "pluto.iso" });
const grub_build_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso", "boot" }) catch unreachable; const grub_build_path = try fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso", "boot" });
const iso_dir_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso" }) catch unreachable; 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).? }); 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); b.default_step.dependOn(&iso_cmd.step);
const run_step = b.step("run", "Run with qemu"); 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{ const qemu_cmd = b.addSystemCommand([_][]const u8{
qemu_bin, qemu_bin,
"-cdrom", "-cdrom",
@ -69,10 +74,15 @@ pub fn build(b: *Builder) !void {
"-serial", "-serial",
"stdio", "stdio",
}); });
if (debug)
if (debug) {
qemu_cmd.addArgs([_][]const u8{ "-s", "-S" }); qemu_cmd.addArgs([_][]const u8{ "-s", "-S" });
if (rt_test) }
if (rt_test) {
qemu_cmd.addArgs([_][]const u8{ "-display", "none" }); qemu_cmd.addArgs([_][]const u8{ "-display", "none" });
}
run_step.dependOn(&qemu_cmd.step); run_step.dependOn(&qemu_cmd.step);
qemu_cmd.step.dependOn(&iso_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 }); const script = b.addSystemCommand([_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe });
test_step.dependOn(&script.step); test_step.dependOn(&script.step);
} else { } else {
inline for ([_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }) |test_mode| { const mock_path = "\"" ++ "../../test/mock/kernel/" ++ "\"";
const mode_str = comptime modeToString(test_mode); const arch_mock_path = "\"" ++ "../../../../test/mock/kernel/" ++ "\"";
const unit_tests = b.addTest("test/unittests/test_all.zig"); 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.setBuildMode(test_mode);
unit_tests.setMainPkgPath("."); unit_tests.setMainPkgPath(".");
unit_tests.setNamePrefix(mode_str ++ " - "); unit_tests.setNamePrefix(mode_str ++ " - ");
unit_tests.addPackagePath("mocking", "test/mock/kernel/mocking.zig"); unit_tests.addPackagePath("constants", constants_path);
unit_tests.addPackagePath("constants", const_path);
unit_tests.addBuildOption(bool, "rt_test", rt_test); 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); test_step.dependOn(&unit_tests.step);
} }
} }
@ -107,12 +126,3 @@ pub fn build(b: *Builder) !void {
}); });
debug_step.dependOn(&debug_cmd.step); 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",
};
}

View file

@ -1,6 +1,8 @@
const builtin = @import("builtin"); 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) { pub const internals = if (is_test) @import(build_options.mock_path ++ "arch_mock.zig") else switch (builtin.arch) {
builtin.Arch.i386 => @import("arch/x86/arch.zig"), .i386 => @import("arch/x86/arch.zig"),
else => unreachable, else => unreachable,
}; };

View file

@ -1,17 +1,19 @@
// Zig version: 0.4.0
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator;
const builtin = @import("builtin"); const builtin = @import("builtin");
const gdt = @import("gdt.zig"); const gdt = @import("gdt.zig");
const idt = @import("idt.zig"); const idt = @import("idt.zig");
const irq = @import("irq.zig"); const irq = @import("irq.zig");
const isr = @import("isr.zig"); const isr = @import("isr.zig");
const log = @import("../../log.zig");
const pit = @import("pit.zig"); const pit = @import("pit.zig");
const paging = @import("paging.zig"); const paging = @import("paging.zig");
const MemProfile = @import("../../mem.zig").MemProfile;
const syscalls = @import("syscalls.zig"); 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 { pub const InterruptContext = struct {
// Extra segments // Extra segments
gs: u32, gs: u32,
@ -44,29 +46,7 @@ pub const InterruptContext = struct {
}; };
/// ///
/// Initialise the architecture /// Assembly to write to a given port with a byte of data.
///
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.
/// ///
/// Arguments: /// Arguments:
/// IN port: u16 - The port to write to. /// 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: /// Arguments:
/// IN port: u16 - The port to read data from. /// IN port: u16 - The port to read data from.
/// ///
/// Return: /// Return: u8
/// The data that the port returns. /// The data that the port returns.
/// ///
pub fn inb(port: u16) u8 { pub fn inb(port: u16) u8 {
@ -101,6 +81,7 @@ pub fn inb(port: u16) u8 {
/// event being waited. /// event being waited.
/// ///
pub fn ioWait() void { pub fn ioWait() void {
// Port 0x80 is free to use
outb(0x80, 0); outb(0x80, 0);
} }
@ -114,13 +95,22 @@ pub fn ioWait() void {
/// ///
pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void { pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void {
// Load the GDT into the CPU // 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 // 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, %%es");
asm volatile ("mov %%bx, %%fs"); asm volatile ("mov %%bx, %%fs");
asm volatile ("mov %%bx, %%gs"); asm volatile ("mov %%bx, %%gs");
asm volatile ("mov %%bx, %%ss"); asm volatile ("mov %%bx, %%ss");
// Load the kernel code segment into the CS register // Load the kernel code segment into the CS register
asm volatile ( asm volatile (
\\ljmp $0x08, $1f \\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 { /// Return: gdt.GdtPtr
asm volatile ("ltr %%ax" : : [TSS_OFFSET] "{ax}" (gdt.TSS_OFFSET)); /// 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. /// 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 { 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 { pub fn enableInterrupts() void {
asm volatile ("sti"); asm volatile ("sti");
} }
/// ///
/// Disable interrupts /// Disable interrupts.
/// ///
pub fn disableInterrupts() void { pub fn disableInterrupts() void {
asm volatile ("cli"); 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 { pub fn halt() void {
asm volatile ("hlt"); 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 { pub fn haltNoInterrupts() noreturn {
while (true) { 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: /// Arguments:
/// IN int: u16 - The arch-specific interrupt number to register for. /// IN mem_profile: *const MemProfile - The memory profile of the computer. Used to set up
/// IN handler: fn (ctx: *InterruptContext) void - The handler to assign to the interrupt. /// 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 { pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime options: type) void {
irq.registerIrq(int, handler); disableInterrupts();
gdt.init();
idt.init();
isr.init();
irq.init();
pit.init();
paging.init(mem_profile, allocator);
syscalls.init(options);
enableInterrupts();
} }

View file

@ -1,5 +1,12 @@
const constants = @import("constants"); const constants = @import("constants");
/// The multiboot header
const MultiBoot = packed struct {
magic: i32,
flags: i32,
checksum: i32,
};
const ALIGN = 1 << 0; const ALIGN = 1 << 0;
const MEMINFO = 1 << 1; const MEMINFO = 1 << 1;
const MAGIC = 0x1BADB002; 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. // The number of pages occupied by the kernel, will need to be increased as we add a heap etc.
const KERNEL_NUM_PAGES = 1; 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{ export var multiboot align(4) linksection(".rodata.boot") = MultiBoot{
.magic = MAGIC, .magic = MAGIC,
.flags = FLAGS, .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; 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 { 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 ( asm volatile (
\\.extern boot_page_directory \\.extern boot_page_directory
\\mov $boot_page_directory, %%ecx \\mov $boot_page_directory, %%ecx
\\mov %%ecx, %%cr3 \\mov %%ecx, %%cr3
); );
// Enable 4 MiB pages // Enable 4 MiB pages
asm volatile ( asm volatile (
\\mov %%cr4, %%ecx \\mov %%cr4, %%ecx
\\or $0x00000010, %%ecx \\or $0x00000010, %%ecx
\\mov %%ecx, %%cr4 \\mov %%ecx, %%cr4
); );
// Enable paging // Enable paging
asm volatile ( asm volatile (
\\mov %%cr0, %%ecx \\mov %%cr0, %%ecx
@ -91,12 +94,14 @@ export nakedcc fn _start() align(16) linksection(".text.boot") noreturn {
export nakedcc fn start_higher_half() noreturn { export nakedcc fn start_higher_half() noreturn {
// Invalidate the page for the first 4MiB as it's no longer needed // Invalidate the page for the first 4MiB as it's no longer needed
asm volatile ("invlpg (0)"); asm volatile ("invlpg (0)");
// Setup the stack // Setup the stack
asm volatile ( asm volatile (
\\.extern KERNEL_STACK_END \\.extern KERNEL_STACK_END
\\mov $KERNEL_STACK_END, %%esp \\mov $KERNEL_STACK_END, %%esp
\\mov %%esp, %%ebp \\mov %%esp, %%ebp
); );
// Push the bootloader magic number and multiboot header address with virtual offset // Push the bootloader magic number and multiboot header address with virtual offset
asm volatile ( asm volatile (
\\.extern KERNEL_ADDR_OFFSET \\.extern KERNEL_ADDR_OFFSET

View file

@ -1 +1,2 @@
/// The virtual address where the kernel will be loaded. This is at 3GB.
pub const KERNEL_ADDR_OFFSET = 0xC0000000; pub const KERNEL_ADDR_OFFSET = 0xC0000000;

View file

@ -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 build_options = @import("build_options");
const log = @import("../../log.zig"); 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; /// The access bits for a GDT entry.
const TABLE_SIZE: u16 = @sizeOf(GdtEntry) * NUMBER_OF_ENTRIES - 1; 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. /// For code segments, when set allows this code segments to be executed from a equal or lower
const NULL_INDEX: u16 = 0x00; /// 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. /// When set, the segment can be executed, a code segments. When not set, the segment can't be
const KERNEL_CODE_INDEX: u16 = 0x01; /// executed, data segment.
executable: u1,
/// The index of the kernel data GDT entry. /// Should be set for code and data segments, but not set for TSS.
const KERNEL_DATA_INDEX: u16 = 0x02; descriptor: u1,
/// The index of the user code GDT entry. /// Privilege/ring level. The kernel level is level 3, the highest privilege. The user level is
const USER_CODE_INDEX: u16 = 0x03; /// level 0, the lowest privilege.
privilege: u2,
/// The index of the user data GDT entry. /// Whether the segment is present. This must be set for all valid selectors, not the null
const USER_DATA_INDEX: u16 = 0x04; /// segment.
present: u1,
};
/// The index of the task state segment GDT entry. /// The flag bits for a GDT entry.
const TSS_INDEX: u16 = 0x05; 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. /// When set indicates the segment is a 32 bit protected mode segment. When not set, indicates
pub const NULL_OFFSET: u16 = 0x00; /// the segment is a 16 bit protected mode segment.
is_32_bit: u1,
/// The offset of the kernel code GDT entry. /// The granularity bit. When set the limit is in 4KB blocks (page granularity). When not set,
pub const KERNEL_CODE_OFFSET: u16 = 0x08; /// then limit is in 1B blocks (byte granularity). This should be set as we are doing paging.
granularity: u1,
/// 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 structure that contains all the information that each GDT entry needs. /// The structure that contains all the information that each GDT entry needs.
const GdtEntry = packed struct { 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. /// The lower 24 bits of the base address. Describes the start of memory for the entry.
base_low: u24, base_low: u24,
/// Bit 0 : accessed - The CPU will set this when the GDT entry is accessed. /// The access bits, see AccessBits for all the options. 8 bits.
/// 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. access: AccessBits,
/// 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 upper 4 bits of the limit address. Describes the size of memory that can be addressed. /// The upper 4 bits of the limit address. Describes the size of memory that can be addressed.
limit_high: u4, limit_high: u4,
/// Bit 0 : reserved_zero - This must always be zero. /// The flag bits, see above for all the options. 4 bits.
/// Bit 1 : is_64bits - Whether this is a 64 bit system. flags: FlagBits,
/// 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 upper 8 bits of the base address. Describes the start of memory for the entry. /// The upper 8 bits of the base address. Describes the start of memory for the entry.
base_high: u8, 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 /// The TSS entry structure
///
const TtsEntry = packed struct { const TtsEntry = packed struct {
/// Pointer to the previous TSS entry /// Pointer to the previous TSS entry
prev_tss: u32, prev_tss: u32,
@ -199,60 +166,186 @@ const TtsEntry = packed struct {
io_permissions_base_offset: u16, io_permissions_base_offset: u16,
}; };
/// /// The GDT pointer structure that contains the pointer to the beginning of the GDT and the number
/// Make a GDT entry. /// of the table (minus 1). Used to load the GDT with LGDT instruction.
/// pub const GdtPtr = packed struct {
/// Arguments: /// 16bit entry for the number of entries (minus 1).
/// IN access: u8 - The access bits for the descriptor. limit: u16,
/// IN flags: u4 - The flag bits for the descriptor.
/// /// 32bit entry for the base address for the GDT.
/// Return: base: u32,
/// 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 { /// The total number of entries in the GTD: null, kernel code, kernel data, user code, user data
return GdtEntry{ /// and TSS
.limit_low = @truncate(u16, limit), const NUMBER_OF_ENTRIES: u16 = 0x06;
.base_low = @truncate(u24, base),
.access = access, /// The size of the GTD in bytes (minus 1).
.limit_high = @truncate(u4, limit >> 16), const TABLE_SIZE: u16 = @sizeOf(GdtEntry) * NUMBER_OF_ENTRIES - 1;
.flags = flags,
.base_high = @truncate(u8, base >> 24), // ----------
}; // 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. /// The GDT entry table of NUMBER_OF_ENTRIES entries.
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = [_]GdtEntry{ var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = [_]GdtEntry{
// Null descriptor // Null descriptor
makeEntry(0, 0, 0, 0), makeEntry(0, 0, NULL_SEGMENT, NULL_FLAGS),
// Kernel Code // 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 // 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 // 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 // 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 // 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 /// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the
/// size. /// size.
const gdt_ptr: GdtPtr = GdtPtr{ var gdt_ptr: GdtPtr = GdtPtr{
.limit = TABLE_SIZE, .limit = TABLE_SIZE,
.base = &gdt_entries[0], .base = undefined,
}; };
/// The task state segment entry. /// The task state segment entry.
var tss: TtsEntry = TtsEntry{ var tss: TtsEntry = TtsEntry{
.prev_tss = 0, .prev_tss = 0,
.esp0 = undefined, .esp0 = 0,
.ss0 = KERNEL_DATA_OFFSET, .ss0 = u32(KERNEL_DATA_OFFSET),
.esp1 = 0, .esp1 = 0,
.ss1 = 0, .ss1 = 0,
.esp2 = 0, .esp2 = 0,
@ -276,31 +369,316 @@ var tss: TtsEntry = TtsEntry{
.gs = 0, .gs = 0,
.ldtr = 0, .ldtr = 0,
.trap = 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: /// 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 { pub fn setTssStack(esp0: u32) void {
tss.esp0 = esp0; tss.esp0 = esp0;
} }
/// ///
/// Initialise the Global Descriptor table /// Initialise the Global Descriptor table.
/// ///
pub fn init() void { pub fn init() void {
log.logInfo("Init gdt\n"); log.logInfo("Init gdt\n");
// Initiate TSS // 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 // Load the GDT
arch.lgdt(&gdt_ptr); arch.lgdt(&gdt_ptr);
// Load the TSS // Load the TSS
arch.ltr(); arch.ltr(TSS_OFFSET);
log.logInfo("Done\n"); 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
View 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");

View file

@ -1,15 +1,13 @@
// Zig version: 0.4.0
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const build_options = @import("build_options");
const arch = @import("arch.zig").internals; const arch = @import("arch.zig").internals;
const multiboot = @import("multiboot.zig"); const multiboot = @import("multiboot.zig");
const tty = @import("tty.zig"); const tty = @import("tty.zig");
const vga = @import("vga.zig"); const vga = @import("vga.zig");
const log = @import("log.zig"); const log = @import("log.zig");
const serial = @import("serial.zig"); const serial = @import("serial.zig");
const mem = if (builtin.is_test) @import("mocking").mem else @import("mem.zig"); const mem = if (builtin.is_test) @import(build_options.mock_path ++ "mem_mock.zig") else @import("mem.zig");
const options = @import("build_options");
comptime { comptime {
switch (builtin.arch) { 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 // 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 // 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 // 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 {
@ -33,7 +31,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
panic_root.panicFmt(error_return_trace, "{}", msg); 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) { if (mb_magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC) {
// Booted with compatible bootloader // Booted with compatible bootloader
const mem_profile = mem.init(mb_info); 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; serial.init(serial.DEFAULT_BAUDRATE, serial.Port.COM1) catch unreachable;
log.logInfo("Init arch " ++ @tagName(builtin.arch) ++ "\n"); 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"); log.logInfo("Arch init done\n");
vga.init(); vga.init();
tty.init(); tty.init();

View file

@ -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 std = @import("std");
const fmt = std.fmt; const fmt = std.fmt;
@ -6,8 +7,9 @@ const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
const expectError = std.testing.expectError; const expectError = std.testing.expectError;
const vga = if (is_test) @import("mocking").vga else @import("vga.zig"); const build_options = @import("build_options");
const log = if (is_test) @import("mocking").log else @import("log.zig"); 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 /// The number of rows down from the top (row 0) where the displayable region starts. Above is
/// where the logo and time is printed /// where the logo and time is printed

View file

@ -1,80 +1,82 @@
const std = @import("std");
const expectEqual = std.testing.expectEqual;
const arch = @import("arch.zig").internals; const arch = @import("arch.zig").internals;
/// The port address for the VGA register selection. /// 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. /// 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 /// The indexes that is passed to the address port to select the register for the data to be
/// read or written to. /// read or written to.
pub const REG_HORIZONTAL_TOTAL: u8 = 0x00; const REG_HORIZONTAL_TOTAL: u8 = 0x00;
pub const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01; const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
pub const REG_START_HORIZONTAL_BLINKING: u8 = 0x02; const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
pub const REG_END_HORIZONTAL_BLINKING: u8 = 0x03; const REG_END_HORIZONTAL_BLINKING: u8 = 0x03;
pub const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04; const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04;
pub const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05; const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05;
pub const REG_VERTICAL_TOTAL: u8 = 0x06; const REG_VERTICAL_TOTAL: u8 = 0x06;
pub const REG_OVERFLOW: u8 = 0x07; const REG_OVERFLOW: u8 = 0x07;
pub const REG_PRESET_ROW_SCAN: u8 = 0x08; const REG_PRESET_ROW_SCAN: u8 = 0x08;
pub const REG_MAXIMUM_SCAN_LINE: u8 = 0x09; const REG_MAXIMUM_SCAN_LINE: u8 = 0x09;
/// The register select for setting the cursor scan lines. /// The register select for setting the cursor scan lines.
pub const REG_CURSOR_START: u8 = 0x0A; const REG_CURSOR_START: u8 = 0x0A;
pub const REG_CURSOR_END: u8 = 0x0B; const REG_CURSOR_END: u8 = 0x0B;
pub const REG_START_ADDRESS_HIGH: u8 = 0x0C; const REG_START_ADDRESS_HIGH: u8 = 0x0C;
pub const REG_START_ADDRESS_LOW: u8 = 0x0D; const REG_START_ADDRESS_LOW: u8 = 0x0D;
/// The command for setting the cursor's linear location. /// The command for setting the cursor's linear location.
pub const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E; const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E;
pub const REG_CURSOR_LOCATION_LOW: u8 = 0x0F; const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
/// Other VGA registers. /// Other VGA registers.
pub const REG_VERTICAL_RETRACE_START: u8 = 0x10; const REG_VERTICAL_RETRACE_START: u8 = 0x10;
pub const REG_VERTICAL_RETRACE_END: u8 = 0x11; const REG_VERTICAL_RETRACE_END: u8 = 0x11;
pub const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12; const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12;
pub const REG_OFFSET: u8 = 0x13; const REG_OFFSET: u8 = 0x13;
pub const REG_UNDERLINE_LOCATION: u8 = 0x14; const REG_UNDERLINE_LOCATION: u8 = 0x14;
pub const REG_START_VERTICAL_BLINKING: u8 = 0x15; const REG_START_VERTICAL_BLINKING: u8 = 0x15;
pub const REG_END_VERTICAL_BLINKING: u8 = 0x16; const REG_END_VERTICAL_BLINKING: u8 = 0x16;
pub const REG_CRT_MODE_CONTROL: u8 = 0x17; const REG_CRT_MODE_CONTROL: u8 = 0x17;
pub const REG_LINE_COMPARE: u8 = 0x18; const REG_LINE_COMPARE: u8 = 0x18;
/// The start of the cursor scan line, the very beginning. /// 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. /// 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. /// 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. /// If set, disables the cursor.
pub const CURSOR_DISABLE: u8 = 0x20; const CURSOR_DISABLE: u8 = 0x20;
/// The number of characters wide the screen is. /// 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. /// 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. /// The set of colours that VGA supports and can display for the foreground and background.
pub const COLOUR_BLACK: u4 = 0x00; pub const COLOUR_BLACK: u4 = 0x00;
pub const COLOUR_BLUE: u4 = 0x01; pub const COLOUR_BLUE: u4 = 0x01;
pub const COLOUR_GREEN: u4 = 0x02; pub const COLOUR_GREEN: u4 = 0x02;
pub const COLOUR_CYAN: u4 = 0x03; pub const COLOUR_CYAN: u4 = 0x03;
pub const COLOUR_RED: u4 = 0x04; pub const COLOUR_RED: u4 = 0x04;
pub const COLOUR_MAGENTA: u4 = 0x05; pub const COLOUR_MAGENTA: u4 = 0x05;
pub const COLOUR_BROWN: u4 = 0x06; pub const COLOUR_BROWN: u4 = 0x06;
pub const COLOUR_LIGHT_GREY: u4 = 0x07; pub const COLOUR_LIGHT_GREY: u4 = 0x07;
pub const COLOUR_DARK_GREY: u4 = 0x08; pub const COLOUR_DARK_GREY: u4 = 0x08;
pub const COLOUR_LIGHT_BLUE: u4 = 0x09; pub const COLOUR_LIGHT_BLUE: u4 = 0x09;
pub const COLOUR_LIGHT_GREEN: u4 = 0x0A; pub const COLOUR_LIGHT_GREEN: u4 = 0x0A;
pub const COLOUR_LIGHT_CYAN: u4 = 0x0B; pub const COLOUR_LIGHT_CYAN: u4 = 0x0B;
pub const COLOUR_LIGHT_RED: u4 = 0x0C; pub const COLOUR_LIGHT_RED: u4 = 0x0C;
pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D; pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D;
pub const COLOUR_LIGHT_BROWN: u4 = 0x0E; pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
pub const COLOUR_WHITE: u4 = 0x0F; pub const COLOUR_WHITE: u4 = 0x0F;
/// The set of shapes that can be displayed. /// The set of shapes that can be displayed.
pub const CursorShape = enum { pub const CursorShape = enum {
@ -234,3 +236,235 @@ pub fn init() void {
// Set by default the underline cursor // Set by default the underline cursor
setCursorShape(CursorShape.UNDERLINE); 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();
}

View file

@ -1,6 +1,7 @@
def getTestCases(TestCase): def getTestCases(TestCase):
return [ return [
TestCase("GDT init", [r"Init gdt", r"Done"]), 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("IDT init", [r"Init idt", r"Done"]),
TestCase("PIT init", [r"Init pit", r".+", "Done"]), TestCase("PIT init", [r"Init pit", r".+", "Done"]),
TestCase("Syscalls init", [r"Init syscalls", "Done"]), TestCase("Syscalls init", [r"Init syscalls", "Done"]),

View file

@ -1,7 +1,8 @@
const std = @import("std"); const std = @import("std");
const MemProfile = @import("mem_mock.zig").MemProfile; const Allocator = std.mem.Allocator;
const expect = std.testing.expect; const mem = @import("mem_mock.zig");
const warn = std.debug.warn; const MemProfile = mem.MemProfile;
const gdt = @import("gdt_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;
@ -11,29 +12,20 @@ pub const addConsumeFunction = mock_framework.addConsumeFunction;
pub const addRepeatFunction = mock_framework.addRepeatFunction; pub const addRepeatFunction = mock_framework.addRepeatFunction;
pub const InterruptContext = struct { pub const InterruptContext = struct {
// Extra segments
gs: u32, gs: u32,
fs: u32, fs: u32,
es: u32, es: u32,
ds: u32, ds: u32,
// Destination, source, base pointer
edi: u32, edi: u32,
esi: u32, esi: u32,
ebp: u32, ebp: u32,
esp: u32, esp: u32,
// General registers
ebx: u32, ebx: u32,
edx: u32, edx: u32,
ecx: u32, ecx: u32,
eax: u32, eax: u32,
// Interrupt number and error code
int_num: u32, int_num: u32,
error_code: u32, error_code: u32,
// Instruction pointer, code segment and flags
eip: u32, eip: u32,
cs: u32, cs: u32,
eflags: u32, eflags: u32,
@ -41,10 +33,6 @@ pub const InterruptContext = struct {
ss: u32, 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 { pub fn outb(port: u16, data: u8) void {
return mock_framework.performAction("outb", void, port, data); return mock_framework.performAction("outb", void, port, data);
} }
@ -57,20 +45,20 @@ pub fn ioWait() void {
return mock_framework.performAction("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 { 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 { pub fn sgdt() gdt.GdtPtr {
return mock_framework.performAction("ltr", void); 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 { 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 { pub fn enableInterrupts() void {
@ -92,3 +80,9 @@ pub fn spinWait() noreturn {
pub fn haltNoInterrupts() noreturn { pub fn haltNoInterrupts() noreturn {
while (true) {} 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);
}

View 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);
}

View file

@ -1,11 +1,11 @@
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;
const GlobalAllocator = std.debug.global_allocator; const GlobalAllocator = std.debug.global_allocator;
const TailQueue = std.TailQueue; const TailQueue = std.TailQueue;
const warn = std.debug.warn; 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 /// The enumeration of types that the mocking framework supports. These include basic types like u8
@ -16,14 +16,17 @@ const DataElementType = enum {
U8, U8,
U16, U16,
U32, U32,
PTR_CONST_GdtPtr,
FN_OVOID, FN_OVOID,
FN_OUSIZE, FN_OUSIZE,
FN_OU16, FN_OU16,
FN_IU16_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_IPTRCONSTGDTPTR_OVOID,
}; };
/// ///
@ -36,14 +39,17 @@ const DataElement = union(DataElementType) {
U8: u8, U8: u8,
U16: u16, U16: u16,
U32: u32, U32: u32,
PTR_CONST_GdtPtr: *const gdt.GdtPtr,
FN_OVOID: fn () void, FN_OVOID: fn () void,
FN_OUSIZE: fn () usize, FN_OUSIZE: fn () usize,
FN_OU16: fn () u16, FN_OU16: fn () u16,
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_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void,
}; };
/// ///
@ -123,14 +129,17 @@ 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 },
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
fn () void => DataElement{ .FN_OVOID = arg }, fn () void => DataElement{ .FN_OVOID = arg },
fn () usize => DataElement{ .FN_OUSIZE = arg }, fn () usize => DataElement{ .FN_OUSIZE = arg },
fn () u16 => DataElement{ .FN_OU16 = arg }, fn () u16 => DataElement{ .FN_OU16 = arg },
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 (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))), else => @compileError("Type not supported: " ++ @typeName(@typeOf(arg))),
}; };
} }
@ -150,13 +159,16 @@ fn Mock() type {
u8 => DataElementType.U8, u8 => DataElementType.U8,
u16 => DataElementType.U16, u16 => DataElementType.U16,
u32 => DataElementType.U32, u32 => DataElementType.U32,
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
fn () void => DataElementType.FN_OVOID, fn () void => DataElementType.FN_OVOID,
fn () u16 => DataElementType.FN_OU16, fn () u16 => DataElementType.FN_OU16,
fn (u16) void => DataElementType.FN_IU16_OVOID,
fn (u16) 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 (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_OVOID,
else => @compileError("Type not supported: " ++ @typeName(T)), else => @compileError("Type not supported: " ++ @typeName(T)),
}; };
} }
@ -178,13 +190,16 @@ fn Mock() type {
u8 => element.U8, u8 => element.U8,
u16 => element.U16, u16 => element.U16,
u32 => element.U32, u32 => element.U32,
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
fn () void => element.FN_OVOID, fn () void => element.FN_OVOID,
fn () u16 => element.FN_OU16, fn () u16 => element.FN_OU16,
fn (u16) void => element.FN_IU16_OVOID,
fn (u16) 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 (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_OVOID,
else => @compileError("Type not supported: " ++ @typeName(T)), else => @compileError("Type not supported: " ++ @typeName(T)),
}; };
} }

View file

@ -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 log = @import("log_mock.zig");
pub const mem = @import("mem_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 panic = @import("panic_mock.zig");
pub const vga = @import("vga_mock.zig");

View file

@ -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");
}

View file

@ -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();
}

View file

@ -1,4 +0,0 @@
test "all" {
_ = @import("kernel/test_vga.zig");
_ = @import("kernel/test_tty.zig");
}