Merge pull request #65 from andrewrk/update-to-zig-master-and-fix-build

Update to zig master and simplify build script
This commit is contained in:
Sam Tebbs 2019-09-05 16:34:16 +01:00 committed by GitHub
commit 01737079df
9 changed files with 143 additions and 636 deletions

View file

@ -15,7 +15,7 @@ pool:
vmImage: 'ubuntu-latest' vmImage: 'ubuntu-latest'
variables: variables:
zig_version: 0.4.0 zig_version: master
steps: steps:
- script: | - script: |
@ -37,6 +37,5 @@ steps:
displayName: 'Download qemu' displayName: 'Download qemu'
- script: | - script: |
zig*/zig build -Drt-test=true zig*/zig build test -Drt-test=true
zig*/zig build test -Drt-test=true -Dzig-path=zig*/zig
displayName: 'Runtime tests' displayName: 'Runtime tests'

230
build.zig
View file

@ -1,187 +1,93 @@
// Zig version: 0.4.0
const Builder = @import("std").build.Builder;
const Step = @import("std").build.Step;
const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const os = std.os; const builtin = @import("builtin");
const ArrayList = std.ArrayList; const Builder = std.build.Builder;
const warn = std.debug.warn; const Step = std.build.Step;
const mem = std.mem; const Target = std.build.Target;
const fs = std.fs;
var src_files: ArrayList([]const u8) = undefined; pub fn build(b: *Builder) !void {
var src_files_asm: ArrayList([]const u8) = undefined; const target = Target{
.Cross = std.build.CrossTarget{
fn concat(allocator: *std.mem.Allocator, str: []const u8, str2: []const u8) !std.Buffer { .arch = .i386,
var b = try std.Buffer.init(allocator, str); .os = .freestanding,
try b.append(str2); .abi = .gnu,
return b; },
} };
pub fn build(b: *Builder) void {
src_files = ArrayList([]const u8).init(b.allocator);
src_files_asm = ArrayList([]const u8).init(b.allocator);
const debug = b.option(bool, "debug", "build with debug symbols / make qemu wait for a debug connection") orelse false; const debug = b.option(bool, "debug", "build with debug symbols / make qemu wait for a debug connection") orelse false;
var build_path = b.option([]const u8, "build-path", "path to build to") orelse "bin";
var src_path = b.option([]const u8, "source-path", "path to source") orelse "src";
var target = b.option([]const u8, "target", "target to build/run for") orelse "x86";
const rt_test = b.option(bool, "rt-test", "enable/disable runtime testing") orelse false; const rt_test = b.option(bool, "rt-test", "enable/disable runtime testing") orelse false;
const builtin_target = if (mem.eql(u8, target, "x86")) builtin.Arch.i386 else unreachable;
const zig_path = b.option([]const u8, "zig-path", "the path to the zig binary to use for rt testing") orelse "/snap/bin/zig";
b.makePath(build_path) catch unreachable; const main_src = "src/kernel/kmain.zig";
var grub_path = concat(b.allocator, build_path, "/iso/boot/grub") catch unreachable; const exec = b.addExecutable("pluto", main_src);
var kern_path = concat(b.allocator, build_path, "/kernel") catch unreachable; exec.setMainPkgPath(".");
var a_path = concat(b.allocator, build_path, "/kernel/arch/") catch unreachable; exec.addBuildOption(bool, "rt_test", rt_test);
a_path = concat(b.allocator, a_path.toSlice(), target) catch unreachable; exec.setLinkerScriptPath("link.ld");
b.makePath(grub_path.toSlice()) catch unreachable; exec.setTheTarget(target);
b.makePath(kern_path.toSlice()) catch unreachable; switch (target.getArch()) {
b.makePath(a_path.toSlice()) catch unreachable; .i386 => {
b.makePath("zig-cache/kernel") catch unreachable; exec.addAssemblyFile("src/kernel/arch/x86/irq_asm.s");
exec.addAssemblyFile("src/kernel/arch/x86/isr_asm.s");
src_files.append("kernel/kmain") catch unreachable;
// Add the assemblies
switch (builtin_target) {
builtin.Arch.i386 => {
src_files_asm.append("kernel/arch/x86/irq_asm") catch unreachable;
src_files_asm.append("kernel/arch/x86/isr_asm") catch unreachable;
}, },
else => {}, else => {},
} }
var arch_boot = concat(b.allocator, "kernel/arch/", target) catch unreachable; const iso_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "pluto.iso" }) catch unreachable;
arch_boot.append("/boot") catch unreachable; const grub_build_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso", "boot" }) catch unreachable;
src_files.append(arch_boot.toSlice()) catch unreachable; const iso_dir_path = fs.path.join(b.allocator, [_][]const u8{ b.exe_dir, "iso" }) catch unreachable;
const iso_path = concat(b.allocator, build_path, "/pluto.iso") catch unreachable; const mkdir_cmd = b.addSystemCommand([_][]const u8{ "mkdir", "-p", fs.path.dirname(grub_build_path).? });
var objects = buildObjects(b, builtin_target, build_path, src_path);
var link_step = buildLink(b, builtin_target, build_path);
const iso_step = buildISO(b, build_path, iso_path.toSlice());
for (objects.toSlice()) |obj| { const grub_cmd = b.addSystemCommand([_][]const u8{ "cp", "-r", "grub", grub_build_path });
if (std.mem.eql(u8, obj.name, "kernel/kmain")) { grub_cmd.step.dependOn(&mkdir_cmd.step);
// Add build options here
obj.addBuildOption(bool, "rt_test", rt_test);
}
b.default_step.dependOn(&obj.step);
}
b.default_step.dependOn(link_step);
for (iso_step.toSlice()) |step| b.default_step.dependOn(step);
buildRun(b, builtin_target, build_path, iso_path.toSlice(), debug, rt_test); const cp_elf_cmd = b.addSystemCommand([_][]const u8{"cp"});
buildDebug(b); cp_elf_cmd.addArtifactArg(exec);
buildTest(b, src_path, rt_test, target, zig_path); cp_elf_cmd.addArg(try fs.path.join(b.allocator, [_][]const u8{ grub_build_path, "pluto.elf" }));
} cp_elf_cmd.step.dependOn(&grub_cmd.step);
cp_elf_cmd.step.dependOn(&exec.step);
fn buildTest(b: *Builder, src_path: []const u8, rt_test: bool, target: []const u8, zig_path: []const u8) void { const iso_cmd = b.addSystemCommand([_][]const u8{ "grub-mkrescue", "-o", iso_path, iso_dir_path });
const step = b.step("test", "Run tests"); iso_cmd.step.dependOn(&cp_elf_cmd.step);
if (rt_test) { b.default_step.dependOn(&iso_cmd.step);
const script = b.addSystemCommand([][]const u8{ "python3", "test/rt-test.py", target, zig_path});
step.dependOn(&script.step);
} else {
const src_path2 = concat(b.allocator, src_path, "/") catch unreachable;
for (src_files.toSlice()) |file| {
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
file_src.append(".zig") catch unreachable;
const tst = b.addTest(file_src.toSlice());
tst.setMainPkgPath(".");
step.dependOn(&tst.step);
}
}
}
fn buildDebug(b: *Builder) void { const run_step = b.step("run", "Run with qemu");
const step = b.step("debug", "Debug with gdb"); const qemu_bin = if (target.getArch() == builtin.Arch.i386) "qemu-system-i386" else unreachable;
const cmd = b.addSystemCommand([][]const u8{ const qemu_cmd = b.addSystemCommand([_][]const u8{
"gdb", qemu_bin,
"-ex",
"symbol-file bin/iso/boot/pluto.elf",
"-ex",
"target remote localhost:1234",
});
step.dependOn(&cmd.step);
}
fn buildRun(b: *Builder, target: builtin.Arch, build_path: []const u8, iso_path: []const u8, debug: bool, rt_test: bool) void {
const step = b.step("run", "Run with qemu");
const qemu = if (target == builtin.Arch.i386) "qemu-system-i386" else unreachable;
var qemu_flags = ArrayList([]const u8).init(b.allocator);
qemu_flags.appendSlice([][]const u8{
qemu,
"-cdrom", "-cdrom",
iso_path, iso_path,
"-boot", "-boot",
"d", "d",
"-serial", "-serial",
"stdio", "stdio",
}) catch unreachable; });
if (debug) if (debug)
qemu_flags.appendSlice([][]const u8{ qemu_cmd.addArgs([_][]const u8{ "-s", "-S" });
"-s",
"-S",
}) catch unreachable;
if (rt_test) if (rt_test)
qemu_flags.appendSlice([][]const u8{ qemu_cmd.addArgs([_][]const u8{ "-display", "none" });
"-display", run_step.dependOn(&qemu_cmd.step);
"none" qemu_cmd.step.dependOn(&iso_cmd.step);
}) catch unreachable;
const cmd = b.addSystemCommand(qemu_flags.toSlice()); const test_step = b.step("test", "Run tests");
step.dependOn(&cmd.step); 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 unit_tests = b.addTest(main_src);
unit_tests.setMainPkgPath(".");
unit_tests.addBuildOption(bool, "rt_test", rt_test);
test_step.dependOn(&unit_tests.step);
} }
fn buildISO(b: *Builder, build_path: []const u8, iso_path: []const u8) ArrayList(*Step) { const debug_step = b.step("debug", "Debug with gdb");
const grub_build_path = concat(b.allocator, build_path, "/iso/boot/") catch unreachable; const debug_cmd = b.addSystemCommand([_][]const u8{
const iso_dir_path = concat(b.allocator, build_path, "/iso") catch unreachable; "gdb",
const grub_cmd = b.addSystemCommand([][]const u8{ "cp", "-r", "grub", grub_build_path.toSlice() }); "-ex",
const iso_cmd = b.addSystemCommand([][]const u8{ "grub-mkrescue", "-o", iso_path, iso_dir_path.toSlice() }); "symbol-file",
var steps = ArrayList(*Step).init(b.allocator); });
steps.append(&grub_cmd.step) catch unreachable; debug_cmd.addArtifactArg(exec);
steps.append(&iso_cmd.step) catch unreachable; debug_cmd.addArgs([_][]const u8{
return steps; "-ex",
} "target remote localhost:1234",
});
fn buildLink(b: *Builder, target: builtin.Arch, build_path: []const u8) *Step { debug_step.dependOn(&debug_cmd.step);
const exec = b.addExecutable("pluto.elf", null);
const elf_path = concat(b.allocator, build_path, "/iso/boot") catch unreachable;
exec.setOutputDir(elf_path.toSlice());
exec.setLinkerScriptPath("link.ld");
exec.setTarget(target, builtin.Os.freestanding, builtin.Abi.gnu);
for (src_files.toSlice()) |file| {
var file_obj = concat(b.allocator, build_path, "/") catch unreachable;
file_obj.append(file) catch unreachable;
file_obj.append(".o") catch unreachable;
exec.addObjectFile(file_obj.toSlice());
exec.setMainPkgPath(".");
}
for (src_files_asm.toSlice()) |file| {
var file_obj = concat(b.allocator, build_path, "/") catch unreachable;
file_obj.append(file) catch unreachable;
file_obj.append(".o") catch unreachable;
exec.addObjectFile(file_obj.toSlice());
}
return &exec.step;
}
fn buildObjects(b: *Builder, target: builtin.Arch, build_path: []const u8, src_path: []const u8) ArrayList(*std.build.LibExeObjStep) {
var objects = ArrayList(*std.build.LibExeObjStep).init(b.allocator);
const src_path2 = concat(b.allocator, src_path, "/") catch unreachable;
for (src_files.toSlice()) |file| {
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
file_src.append(".zig") catch unreachable;
const obj = b.addObject(file, file_src.toSlice());
obj.setMainPkgPath(".");
obj.setOutputDir(build_path);
obj.setTarget(target, builtin.Os.freestanding, builtin.Abi.gnu);
objects.append(obj) catch unreachable;
}
for (src_files_asm.toSlice()) |file| {
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
file_src.append(".s") catch unreachable;
const obj = b.addAssemble(file, file_src.toSlice());
obj.setOutputDir(build_path);
obj.setTarget(target, builtin.Os.freestanding, builtin.Abi.gnu);
objects.append(obj) catch unreachable;
}
return objects;
} }

