authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-11 13:02:28-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-11 13:02:28-05:00
log64363b10f5fad74c8c6ca279bba632b6dde8482f
tree5487ca931cda98e814fd8b01cbfd89f4c276c024
parentf4b3f1d6022265992f87cea1d9591ffa8ec226d6
parent3d89ff51300454467a39427b938110afe2d1039c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10566 from fifty-six/master

std.os.uefi improvements/fixes

6 files changed, 278 insertions(+), 17 deletions(-)

lib/std/fs/path.zig+29-7
......@@ -14,11 +14,17 @@ const native_os = builtin.target.os.tag;
1414
1515pub const sep_windows = '\\';
1616pub const sep_posix = '/';
17pub const sep = if (native_os == .windows) sep_windows else sep_posix;
17pub const sep = switch (native_os) {
18 .windows, .uefi => sep_windows,
19 else => sep_posix,
20};
1821
1922pub const sep_str_windows = "\\";
2023pub const sep_str_posix = "/";
21pub const sep_str = if (native_os == .windows) sep_str_windows else sep_str_posix;
24pub const sep_str = switch (native_os) {
25 .windows, .uefi => sep_str_windows,
26 else => sep_str_posix,
27};
2228
2329pub const delimiter_windows = ';';
2430pub const delimiter_posix = ':';
......@@ -26,11 +32,11 @@ pub const delimiter = if (native_os == .windows) delimiter_windows else delimite
2632
2733/// Returns if the given byte is a valid path separator
2834pub fn isSep(byte: u8) bool {
29 if (native_os == .windows) {
30 return byte == '/' or byte == '\\';
31 } else {
32 return byte == '/';
33 }
35 return switch (native_os) {
36 .windows => byte == '/' or byte == '\\',
37 .uefi => byte == '\\',
38 else => byte == '/',
39 };
3440}
3541
3642/// This is different from mem.join in that the separator will not be repeated if
......@@ -110,6 +116,17 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 {
110116 return out[0 .. out.len - 1 :0];
111117}
112118
119fn testJoinMaybeZUefi(paths: []const []const u8, expected: []const u8, zero: bool) !void {
120 const uefiIsSep = struct {
121 fn isSep(byte: u8) bool {
122 return byte == '\\';
123 }
124 }.isSep;
125 const actual = try joinSepMaybeZ(testing.allocator, sep_windows, uefiIsSep, paths, zero);
126 defer testing.allocator.free(actual);
127 try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual);
128}
129
113130fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: bool) !void {
114131 const windowsIsSep = struct {
115132 fn isSep(byte: u8) bool {
......@@ -158,6 +175,11 @@ test "join" {
158175 zero,
159176 );
160177
178 try testJoinMaybeZUefi(&[_][]const u8{ "EFI", "Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
179 try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
180 try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
181 try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot\\", "\\bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
182
161183 try testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c", zero);
162184 try testJoinMaybeZWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c", zero);
163185
lib/std/os/uefi.zig+7
......@@ -7,6 +7,13 @@ pub const protocols = @import("uefi/protocols.zig");
77pub const Status = @import("uefi/status.zig").Status;
88pub const tables = @import("uefi/tables.zig");
99
10/// The memory type to allocate when using the pool
11/// Defaults to .LoaderData, the default data allocation type
12/// used by UEFI applications to allocate pool memory.
13pub var efi_pool_memory_type: tables.MemoryType = .LoaderData;
14pub const pool_allocator = @import("uefi/pool_allocator.zig").pool_allocator;
15pub const raw_pool_allocator = @import("uefi/pool_allocator.zig").raw_pool_allocator;
16
1017/// The EFI image's handle that is passed to its entry point.
1118pub var handle: Handle = undefined;
1219
lib/std/os/uefi/pool_allocator.zig created+153
......@@ -0,0 +1,153 @@
1const std = @import("std");
2
3const mem = std.mem;
4const uefi = std.os.uefi;
5
6const assert = std.debug.assert;
7
8const Allocator = mem.Allocator;
9
10const UefiPoolAllocator = struct {
11 fn getHeader(ptr: [*]u8) *[*]align(8) u8 {
12 return @intToPtr(*[*]align(8) u8, @ptrToInt(ptr) - @sizeOf(usize));
13 }
14
15 fn alignedAlloc(len: usize, alignment: usize) ?[*]u8 {
16 var unaligned_ptr: [*]align(8) u8 = undefined;
17
18 if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, len, &unaligned_ptr) != .Success)
19 return null;
20
21 const unaligned_addr = @ptrToInt(unaligned_ptr);
22 const aligned_addr = mem.alignForward(unaligned_addr + @sizeOf(usize), alignment);
23
24 var aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr);
25 getHeader(aligned_ptr).* = unaligned_ptr;
26
27 return aligned_ptr;
28 }
29
30 fn alignedFree(ptr: [*]u8) void {
31 _ = uefi.system_table.boot_services.?.freePool(getHeader(ptr).*);
32 }
33
34 fn alloc(
35 _: *anyopaque,
36 len: usize,
37 ptr_align: u29,
38 len_align: u29,
39 ret_addr: usize,
40 ) Allocator.Error![]u8 {
41 _ = ret_addr;
42
43 assert(len > 0);
44 assert(std.math.isPowerOfTwo(ptr_align));
45
46 var ptr = alignedAlloc(len, ptr_align) orelse return error.OutOfMemory;
47
48 if (len_align == 0)
49 return ptr[0..len];
50
51 return ptr[0..mem.alignBackwardAnyAlign(len, len_align)];
52 }
53
54 fn resize(
55 _: *anyopaque,
56 buf: []u8,
57 buf_align: u29,
58 new_len: usize,
59 len_align: u29,
60 ret_addr: usize,
61 ) ?usize {
62 _ = buf_align;
63 _ = ret_addr;
64
65 return if (new_len <= buf.len) mem.alignAllocLen(buf.len, new_len, len_align) else null;
66 }
67
68 fn free(
69 _: *anyopaque,
70 buf: []u8,
71 buf_align: u29,
72 ret_addr: usize,
73 ) void {
74 _ = buf_align;
75 _ = ret_addr;
76 alignedFree(buf.ptr);
77 }
78};
79
80/// Supports the full Allocator interface, including alignment.
81/// For a direct call of `allocatePool`, see `raw_pool_allocator`.
82pub const pool_allocator = Allocator{
83 .ptr = undefined,
84 .vtable = &pool_allocator_vtable,
85};
86
87const pool_allocator_vtable = Allocator.VTable{
88 .alloc = UefiPoolAllocator.alloc,
89 .resize = UefiPoolAllocator.resize,
90 .free = UefiPoolAllocator.free,
91};
92
93/// Asserts allocations are 8 byte aligned and calls `boot_services.allocatePool`.
94pub const raw_pool_allocator = Allocator{
95 .ptr = undefined,
96 .vtable = &raw_pool_allocator_table,
97};
98
99const raw_pool_allocator_table = Allocator.VTable{
100 .alloc = uefi_alloc,
101 .resize = uefi_resize,
102 .free = uefi_free,
103};
104
105fn uefi_alloc(
106 _: *anyopaque,
107 len: usize,
108 ptr_align: u29,
109 len_align: u29,
110 ret_addr: usize,
111) Allocator.Error![]u8 {
112 _ = len_align;
113 _ = ret_addr;
114
115 std.debug.assert(ptr_align <= 8);
116
117 var ptr: [*]align(8) u8 = undefined;
118
119 if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, len, &ptr) != .Success) {
120 return error.OutOfMemory;
121 }
122
123 return ptr[0..len];
124}
125
126fn uefi_resize(
127 _: *anyopaque,
128 buf: []u8,
129 old_align: u29,
130 new_len: usize,
131 len_align: u29,
132 ret_addr: usize,
133) ?usize {
134 _ = old_align;
135 _ = ret_addr;
136
137 if (new_len <= buf.len) {
138 return mem.alignAllocLen(buf.len, new_len, len_align);
139 }
140
141 return null;
142}
143
144fn uefi_free(
145 _: *anyopaque,
146 buf: []u8,
147 buf_align: u29,
148 ret_addr: usize,
149) void {
150 _ = buf_align;
151 _ = ret_addr;
152 _ = uefi.system_table.boot_services.?.freePool(@alignCast(8, buf.ptr));
153}
lib/std/os/uefi/protocols.zig+1
......@@ -14,6 +14,7 @@ pub const MessagingDevicePath = @import("protocols/device_path_protocol.zig").Me
1414pub const SimpleFileSystemProtocol = @import("protocols/simple_file_system_protocol.zig").SimpleFileSystemProtocol;
1515pub const FileProtocol = @import("protocols/file_protocol.zig").FileProtocol;
1616pub const FileInfo = @import("protocols/file_protocol.zig").FileInfo;
17pub const FileSystemInfo = @import("protocols/file_protocol.zig").FileSystemInfo;
1718
1819pub const InputKey = @import("protocols/simple_text_input_ex_protocol.zig").InputKey;
1920pub const KeyData = @import("protocols/simple_text_input_ex_protocol.zig").KeyData;
lib/std/os/uefi/protocols/device_path_protocol.zig+57-1
......@@ -1,4 +1,7 @@
1const uefi = @import("std").os.uefi;
1const std = @import("std");
2const mem = std.mem;
3const uefi = std.os.uefi;
4const Allocator = mem.Allocator;
25const Guid = uefi.Guid;
36
47pub const DevicePathProtocol = packed struct {
......@@ -15,6 +18,59 @@ pub const DevicePathProtocol = packed struct {
1518 .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b },
1619 };
1720
21 /// Returns the next DevicePathProtocol node in the sequence, if any.
22 pub fn next(self: *DevicePathProtocol) ?*DevicePathProtocol {
23 if (self.type == .End and @intToEnum(EndDevicePath.Subtype, self.subtype) == .EndEntire)
24 return null;
25
26 return @ptrCast(*DevicePathProtocol, @ptrCast([*]u8, self) + self.length);
27 }
28
29 /// Calculates the total length of the device path structure in bytes, including the end of device path node.
30 pub fn size(self: *DevicePathProtocol) usize {
31 var node = self;
32
33 while (node.next()) |next_node| {
34 node = next_node;
35 }
36
37 return (@ptrToInt(node) + node.length) - @ptrToInt(self);
38 }
39
40 /// Creates a file device path from the existing device path and a file path.
41 pub fn create_file_device_path(self: *DevicePathProtocol, allocator: Allocator, path: [:0]const u16) !*DevicePathProtocol {
42 var path_size = self.size();
43
44 // 2 * (path.len + 1) for the path and its null terminator, which are u16s
45 // DevicePathProtocol for the extra node before the end
46 var buf = try allocator.alloc(u8, path_size + 2 * (path.len + 1) + @sizeOf(DevicePathProtocol));
47
48 mem.copy(u8, buf, @ptrCast([*]const u8, self)[0..path_size]);
49
50 // Pointer to the copy of the end node of the current chain, which is - 4 from the buffer
51 // as the end node itself is 4 bytes (type: u8 + subtype: u8 + length: u16).
52 var new = @ptrCast(*MediaDevicePath.FilePathDevicePath, buf.ptr + path_size - 4);
53
54 new.type = .Media;
55 new.subtype = .FilePath;
56 new.length = @sizeOf(MediaDevicePath.FilePathDevicePath) + 2 * (@intCast(u16, path.len) + 1);
57
58 // The same as new.getPath(), but not const as we're filling it in.
59 var ptr = @ptrCast([*:0]u16, @alignCast(2, @ptrCast([*]u8, new)) + @sizeOf(MediaDevicePath.FilePathDevicePath));
60
61 for (path) |s, i|
62 ptr[i] = s;
63
64 ptr[path.len] = 0;
65
66 var end = @ptrCast(*EndDevicePath.EndEntireDevicePath, @ptrCast(*DevicePathProtocol, new).next().?);
67 end.type = .End;
68 end.subtype = .EndEntire;
69 end.length = @sizeOf(EndDevicePath.EndEntireDevicePath);
70
71 return @ptrCast(*DevicePathProtocol, buf.ptr);
72 }
73
1874 pub fn getDevicePath(self: *const DevicePathProtocol) ?DevicePath {
1975 return switch (self.type) {
2076 .Hardware => blk: {
lib/std/os/uefi/protocols/file_protocol.zig+31-9
......@@ -127,15 +127,6 @@ pub const FileProtocol = extern struct {
127127 return self._flush(self);
128128 }
129129
130 pub const guid align(8) = Guid{
131 .time_low = 0x09576e92,
132 .time_mid = 0x6d3f,
133 .time_high_and_version = 0x11d2,
134 .clock_seq_high_and_reserved = 0x8e,
135 .clock_seq_low = 0x39,
136 .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b },
137 };
138
139130 pub const efi_file_mode_read: u64 = 0x0000000000000001;
140131 pub const efi_file_mode_write: u64 = 0x0000000000000002;
141132 pub const efi_file_mode_create: u64 = 0x8000000000000000;
......@@ -171,4 +162,35 @@ pub const FileInfo = extern struct {
171162 pub const efi_file_directory: u64 = 0x0000000000000010;
172163 pub const efi_file_archive: u64 = 0x0000000000000020;
173164 pub const efi_file_valid_attr: u64 = 0x0000000000000037;
165
166 pub const guid align(8) = Guid{
167 .time_low = 0x09576e92,
168 .time_mid = 0x6d3f,
169 .time_high_and_version = 0x11d2,
170 .clock_seq_high_and_reserved = 0x8e,
171 .clock_seq_low = 0x39,
172 .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b },
173 };
174};
175
176pub const FileSystemInfo = extern struct {
177 size: u64,
178 read_only: bool,
179 volume_size: u64,
180 free_space: u64,
181 block_size: u32,
182 _volume_label: u16,
183
184 pub fn getVolumeLabel(self: *const FileSystemInfo) [*:0]const u16 {
185 return @ptrCast([*:0]const u16, &self._volume_label);
186 }
187
188 pub const guid align(8) = Guid{
189 .time_low = 0x09576e93,
190 .time_mid = 0x6d3f,
191 .time_high_and_version = 0x11d2,
192 .clock_seq_high_and_reserved = 0x8e,
193 .clock_seq_low = 0x39,
194 .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b },
195 };
174196};