λ

lumine

UEFI x86_64 bootloader for linux kernel images

settings

diff

1a28f8f0 to 43971fc7

+138 -7

res/lumine.zon modified +2 -3

diff --git a/res/lumine.zon b/res/lumine.zon
index 16b572fd33fd13d1fad195d3a0d902e1fb76e705..de2f5c509c6087bd1793d3e567740eb296e80043 100644
--- a/res/lumine.zon
+++ b/res/lumine.zon
@@ -1,9 +1,8 @@
 .{
     .boot = .{
         .linux = .{
-            .kernel_path = .{
-                .boot = "/linux/vmlinuz",
-            },
+            .kernel_path = .{ .boot = "/linux/vmlinuz" },
+            .initrd_path = .{ .boot = "/linux/initrd.xz" },
             .kernel_cmdline = "console=ttyS0",
         },
     },

src/Config.zig modified +1 -0

diff --git a/src/Config.zig b/src/Config.zig
index 88be0a33fcb0bcf7bed603fbeb95d213d87c089c..374059b0757ece64fae0b068bf666643746bd07a 100644
--- a/src/Config.zig
+++ b/src/Config.zig
@@ -18,6 +18,7 @@     chainload: Path,
     linux: struct {
         kernel_path: Path,
         kernel_cmdline: []const u8 = &.{},
+        initrd_path: Path,
     },
 };
 

src/boot/Chainload.zig modified +1 -1

diff --git a/src/boot/Chainload.zig b/src/boot/Chainload.zig
index c205dcef3420483308c42d99f720284585815c92..dff377991104e3c37c5ecdcc3e5b9d62a26ff3cb 100644
--- a/src/boot/Chainload.zig
+++ b/src/boot/Chainload.zig
@@ -19,7 +19,7 @@         defer file.close() catch unreachable;
 
         break :blk try luefi.fileReadAlloc(file);
     };
-    defer luefi.freePages(buffer, pages);
+    errdefer luefi.freePages(buffer, pages);
 
     const handle, const loaded = try luefi.loadImage(buffer);
 

src/boot/linux.zig modified +1 -0

diff --git a/src/boot/linux.zig b/src/boot/linux.zig
index 34738dea0ed7fcbbef052723f925a0daeb87c5ce..c285e0caf611c42048b90c3ed5aa35084232f5aa 100644
--- a/src/boot/linux.zig
+++ b/src/boot/linux.zig
@@ -1 +1,2 @@
 pub const Kernel = @import("./linux/Kernel.zig");
+pub const Initrd = @import("./linux/Initrd.zig");

src/boot/linux/Initrd.zig added +92 -0

diff --git a/src/boot/linux/Initrd.zig b/src/boot/linux/Initrd.zig
new file mode 100644
index 0000000000000000000000000000000000000000..8a5707cb51a64df1da28fab1b21978c2ef94f0de
--- /dev/null
+++ b/src/boot/linux/Initrd.zig
@@ -0,0 +1,92 @@
+const std = @import("std");
+const uefi = std.os.uefi;
+
+const luefi = @import("../../luefi.zig");
+const Path = @import("../../Config.zig").Path;
+
+const Initrd = @This();
+
+protocol: LoadFile2 = .{},
+
+buffer: []align(4096) u8,
+pages: usize,
+
+pub fn load(path: Path) !*Initrd {
+    const buffer, const pages = blk: {
+        const file = try luefi.fileOpen(path);
+        defer file.close() catch unreachable;
+
+        break :blk try luefi.fileReadAlloc(file);
+    };
+    errdefer luefi.freePages(buffer, pages);
+
+    const self = try uefi.pool_allocator.create(Initrd);
+    self.* = .{ .buffer = buffer, .pages = pages };
+
+    try luefi.installProtocols(.{ &device_path, &self.protocol });
+
+    return self;
+}
+
+pub fn deinit(self: *Initrd) void {
+    luefi.freePages(self.buffer, self.pages);
+    uefi.pool_allocator.destroy(self);
+}
+
+pub const LoadFile2 = extern struct {
+    loadFile: *const @TypeOf(impl) = impl,
+
+    pub fn impl(
+        protocol: *LoadFile2,
+        _: *uefi.protocol.DevicePath,
+        _: bool,
+        buf_size: *usize,
+        buf_out: ?[*]u8,
+    ) callconv(uefi.cc) uefi.Status {
+        const initrd: *Initrd = @fieldParentPtr("protocol", protocol);
+
+        if (buf_out == null or buf_size.* < initrd.buffer.len) {
+            buf_size.* = initrd.buffer.len;
+            return .buffer_too_small;
+        }
+
+        @memcpy(buf_out.?, initrd.buffer);
+
+        return .success;
+    }
+
+    pub const guid: uefi.Guid = .{
+        .time_low = 0x4006c0c1,
+        .time_mid = 0xfcb3,
+        .time_high_and_version = 0x403e,
+        .clock_seq_high_and_reserved = 0x99,
+        .clock_seq_low = 0x6d,
+        .node = [_]u8{ 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d },
+    };
+};
+
+pub var device_path = struct {
+    vendor: uefi.DevicePath.Media.VendorDevicePath,
+    end: uefi.DevicePath.End.EndEntireDevicePath,
+
+    pub const guid = uefi.protocol.DevicePath.guid;
+}{
+    .vendor = uefi.DevicePath.Media.VendorDevicePath{
+        .type = .media,
+        .subtype = .vendor,
+        .length = @sizeOf(uefi.DevicePath.Media.VendorDevicePath),
+        .guid = .{
+            .time_low = 0x5568e427,
+            .time_mid = 0x68fc,
+            .time_high_and_version = 0x4f3d,
+            .clock_seq_high_and_reserved = 0xac,
+            .clock_seq_low = 0x74,
+            .node = [_]u8{ 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 },
+        },
+    },
+    .end = uefi.DevicePath.End.EndEntireDevicePath{
+        .type = .end,
+        .subtype = .end_entire,
+        .length = @sizeOf(uefi.DevicePath.End.EndEntireDevicePath),
+    },
+};