View file

@ -26,7 +26,6 @@ const USER_DATA_INDEX: u16 = 0x04;
/// The index of the task state segment GDT entry. /// The index of the task state segment GDT entry.
const TSS_INDEX: u16 = 0x05; const TSS_INDEX: u16 = 0x05;
// The offsets into the GDT where each segment resides. // The offsets into the GDT where each segment resides.
/// The offset of the NULL GDT entry. /// The offset of the NULL GDT entry.
@ -61,7 +60,6 @@ const PRIVILEGE_RING_3 = 0x60; // 01100000
const PRESENT_BIT = 0x80; // 10000000 const PRESENT_BIT = 0x80; // 10000000
const KERNEL_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_0 | DESCRIPTOR_BIT; const KERNEL_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_0 | DESCRIPTOR_BIT;
const USER_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_3 | DESCRIPTOR_BIT; const USER_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_3 | DESCRIPTOR_BIT;
@ -70,7 +68,6 @@ const DATA_SEGMENT = WRITABLE_BIT;
const TSS_SEGMENT = PRESENT_BIT | EXECUTABLE_BIT | ACCESSED_BIT; const TSS_SEGMENT = PRESENT_BIT | EXECUTABLE_BIT | ACCESSED_BIT;
// The flag bits // The flag bits
const IS_64_BIT = 0x02; // 0010 const IS_64_BIT = 0x02; // 0010
const IS_32_BIT = 0x04; // 0100 const IS_32_BIT = 0x04; // 0100
@ -224,7 +221,7 @@ fn makeEntry(base: u32, limit: u20, access: u8, flags: u4) GdtEntry {
} }
/// The GDT entry table of NUMBER_OF_ENTRIES entries. /// The GDT entry table of NUMBER_OF_ENTRIES entries.
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = []GdtEntry { var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = [_]GdtEntry{
// Null descriptor // Null descriptor
makeEntry(0, 0, 0, 0), makeEntry(0, 0, 0, 0),

View file

@ -58,7 +58,7 @@ pub const IdtPtr = packed struct {
}; };
/// The IDT entry table of NUMBER_OF_ENTRIES entries. /// The IDT entry table of NUMBER_OF_ENTRIES entries.
var idt: [NUMBER_OF_ENTRIES]IdtEntry = []IdtEntry{makeEntry(0, 0, 0, 0, 0)} ** NUMBER_OF_ENTRIES; var idt: [NUMBER_OF_ENTRIES]IdtEntry = [_]IdtEntry{makeEntry(0, 0, 0, 0, 0)} ** NUMBER_OF_ENTRIES;
/// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the /// The IDT pointer that the CPU is loaded with that contains the base address of the IDT and the
/// size. /// size.

View file

@ -28,7 +28,7 @@ extern fn irq14() void;
extern fn irq15() void; extern fn irq15() void;
/// The list of IRQ handlers initialised to unhandled. /// The list of IRQ handlers initialised to unhandled.
var irq_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void {unhandled} ** NUMBER_OF_ENTRIES; var irq_handlers: [NUMBER_OF_ENTRIES]fn (*arch.InterruptContext) void = [_]fn (*arch.InterruptContext) void{unhandled} ** NUMBER_OF_ENTRIES;
/// ///
/// A dummy handler that will make a call to panic as it is a unhandled interrupt. /// A dummy handler that will make a call to panic as it is a unhandled interrupt.

View file

@ -75,20 +75,18 @@ const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const
"Reserved", "Reserved",
"Reserved", "Reserved",
"Security", "Security",
"Reserved" "Reserved",
}; };
/// Errors that an isr function can return /// Errors that an isr function can return
pub const IsrError = error { pub const IsrError = error{UnrecognisedIsr};
UnrecognisedIsr
};
/// An isr handler. Takes an interrupt context and returns void. /// An isr handler. Takes an interrupt context and returns void.
/// Should finish quickly to avoid delaying further interrupts and the previously running code /// Should finish quickly to avoid delaying further interrupts and the previously running code
pub const IsrHandler = fn (*arch.InterruptContext) void; pub const IsrHandler = fn (*arch.InterruptContext) void;
// The of exception handlers initialised to unhandled. // The of exception handlers initialised to unhandled.
var isr_handlers: [NUMBER_OF_ENTRIES]IsrHandler = []IsrHandler{unhandled} ** NUMBER_OF_ENTRIES; var isr_handlers: [NUMBER_OF_ENTRIES]IsrHandler = [_]IsrHandler{unhandled} ** NUMBER_OF_ENTRIES;
var syscall_handler: IsrHandler = unhandled; var syscall_handler: IsrHandler = unhandled;
/// ///

