λ

lumine

UEFI x86_64 bootloader for linux kernel images

settings

build.zig

4026 bytes raw

const std = @import("std");

const Arch = enum {
    x86_64,
    aarch64,

    pub fn target(self: Arch) std.Target.Cpu.Arch {
        return switch (self) {
            .x86_64 => .x86_64,
            .aarch64 => .aarch64,
        };
    }

    pub const EfiName = enum { X64, AA64 };

    pub fn efi(self: Arch) EfiName {
        return switch (self) {
            .x86_64 => .X64,
            .aarch64 => .AA64,
        };
    }
};

fn lumineExecutable(b: *std.Build, cpu_arch: Arch, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile {
    const target = b.resolveTargetQuery(.{
        .cpu_arch = cpu_arch.target(),
        .cpu_model = .baseline,
        .os_tag = .uefi,
    });

    return b.addExecutable(.{
        .name = b.fmt("BOOT{t}", .{cpu_arch.efi()}),
        .root_module = b.createModule(.{
            .target = target,
            .optimize = optimize,
            .root_source_file = b.path("src/main.zig"),
        }),
    });
}

pub fn build(b: *std.Build) void {
    const optimize = b.standardOptimizeOption(.{});

    const cpu_arch = b.option(Arch, "arch", "cpu architecture") orelse .x86_64;

    const install_step = b.getInstallStep();

    const lumine_exe = lumineExecutable(b, cpu_arch, optimize);
    const lumine_install = b.addInstallArtifact(lumine_exe, .{});
    install_step.dependOn(&lumine_install.step);

    const all_step = b.step("all", "build for all supported architectures");

    inline for (@typeInfo(Arch).@"enum".fields) |field| {
        const arch_exe = lumineExecutable(b, @field(Arch, field.name), optimize);

        const arch_install = b.addInstallArtifact(arch_exe, .{});
        all_step.dependOn(&arch_install.step);
    }

    const qemu_exe = b.option([]const u8, "qemu", "overide qemu executable") orelse b.fmt("qemu-system-{t}", .{cpu_arch});
    const qemu_cmd = b.addSystemCommand(&.{ qemu_exe, "-m", "512M", "-no-reboot", "-no-shutdown" });

    const no_graphic = b.option(bool, "no-graphic", "disable graphical output") orelse false;
    if (no_graphic) qemu_cmd.addArgs(&.{ "-serial", "mon:stdio", "-nographic" });

    switch (cpu_arch) {
        .aarch64 => qemu_cmd.addArgs(&.{ "-M", "virt", "-cpu", "cortex-a57", "-device", "ramfb" }),
        .x86_64 => {},
    }

    const ovmf_override = b.option(std.Build.LazyPath, "ovmf", "override ovmf firmware dependency");
    const ovmf_use_dep = b.option(bool, "ovmf-dep", "use ovmf dependency instead") orelse false;

    if (ovmf_use_dep) {
        if (b.lazyDependency("ovmf", .{})) |ovmf_dep| {
            const ovmf_code = ovmf_dep.path(b.fmt("ovmf-code-{t}.fd", .{cpu_arch}));

            qemu_cmd.addArg("-bios");
            qemu_cmd.addFileArg(ovmf_code);
        }
    } else if (ovmf_override) |override| {
        qemu_cmd.addArg("-bios");
        qemu_cmd.addFileArg(override);
    } else {
        const fail = b.addFail("ovmf not specified, either use -Dovmf=PATH_TO_OVMF_CODE_BIN or use -Dovmf-dep to download as dependency");
        qemu_cmd.step.dependOn(&fail.step);
    }

    {
        // https://codeberg.org/ziglang/zig/issues/32041
        // https://codeberg.org/dragsbruh/minimux/releases
        const linux_dir = b.option([]const u8, "linux", "dir containing `vmlinuz` and `initrd`") orelse b.fmt("res/{t}", .{cpu_arch});
        const config_file = b.option([]const u8, "config", "path to config file") orelse "res/lumine.zon";

        const boot_dir = b.addTempFiles();

        _ = boot_dir.addCopyFile(b.path(config_file), "lumine.zon");
        _ = boot_dir.addCopyFile(lumine_exe.getEmittedBin(), b.fmt("EFI/BOOT/BOOT{t}.EFI", .{cpu_arch.efi()}));

        _ = boot_dir.addCopyFile(b.path(linux_dir).path(b, "vmlinuz"), "linux/vmlinuz");
        _ = boot_dir.addCopyFile(b.path(linux_dir).path(b, "initrd"), "linux/initrd");

        qemu_cmd.addArg("-drive");
        qemu_cmd.addPrefixedDirectoryArg("format=raw,file=fat:rw:", boot_dir.getDirectory());
    }

    const run_step = b.step("run", "run in qemu");
    run_step.dependOn(&qemu_cmd.step);
}