diff --git a/build.zig b/build.zig index 9c3835c..00b2460 100644 --- a/build.zig +++ b/build.zig @@ -1,5 +1,86 @@ const std = @import("std"); +const DEFAULT_ARCH = "riscv64"; + +const SupportedArch = enum { + aarch64, + riscv64, + + fn fromStr(s: []const u8) ?SupportedArch { + return if (std.mem.eql(u8, s, "riscv64")) // + .riscv64 + else if (std.mem.eql(u8, s, "aarch64")) // + .aarch64 + else // + null; + } + + fn makeTarget(self: SupportedArch, b: *std.Build) std.Build.ResolvedTarget { + return switch (self) { + .riscv64 => b.standardTargetOptions(.{ .default_target = .{ + .cpu_arch = .riscv64, + .os_tag = .freestanding, + .abi = .none, + } }), + .aarch64 => b.standardTargetOptions(.{ .default_target = .{ + .cpu_arch = .aarch64, + .os_tag = .freestanding, + .abi = .none, + } }), + }; + } + + fn addTargetSpecific(self: SupportedArch, b: *std.Build, kernel: *std.Build.Step.Compile) anyerror!*std.Build.Step { + switch (self) { + .riscv64 => { + kernel.entry = .{ .symbol_name = "__rv64_entry" }; + + kernel.setLinkerScript(b.path("etc/riscv64-unknown-none.ld")); + kernel.addCSourceFiles(.{ + .files = &.{"src/arch/riscv64/entry.S"}, + .flags = &.{}, + }); + }, + .aarch64 => { + kernel.entry = .{ .symbol_name = "__aa64_entry" }; + + kernel.setLinkerScript(b.path("etc/aarch64-unknown-none.ld")); + kernel.addCSourceFiles(.{ + .files = &.{"src/arch/aarch64/entry.S"}, + .flags = &.{}, + }); + }, + } + + b.installArtifact(kernel); + + if (self == .riscv64 or self == .aarch64) { + const fakeLinuxHeader: *std.Build.Step = try b.allocator.create(std.Build.Step); + fakeLinuxHeader.* = std.Build.Step.init(.{ + .id = std.Build.Step.Id.custom, + .name = "insert fake linux header", + .owner = kernel.step.owner, + .makeFn = insertFakeLinuxImageHeader, + }); + + const elf2bin = b.addSystemCommand(&.{ + "llvm-objcopy", + "-O", + "binary", + "zig-out/bin/kernel", + "zig-out/bin/kernel.bin", + }); + + fakeLinuxHeader.dependOn(b.getInstallStep()); + elf2bin.step.dependOn(fakeLinuxHeader); + + return &elf2bin.step; + } else { + @panic("Unreachable + don't care"); + } + } +}; + fn insertFakeLinuxImageHeader(step: *std.Build.Step, opts: std.Build.Step.MakeOptions) anyerror!void { const RISCV_MAGIC1 = "RISCV\x00\x00\x00"; const RISCV_MAGIC2 = "RSC\x05"; @@ -46,53 +127,72 @@ fn insertFakeLinuxImageHeader(step: *std.Build.Step, opts: std.Build.Step.MakeOp _ = opts; } +fn build_riscv64(b: *std.Build) anyerror!void { + _ = b; +} + pub fn build(b: *std.Build) anyerror!void { + const maybeArchOption = b.option([]const u8, "arch", "Architecture to use"); + + const archOption = maybeArchOption orelse DEFAULT_ARCH; + + const sTarget = SupportedArch.fromStr(archOption) orelse { + std.debug.print("Unsupported target arch: '{s}'", .{archOption}); + return error.InvalidArgument; + }; + const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast }); - const target = b.standardTargetOptions(.{ .default_target = .{ - .cpu_arch = .riscv64, - .os_tag = .freestanding, - .abi = .none, - } }); + const target = sTarget.makeTarget(b); - const kernel_module = b.addModule("kernel", .{ .optimize = optimize, .target = target, .pic = true, .red_zone = false, .code_model = .medium, .root_source_file = b.path("src/kernel.zig") }); - - const kernel = b.addExecutable(.{ .name = "kernel", .root_module = kernel_module, .pic = true }); + const codeModel: std.builtin.CodeModel = switch (sTarget) { + .riscv64 => .medium, + .aarch64 => .small, + }; + const kernelModule = b.addModule("kernel", .{ + .optimize = optimize, + .target = target, + .pic = true, + .red_zone = false, + .code_model = codeModel, + .root_source_file = b.path("src/kernel.zig"), + }); + const kernel = b.addExecutable(.{ + .name = "kernel", + .root_module = kernelModule, + .pic = true, + }); kernel.pie = true; - kernel.entry = .{ .symbol_name = "__rv64_entry" }; - - kernel.setLinkerScript(b.path("etc/riscv64-unknown-none.ld")); - kernel.addCSourceFiles(.{ - .files = &.{"src/arch/riscv64/entry.S"}, - .flags = &.{}, - }); - b.installArtifact(kernel); - - const fakeLinuxHeader: *std.Build.Step = try b.allocator.create(std.Build.Step); - fakeLinuxHeader.* = std.Build.Step.init(.{ - .id = std.Build.Step.Id.custom, - .name = "insert fake linux header", - .owner = kernel.step.owner, - .makeFn = insertFakeLinuxImageHeader, - }); - - const elf2bin = b.addSystemCommand(&.{ "llvm-objcopy", "-O", "binary", "zig-out/bin/kernel", "zig-out/bin/kernel.bin" }); + const kernelStep = try sTarget.addTargetSpecific(b, kernel); // TODO QEMU binary override const qemu_info = switch (target.result.cpu.arch) { .riscv64 => .{ "qemu-system-riscv64", "rv64" }, + .aarch64 => .{ "qemu-system-aarch64", "cortex-a72" }, else => unreachable, }; - const qemu_cmd = b.addSystemCommand(&.{ qemu_info[0], "-M", "virt", "-kernel", "zig-out/bin/kernel.bin", "-m", "256M", "-cpu", qemu_info[1], "-serial", "mon:stdio", "-display", "none" }); + const qemu_cmd = b.addSystemCommand(&.{ + qemu_info[0], + "-M", + "virt", + "-kernel", + "zig-out/bin/kernel.bin", + "-m", + "256M", + "-cpu", + qemu_info[1], + "-serial", + "mon:stdio", + "-display", + "none", + }); if (target.result.cpu.arch == .riscv64) { qemu_cmd.addArgs(&.{ "-bios", "etc/boot/rv64_fw_jump.bin" }); } - fakeLinuxHeader.dependOn(b.getInstallStep()); - elf2bin.step.dependOn(fakeLinuxHeader); - qemu_cmd.step.dependOn(&elf2bin.step); + qemu_cmd.step.dependOn(kernelStep); if (b.args) |args| qemu_cmd.addArgs(args); const run_step = b.step("run", "Start the OS in qemu"); run_step.dependOn(&qemu_cmd.step); diff --git a/etc/aarch64-unknown-none.ld b/etc/aarch64-unknown-none.ld new file mode 100644 index 0000000..77ce018 --- /dev/null +++ b/etc/aarch64-unknown-none.ld @@ -0,0 +1,57 @@ +SECTIONS { + . = 0x0; + + PROVIDE(__kernel_start = .); + + .text : ALIGN(4K) { + *(.text.entry*) + *(.text*) + } + + .rodata : ALIGN(4K) { + *(.rodata*) + *(.srodata*) + *(.got*) + *(.plt*) + } + + .dynamic : ALIGN(4K) { + *(.dynamic*) + } + + .tdata : ALIGN(4K) { + PROVIDE(__tdata_start = .); + *(.tdata*) + PROVIDE(__tdata_end = .); + } + + .rela : ALIGN(4K) { + PROVIDE(__rela_start = .); + *(.rela*) + PROVIDE(__rela_end = .); + } + + .data : ALIGN(4K) { + *(.data*) + *(.sdata*) + } + + .bss : { + . = ALIGN(4K); + PROVIDE(__bss_start = .); + *(COMMON) + *(.bss*) + *(.sbss*) + . = ALIGN(4K); + PROVIDE(__bss_end = .); + } + + .tbss : { + PROVIDE(__tbss_start = .); + *(.tbss*) + PROVIDE(__tbss_end = .); + } + + . = ALIGN(4K); + PROVIDE(__kernel_end = .); +} diff --git a/etc/aarch64.lldb b/etc/aarch64.lldb new file mode 100644 index 0000000..9fb651b --- /dev/null +++ b/etc/aarch64.lldb @@ -0,0 +1,11 @@ +gdb-remote localhost:1234 + +target modules add zig-out/bin/kernel +# target modules load -f zig-out/bin/kernel -s 0xFFFFFFF000200000 +target modules load -f zig-out/bin/kernel -s 0x40080000 + +# breakpoint set -H -a 0x80200000 +# process continue + +# breakpoint set -H -n arch.riscv64.boot.rv64BspEntryLower +# process continue diff --git a/src/arch.zig b/src/arch.zig index b5bdae1..9106f47 100644 --- a/src/arch.zig +++ b/src/arch.zig @@ -5,6 +5,9 @@ pub fn arch() type { .riscv64 => { return @import("arch/riscv64.zig"); }, + .aarch64 => { + return @import("arch/aarch64.zig"); + }, else => { @panic("Architecture is not supported"); }, diff --git a/src/arch/aarch64.zig b/src/arch/aarch64.zig new file mode 100644 index 0000000..9c8746c --- /dev/null +++ b/src/arch/aarch64.zig @@ -0,0 +1,34 @@ +const boot = @import("aarch64/boot.zig"); + +export const _ = boot.aa64BspLowerEntry; + +pub const Context = struct { + pub fn idle() Context { + @panic("TODO"); + } + + pub fn kernel(pc: usize, arg: usize) Context { + _ = pc; + _ = arg; + @panic("TODO"); + } + + pub fn enter(self: *Context) noreturn { + _ = self; + @panic("TODO"); + } + + pub fn switchFrom(self: *Context, from: *Context) void { + _ = self; + _ = from; + @panic("TODO"); + } +}; + +pub fn halt() noreturn { + while (true) {} +} + +pub fn spinHint() void { + // TODO +} diff --git a/src/arch/aarch64/boot.zig b/src/arch/aarch64/boot.zig new file mode 100644 index 0000000..96a02d6 --- /dev/null +++ b/src/arch/aarch64/boot.zig @@ -0,0 +1,3 @@ +pub export fn aa64BspLowerEntry() callconv(.C) noreturn { + while (true) {} +} diff --git a/src/arch/aarch64/entry.S b/src/arch/aarch64/entry.S new file mode 100644 index 0000000..33b4bcb --- /dev/null +++ b/src/arch/aarch64/entry.S @@ -0,0 +1,49 @@ +.global __aa64_entry + +.pushsection .text.entry +__aa64_entry: + // x0 -- DTB physical address + mrs x1, mpidr_el1 + ands x1, x1, #0xF + bne .parking_lot + + mrs x1, CurrentEL + and x1, x1, #~0x3 + adr x2, .el_jump_table + add x2, x2, x1 + br x2 + +.el_jump_table: + b . // EL0 impossible + b .bsp_el1 // EL1 + b . // EL2 not implemented + b . // EL3 not implemented + +.bsp_el1: + // Zero out the .bss + adr x1, __bss_start + adr x2, __bss_end +1: + cmp x1, x2 + beq 2f + str xzr, [x1], #8 + b 1b +2: + + // Setup a stack + adr x1, __aa64_bsp_stack_top + mov sp, x1 + + b aa64BspLowerEntry + +.parking_lot: + wfe + b .parking_lot +.popsection // .text.entry + +.pushsection .bss +.p2align 4 +__aa64_bsp_stack_bottom: + .skip 65536 +__aa64_bsp_stack_top: +.popsection // .bss