From 0746048a00152b3977159b24bd22e507dcab0053 Mon Sep 17 00:00:00 2001 From: ED Date: Thu, 9 Jan 2020 13:08:00 +0000 Subject: [PATCH] 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 --- .github/workflows/main.yml | 8 +-- build.zig | 123 +++++++++++++++++---------------- make_map.sh | 4 -- makeiso.sh | 31 +++++++++ src/kernel/arch/x86/gdt.zig | 6 +- src/kernel/arch/x86/idt.zig | 12 ++-- src/kernel/arch/x86/paging.zig | 6 +- src/kernel/kmain.zig | 8 ++- 8 files changed, 114 insertions(+), 84 deletions(-) delete mode 100755 make_map.sh create mode 100755 makeiso.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 272aef6..7da68e2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 diff --git a/build.zig b/build.zig index 9808d9d..de58a15 100644 --- a/build.zig +++ b/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", diff --git a/make_map.sh b/make_map.sh deleted file mode 100755 index a7d7d93..0000000 --- a/make_map.sh +++ /dev/null @@ -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 diff --git a/makeiso.sh b/makeiso.sh new file mode 100755 index 0000000..9b74bfd --- /dev/null +++ b/makeiso.sh @@ -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 diff --git a/src/kernel/arch/x86/gdt.zig b/src/kernel/arch/x86/gdt.zig index 76b5452..09a22d9 100644 --- a/src/kernel/arch/x86/gdt.zig +++ b/src/kernel/arch/x86/gdt.zig @@ -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" { diff --git a/src/kernel/arch/x86/idt.zig b/src/kernel/arch/x86/idt.zig index fbd16a2..64cbdb9 100644 --- a/src/kernel/arch/x86/idt.zig +++ b/src/kernel/arch/x86/idt.zig @@ -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; diff --git a/src/kernel/arch/x86/paging.zig b/src/kernel/arch/x86/paging.zig index 70cf1ce..5a5a114 100644 --- a/src/kernel/arch/x86/paging.zig +++ b/src/kernel/arch/x86/paging.zig @@ -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" { diff --git a/src/kernel/kmain.zig b/src/kernel/kmain.zig index aaf6bf1..fc88663 100644 --- a/src/kernel/kmain.zig +++ b/src/kernel/kmain.zig @@ -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 => {}, + } } }