lumine
UEFI x86_64 bootloader for linux kernel images
commit 43971fc7
| author | dragsbruh <dragsbruh@protonmail.com> |
|---|---|
| date | 2026-07-22 20:08:48 +0530 |
| committer | dragsbruh <dragsbruh@protonmail.com> (2026-07-22 20:08:48 +0530) |
| tree | 756ebecb5885a035d9f36dde2fcb0f0478cc3ed4 |
| parent | 1a28f8f0 |
feat: linux initrd support
+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); }, }