Merge pull request #122 from SamTebbs33/feature/unittest-in-build-arch

Unittest in build arch
This commit is contained in:
Edward Dean 2020-01-09 13:11:01 +00:00 committed by GitHub
commit c581db8e16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 114 additions and 84 deletions

View file

@ -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
View file

@ -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",

View file

@ -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
View 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

View file

@ -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" {

View file

@ -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;

View file

@ -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" {

View file

@ -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