lumine
UEFI x86_64 bootloader for linux kernel images
src/boot/linux/Initrd.zig
3833 bytes raw
1const std = @import("std");
2const uefi = std.os.uefi;
3
4const Path = @import("../../Config.zig").Path;
5const luefi = @import("../../luefi.zig");
6
7const log = std.log.scoped(.initrd);
8
9const Initrd = @This();
10
11protocol: LoadFile2 = .{},
12
13initrd_buf: []align(4096) u8,
14initrd_pages: usize,
15
16handle: uefi.Handle,
17
18pub fn load(paths: []const Path) !*Initrd {
19 const File = struct { file: *uefi.protocol.File, size: usize };
20
21 var files: std.ArrayList(File) = try .initCapacity(uefi.pool_allocator, paths.len);
22 defer files.deinit(uefi.pool_allocator);
23
24 defer for (files.items) |f| f.file.close() catch unreachable;
25
26 var total_size: usize = 0;
27 for (paths) |path| {
28 log.info("loading initrd {f}", .{path});
29 const file = try luefi.fileOpen(path);
30 errdefer file.close() catch unreachable;
31
32 const size = try luefi.fileSize(file);
33 try files.append(uefi.pool_allocator, .{ .file = file, .size = size });
34
35 total_size += size;
36 }
37
38 const pages_count = try std.math.divCeil(usize, total_size, 4096);
39
40 const boot_svc = uefi.system_table.boot_services.?;
41
42 const initrd_pages = try boot_svc.allocatePages(.any, .loader_data, pages_count);
43 errdefer boot_svc.freePages(initrd_pages) catch unreachable;
44
45 const initrd_buf_ptr: [*]align(4096) u8 = @ptrCast(initrd_pages);
46 const initrd_buf = initrd_buf_ptr[0..total_size];
47
48 var start_pos: usize = 0;
49 for (files.items) |file| {
50 const slice = initrd_buf[start_pos .. start_pos + file.size];
51 try luefi.fileReadSlice(file.file, slice);
52 start_pos += file.size;
53 }
54
55 const self = try uefi.pool_allocator.create(Initrd);
56
57 self.* = .{
58 .initrd_buf = initrd_buf,
59 .initrd_pages = pages_count,
60 .handle = undefined,
61 };
62
63 self.handle = try luefi.installProtocols(.{ &device_path, &self.protocol });
64
65 return self;
66}
67
68pub fn deinit(self: *Initrd) void {
69 luefi.uninstallProtocols(self.handle, .{ &device_path, &self.protocol });
70 luefi.freePages(self.initrd_buf, self.initrd_pages);
71 uefi.pool_allocator.destroy(self);
72}
73
74pub const LoadFile2 = extern struct {
75 loadFile: *const @TypeOf(impl) = impl,
76
77 pub fn impl(
78 protocol: *LoadFile2,
79 _: *uefi.protocol.DevicePath,
80 _: bool,
81 buf_size: *usize,
82 buf_out: ?[*]u8,
83 ) callconv(uefi.cc) uefi.Status {
84 const initrd: *Initrd = @fieldParentPtr("protocol", protocol);
85
86 if (buf_out == null or buf_size.* < initrd.initrd_buf.len) {
87 buf_size.* = initrd.initrd_buf.len;
88 return .buffer_too_small;
89 }
90
91 @memcpy(buf_out.?, initrd.initrd_buf);
92
93 return .success;
94 }
95
96 pub const guid: uefi.Guid = .{
97 .time_low = 0x4006c0c1,
98 .time_mid = 0xfcb3,
99 .time_high_and_version = 0x403e,
100 .clock_seq_high_and_reserved = 0x99,
101 .clock_seq_low = 0x6d,
102 .node = [_]u8{ 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d },
103 };
104};
105
106pub var device_path = struct {
107 vendor: uefi.DevicePath.Media.VendorDevicePath,
108 end: uefi.DevicePath.End.EndEntireDevicePath,
109
110 pub const guid = uefi.protocol.DevicePath.guid;
111}{
112 .vendor = uefi.DevicePath.Media.VendorDevicePath{
113 .type = .media,
114 .subtype = .vendor,
115 .length = @sizeOf(uefi.DevicePath.Media.VendorDevicePath),
116 .guid = .{
117 .time_low = 0x5568e427,
118 .time_mid = 0x68fc,
119 .time_high_and_version = 0x4f3d,
120 .clock_seq_high_and_reserved = 0xac,
121 .clock_seq_low = 0x74,
122 .node = [_]u8{ 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 },
123 },
124 },
125 .end = uefi.DevicePath.End.EndEntireDevicePath{
126 .type = .end,
127 .subtype = .end_entire,
128 .length = @sizeOf(uefi.DevicePath.End.EndEntireDevicePath),
129 },
130};