Run unit tests under the building architecture
This also removed the need for `@intCase()` Also move the making directories and copying to a script to the build is simpler. Install qemu earler Added chmod +x Fixed copy elf Added missing switch for qemu binary Spelling Make makeiso.sh executable No longer chmod makeiso.sh Use the cache root to set the output directory
This commit is contained in:
parent
a095fd3947
commit
0746048a00
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,9 +15,11 @@ const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig") else @imp
|
|||
const options = @import("build_options");
|
||||
|
||||
comptime {
|
||||
switch (builtin.arch) {
|
||||
.i386 => _ = @import("arch/x86/boot.zig"),
|
||||
else => {},
|
||||
if (!is_test) {
|
||||
switch (builtin.arch) {
|
||||
.i386 => _ = @import("arch/x86/boot.zig"),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue