λ

lumine

UEFI x86_64 bootloader for linux kernel images

settings

src/linux/Kernel.zig

2078 bytes raw

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

const luefi = @import("../luefi.zig");
const Path = @import("../Config.zig").Path;

const log = std.log.scoped(.linux);

const Kernel = @This();

kernel_pages: usize,
kernel_buf: []align(4096) u8,

pub fn load(path: Path) !Kernel {
    const boot_svc = uefi.system_table.boot_services.?;

    log.info("loading kernel from {f}", .{path});
    const file = try luefi.fileOpen(path);
    defer file.close() catch unreachable;

    const size = try luefi.fileSize(file);
    const page_count = try std.math.divCeil(usize, size, 4096);

    const pages = try boot_svc.allocatePages(.any, .loader_data, page_count);
    errdefer boot_svc.freePages(pages) catch unreachable;

    const slice = @as([*]align(4096) u8, @ptrCast(pages))[0..size];
    try luefi.fileReadSlice(file, slice);

    return .{
        .kernel_buf = slice,
        .kernel_pages = size,
    };
}

pub fn deinit(self: Kernel) void {
    const boot_svc = uefi.system_table.boot_services.?;
    const pages = @as([*]align(4096) [4096]u8, @ptrCast(self.kernel_buf));
    boot_svc.freePages(pages[0..self.kernel_pages]) catch unreachable;
}

pub fn start(self: Kernel, cmdline: []const u8) !void {
    const boot_svc = uefi.system_table.boot_services.?;
    const allocator = uefi.pool_allocator;

    const cmdline_utf16 = try std.unicode.utf8ToUtf16LeAllocZ(allocator, cmdline);
    defer allocator.free(cmdline_utf16);

    const img_handle = try boot_svc.loadImage(false, uefi.handle, .{ .buffer = self.kernel_buf });
    defer _ = boot_svc.unloadImage(img_handle) catch unreachable;

    const loaded_img = try boot_svc.openProtocol(uefi.protocol.LoadedImage, img_handle, .{
        .get_protocol = .{ .agent = uefi.handle },
    }) orelse unreachable;

    loaded_img.load_options = @ptrCast(cmdline_utf16);
    loaded_img.load_options_size = @intCast(cmdline_utf16.len * @sizeOf(u16));

    const exit_data = try boot_svc.startImage(img_handle);
    log.err("kernel image exited with exit code {t}", .{exit_data.code});

    return error.KernelExited;
}