| author | |
| committer | |
| log | 64363b10f5fad74c8c6ca279bba632b6dde8482f |
| tree | 5487ca931cda98e814fd8b01cbfd89f4c276c024 |
| parent | f4b3f1d6022265992f87cea1d9591ffa8ec226d6 |
| parent | 3d89ff51300454467a39427b938110afe2d1039c |
| signature |
std.os.uefi improvements/fixes6 files changed, 278 insertions(+), 17 deletions(-)
lib/std/fs/path.zig+29-7| ... | @@ -14,11 +14,17 @@ const native_os = builtin.target.os.tag; | ... | @@ -14,11 +14,17 @@ const native_os = builtin.target.os.tag; |
| 14 | 14 | ||
| 15 | pub const sep_windows = '\\'; | 15 | pub const sep_windows = '\\'; |
| 16 | pub const sep_posix = '/'; | 16 | pub const sep_posix = '/'; |
| 17 | pub const sep = if (native_os == .windows) sep_windows else sep_posix; | 17 | pub const sep = switch (native_os) { |
| 18 | .windows, .uefi => sep_windows, | ||
| 19 | else => sep_posix, | ||
| 20 | }; | ||
| 18 | 21 | ||
| 19 | pub const sep_str_windows = "\\"; | 22 | pub const sep_str_windows = "\\"; |
| 20 | pub const sep_str_posix = "/"; | 23 | pub const sep_str_posix = "/"; |
| 21 | pub const sep_str = if (native_os == .windows) sep_str_windows else sep_str_posix; | 24 | pub const sep_str = switch (native_os) { |
| 25 | .windows, .uefi => sep_str_windows, | ||
| 26 | else => sep_str_posix, | ||
| 27 | }; | ||
| 22 | 28 | ||
| 23 | pub const delimiter_windows = ';'; | 29 | pub const delimiter_windows = ';'; |
| 24 | pub const delimiter_posix = ':'; | 30 | pub const delimiter_posix = ':'; |
| ... | @@ -26,11 +32,11 @@ pub const delimiter = if (native_os == .windows) delimiter_windows else delimite | ... | @@ -26,11 +32,11 @@ pub const delimiter = if (native_os == .windows) delimiter_windows else delimite |
| 26 | 32 | ||
| 27 | /// Returns if the given byte is a valid path separator | 33 | /// Returns if the given byte is a valid path separator |
| 28 | pub fn isSep(byte: u8) bool { | 34 | pub fn isSep(byte: u8) bool { |
| 29 | if (native_os == .windows) { | 35 | return switch (native_os) { |
| 30 | return byte == '/' or byte == '\\'; | 36 | .windows => byte == '/' or byte == '\\', |
| 31 | } else { | 37 | .uefi => byte == '\\', |
| 32 | return byte == '/'; | 38 | else => byte == '/', |
| 33 | } | 39 | }; |
| 34 | } | 40 | } |
| 35 | 41 | ||
| 36 | /// This is different from mem.join in that the separator will not be repeated if | 42 | /// 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 { | ... | @@ -110,6 +116,17 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 { |
| 110 | return out[0 .. out.len - 1 :0]; | 116 | return out[0 .. out.len - 1 :0]; |
| 111 | } | 117 | } |
| 112 | 118 | ||
| 119 | fn 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 | |||
| 113 | fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: bool) !void { | 130 | fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: bool) !void { |
| 114 | const windowsIsSep = struct { | 131 | const windowsIsSep = struct { |
| 115 | fn isSep(byte: u8) bool { | 132 | fn isSep(byte: u8) bool { |
| ... | @@ -158,6 +175,11 @@ test "join" { | ... | @@ -158,6 +175,11 @@ test "join" { |
| 158 | zero, | 175 | zero, |
| 159 | ); | 176 | ); |
| 160 | 177 | ||
| 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 | |||
| 161 | try testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c", zero); | 183 | try testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c", zero); |
| 162 | try testJoinMaybeZWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c", zero); | 184 | try testJoinMaybeZWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c", zero); |
| 163 | 185 |
lib/std/os/uefi.zig+7| ... | @@ -7,6 +7,13 @@ pub const protocols = @import("uefi/protocols.zig"); | ... | @@ -7,6 +7,13 @@ pub const protocols = @import("uefi/protocols.zig"); |
| 7 | pub const Status = @import("uefi/status.zig").Status; | 7 | pub const Status = @import("uefi/status.zig").Status; |
| 8 | pub const tables = @import("uefi/tables.zig"); | 8 | pub const tables = @import("uefi/tables.zig"); |
| 9 | 9 | ||
| 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. | ||
| 13 | pub var efi_pool_memory_type: tables.MemoryType = .LoaderData; | ||
| 14 | pub const pool_allocator = @import("uefi/pool_allocator.zig").pool_allocator; | ||
| 15 | pub const raw_pool_allocator = @import("uefi/pool_allocator.zig").raw_pool_allocator; | ||
| 16 | |||
| 10 | /// The EFI image's handle that is passed to its entry point. | 17 | /// The EFI image's handle that is passed to its entry point. |
| 11 | pub var handle: Handle = undefined; | 18 | pub var handle: Handle = undefined; |
| 12 | 19 |
lib/std/os/uefi/pool_allocator.zig created+153| ... | @@ -0,0 +1,153 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const mem = std.mem; | ||
| 4 | const uefi = std.os.uefi; | ||
| 5 | |||
| 6 | const assert = std.debug.assert; | ||
| 7 | |||
| 8 | const Allocator = mem.Allocator; | ||
| 9 | |||
| 10 | const 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`. | ||
| 82 | pub const pool_allocator = Allocator{ | ||
| 83 | .ptr = undefined, | ||
| 84 | .vtable = &pool_allocator_vtable, | ||
| 85 | }; | ||
| 86 | |||
| 87 | const 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`. | ||
| 94 | pub const raw_pool_allocator = Allocator{ | ||
| 95 | .ptr = undefined, | ||
| 96 | .vtable = &raw_pool_allocator_table, | ||
| 97 | }; | ||
| 98 | |||
| 99 | const raw_pool_allocator_table = Allocator.VTable{ | ||
| 100 | .alloc = uefi_alloc, | ||
| 101 | .resize = uefi_resize, | ||
| 102 | .free = uefi_free, | ||
| 103 | }; | ||
| 104 | |||
| 105 | fn 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 | |||
| 126 | fn 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 | |||
| 144 | fn 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 | ... | @@ -14,6 +14,7 @@ pub const MessagingDevicePath = @import("protocols/device_path_protocol.zig").Me |
| 14 | pub const SimpleFileSystemProtocol = @import("protocols/simple_file_system_protocol.zig").SimpleFileSystemProtocol; | 14 | pub const SimpleFileSystemProtocol = @import("protocols/simple_file_system_protocol.zig").SimpleFileSystemProtocol; |
| 15 | pub const FileProtocol = @import("protocols/file_protocol.zig").FileProtocol; | 15 | pub const FileProtocol = @import("protocols/file_protocol.zig").FileProtocol; |
| 16 | pub const FileInfo = @import("protocols/file_protocol.zig").FileInfo; | 16 | pub const FileInfo = @import("protocols/file_protocol.zig").FileInfo; |
| 17 | pub const FileSystemInfo = @import("protocols/file_protocol.zig").FileSystemInfo; | ||
| 17 | 18 | ||
| 18 | pub const InputKey = @import("protocols/simple_text_input_ex_protocol.zig").InputKey; | 19 | pub const InputKey = @import("protocols/simple_text_input_ex_protocol.zig").InputKey; |
| 19 | pub const KeyData = @import("protocols/simple_text_input_ex_protocol.zig").KeyData; | 20 | pub 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 @@ | ... | @@ -1,4 +1,7 @@ |
| 1 | const uefi = @import("std").os.uefi; | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | ||
| 3 | const uefi = std.os.uefi; | ||
| 4 | const Allocator = mem.Allocator; | ||
| 2 | const Guid = uefi.Guid; | 5 | const Guid = uefi.Guid; |
| 3 | 6 | ||
| 4 | pub const DevicePathProtocol = packed struct { | 7 | pub const DevicePathProtocol = packed struct { |
| ... | @@ -15,6 +18,59 @@ pub const DevicePathProtocol = packed struct { | ... | @@ -15,6 +18,59 @@ pub const DevicePathProtocol = packed struct { |
| 15 | .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, | 18 | .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, |
| 16 | }; | 19 | }; |
| 17 | 20 | ||
| 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 | |||
| 18 | pub fn getDevicePath(self: *const DevicePathProtocol) ?DevicePath { | 74 | pub fn getDevicePath(self: *const DevicePathProtocol) ?DevicePath { |
| 19 | return switch (self.type) { | 75 | return switch (self.type) { |
| 20 | .Hardware => blk: { | 76 | .Hardware => blk: { |
lib/std/os/uefi/protocols/file_protocol.zig+31-9| ... | @@ -127,15 +127,6 @@ pub const FileProtocol = extern struct { | ... | @@ -127,15 +127,6 @@ pub const FileProtocol = extern struct { |
| 127 | return self._flush(self); | 127 | return self._flush(self); |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 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 | |||
| 139 | pub const efi_file_mode_read: u64 = 0x0000000000000001; | 130 | pub const efi_file_mode_read: u64 = 0x0000000000000001; |
| 140 | pub const efi_file_mode_write: u64 = 0x0000000000000002; | 131 | pub const efi_file_mode_write: u64 = 0x0000000000000002; |
| 141 | pub const efi_file_mode_create: u64 = 0x8000000000000000; | 132 | pub const efi_file_mode_create: u64 = 0x8000000000000000; |
| ... | @@ -171,4 +162,35 @@ pub const FileInfo = extern struct { | ... | @@ -171,4 +162,35 @@ pub const FileInfo = extern struct { |
| 171 | pub const efi_file_directory: u64 = 0x0000000000000010; | 162 | pub const efi_file_directory: u64 = 0x0000000000000010; |
| 172 | pub const efi_file_archive: u64 = 0x0000000000000020; | 163 | pub const efi_file_archive: u64 = 0x0000000000000020; |
| 173 | pub const efi_file_valid_attr: u64 = 0x0000000000000037; | 164 | 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 | |||
| 176 | pub 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 | }; | ||
| 174 | }; | 196 | }; |