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