lumine
UEFI x86_64 bootloader for linux kernel images
build.zig
4026 bytes raw
1const std = @import("std");
2
3const Arch = enum {
4 x86_64,
5 aarch64,
6
7 pub fn target(self: Arch) std.Target.Cpu.Arch {
8 return switch (self) {
9 .x86_64 => .x86_64,
10 .aarch64 => .aarch64,
11 };
12 }
13
14 pub const EfiName = enum { X64, AA64 };
15
16 pub fn efi(self: Arch) EfiName {
17 return switch (self) {
18 .x86_64 => .X64,
19 .aarch64 => .AA64,
20 };
21 }
22};
23
24fn lumineExecutable(b: *std.Build, cpu_arch: Arch, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile {
25 const target = b.resolveTargetQuery(.{
26 .cpu_arch = cpu_arch.target(),
27 .cpu_model = .baseline,
28 .os_tag = .uefi,
29 });
30
31 return b.addExecutable(.{
32 .name = b.fmt("BOOT{t}", .{cpu_arch.efi()}),
33 .root_module = b.createModule(.{
34 .target = target,
35 .optimize = optimize,
36 .root_source_file = b.path("src/main.zig"),
37 }),
38 });
39}
40
41pub fn build(b: *std.Build) void {
42 const optimize = b.standardOptimizeOption(.{});
43
44 const cpu_arch = b.option(Arch, "arch", "cpu architecture") orelse .x86_64;
45
46 const install_step = b.getInstallStep();
47
48 const lumine_exe = lumineExecutable(b, cpu_arch, optimize);
49 const lumine_install = b.addInstallArtifact(lumine_exe, .{});
50 install_step.dependOn(&lumine_install.step);
51
52 const all_step = b.step("all", "build for all supported architectures");
53
54 inline for (@typeInfo(Arch).@"enum".fields) |field| {
55 const arch_exe = lumineExecutable(b, @field(Arch, field.name), optimize);
56
57 const arch_install = b.addInstallArtifact(arch_exe, .{});
58 all_step.dependOn(&arch_install.step);
59 }
60
61 const qemu_exe = b.option([]const u8, "qemu", "overide qemu executable") orelse b.fmt("qemu-system-{t}", .{cpu_arch});
62 const qemu_cmd = b.addSystemCommand(&.{ qemu_exe, "-m", "512M", "-no-reboot", "-no-shutdown" });
63
64 const no_graphic = b.option(bool, "no-graphic", "disable graphical output") orelse false;
65 if (no_graphic) qemu_cmd.addArgs(&.{ "-serial", "mon:stdio", "-nographic" });
66
67 switch (cpu_arch) {
68 .aarch64 => qemu_cmd.addArgs(&.{ "-M", "virt", "-cpu", "cortex-a57", "-device", "ramfb" }),
69 .x86_64 => {},
70 }
71
72 const ovmf_override = b.option(std.Build.LazyPath, "ovmf", "override ovmf firmware dependency");
73 const ovmf_use_dep = b.option(bool, "ovmf-dep", "use ovmf dependency instead") orelse false;
74
75 if (ovmf_use_dep) {
76 if (b.lazyDependency("ovmf", .{})) |ovmf_dep| {
77 const ovmf_code = ovmf_dep.path(b.fmt("ovmf-code-{t}.fd", .{cpu_arch}));
78
79 qemu_cmd.addArg("-bios");
80 qemu_cmd.addFileArg(ovmf_code);
81 }
82 } else if (ovmf_override) |override| {
83 qemu_cmd.addArg("-bios");
84 qemu_cmd.addFileArg(override);
85 } else {
86 const fail = b.addFail("ovmf not specified, either use -Dovmf=PATH_TO_OVMF_CODE_BIN or use -Dovmf-dep to download as dependency");
87 qemu_cmd.step.dependOn(&fail.step);
88 }
89
90 {
91 // https://codeberg.org/ziglang/zig/issues/32041
92 // https://codeberg.org/dragsbruh/minimux/releases
93 const linux_dir = b.option([]const u8, "linux", "dir containing `vmlinuz` and `initrd`") orelse b.fmt("res/{t}", .{cpu_arch});
94 const config_file = b.option([]const u8, "config", "path to config file") orelse "res/lumine.zon";
95
96 const boot_dir = b.addTempFiles();
97
98 _ = boot_dir.addCopyFile(b.path(config_file), "lumine.zon");
99 _ = boot_dir.addCopyFile(lumine_exe.getEmittedBin(), b.fmt("EFI/BOOT/BOOT{t}.EFI", .{cpu_arch.efi()}));
100
101 _ = boot_dir.addCopyFile(b.path(linux_dir).path(b, "vmlinuz"), "linux/vmlinuz");
102 _ = boot_dir.addCopyFile(b.path(linux_dir).path(b, "initrd"), "linux/initrd");
103
104 qemu_cmd.addArg("-drive");
105 qemu_cmd.addPrefixedDirectoryArg("format=raw,file=fat:rw:", boot_dir.getDirectory());
106 }
107
108 const run_step = b.step("run", "run in qemu");
109 run_step.dependOn(&qemu_cmd.step);
110}