View file

@ -16,11 +16,11 @@ pub const SyscallHandler = fn (ctx: *arch.InterruptContext) u32;
/// Errors that syscall utility functions can throw /// Errors that syscall utility functions can throw
pub const SyscallError = error{ pub const SyscallError = error{
SyscallExists, SyscallExists,
InvalidSyscall InvalidSyscall,
}; };
/// The array of registered syscalls /// The array of registered syscalls
var handlers: [NUM_HANDLERS]?SyscallHandler = []?SyscallHandler{null} ** NUM_HANDLERS; var handlers: [NUM_HANDLERS]?SyscallHandler = [_]?SyscallHandler{null} ** NUM_HANDLERS;
/// ///
/// Returns true if the syscall is valid, else false. /// Returns true if the syscall is valid, else false.
@ -84,7 +84,7 @@ inline fn syscall0(syscall: u32) u32 {
return asm volatile ( return asm volatile (
\\int $0x80 \\int $0x80
: [ret] "={eax}" (-> u32) : [ret] "={eax}" (-> u32)
: [syscall] "{eax}" (syscall), : [syscall] "{eax}" (syscall)
); );
} }
@ -100,7 +100,7 @@ inline fn syscall1(syscall: u32, arg: u32) u32 {
\\int $0x80 \\int $0x80
: [ret] "={eax}" (-> u32) : [ret] "={eax}" (-> u32)
: [syscall] "{eax}" (syscall), : [syscall] "{eax}" (syscall),
[arg1] "{ebx}" (arg), [arg1] "{ebx}" (arg)
); );
} }
@ -118,7 +118,7 @@ inline fn syscall2(syscall: u32, arg1: u32, arg2: u32) u32 {
: [ret] "={eax}" (-> u32) : [ret] "={eax}" (-> u32)
: [syscall] "{eax}" (syscall), : [syscall] "{eax}" (syscall),
[arg1] "{ebx}" (arg1), [arg1] "{ebx}" (arg1),
[arg2] "{ecx}" (arg2), [arg2] "{ecx}" (arg2)
); );
} }
@ -138,7 +138,7 @@ inline fn syscall3(syscall: u32, arg1: u32, arg2: u32, arg3: u32) u32 {
: [syscall] "{eax}" (syscall), : [syscall] "{eax}" (syscall),
[arg1] "{ebx}" (arg1), [arg1] "{ebx}" (arg1),
[arg2] "{ecx}" (arg2), [arg2] "{ecx}" (arg2),
[arg3] "{edx}" (arg3), [arg3] "{edx}" (arg3)
); );
} }
@ -160,7 +160,7 @@ inline fn syscall4(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32
[arg1] "{ebx}" (arg1), [arg1] "{ebx}" (arg1),
[arg2] "{ecx}" (arg2), [arg2] "{ecx}" (arg2),
[arg3] "{edx}" (arg3), [arg3] "{edx}" (arg3),
[arg4] "{esi}" (arg4), [arg4] "{esi}" (arg4)
); );
} }
@ -184,7 +184,7 @@ inline fn syscall5(syscall: u32, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg
[arg2] "{ecx}" (arg2), [arg2] "{ecx}" (arg2),
[arg3] "{edx}" (arg3), [arg3] "{edx}" (arg3),
[arg4] "{esi}" (arg4), [arg4] "{esi}" (arg4),
[arg5] "{edi}" (arg5), [arg5] "{edi}" (arg5)
); );
} }
@ -202,7 +202,7 @@ inline fn syscallArg(ctx: *arch.InterruptContext, comptime arg_idx: u32) u32 {
2 => ctx.edx, 2 => ctx.edx,
3 => ctx.esi, 3 => ctx.esi,
4 => ctx.edi, 4 => ctx.edi,
else => @compileError("Arg index must be between 0 and 4") else => @compileError("Arg index must be between 0 and 4"),
}; };
} }
@ -217,7 +217,6 @@ pub fn init(comptime options: type) void {
} }
/// Tests /// Tests
var testInt: u32 = 0; var testInt: u32 = 0;
fn testHandler0(ctx: *arch.InterruptContext) u32 { fn testHandler0(ctx: *arch.InterruptContext) u32 {

View file

@ -11,6 +11,13 @@ const serial = @import("serial.zig");
const mem = @import("mem.zig"); const mem = @import("mem.zig");
const options = @import("build_options"); const options = @import("build_options");
comptime {
switch (builtin.arch) {
.i386 => _ = @import("arch/x86/boot.zig"),
else => {},
}
}
// Need to import this as we need the panic to be in the root source file, or zig will just use the // Need to import this as we need the panic to be in the root source file, or zig will just use the
// builtin panic and just loop, which is what we don't want // builtin panic and just loop, which is what we don't want
const panic_root = @import("panic.zig"); const panic_root = @import("panic.zig");

File diff suppressed because it is too large Load diff