src/luefi.zig modified +37 -3

diff --git a/src/luefi.zig b/src/luefi.zig
index 872ae2ccb6b49f1b36c54d52d4556db1c06ae45f..9f3640b3c8d19454e2b9e087b60ad2a7f2ffdd1d 100644
--- a/src/luefi.zig
+++ b/src/luefi.zig
@@ -73,7 +73,8 @@
     const pages = try boot_svc.allocatePages(.any, .loader_data, page_count);
     errdefer boot_svc.freePages(pages) catch unreachable;
 
-    const slice: []align(4096) u8 = @as([*]align(4096) u8, @ptrCast(pages))[0..size];
+    const slice_ptr: [*]align(4096) u8 = @ptrCast(pages);
+    const slice = slice_ptr[0..size];
     try fileReadSlice(file, slice);
 
     return .{ slice, page_count };
@@ -82,8 +83,8 @@
 pub fn freePages(buf: []align(4096) u8, page_count: usize) void {
     const boot_svc = uefi.system_table.boot_services.?;
 
-    const pages = @as([*]align(4096) [4096]u8, @ptrCast(buf))[0..page_count];
-    boot_svc.freePages(pages) catch unreachable;
+    const slice_ptr: [*]align(4096) [4096]u8 = @ptrCast(buf);
+    boot_svc.freePages(slice_ptr[0..page_count]) catch unreachable;
 }
 
 pub fn loadImage(buf: []u8) !struct { uefi.Handle, *uefi.protocol.LoadedImage } {
@@ -103,3 +104,36 @@ pub fn unloadImage(handle: uefi.Handle) void {
     const boot_svc = uefi.system_table.boot_services.?;
     _ = boot_svc.unloadImage(handle) catch unreachable;
 }
+
+// https://codeberg.org/ziglang/zig/pulls/31934
+pub fn installProtocols(interfaces: anytype) !void {
+    const fields = @typeInfo(@TypeOf(interfaces)).@"struct".fields;
+    comptime var tuple_types: [fields.len * 2 + 1]type = undefined;
+    inline for (0..fields.len) |i| {
+        tuple_types[i * 2 + 1] = *const uefi.Guid;
+        tuple_types[i * 2 + 2] = *const anyopaque;
+    }
+    tuple_types[0] = *?uefi.Handle;
+
+    var args_tuple: @Tuple(tuple_types[0..]) = undefined;
+    inline for (interfaces, 0..) |iface, i| {
+        const child = @typeInfo(@TypeOf(iface)).pointer.child;
+        args_tuple[i * 2 + 1] = &@field(child, "guid");
+        args_tuple[i * 2 + 2] = @ptrCast(iface);
+    }
+    var handle: ?uefi.Handle = null;
+    args_tuple[0] = &handle;
+
+    const boot_svc = uefi.system_table.boot_services.?;
+    const InstallFn = *const fn (handle: *?uefi.Handle, ...) callconv(uefi.cc) uefi.Status;
+    const installFn: InstallFn = @ptrCast(boot_svc._installMultipleProtocolInterfaces);
+
+    const status = @call(.auto, installFn, args_tuple);
+    return switch (status) {
+        .success => return,
+        .already_started => return error.AlreadyStarted,
+        .out_of_resources => return error.OutOfResources,
+        .invalid_parameter => return error.InvalidParameter,
+        else => return uefi.unexpectedStatus(status),
+    };
+}

src/main.zig modified +4 -0

diff --git a/src/main.zig b/src/main.zig
index fa44f0f86b608ad6ec12ee3f9f1222f6a51707be..5bc90544328023184f6fb9d4209e5c5ac736bfe8 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -52,6 +52,10 @@             log.info("loading kernel {f}", .{linux_conf.kernel_path});
             const kernel: linux.Kernel = try .load(linux_conf.kernel_path);
             defer kernel.deinit();
 
+            log.info("loading initrd {f}", .{linux_conf.initrd_path});
+            const initrd: *linux.Initrd = try .load(linux_conf.initrd_path);
+            defer initrd.deinit();
+
             return kernel.start(linux_conf.kernel_cmdline);
         },
     }