λ

lumine

UEFI x86_64 bootloader for linux kernel images

settings

src/main.zig

2802 bytes raw

const std = @import("std");
const uefi = std.os.uefi;
const log = std.log;
const Io = std.Io;

const Chainload = @import("./boot/Chainload.zig");
const linux = @import("./boot/linux.zig");
const Config = @import("./Config.zig");
const Console = @import("./Console.zig");
const luefi = @import("./luefi.zig");

// TODO: relative to EFI
const config_paths: []const Config.Path = &.{
    .{ .boot = "/lumine/config.zon" },
    .{ .boot = "/lumine/lumine.zon" },
    .{ .boot = "/lumine.zon" },
};

pub fn bmain() !uefi.Status {
    const allocator = uefi.pool_allocator;

    const config: Config = try config: {
        var file = reader: {
            for (config_paths) |path| {
                break :reader luefi.fileOpen(path) catch continue;
            }

            log.err("failed to detect config, tried paths:", .{});
            for (config_paths) |path| log.err("\t{f}", .{path});
            return error.NoConfig;
        };
        defer file.close() catch unreachable;

        const source = try allocator.allocSentinel(u8, try luefi.fileSize(file), 0);
        defer allocator.free(source);

        try luefi.fileReadSlice(file, source);

        break :config std.zon.parse.fromSliceAlloc(Config, allocator, source, null, .{});
    };
    defer std.zon.parse.free(allocator, config);

    switch (config.boot) {
        .chainload => |efi_path| {
            log.info("loading efi {f}", .{efi_path});
            const chainload: Chainload = try .load(efi_path);
            defer chainload.deinit();

            return chainload.start();
        },
        .linux => |linux_conf| {
            const kernel: linux.Kernel = try .load(linux_conf.kernel_path);
            defer kernel.deinit();

            const initrd: *linux.Initrd = try .load(linux_conf.initrd_paths);
            defer initrd.deinit();

            return kernel.start(linux_conf.kernel_cmdline);
        },
    }
}

pub fn main() uefi.Status {
    return bmain() catch |err| {
        log.err("unrecoverable error in bmain: {}", .{err});
        return .aborted;
    };
}

pub fn logFn(
    comptime level: log.Level,
    comptime scope: @EnumLiteral(),
    comptime format: []const u8,
    args: anytype,
) void {
    // stderr should be equivalent to stdout?
    // but i dont see logs on my hardware if i use stderr
    var stderr_buf: [512]u8 = undefined;
    var stderr: Console = .stdout(&stderr_buf);
    stderr.interface.print("{t}({}): ", .{ level, scope }) catch return;
    stderr.interface.print(format ++ "\r\n", args) catch return;
    stderr.interface.flush() catch return;
}

pub fn panicFn(msg: []const u8, _: ?usize) noreturn {
    logFn(.err, .panic, "{s}", .{msg});
    @trap();
}

pub const panic = std.debug.FullPanic(panicFn);
pub const std_options: std.Options = .{ .logFn = logFn };