Merge pull request #122 from SamTebbs33/feature/unittest-in-build-arch
Unittest in build arch
This commit is contained in:
commit
c581db8e16
8 changed files with 114 additions and 84 deletions
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
|
@ -19,14 +19,14 @@ jobs:
|
|||
wget $(curl -s 'https://ziglang.org/download/index.json' | python3 -c "import sys, json; print(json.load(sys.stdin)['master']['x86_64-linux']['tarball'])")
|
||||
sudo apt-get install mtools
|
||||
tar -xvf zig*
|
||||
- name: Build kernel
|
||||
run: zig*/zig build ${{ matrix.build_mode }}
|
||||
- name: Run unit tests
|
||||
run: zig*/zig build test ${{ matrix.build_mode }}
|
||||
- name: Install qemu
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install qemu qemu-system --fix-missing
|
||||
- name: Build kernel
|
||||
run: zig*/zig build ${{ matrix.build_mode }}
|
||||
- name: Run unit tests
|
||||
run: zig*/zig build test ${{ matrix.build_mode }}
|
||||
- name: Run runtime tests
|
||||
run: zig*/zig build test -Drt-test=true ${{ matrix.build_mode }}
|
||||
- name: Check formatting
|
||||
|
|
123
build.zig
123
build.zig
|
@ -9,90 +9,49 @@ const Mode = builtin.Mode;
|
|||
|
||||
pub fn build(b: *Builder) !void {
|
||||
const target = Target{
|
||||
.Cross = std.build.CrossTarget{
|
||||
.Cross = Target.Cross{
|
||||
.arch = .i386,
|
||||
.os = .freestanding,
|
||||
.abi = .gnu,
|
||||
},
|
||||
};
|
||||
|
||||
const test_target = Target{
|
||||
.Cross = Target.Cross{
|
||||
.arch = .i386,
|
||||
.os = .linux,
|
||||
.abi = .gnu,
|
||||
},
|
||||
};
|
||||
|
||||
const target_str = switch (target.getArch()) {
|
||||
.i386 => "x86",
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const main_src = "src/kernel/kmain.zig";
|
||||
const constants_path = try fs.path.join(b.allocator, &[_][]const u8{ "src/kernel/arch", target_str, "constants.zig" });
|
||||
|
||||
const build_mode = b.standardReleaseOptions();
|
||||
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);
|
||||
const constants_path = try fs.path.join(b.allocator, &[_][]const u8{ "src/kernel/arch", target_str, "constants.zig" });
|
||||
exec.addPackagePath("constants", constants_path);
|
||||
exec.setBuildMode(build_mode);
|
||||
exec.setOutputDir(b.cache_root);
|
||||
exec.addBuildOption(bool, "rt_test", rt_test);
|
||||
exec.setBuildMode(build_mode);
|
||||
exec.setLinkerScriptPath("link.ld");
|
||||
exec.setTheTarget(target);
|
||||
|
||||
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 output_iso = try fs.path.join(b.allocator, &[_][]const u8{ b.exe_dir, "pluto.iso" });
|
||||
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 grub_cmd = b.addSystemCommand(&[_][]const u8{ "cp", "-r", "grub", grub_build_path });
|
||||
grub_cmd.step.dependOn(&mkdir_cmd.step);
|
||||
|
||||
const cp_elf_cmd = b.addSystemCommand(&[_][]const u8{"cp"});
|
||||
const elf_path = try fs.path.join(b.allocator, &[_][]const u8{ grub_build_path, "pluto.elf" });
|
||||
cp_elf_cmd.addArtifactArg(exec);
|
||||
cp_elf_cmd.addArg(elf_path);
|
||||
cp_elf_cmd.step.dependOn(&grub_cmd.step);
|
||||
cp_elf_cmd.step.dependOn(&exec.step);
|
||||
|
||||
const boot_path = try fs.path.join(b.allocator, &[_][]const u8{ b.exe_dir, "iso", "boot" });
|
||||
const modules_path = try fs.path.join(b.allocator, &[_][]const u8{ b.exe_dir, "iso", "modules" });
|
||||
const mkdir_modules_cmd = b.addSystemCommand(&[_][]const u8{ "mkdir", "-p", modules_path });
|
||||
|
||||
const map_file_path = try fs.path.join(b.allocator, &[_][]const u8{ modules_path, "kernel.map" });
|
||||
const map_file_cmd = b.addSystemCommand(&[_][]const u8{ "./make_map.sh", elf_path, map_file_path });
|
||||
map_file_cmd.step.dependOn(&cp_elf_cmd.step);
|
||||
map_file_cmd.step.dependOn(&mkdir_modules_cmd.step);
|
||||
const make_iso = b.addSystemCommand(&[_][]const u8{ "./makeiso.sh", boot_path, modules_path, iso_dir_path, exec.getOutputPath(), output_iso });
|
||||
|
||||
const iso_cmd = b.addSystemCommand(&[_][]const u8{ "grub-mkrescue", "-o", iso_path, iso_dir_path });
|
||||
iso_cmd.step.dependOn(&map_file_cmd.step);
|
||||
b.default_step.dependOn(&iso_cmd.step);
|
||||
|
||||
const run_step = b.step("run", "Run with qemu");
|
||||
const run_debug_step = b.step("debug-run", "Run with qemu and wait for a gdb connection");
|
||||
|
||||
const qemu_bin = switch (target.getArch()) {
|
||||
.i386 => "qemu-system-i386",
|
||||
else => unreachable,
|
||||
};
|
||||
const qemu_args = &[_][]const u8{
|
||||
qemu_bin,
|
||||
"-cdrom",
|
||||
iso_path,
|
||||
"-boot",
|
||||
"d",
|
||||
"-serial",
|
||||
"stdio",
|
||||
};
|
||||
const qemu_cmd = b.addSystemCommand(qemu_args);
|
||||
const qemu_debug_cmd = b.addSystemCommand(qemu_args);
|
||||
qemu_debug_cmd.addArgs(&[_][]const u8{ "-s", "-S" });
|
||||
|
||||
if (rt_test) {
|
||||
const qemu_rt_test_args = &[_][]const u8{ "-display", "none" };
|
||||
qemu_cmd.addArgs(qemu_rt_test_args);
|
||||
qemu_debug_cmd.addArgs(qemu_rt_test_args);
|
||||
}
|
||||
|
||||
qemu_cmd.step.dependOn(&iso_cmd.step);
|
||||
qemu_debug_cmd.step.dependOn(&iso_cmd.step);
|
||||
|
||||
run_step.dependOn(&qemu_cmd.step);
|
||||
run_debug_step.dependOn(&qemu_debug_cmd.step);
|
||||
make_iso.step.dependOn(&exec.step);
|
||||
b.default_step.dependOn(&make_iso.step);
|
||||
|
||||
const test_step = b.step("test", "Run tests");
|
||||
if (rt_test) {
|
||||
|
@ -108,11 +67,53 @@ pub fn build(b: *Builder) !void {
|
|||
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);
|
||||
|
||||
const qemu_bin = switch (test_target.getArch()) {
|
||||
.i386 => "qemu-i386",
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
// We need this as the build as the make() doesn't handle it properly
|
||||
unit_tests.setExecCmd(&[_]?[]const u8{ qemu_bin, null });
|
||||
unit_tests.setTheTarget(test_target);
|
||||
|
||||
test_step.dependOn(&unit_tests.step);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run with qemu");
|
||||
const run_debug_step = b.step("debug-run", "Run with qemu and wait for a gdb connection");
|
||||
|
||||
const qemu_bin = switch (target.getArch()) {
|
||||
.i386 => "qemu-system-i386",
|
||||
else => unreachable,
|
||||
};
|
||||
const qemu_args = &[_][]const u8{
|
||||
qemu_bin,
|
||||
"-cdrom",
|
||||
output_iso,
|
||||
"-boot",
|
||||
"d",
|
||||
"-serial",
|
||||
"stdio",
|
||||
};
|
||||
const qemu_cmd = b.addSystemCommand(qemu_args);
|
||||
const qemu_debug_cmd = b.addSystemCommand(qemu_args);
|
||||
qemu_debug_cmd.addArgs(&[_][]const u8{ "-s", "-S" });
|
||||
|
||||
if (rt_test) {
|
||||
const qemu_rt_test_args = &[_][]const u8{ "-display", "none" };
|
||||
qemu_cmd.addArgs(qemu_rt_test_args);
|
||||
qemu_debug_cmd.addArgs(qemu_rt_test_args);
|
||||
}
|
||||
|
||||
qemu_cmd.step.dependOn(&make_iso.step);
|
||||
qemu_debug_cmd.step.dependOn(&make_iso.step);
|
||||
|
||||
run_step.dependOn(&qemu_cmd.step);
|
||||
run_debug_step.dependOn(&qemu_debug_cmd.step);
|
||||
|
||||
const debug_step = b.step("debug", "Debug with gdb and connect to a running qemu instance");
|
||||
const symbol_file_arg = try std.mem.join(b.allocator, " ", &[_][]const u8{ "symbol-file", elf_path });
|
||||
const symbol_file_arg = try std.mem.join(b.allocator, " ", &[_][]const u8{ "symbol-file", exec.getOutputPath() });
|
||||
const debug_cmd = b.addSystemCommand(&[_][]const u8{
|
||||
"gdb",
|
||||
"-ex",
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Read the symbols from the binary, remove all the unnecessary columns with awk and emit to a map file
|
||||
readelf -s $1 | grep -F "FUNC" | awk '{$1=$3=$4=$5=$6=$7=""; print $0}' | sort -k 1 > $2
|
||||
echo "" >> $2
|
31
makeiso.sh
Executable file
31
makeiso.sh
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
|
||||
BOOT_DIR=$1
|
||||
MODULES_DIR=$2
|
||||
ISO_DIR=$3
|
||||
PLUTO_ELF=$4
|
||||
OUTPUT_FILE=$5
|
||||
|
||||
MAP_FILE=$MODULES_DIR/'kernel.map'
|
||||
|
||||
exit_missing() {
|
||||
printf "$_ must be installed\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Check dependencies
|
||||
which xorriso > /dev/null || exit_missing
|
||||
which grub-mkrescue > /dev/null || exit_missing
|
||||
which readelf > /dev/null || exit_missing
|
||||
|
||||
mkdir -p $BOOT_DIR
|
||||
mkdir -p $MODULES_DIR
|
||||
|
||||
cp -r grub $BOOT_DIR
|
||||
cp $PLUTO_ELF $BOOT_DIR/"pluto.elf"
|
||||
|
||||
# Read the symbols from the binary, remove all the unnecessary columns with awk and emit to a map file
|
||||
readelf -s $PLUTO_ELF | grep -F "FUNC" | awk '{$1=$3=$4=$5=$6=$7=""; print $0}' | sort -k 1 > $MAP_FILE
|
||||
echo "" >> $MAP_FILE
|
||||
|
||||
grub-mkrescue -o $OUTPUT_FILE $ISO_DIR
|
|
@ -426,10 +426,10 @@ pub fn setTssStack(esp0: u32) void {
|
|||
pub fn init() void {
|
||||
log.logInfo("Init gdt\n", .{});
|
||||
// Initiate TSS
|
||||
gdt_entries[TSS_INDEX] = makeEntry(@intCast(u32, @ptrToInt(&tss)), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, NULL_FLAGS);
|
||||
gdt_entries[TSS_INDEX] = makeEntry(@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]));
|
||||
gdt_ptr.base = @ptrToInt(&gdt_entries[0]);
|
||||
|
||||
// Load the GDT
|
||||
arch.lgdt(&gdt_ptr);
|
||||
|
@ -444,7 +444,7 @@ pub fn init() void {
|
|||
|
||||
fn mock_lgdt(ptr: *const GdtPtr) void {
|
||||
expectEqual(TABLE_SIZE, ptr.limit);
|
||||
expectEqual(@intCast(u32, @ptrToInt(&gdt_entries[0])), ptr.base);
|
||||
expectEqual(@ptrToInt(&gdt_entries[0]), ptr.base);
|
||||
}
|
||||
|
||||
test "GDT entries" {
|
||||
|
|
|
@ -172,7 +172,7 @@ pub fn openInterruptGate(index: u8, handler: InterruptHandler) IdtError!void {
|
|||
return IdtError.IdtEntryExists;
|
||||
}
|
||||
|
||||
idt_entries[index] = makeEntry(@intCast(u32, @ptrToInt(handler)), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE, PRIVILEGE_RING_0);
|
||||
idt_entries[index] = makeEntry(@ptrToInt(handler), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE, PRIVILEGE_RING_0);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -181,7 +181,7 @@ pub fn openInterruptGate(index: u8, handler: InterruptHandler) IdtError!void {
|
|||
pub fn init() void {
|
||||
log.logInfo("Init idt\n", .{});
|
||||
|
||||
idt_ptr.base = @intCast(u32, @ptrToInt(&idt_entries));
|
||||
idt_ptr.base = @ptrToInt(&idt_entries);
|
||||
|
||||
arch.lidt(&idt_ptr);
|
||||
log.logInfo("Done\n", .{});
|
||||
|
@ -194,7 +194,7 @@ fn testHandler1() callconv(.Naked) void {}
|
|||
|
||||
fn mock_lidt(ptr: *const IdtPtr) void {
|
||||
expectEqual(TABLE_SIZE, ptr.limit);
|
||||
expectEqual(@intCast(u32, @ptrToInt(&idt_entries[0])), ptr.base);
|
||||
expectEqual(@ptrToInt(&idt_entries[0]), ptr.base);
|
||||
}
|
||||
|
||||
test "IDT entries" {
|
||||
|
@ -244,8 +244,8 @@ test "openInterruptGate" {
|
|||
openInterruptGate(index, testHandler0) catch unreachable;
|
||||
expectError(IdtError.IdtEntryExists, openInterruptGate(index, testHandler0));
|
||||
|
||||
const test_fn_0_addr = @intCast(u32, @ptrToInt(testHandler0));
|
||||
const test_fn_1_addr = @intCast(u32, @ptrToInt(testHandler1));
|
||||
const test_fn_0_addr = @ptrToInt(testHandler0);
|
||||
const test_fn_1_addr = @ptrToInt(testHandler1);
|
||||
|
||||
const expected_entry0 = IdtEntry{
|
||||
.base_low = @truncate(u16, test_fn_0_addr),
|
||||
|
@ -313,7 +313,7 @@ test "init" {
|
|||
init();
|
||||
|
||||
// Post testing
|
||||
expectEqual(@intCast(u32, @ptrToInt(&idt_entries)), idt_ptr.base);
|
||||
expectEqual(@ptrToInt(&idt_entries), idt_ptr.base);
|
||||
|
||||
// Reset
|
||||
idt_ptr.base = 0;
|
||||
|
|
|
@ -205,7 +205,7 @@ fn mapDirEntry(dir: *Directory, virt_start: usize, virt_end: usize, phys_start:
|
|||
table = &(try allocator.alignedAlloc(Table, @truncate(u29, PAGE_SIZE_4KB), 1))[0];
|
||||
@memset(@ptrCast([*]u8, table), 0, @sizeOf(Table));
|
||||
const table_phys_addr = @ptrToInt(mem.virtToPhys(table));
|
||||
dir_entry.* |= @intCast(u32, DENTRY_PAGE_ADDR & table_phys_addr);
|
||||
dir_entry.* |= DENTRY_PAGE_ADDR & table_phys_addr;
|
||||
dir.tables[entry] = table;
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@ fn mapTableEntry(entry: *align(1) TableEntry, phys_addr: usize) PagingError!void
|
|||
entry.* |= TENTRY_WRITE_THROUGH;
|
||||
entry.* &= ~TENTRY_CACHE_DISABLED;
|
||||
entry.* &= ~TENTRY_GLOBAL;
|
||||
entry.* |= TENTRY_PAGE_ADDR & @intCast(u32, phys_addr);
|
||||
entry.* |= TENTRY_PAGE_ADDR & phys_addr;
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -385,7 +385,7 @@ fn checkTableEntry(entry: TableEntry, page_phys: usize) void {
|
|||
expectEqual(entry & TENTRY_CACHE_DISABLED, 0);
|
||||
expectEqual(entry & TENTRY_ZERO, 0);
|
||||
expectEqual(entry & TENTRY_GLOBAL, 0);
|
||||
expectEqual(entry & TENTRY_PAGE_ADDR, @intCast(u32, page_phys));
|
||||
expectEqual(entry & TENTRY_PAGE_ADDR, page_phys);
|
||||
}
|
||||
|
||||
test "virtToDirEntryIdx" {
|
||||
|
|
|
@ -15,10 +15,12 @@ const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig") else @imp
|
|||
const options = @import("build_options");
|
||||
|
||||
comptime {
|
||||
if (!is_test) {
|
||||
switch (builtin.arch) {
|
||||
.i386 => _ = @import("arch/x86/boot.zig"),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is for unit testing as we need to export KERNEL_ADDR_OFFSET as it is no longer available
|
||||
|
|
Loading…
Reference in a new issue