Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
93 changes: 47 additions & 46 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,76 @@ 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 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_cmd = b.addSystemCommand(&[_][]const u8{ "mkdir", "-p", fs.path.dirname(grub_build_path).? });
const make_iso = b.addSystemCommand(&[_][]const u8{ "./makeiso.sh", boot_path, modules_path, iso_dir_path, exec.getOutputPath(), output_iso });

const grub_cmd = b.addSystemCommand(&[_][]const u8{ "cp", "-r", "grub", grub_build_path });
grub_cmd.step.dependOn(&mkdir_cmd.step);
make_iso.step.dependOn(&exec.step);
b.default_step.dependOn(&make_iso.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 test_step = b.step("test", "Run tests");
if (rt_test) {
const script = b.addSystemCommand(&[_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe });
test_step.dependOn(&script.step);
} else {
const mock_path = "\"../../test/mock/kernel/\"";
const arch_mock_path = "\"../../../../test/mock/kernel/\"";
const unit_tests = b.addTest(main_src);
unit_tests.setBuildMode(build_mode);
unit_tests.setMainPkgPath(".");
unit_tests.addPackagePath("constants", constants_path);
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 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 qemu_bin = switch (test_target.getArch()) {
.i386 => "qemu-i386",
else => unreachable,
};

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);
// 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);

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);
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");
Expand All @@ -72,7 +90,7 @@ pub fn build(b: *Builder) !void {
const qemu_args = &[_][]const u8{
qemu_bin,
"-cdrom",
iso_path,
output_iso,
"-boot",
"d",
"-serial",
Expand All @@ -88,31 +106,14 @@ pub fn build(b: *Builder) !void {
qemu_debug_cmd.addArgs(qemu_rt_test_args);
}

qemu_cmd.step.dependOn(&iso_cmd.step);
qemu_debug_cmd.step.dependOn(&iso_cmd.step);
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 test_step = b.step("test", "Run tests");
if (rt_test) {
const script = b.addSystemCommand(&[_][]const u8{ "python3", "test/rt-test.py", "x86", b.zig_exe });
test_step.dependOn(&script.step);
} else {
const mock_path = "\"../../test/mock/kernel/\"";
const arch_mock_path = "\"../../../../test/mock/kernel/\"";
const unit_tests = b.addTest(main_src);
unit_tests.setBuildMode(build_mode);
unit_tests.setMainPkgPath(".");
unit_tests.addPackagePath("constants", constants_path);
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);
}

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",
Expand Down
4 changes: 0 additions & 4 deletions make_map.sh

This file was deleted.

31 changes: 31 additions & 0 deletions makeiso.sh
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions src/kernel/arch/x86/gdt.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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" {
Expand Down
12 changes: 6 additions & 6 deletions src/kernel/arch/x86/idt.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

///
Expand All @@ -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", .{});
Expand All @@ -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" {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/kernel/arch/x86/paging.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

///
Expand Down Expand Up @@ -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" {
Expand Down
8 changes: 5 additions & 3 deletions src/kernel/kmain.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {},
}
}
}

Expand Down