diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index fd2111da71..fdde73142b 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -58,7 +58,7 @@ pub fn sleep(nanoseconds: u64) void { const boot_services = std.os.uefi.system_table.boot_services.?; const us_from_ns = nanoseconds / std.time.ns_per_us; const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize); - _ = boot_services.stall(us); + boot_services.stall(us) catch unreachable; return; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index dae233efda..0950441f73 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -654,9 +654,8 @@ pub fn defaultPanic( if (uefi.system_table.boot_services) |bs| { // ExitData buffer must be allocated using boot_services.allocatePool (spec: page 220) - const exit_data: []u16 = uefi.raw_pool_allocator.alloc(u16, exit_msg.len + 1) catch @trap(); - @memcpy(exit_data, exit_msg[0..exit_data.len]); // Includes null terminator. - _ = bs.exit(uefi.handle, .aborted, exit_data.len, exit_data.ptr); + const exit_data = uefi.raw_pool_allocator.dupeZ(u16, exit_msg) catch @trap(); + bs.exit(uefi.handle, .aborted, exit_data) catch {}; } @trap(); }, diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 6526f590fa..53aa884d4a 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -24,9 +24,51 @@ pub var handle: Handle = undefined; /// A pointer to the EFI System Table that is passed to the EFI image's entry point. pub var system_table: *tables.SystemTable = undefined; +/// UEFI's memory interfaces exclusively act on 4096-byte pages. +pub const Page = [4096]u8; + /// A handle to an event structure. pub const Event = *opaque {}; +pub const EventRegistration = *const opaque {}; + +pub const EventType = packed struct(u32) { + lo_context: u8 = 0, + /// If an event of this type is not already in the signaled state, then + /// the event’s NotificationFunction will be queued at the event’s NotifyTpl + /// whenever the event is being waited on via EFI_BOOT_SERVICES.WaitForEvent() + /// or EFI_BOOT_SERVICES.CheckEvent() . + wait: bool = false, + /// The event’s NotifyFunction is queued whenever the event is signaled. + signal: bool = false, + hi_context: u20 = 0, + /// The event is allocated from runtime memory. If an event is to be signaled + /// after the call to EFI_BOOT_SERVICES.ExitBootServices() the event’s data + /// structure and notification function need to be allocated from runtime + /// memory. + runtime: bool = false, + timer: bool = false, + + /// This event should not be combined with any other event types. This event + /// type is functionally equivalent to the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES + /// event group. + pub const signal_exit_boot_services: EventType = .{ + .signal = true, + .lo_context = 1, + }; + + /// The event is to be notified by the system when SetVirtualAddressMap() + /// is performed. This event type is a composite of EVT_NOTIFY_SIGNAL, + /// EVT_RUNTIME, and EVT_RUNTIME_CONTEXT and should not be combined with + /// any other event types. + pub const signal_virtual_address_change: EventType = .{ + .runtime = true, + .hi_context = 0x20000, + .signal = true, + .lo_context = 2, + }; +}; + /// The calling convention used for all external functions part of the UEFI API. pub const cc: std.builtin.CallingConvention = switch (@import("builtin").target.cpu.arch) { .x86_64 => .{ .x86_64_win = .{} }, @@ -52,7 +94,11 @@ pub const IpAddress = extern union { /// GUIDs are align(8) unless otherwise specified. pub const Guid = extern struct { - time_low: u32, + comptime { + std.debug.assert(std.mem.Alignment.of(Guid) == .@"8"); + } + + time_low: u32 align(8), time_mid: u16, time_high_and_version: u16, clock_seq_high_and_reserved: u8, @@ -60,7 +106,7 @@ pub const Guid = extern struct { node: [6]u8, /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format - pub fn format(self: @This(), writer: *std.io.Writer) std.io.Writer.Error!void { + pub fn format(self: Guid, writer: *std.io.Writer) std.io.Writer.Error!void { const time_low = @byteSwap(self.time_low); const time_mid = @byteSwap(self.time_mid); const time_high_and_version = @byteSwap(self.time_high_and_version); @@ -75,7 +121,7 @@ pub const Guid = extern struct { }); } - pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool { + pub fn eql(a: Guid, b: Guid) bool { return a.time_low == b.time_low and a.time_mid == b.time_mid and a.time_high_and_version == b.time_high_and_version and diff --git a/lib/std/os/uefi/pool_allocator.zig b/lib/std/os/uefi/pool_allocator.zig index 0508ad0312..5acdcf6af4 100644 --- a/lib/std/os/uefi/pool_allocator.zig +++ b/lib/std/os/uefi/pool_allocator.zig @@ -28,14 +28,16 @@ const UefiPoolAllocator = struct { const full_len = metadata_len + len; - var unaligned_ptr: [*]align(8) u8 = undefined; - if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, full_len, &unaligned_ptr) != .success) return null; + const unaligned_slice = uefi.system_table.boot_services.?.allocatePool( + uefi.efi_pool_memory_type, + full_len, + ) catch return null; - const unaligned_addr = @intFromPtr(unaligned_ptr); + const unaligned_addr = @intFromPtr(unaligned_slice.ptr); const aligned_addr = mem.alignForward(usize, unaligned_addr + @sizeOf(usize), ptr_align); - const aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr); - getHeader(aligned_ptr).* = unaligned_ptr; + const aligned_ptr = unaligned_slice.ptr + (aligned_addr - unaligned_addr); + getHeader(aligned_ptr).* = unaligned_slice.ptr; return aligned_ptr; } @@ -76,7 +78,7 @@ const UefiPoolAllocator = struct { ) void { _ = alignment; _ = ret_addr; - _ = uefi.system_table.boot_services.?.freePool(getHeader(buf.ptr).*); + uefi.system_table.boot_services.?.freePool(getHeader(buf.ptr).*) catch unreachable; } }; @@ -117,10 +119,12 @@ fn uefi_alloc( std.debug.assert(@intFromEnum(alignment) <= 3); - var ptr: [*]align(8) u8 = undefined; - if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, len, &ptr) != .success) return null; + const slice = uefi.system_table.boot_services.?.allocatePool( + uefi.efi_pool_memory_type, + len, + ) catch return null; - return ptr; + return slice.ptr; } fn uefi_resize( @@ -161,5 +165,5 @@ fn uefi_free( ) void { _ = alignment; _ = ret_addr; - _ = uefi.system_table.boot_services.?.freePool(@alignCast(buf.ptr)); + uefi.system_table.boot_services.?.freePool(@alignCast(buf.ptr)) catch unreachable; } diff --git a/lib/std/os/uefi/tables.zig b/lib/std/os/uefi/tables.zig index 0e0a1e0aa8..f8541dc321 100644 --- a/lib/std/os/uefi/tables.zig +++ b/lib/std/os/uefi/tables.zig @@ -1,3 +1,12 @@ +const std = @import("std"); +const uefi = std.os.uefi; +const Handle = uefi.Handle; +const Event = uefi.Event; +const Guid = uefi.Guid; +const cc = uefi.cc; +const math = std.math; +const assert = std.debug.assert; + pub const BootServices = @import("tables/boot_services.zig").BootServices; pub const RuntimeServices = @import("tables/runtime_services.zig").RuntimeServices; pub const ConfigurationTable = @import("tables/configuration_table.zig").ConfigurationTable; @@ -7,29 +16,90 @@ pub const TableHeader = @import("tables/table_header.zig").TableHeader; pub const EventNotify = *const fn (event: Event, ctx: *anyopaque) callconv(cc) void; pub const TimerDelay = enum(u32) { - timer_cancel, - timer_periodic, - timer_relative, + cancel, + periodic, + relative, }; pub const MemoryType = enum(u32) { + pub const Oem = math.IntFittingRange( + 0, + @intFromEnum(MemoryType.oem_end) - @intFromEnum(MemoryType.oem_start), + ); + pub const Vendor = math.IntFittingRange( + 0, + @intFromEnum(MemoryType.vendor_end) - @intFromEnum(MemoryType.vendor_start), + ); + + /// can only be allocated using .allocate_any_pages mode unless you are explicitly targeting an interface that states otherwise reserved_memory_type, loader_code, loader_data, boot_services_code, boot_services_data, + /// can only be allocated using .allocate_any_pages mode unless you are explicitly targeting an interface that states otherwise runtime_services_code, + /// can only be allocated using .allocate_any_pages mode unless you are explicitly targeting an interface that states otherwise runtime_services_data, conventional_memory, unusable_memory, + /// can only be allocated using .allocate_any_pages mode unless you are explicitly targeting an interface that states otherwise acpi_reclaim_memory, + /// can only be allocated using .allocate_any_pages mode unless you are explicitly targeting an interface that states otherwise acpi_memory_nvs, memory_mapped_io, memory_mapped_io_port_space, pal_code, persistent_memory, + unaccepted_memory, max_memory_type, + invalid_start, + invalid_end = 0x6FFFFFFF, + /// MemoryType values in the range 0x70000000..0x7FFFFFFF are reserved for OEM use. + oem_start = 0x70000000, + oem_end = 0x7FFFFFFF, + /// MemoryType values in the range 0x80000000..0xFFFFFFFF are reserved for use by UEFI + /// OS loaders that are provided by operating system vendors. + vendor_start = 0x80000000, + vendor_end = 0xFFFFFFFF, _, + + pub fn fromOem(value: Oem) MemoryType { + const oem_start = @intFromEnum(MemoryType.oem_start); + return @enumFromInt(oem_start + value); + } + + pub fn toOem(memtype: MemoryType) ?Oem { + const as_int = @intFromEnum(memtype); + const oem_start = @intFromEnum(MemoryType.oem_start); + if (as_int < oem_start) return null; + if (as_int > @intFromEnum(MemoryType.oem_end)) return null; + return @truncate(as_int - oem_start); + } + + pub fn fromVendor(value: Vendor) MemoryType { + const vendor_start = @intFromEnum(MemoryType.vendor_start); + return @enumFromInt(vendor_start + value); + } + + pub fn toVendor(memtype: MemoryType) ?Vendor { + const as_int = @intFromEnum(memtype); + const vendor_start = @intFromEnum(MemoryType.vendor_start); + if (as_int < @intFromEnum(MemoryType.vendor_end)) return null; + if (as_int > @intFromEnum(MemoryType.vendor_end)) return null; + return @truncate(as_int - vendor_start); + } + + pub fn format(self: MemoryType, w: *std.io.Writer) std.io.WriteError!void { + if (self.toOem()) |oemval| + try w.print("OEM({X})", .{oemval}) + else if (self.toVendor()) |vendorval| + try w.print("Vendor({X})", .{vendorval}) + else if (std.enums.tagName(MemoryType, self)) |name| + try w.print("{s}", .{name}) + else + try w.print("INVALID({X})", .{@intFromEnum(self)}); + } }; pub const MemoryDescriptorAttribute = packed struct(u64) { @@ -51,6 +121,8 @@ pub const MemoryDescriptorAttribute = packed struct(u64) { memory_runtime: bool, }; +pub const MemoryMapKey = enum(usize) { _ }; + pub const MemoryDescriptor = extern struct { type: MemoryType, physical_start: u64, @@ -59,20 +131,121 @@ pub const MemoryDescriptor = extern struct { attribute: MemoryDescriptorAttribute, }; +pub const MemoryMapInfo = struct { + key: MemoryMapKey, + descriptor_size: usize, + descriptor_version: u32, + /// The number of descriptors in the map. + len: usize, +}; + +pub const MemoryMapSlice = struct { + info: MemoryMapInfo, + ptr: [*]align(@alignOf(MemoryDescriptor)) u8, + + pub fn iterator(self: MemoryMapSlice) MemoryDescriptorIterator { + return .{ .ctx = self }; + } + + pub fn get(self: MemoryMapSlice, index: usize) ?*MemoryDescriptor { + if (index >= self.info.len) return null; + return self.getUnchecked(index); + } + + pub fn getUnchecked(self: MemoryMapSlice, index: usize) *MemoryDescriptor { + const offset: usize = index * self.info.descriptor_size; + return @alignCast(@ptrCast(self.ptr[offset..])); + } +}; + +pub const MemoryDescriptorIterator = struct { + ctx: MemoryMapSlice, + index: usize = 0, + + pub fn next(self: *MemoryDescriptorIterator) ?*MemoryDescriptor { + const md = self.ctx.get(self.index) orelse return null; + self.index += 1; + return md; + } +}; + pub const LocateSearchType = enum(u32) { all_handles, by_register_notify, by_protocol, }; -pub const OpenProtocolAttributes = packed struct(u32) { - by_handle_protocol: bool = false, - get_protocol: bool = false, - test_protocol: bool = false, - by_child_controller: bool = false, - by_driver: bool = false, - exclusive: bool = false, - reserved: u26 = 0, +pub const LocateSearch = union(LocateSearchType) { + all_handles, + by_register_notify: uefi.EventRegistration, + by_protocol: *const Guid, +}; + +pub const OpenProtocolAttributes = enum(u32) { + pub const Bits = packed struct(u32) { + by_handle_protocol: bool = false, + get_protocol: bool = false, + test_protocol: bool = false, + by_child_controller: bool = false, + by_driver: bool = false, + exclusive: bool = false, + reserved: u26 = 0, + }; + + by_handle_protocol = @bitCast(Bits{ .by_handle_protocol = true }), + get_protocol = @bitCast(Bits{ .get_protocol = true }), + test_protocol = @bitCast(Bits{ .test_protocol = true }), + by_child_controller = @bitCast(Bits{ .by_child_controller = true }), + by_driver = @bitCast(Bits{ .by_driver = true }), + by_driver_exclusive = @bitCast(Bits{ .by_driver = true, .exclusive = true }), + exclusive = @bitCast(Bits{ .exclusive = true }), + _, + + pub fn fromBits(bits: Bits) OpenProtocolAttributes { + return @bitCast(bits); + } + + pub fn toBits(self: OpenProtocolAttributes) Bits { + return @bitCast(self); + } +}; + +pub const OpenProtocolArgs = union(OpenProtocolAttributes) { + /// Used in the implementation of `handleProtocol`. + by_handle_protocol: struct { agent: ?Handle = null, controller: ?Handle = null }, + /// Used by a driver to get a protocol interface from a handle. Care must be + /// taken when using this open mode because the driver that opens a protocol + /// interface in this manner will not be informed if the protocol interface + /// is uninstalled or reinstalled. The caller is also not required to close + /// the protocol interface with `closeProtocol`. + get_protocol: struct { agent: ?Handle = null, controller: ?Handle = null }, + /// Used by a driver to test for the existence of a protocol interface on a + /// handle. The caller only use the return status code. The caller is also + /// not required to close the protocol interface with `closeProtocol`. + test_protocol: struct { agent: ?Handle = null, controller: ?Handle = null }, + /// Used by bus drivers to show that a protocol interface is being used by one + /// of the child controllers of a bus. This information is used by + /// `BootServices.connectController` to recursively connect all child controllers + /// and by `BootServices.disconnectController` to get the list of child + /// controllers that a bus driver created. + by_child_controller: struct { agent: Handle, controller: Handle }, + /// Used by a driver to gain access to a protocol interface. When this mode + /// is used, the driver’s Stop() function will be called by + /// `BootServices.disconnectController` if the protocol interface is reinstalled + /// or uninstalled. Once a protocol interface is opened by a driver with this + /// attribute, no other drivers will be allowed to open the same protocol interface + /// with the `.by_driver` attribute. + by_driver: struct { agent: Handle, controller: Handle }, + /// Used by a driver to gain exclusive access to a protocol interface. If any + /// other drivers have the protocol interface opened with an attribute of + /// `.by_driver`, then an attempt will be made to remove them with + /// `BootServices.disconnectController`. + by_driver_exclusive: struct { agent: Handle, controller: Handle }, + /// Used by applications to gain exclusive access to a protocol interface. If + /// any drivers have the protocol interface opened with an attribute of + /// `.by_driver`, then an attempt will be made to remove them by calling the + /// driver’s Stop() function. + exclusive: struct { agent: Handle, controller: ?Handle = null }, }; pub const ProtocolInformationEntry = extern struct { @@ -83,19 +256,25 @@ pub const ProtocolInformationEntry = extern struct { }; pub const InterfaceType = enum(u32) { - efi_native_interface, + native, +}; + +pub const AllocateLocation = union(AllocateType) { + any, + max_address: [*]align(4096) uefi.Page, + address: [*]align(4096) uefi.Page, }; pub const AllocateType = enum(u32) { - allocate_any_pages, - allocate_max_address, - allocate_address, + any, + max_address, + address, }; pub const PhysicalAddress = u64; pub const CapsuleHeader = extern struct { - capsule_guid: Guid align(8), + capsule_guid: Guid, header_size: u32, flags: u32, capsule_image_size: u32, @@ -110,13 +289,13 @@ pub const UefiCapsuleBlockDescriptor = extern struct { }; pub const ResetType = enum(u32) { - reset_cold, - reset_warm, - reset_shutdown, - reset_platform_specific, + cold, + warm, + shutdown, + platform_specific, }; -pub const global_variable align(8) = Guid{ +pub const global_variable = Guid{ .time_low = 0x8be4df61, .time_mid = 0x93ca, .time_high_and_version = 0x11d2, @@ -128,10 +307,3 @@ pub const global_variable align(8) = Guid{ test { std.testing.refAllDeclsRecursive(@This()); } - -const std = @import("std"); -const uefi = std.os.uefi; -const Handle = uefi.Handle; -const Event = uefi.Event; -const Guid = uefi.Guid; -const cc = uefi.cc; diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index c889ce3ffe..f2a8b73b83 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -1,21 +1,31 @@ const std = @import("std"); const uefi = std.os.uefi; const Event = uefi.Event; +const EventRegistration = uefi.EventRegistration; const Guid = uefi.Guid; const Handle = uefi.Handle; +const Page = uefi.Page; +const Pages = uefi.Pages; const Status = uefi.Status; const TableHeader = uefi.tables.TableHeader; const DevicePathProtocol = uefi.protocol.DevicePath; +const AllocateLocation = uefi.tables.AllocateLocation; const AllocateType = uefi.tables.AllocateType; const MemoryType = uefi.tables.MemoryType; const MemoryDescriptor = uefi.tables.MemoryDescriptor; +const MemoryMapKey = uefi.tables.MemoryMapKey; +const MemoryMapInfo = uefi.tables.MemoryMapInfo; +const MemoryMapSlice = uefi.tables.MemoryMapSlice; const TimerDelay = uefi.tables.TimerDelay; const InterfaceType = uefi.tables.InterfaceType; +const LocateSearch = uefi.tables.LocateSearch; const LocateSearchType = uefi.tables.LocateSearchType; +const OpenProtocolArgs = uefi.tables.OpenProtocolArgs; const OpenProtocolAttributes = uefi.tables.OpenProtocolAttributes; const ProtocolInformationEntry = uefi.tables.ProtocolInformationEntry; const EventNotify = uefi.tables.EventNotify; const cc = uefi.cc; +const Error = Status.Error; /// Boot services are services provided by the system's firmware until the operating system takes /// over control over the hardware by calling exitBootServices. @@ -32,173 +42,1236 @@ pub const BootServices = extern struct { hdr: TableHeader, /// Raises a task's priority level and returns its previous level. - raiseTpl: *const fn (new_tpl: usize) callconv(cc) usize, + raiseTpl: *const fn (new_tpl: TaskPriorityLevel) callconv(cc) TaskPriorityLevel, /// Restores a task's priority level to its previous value. - restoreTpl: *const fn (old_tpl: usize) callconv(cc) void, + restoreTpl: *const fn (old_tpl: TaskPriorityLevel) callconv(cc) void, /// Allocates memory pages from the system. - allocatePages: *const fn (alloc_type: AllocateType, mem_type: MemoryType, pages: usize, memory: *[*]align(4096) u8) callconv(cc) Status, + _allocatePages: *const fn (alloc_type: AllocateType, mem_type: MemoryType, pages: usize, memory: *[*]align(4096) Page) callconv(cc) Status, /// Frees memory pages. - freePages: *const fn (memory: [*]align(4096) u8, pages: usize) callconv(cc) Status, + _freePages: *const fn (memory: [*]align(4096) Page, pages: usize) callconv(cc) Status, /// Returns the current memory map. - getMemoryMap: *const fn (mmap_size: *usize, mmap: ?[*]MemoryDescriptor, map_key: *usize, descriptor_size: *usize, descriptor_version: *u32) callconv(cc) Status, + _getMemoryMap: *const fn (mmap_size: *usize, mmap: ?[*]align(@alignOf(MemoryDescriptor)) u8, map_key: *MemoryMapKey, descriptor_size: *usize, descriptor_version: *u32) callconv(cc) Status, /// Allocates pool memory. - allocatePool: *const fn (pool_type: MemoryType, size: usize, buffer: *[*]align(8) u8) callconv(cc) Status, + _allocatePool: *const fn (pool_type: MemoryType, size: usize, buffer: *[*]align(8) u8) callconv(cc) Status, /// Returns pool memory to the system. - freePool: *const fn (buffer: [*]align(8) u8) callconv(cc) Status, + _freePool: *const fn (buffer: [*]align(8) u8) callconv(cc) Status, /// Creates an event. - createEvent: *const fn (type: u32, notify_tpl: usize, notify_func: ?*const fn (Event, ?*anyopaque) callconv(cc) void, notify_ctx: ?*const anyopaque, event: *Event) callconv(cc) Status, + _createEvent: *const fn (type: u32, notify_tpl: TaskPriorityLevel, notify_func: ?*const fn (Event, ?*anyopaque) callconv(cc) void, notify_ctx: ?*anyopaque, event: *Event) callconv(cc) Status, /// Sets the type of timer and the trigger time for a timer event. - setTimer: *const fn (event: Event, type: TimerDelay, trigger_time: u64) callconv(cc) Status, + _setTimer: *const fn (event: Event, type: TimerDelay, trigger_time: u64) callconv(cc) Status, /// Stops execution until an event is signaled. - waitForEvent: *const fn (event_len: usize, events: [*]const Event, index: *usize) callconv(cc) Status, + _waitForEvent: *const fn (event_len: usize, events: [*]const Event, index: *usize) callconv(cc) Status, /// Signals an event. - signalEvent: *const fn (event: Event) callconv(cc) Status, + _signalEvent: *const fn (event: Event) callconv(cc) Status, /// Closes an event. - closeEvent: *const fn (event: Event) callconv(cc) Status, + _closeEvent: *const fn (event: Event) callconv(cc) Status, /// Checks whether an event is in the signaled state. - checkEvent: *const fn (event: Event) callconv(cc) Status, + _checkEvent: *const fn (event: Event) callconv(cc) Status, /// Installs a protocol interface on a device handle. If the handle does not exist, it is created /// and added to the list of handles in the system. installMultipleProtocolInterfaces() /// performs more error checking than installProtocolInterface(), so its use is recommended over this. - installProtocolInterface: *const fn (handle: Handle, protocol: *align(8) const Guid, interface_type: InterfaceType, interface: *anyopaque) callconv(cc) Status, + _installProtocolInterface: *const fn (handle: Handle, protocol: *const Guid, interface_type: InterfaceType, interface: *anyopaque) callconv(cc) Status, /// Reinstalls a protocol interface on a device handle - reinstallProtocolInterface: *const fn (handle: Handle, protocol: *align(8) const Guid, old_interface: *anyopaque, new_interface: *anyopaque) callconv(cc) Status, + _reinstallProtocolInterface: *const fn (handle: Handle, protocol: *const Guid, old_interface: *anyopaque, new_interface: *anyopaque) callconv(cc) Status, /// Removes a protocol interface from a device handle. Usage of /// uninstallMultipleProtocolInterfaces is recommended over this. - uninstallProtocolInterface: *const fn (handle: Handle, protocol: *align(8) const Guid, interface: *anyopaque) callconv(cc) Status, + _uninstallProtocolInterface: *const fn (handle: Handle, protocol: *const Guid, interface: *anyopaque) callconv(cc) Status, /// Queries a handle to determine if it supports a specified protocol. - handleProtocol: *const fn (handle: Handle, protocol: *align(8) const Guid, interface: *?*anyopaque) callconv(cc) Status, + _handleProtocol: *const fn (handle: Handle, protocol: *const Guid, interface: *?*anyopaque) callconv(cc) Status, - reserved: *anyopaque, + _reserved: *anyopaque, /// Creates an event that is to be signaled whenever an interface is installed for a specified protocol. - registerProtocolNotify: *const fn (protocol: *align(8) const Guid, event: Event, registration: **anyopaque) callconv(cc) Status, + _registerProtocolNotify: *const fn (protocol: *const Guid, event: Event, registration: *EventRegistration) callconv(cc) Status, /// Returns an array of handles that support a specified protocol. - locateHandle: *const fn (search_type: LocateSearchType, protocol: ?*align(8) const Guid, search_key: ?*const anyopaque, buffer_size: *usize, buffer: [*]Handle) callconv(cc) Status, + _locateHandle: *const fn (search_type: LocateSearchType, protocol: ?*const Guid, search_key: ?*const anyopaque, buffer_size: *usize, buffer: ?[*]Handle) callconv(cc) Status, /// Locates the handle to a device on the device path that supports the specified protocol - locateDevicePath: *const fn (protocols: *align(8) const Guid, device_path: **const DevicePathProtocol, device: *?Handle) callconv(cc) Status, + _locateDevicePath: *const fn (protocols: *const Guid, device_path: **const DevicePathProtocol, device: *?Handle) callconv(cc) Status, /// Adds, updates, or removes a configuration table entry from the EFI System Table. - installConfigurationTable: *const fn (guid: *align(8) const Guid, table: ?*anyopaque) callconv(cc) Status, + _installConfigurationTable: *const fn (guid: *const Guid, table: ?*anyopaque) callconv(cc) Status, /// Loads an EFI image into memory. - loadImage: *const fn (boot_policy: bool, parent_image_handle: Handle, device_path: ?*const DevicePathProtocol, source_buffer: ?[*]const u8, source_size: usize, image_handle: *?Handle) callconv(cc) Status, + _loadImage: *const fn (boot_policy: bool, parent_image_handle: Handle, device_path: ?*const DevicePathProtocol, source_buffer: ?[*]const u8, source_size: usize, image_handle: *Handle) callconv(cc) Status, /// Transfers control to a loaded image's entry point. - startImage: *const fn (image_handle: Handle, exit_data_size: ?*usize, exit_data: ?*[*]u16) callconv(cc) Status, + _startImage: *const fn (image_handle: Handle, exit_data_size: ?*usize, exit_data: ?*[*]u16) callconv(cc) Status, /// Terminates a loaded EFI image and returns control to boot services. - exit: *const fn (image_handle: Handle, exit_status: Status, exit_data_size: usize, exit_data: ?*const anyopaque) callconv(cc) Status, + _exit: *const fn (image_handle: Handle, exit_status: Status, exit_data_size: usize, exit_data: ?[*]align(2) const u8) callconv(cc) Status, /// Unloads an image. - unloadImage: *const fn (image_handle: Handle) callconv(cc) Status, + _unloadImage: *const fn (image_handle: Handle) callconv(cc) Status, /// Terminates all boot services. - exitBootServices: *const fn (image_handle: Handle, map_key: usize) callconv(cc) Status, + _exitBootServices: *const fn (image_handle: Handle, map_key: MemoryMapKey) callconv(cc) Status, /// Returns a monotonically increasing count for the platform. - getNextMonotonicCount: *const fn (count: *u64) callconv(cc) Status, + _getNextMonotonicCount: *const fn (count: *u64) callconv(cc) Status, /// Induces a fine-grained stall. - stall: *const fn (microseconds: usize) callconv(cc) Status, + _stall: *const fn (microseconds: usize) callconv(cc) Status, /// Sets the system's watchdog timer. - setWatchdogTimer: *const fn (timeout: usize, watchdog_code: u64, data_size: usize, watchdog_data: ?[*]const u16) callconv(cc) Status, + _setWatchdogTimer: *const fn (timeout: usize, watchdog_code: u64, data_size: usize, watchdog_data: ?[*]const u16) callconv(cc) Status, /// Connects one or more drives to a controller. - connectController: *const fn (controller_handle: Handle, driver_image_handle: ?Handle, remaining_device_path: ?*DevicePathProtocol, recursive: bool) callconv(cc) Status, + _connectController: *const fn (controller_handle: Handle, driver_image_handle: ?[*:null]?Handle, remaining_device_path: ?*const DevicePathProtocol, recursive: bool) callconv(cc) Status, // Disconnects one or more drivers from a controller - disconnectController: *const fn (controller_handle: Handle, driver_image_handle: ?Handle, child_handle: ?Handle) callconv(cc) Status, + _disconnectController: *const fn (controller_handle: Handle, driver_image_handle: ?Handle, child_handle: ?Handle) callconv(cc) Status, /// Queries a handle to determine if it supports a specified protocol. - openProtocol: *const fn (handle: Handle, protocol: *align(8) const Guid, interface: *?*anyopaque, agent_handle: ?Handle, controller_handle: ?Handle, attributes: OpenProtocolAttributes) callconv(cc) Status, + _openProtocol: *const fn (handle: Handle, protocol: *const Guid, interface: ?*?*anyopaque, agent_handle: ?Handle, controller_handle: ?Handle, attributes: OpenProtocolAttributes) callconv(cc) Status, /// Closes a protocol on a handle that was opened using openProtocol(). - closeProtocol: *const fn (handle: Handle, protocol: *align(8) const Guid, agent_handle: Handle, controller_handle: ?Handle) callconv(cc) Status, + _closeProtocol: *const fn (handle: Handle, protocol: *const Guid, agent_handle: Handle, controller_handle: ?Handle) callconv(cc) Status, /// Retrieves the list of agents that currently have a protocol interface opened. - openProtocolInformation: *const fn (handle: Handle, protocol: *align(8) const Guid, entry_buffer: *[*]ProtocolInformationEntry, entry_count: *usize) callconv(cc) Status, + _openProtocolInformation: *const fn (handle: Handle, protocol: *const Guid, entry_buffer: *[*]ProtocolInformationEntry, entry_count: *usize) callconv(cc) Status, /// Retrieves the list of protocol interface GUIDs that are installed on a handle in a buffer allocated from pool. - protocolsPerHandle: *const fn (handle: Handle, protocol_buffer: *[*]*align(8) const Guid, protocol_buffer_count: *usize) callconv(cc) Status, + _protocolsPerHandle: *const fn (handle: Handle, protocol_buffer: *[*]*const Guid, protocol_buffer_count: *usize) callconv(cc) Status, /// Returns an array of handles that support the requested protocol in a buffer allocated from pool. - locateHandleBuffer: *const fn (search_type: LocateSearchType, protocol: ?*align(8) const Guid, search_key: ?*const anyopaque, num_handles: *usize, buffer: *[*]Handle) callconv(cc) Status, + _locateHandleBuffer: *const fn (search_type: LocateSearchType, protocol: ?*const Guid, search_key: ?*const anyopaque, num_handles: *usize, buffer: *[*]Handle) callconv(cc) Status, /// Returns the first protocol instance that matches the given protocol. - locateProtocol: *const fn (protocol: *align(8) const Guid, registration: ?*const anyopaque, interface: *?*anyopaque) callconv(cc) Status, + _locateProtocol: *const fn (protocol: *const Guid, registration: ?EventRegistration, interface: *?*const anyopaque) callconv(cc) Status, /// Installs one or more protocol interfaces into the boot services environment // TODO: use callconv(cc) instead once that works - installMultipleProtocolInterfaces: *const fn (handle: *Handle, ...) callconv(.c) Status, + _installMultipleProtocolInterfaces: *const fn (handle: *Handle, ...) callconv(.c) Status, /// Removes one or more protocol interfaces into the boot services environment // TODO: use callconv(cc) instead once that works - uninstallMultipleProtocolInterfaces: *const fn (handle: *Handle, ...) callconv(.c) Status, + _uninstallMultipleProtocolInterfaces: *const fn (handle: *Handle, ...) callconv(.c) Status, /// Computes and returns a 32-bit CRC for a data buffer. - calculateCrc32: *const fn (data: [*]const u8, data_size: usize, *u32) callconv(cc) Status, + _calculateCrc32: *const fn (data: [*]const u8, data_size: usize, *u32) callconv(cc) Status, /// Copies the contents of one buffer to another buffer - copyMem: *const fn (dest: [*]u8, src: [*]const u8, len: usize) callconv(cc) void, + _copyMem: *const fn (dest: [*]u8, src: [*]const u8, len: usize) callconv(cc) void, /// Fills a buffer with a specified value - setMem: *const fn (buffer: [*]u8, size: usize, value: u8) callconv(cc) void, + _setMem: *const fn (buffer: [*]u8, size: usize, value: u8) callconv(cc) void, /// Creates an event in a group. - createEventEx: *const fn (type: u32, notify_tpl: usize, notify_func: EventNotify, notify_ctx: *const anyopaque, event_group: *align(8) const Guid, event: *Event) callconv(cc) Status, + _createEventEx: *const fn (type: u32, notify_tpl: usize, notify_func: EventNotify, notify_ctx: *const anyopaque, event_group: *const Guid, event: *Event) callconv(cc) Status, + + pub const AllocatePagesError = uefi.UnexpectedError || error{ + OutOfResources, + InvalidParameter, + NotFound, + }; + + pub const FreePagesError = uefi.UnexpectedError || error{ + NotFound, + InvalidParameter, + }; + + pub const GetMemoryMapError = uefi.UnexpectedError || error{ + InvalidParameter, + BufferTooSmall, + }; + + pub const AllocatePoolError = uefi.UnexpectedError || error{ + OutOfResources, + InvalidParameter, + }; + + pub const FreePoolError = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + pub const CreateEventError = uefi.UnexpectedError || error{ + InvalidParameter, + OutOfResources, + }; + + pub const SetTimerError = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + pub const WaitForEventError = uefi.UnexpectedError || error{ + InvalidParameter, + Unsupported, + }; + + pub const CheckEventError = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + pub const ReinstallProtocolInterfaceError = uefi.UnexpectedError || error{ + NotFound, + AccessDenied, + InvalidParameter, + }; + + pub const HandleProtocolError = uefi.UnexpectedError || error{ + Unsupported, + }; + + pub const RegisterProtocolNotifyError = uefi.UnexpectedError || error{ + OutOfResources, + InvalidParameter, + }; + + pub const NumHandlesError = uefi.UnexpectedError || error{ + OutOfResources, + }; + + pub const LocateHandleError = uefi.UnexpectedError || error{ + BufferTooSmall, + InvalidParameter, + }; + + pub const LocateDevicePathError = uefi.UnexpectedError || error{ + NotFound, + InvalidParameter, + }; + + pub const InstallConfigurationTableError = uefi.UnexpectedError || error{ + InvalidParameter, + OutOfResources, + }; + + pub const UninstallConfigurationTableError = InstallConfigurationTableError || error{ + NotFound, + }; + + pub const LoadImageError = uefi.UnexpectedError || error{ + NotFound, + InvalidParameter, + Unsupported, + OutOfResources, + LoadError, + DeviceError, + AccessDenied, + SecurityViolation, + }; + + pub const StartImageError = uefi.UnexpectedError || error{ + InvalidParameter, + SecurityViolation, + }; + + pub const ExitError = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + pub const ExitBootServicesError = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + pub const GetNextMonotonicCountError = uefi.UnexpectedError || error{ + DeviceError, + InvalidParameter, + }; + + pub const SetWatchdogTimerError = uefi.UnexpectedError || error{ + InvalidParameter, + Unsupported, + DeviceError, + }; + + pub const ConnectControllerError = uefi.UnexpectedError || error{ + InvalidParameter, + NotFound, + SecurityViolation, + }; + + pub const DisconnectControllerError = uefi.UnexpectedError || error{ + InvalidParameter, + OutOfResources, + DeviceError, + }; + + pub const OpenProtocolError = uefi.UnexpectedError || error{ + InvalidParameter, + Unsupported, + AccessDenied, + AlreadyStarted, + }; + + pub const CloseProtocolError = uefi.UnexpectedError || error{ + InvalidParameter, + NotFound, + }; + + pub const OpenProtocolInformationError = uefi.UnexpectedError || error{ + OutOfResources, + }; + + pub const ProtocolsPerHandleError = uefi.UnexpectedError || error{ + InvalidParameter, + OutOfResources, + }; + + pub const LocateHandleBufferError = uefi.UnexpectedError || error{ + InvalidParameter, + OutOfResources, + }; + + pub const LocateProtocolError = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + pub const InstallProtocolInterfacesError = uefi.UnexpectedError || error{ + AlreadyStarted, + OutOfResources, + InvalidParameter, + }; + + pub const UninstallProtocolInterfacesError = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + pub const CalculateCrc32Error = uefi.UnexpectedError || error{ + InvalidParameter, + }; + + /// Allocates pages of memory. + /// + /// This function scans the memory map to locate free pages. When it finds a + /// physically contiguous block of pages that is large enough and also satisfies + /// the allocation requirements of `alloc_type`, it changes the memory map to + /// indicate that the pages are now of type `mem_type`. + /// + /// In general, UEFI OS loaders and UEFI applications should allocate memory + /// (and pool) of type `.loader_data`. UEFI boot service drivers must allocate + /// memory (and pool) of type `.boot_services_data`. UREFI runtime drivers + /// should allocate memory (and pool) of type `.runtime_services_data` + /// (although such allocation can only be made during boot services time). + /// + /// Allocation requests of `.allocate_any_pages` allocate any available range + /// of pages that satisfies the request. + /// + /// Allocation requests of `.allocate_max_address` allocate any available range + /// of pages whose uppermost address is less than or equal to the address + /// pointed to by the input. + /// + /// Allocation requests of `.allocate_address` allocate pages at the address + /// pointed to by the input. + pub fn allocatePages( + self: *BootServices, + location: AllocateLocation, + mem_type: MemoryType, + pages: usize, + ) AllocatePagesError![]align(4096) Page { + var ptr: [*]align(4096) Page = switch (location) { + .any => undefined, + .address, .max_address => |ptr| ptr, + }; + + switch (self._allocatePages( + std.meta.activeTag(location), + mem_type, + pages, + &ptr, + )) { + .success => return ptr[0..pages], + .out_of_resources => return error.OutOfResources, + .invalid_parameter => return error.InvalidParameter, + .not_found => return error.NotFound, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn freePages(self: *BootServices, pages: []align(4096) Page) FreePagesError!void { + switch (self._freePages(pages.ptr, pages.len)) { + .success => {}, + .not_found => return error.NotFound, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn getMemoryMapInfo(self: *const BootServices) uefi.UnexpectedError!MemoryMapInfo { + var info: MemoryMapInfo = undefined; + info.len = 0; + + switch (self._getMemoryMap( + &info.len, + null, + &info.key, + &info.descriptor_size, + &info.descriptor_version, + )) { + .success, .buffer_too_small => { + info.len = @divExact(info.len, info.descriptor_size); + return info; + }, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn getMemoryMap( + self: *const BootServices, + buffer: []align(@alignOf(MemoryDescriptor)) u8, + ) GetMemoryMapError!MemoryMapSlice { + var info: MemoryMapInfo = undefined; + info.len = buffer.len; + + switch (self._getMemoryMap( + &info.len, + buffer.ptr, + &info.key, + &info.descriptor_size, + &info.descriptor_version, + )) { + .success => { + info.len = @divExact(info.len, info.descriptor_size); + return .{ .info = info, .ptr = buffer.ptr }; + }, + .buffer_too_small => return error.BufferTooSmall, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Allocates a memory region of `size` bytes from memory of type `pool_type` + /// and returns the allocated memory. Allocates pages from `.conventional_memory` + /// as needed to grow the requested pool type. + pub fn allocatePool( + self: *BootServices, + pool_type: MemoryType, + size: usize, + ) AllocatePoolError![]align(8) u8 { + var ptr: [*]align(8) u8 = undefined; + + switch (self._allocatePool(pool_type, size, &ptr)) { + .success => return ptr[0..size], + .out_of_resources => return error.OutOfResources, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn freePool(self: *BootServices, ptr: [*]align(8) u8) FreePoolError!void { + switch (self._freePool(ptr)) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn createEvent( + self: *BootServices, + event_type: uefi.EventType, + notify_opts: NotifyOpts, + ) CreateEventError!Event { + var evt: Event = undefined; + + switch (self._createEvent( + @bitCast(event_type), + notify_opts.tpl, + notify_opts.function, + notify_opts.context, + &evt, + )) { + .success => return evt, + .invalid_parameter => return error.InvalidParameter, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Cancels any previous time trigger setting for the event, and sets a new + /// trigger timer for the event. + /// + /// Returns `error.InvalidParameter` if the event is not a timer event. + pub fn setTimer( + self: *BootServices, + event: Event, + @"type": TimerDelay, + trigger_time: u64, + ) SetTimerError!void { + switch (self._setTimer(event, @"type", trigger_time)) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Returns the event that was signaled, along with its index in the slice. + pub fn waitForEvent( + self: *BootServices, + events: []const Event, + ) WaitForEventError!struct { *const Event, usize } { + var idx: usize = undefined; + switch (self._waitForEvent(events.len, events.ptr, &idx)) { + .success => return .{ &events[idx], idx }, + .invalid_parameter => return error.InvalidParameter, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// If `event` is `EventType.signal`, then the event’s notification function + /// is scheduled to be invoked at the event’s notification task priority level. + /// This function may be invoked from any task priority level. + /// + /// If the supplied Event is a part of an event group, then all of the events + /// in the event group are also signaled and their notification functions are + /// scheduled. + /// + /// When signaling an event group, it is possible to create an event in the + /// group, signal it and then close the event to remove it from the group. + pub fn signalEvent(self: *BootServices, event: Event) uefi.UnexpectedError!void { + switch (self._signalEvent(event)) { + .success => {}, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn closeEvent(self: *BootServices, event: Event) uefi.UnexpectedError!void { + switch (self._closeEvent(event)) { + .success => {}, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Checks to see whether an event is signaled. + /// + /// The underlying function is equivalent to this pseudo-code: + /// ``` + /// if (event.type.signal) + /// return error.InvalidParameter; + /// + /// if (event.signaled) { + /// event.signaled = false; + /// return true; + /// } + /// + /// const notify = event.notification_function orelse return false; + /// notify(); + /// + /// if (event.signaled) { + /// event.signaled = false; + /// return true; + /// } + /// + /// return false; + /// ``` + pub fn checkEvent(self: *BootServices, event: Event) CheckEventError!bool { + switch (self._checkEvent(event)) { + .success => return true, + .not_ready => return false, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// See `installProtocolInterfaces`. + /// + /// Does not call `self._installProtocolInterface`, because + /// `self._installMultipleProtocolInterfaces` performs more error checks. + pub fn installProtocolInterface( + self: *BootServices, + handle: ?Handle, + interface: anytype, + ) InstallProtocolInterfacesError!Handle { + return self.installProtocolInterfaces(handle, .{ + interface, + }); + } + + /// Reinstalls a protocol interface on a device handle. + /// + /// `new` may be the same as `old`. If it is, the registered protocol notifications + /// occur for the handle without replacing the interface on the handle. + /// + /// Any process that has registered to wait for the installation of the interface + /// is notified. + /// + /// The caller is responsible for ensuring that there are no references to `old` + /// if it is being removed. + pub fn reinstallProtocolInterface( + self: *BootServices, + handle: Handle, + Protocol: type, + old: ?*const Protocol, + new: ?*const Protocol, + ) ReinstallProtocolInterfaceError!void { + if (!@hasDecl(Protocol, "guid")) + @compileError("protocol is missing guid"); + + switch (self._reinstallProtocolInterface( + handle, + &Protocol.guid, + old, + new, + )) { + .success => {}, + .not_found => return error.NotFound, + .access_denied => return error.AccessDenied, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// See `uninstallProtocolInterfaces`. + /// + /// Does not call `self._uninstallProtocolInterface`, because + /// `self._uninstallMultipleProtocolInterfaces` performs more error checks. + pub fn uninstallProtocolInterface( + self: *BootServices, + handle: Handle, + interface: anytype, + ) UninstallProtocolInterfacesError!void { + return self.uninstallProtocolInterfaces(handle, .{ + interface, + }); + } + + /// Returns a pointer to the `Protocol` interface if it's supported by the + /// handle. + /// + /// Note that UEFI implementations are no longer required to implement this + /// function, so it's implemented using `openProtocol` instead. + pub fn handleProtocol( + self: *BootServices, + Protocol: type, + handle: Handle, + ) HandleProtocolError!?*Protocol { + // per https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#efi-boot-services-handleprotocol + // handleProtocol is basically `openProtocol` where: + // 1. agent_handle is `uefi.handle` (aka handle passed to `EfiMain`) + // 2. controller_handle is `null` + // 3. attributes is `EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL` + + return self.openProtocol( + Protocol, + handle, + .{ .by_handle_protocol = .{ .agent = uefi.handle } }, + ) catch |err| switch (err) { + error.AlreadyStarted => return uefi.unexpectedStatus(.already_started), + error.AccessDenied => return uefi.unexpectedStatus(.access_denied), + error.InvalidParameter => return uefi.unexpectedStatus(.invalid_parameter), + else => return @errorCast(err), + }; + } + + pub fn registerProtocolNotify( + self: *BootServices, + Protocol: type, + event: Event, + ) RegisterProtocolNotifyError!EventRegistration { + if (!@hasDecl(Protocol, "guid")) + @compileError("Protocol is missing guid"); + + var registration: EventRegistration = undefined; + switch (self._registerProtocolNotify( + &Protocol.guid, + event, + ®istration, + )) { + .success => return registration, + .out_of_resources => return error.OutOfResources, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Returns the number of handles that match the given search criteria. + pub fn locateHandleLen(self: *const BootServices, search: LocateSearch) NumHandlesError!usize { + var len: usize = 0; + switch (self._locateHandle( + std.meta.activeTag(search), + if (search == .by_protocol) search.by_protocol else null, + if (search == .by_register_notify) search.by_register_notify else null, + &len, + null, + )) { + .success => return @divExact(len, @sizeOf(Handle)), + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// To determine the necessary size of `buffer`, call `locateHandleLen` first. + pub fn locateHandle( + self: *BootServices, + search: LocateSearch, + buffer: []Handle, + ) LocateHandleError![]Handle { + var len: usize = @sizeOf(Handle) * buffer.len; + switch (self._locateHandle( + std.meta.activeTag(search), + if (search == .by_protocol) search.by_protocol else null, + if (search == .by_register_notify) search.by_register_notify else null, + &len, + buffer.ptr, + )) { + .success => return buffer[0..@divExact(len, @sizeOf(Handle))], + .not_found => return buffer[0..0], + .buffer_too_small => return error.BufferTooSmall, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Locates all devices on `device_path` that support `Protocol`. Once the closest + /// match to `device_path` is found, it returns the unmatched device path and handle. + pub fn locateDevicePath( + self: *const BootServices, + device_path: *const DevicePathProtocol, + Protocol: type, + ) LocateHandleError!?struct { *const DevicePathProtocol, Handle } { + if (!@hasDecl(Protocol, "guid")) + @compileError("Protocol is missing guid"); + + var dev_path = device_path; + var device: ?Handle = undefined; + switch (self._locateDevicePath( + &Protocol.guid, + &dev_path, + &device, + )) { + .success => return .{ dev_path, device.? }, + .not_found => return null, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn installConfigurationTable( + self: *BootServices, + guid: *const Guid, + table: *anyopaque, + ) InstallConfigurationTableError!void { + switch (self._installConfigurationTable( + guid, + table, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn uninstallConfigurationTable( + self: *BootServices, + guid: *const Guid, + ) UninstallConfigurationTableError!void { + switch (self._installConfigurationTable( + guid, + null, + )) { + .success => {}, + .not_found => return error.NotFound, + .invalid_parameter => return error.InvalidParameter, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub const LoadImageSource = union(enum) { + buffer: []const u8, + device_path: *const DevicePathProtocol, + }; + + pub fn loadImage( + self: *BootServices, + boot_policy: bool, + parent_image: Handle, + source: LoadImageSource, + ) LoadImageError!Handle { + var handle: Handle = undefined; + + switch (self._loadImage( + boot_policy, + parent_image, + if (source == .device_path) source.device_path else null, + if (source == .buffer) source.buffer.ptr else null, + if (source == .buffer) source.buffer.len else 0, + &handle, + )) { + .success => return handle, + .not_found => return error.NotFound, + .invalid_parameter => return error.InvalidParameter, + .unsupported => return error.Unsupported, + .out_of_resources => return error.OutOfResources, + .load_error => return error.LoadError, + .device_error => return error.DeviceError, + .access_denied => return error.AccessDenied, + .security_violation => return error.SecurityViolation, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn startImage(self: *BootServices, image: Handle) StartImageError!ImageExitData { + var exit_data_size: usize = undefined; + var exit_data: [*]u16 = undefined; + + const exit_code = switch (self._startImage( + image, + &exit_data_size, + &exit_data, + )) { + .invalid_parameter => return error.InvalidParameter, + .security_violation => return error.SecurityViolation, + else => |exit_code| exit_code, + }; + + if (exit_data_size == 0) return .{ + .code = exit_code, + .description = null, + .data = null, + }; + + const description_ptr: [*:0]const u16 = @ptrCast(exit_data); + const description = std.mem.sliceTo(description_ptr, 0); + + return ImageExitData{ + .code = exit_code, + .description = description, + .data = exit_data[description.len + 1 .. exit_data_size], + }; + } + + /// `message` must be allocated using `allocatePool`. + pub fn exit( + self: *BootServices, + handle: Handle, + status: Status, + message: ?[:0]const u16, + ) ExitError!void { + switch (self._exit( + handle, + status, + if (message) |msg| (2 * msg.len) + 1 else 0, + if (message) |msg| @ptrCast(msg.ptr) else null, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + else => |exit_status| return uefi.unexpectedStatus(exit_status), + } + } + + /// `message` should be a null-terminated u16 string followed by binary data + /// allocated using `allocatePool`. + pub fn exitWithData( + self: *BootServices, + handle: Handle, + status: Status, + data: []align(2) const u8, + ) ExitError!void { + switch (self._exit(handle, status, data.len, data.ptr)) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + else => |exit_status| return uefi.unexpectedStatus(exit_status), + } + } + + /// The result is the exit code of the unload handler. Any error codes are + /// `try/catch`-able, leaving only success and warning codes as the result. + pub fn unloadImage( + self: *BootServices, + image: Handle, + ) Status.Error!Status { + const status = self._unloadImage(image); + try status.err(); + return status; + } + + pub fn exitBootServices( + self: *BootServices, + image: Handle, + map_key: MemoryMapKey, + ) ExitBootServicesError!void { + switch (self._exitBootServices(image, map_key)) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn getNextMonotonicCount( + self: *const BootServices, + count: *u64, + ) GetNextMonotonicCountError!void { + switch (self._getNextMonotonicCount(count)) { + .success => {}, + .device_error => return error.DeviceError, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn stall(self: *const BootServices, microseconds: usize) uefi.UnexpectedError!void { + switch (self._stall(microseconds)) { + .success => {}, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn setWatchdogTimer( + self: *BootServices, + timeout: usize, + watchdog_code: u64, + data: ?[]const u16, + ) SetWatchdogTimerError!void { + switch (self._setWatchdogTimer( + timeout, + watchdog_code, + if (data) |d| d.len else 0, + if (data) |d| d.ptr else null, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .unsupported => return error.Unsupported, + .device_error => return error.DeviceError, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// `driver_image` should be a null-terminated ordered list of handles. + pub fn connectController( + self: *BootServices, + controller: Handle, + driver_image: ?[*:null]?Handle, + remaining_device_path: ?*const DevicePathProtocol, + recursive: bool, + ) ConnectControllerError!void { + switch (self._connectController( + controller, + driver_image, + remaining_device_path, + recursive, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .not_found => return error.NotFound, + .security_violation => return error.SecurityViolation, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn disconnectController( + self: *BootServices, + controller: Handle, + driver_image: ?Handle, + child: ?Handle, + ) DisconnectControllerError!void { + switch (self._disconnectController( + controller, + driver_image, + child, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .out_of_resources => return error.OutOfResources, + .device_error => return error.DeviceError, + else => |status| return uefi.unexpectedStatus(status), + } + } /// Opens a protocol with a structure as the loaded image for a UEFI application - pub fn openProtocolSt(self: *BootServices, comptime protocol: type, handle: Handle) !*protocol { - if (!@hasDecl(protocol, "guid")) - @compileError("Protocol is missing guid!"); + /// + /// If `flag` is `.test_protocol`, then the only valid return value is `null`, + /// and `Status.unsupported` is returned. Otherwise, if `_openProtocol` returns + /// `Status.unsupported`, then `null` is returned. + pub fn openProtocol( + self: *BootServices, + Protocol: type, + handle: Handle, + attributes: OpenProtocolArgs, + ) OpenProtocolError!?*Protocol { + if (!@hasDecl(Protocol, "guid")) + @compileError("Protocol is missing guid: " ++ @typeName(Protocol)); - var ptr: ?*protocol = undefined; + const agent_handle: ?Handle, const controller_handle: ?Handle = switch (attributes) { + inline else => |arg| .{ arg.agent, arg.controller }, + }; - try self.openProtocol( + var ptr: ?*Protocol = undefined; + + switch (self._openProtocol( handle, - &protocol.guid, + &Protocol.guid, @as(*?*anyopaque, @ptrCast(&ptr)), - // Invoking handle (loaded image) - uefi.handle, - // Control handle (null as not a driver) - null, - uefi.tables.OpenProtocolAttributes{ .by_handle_protocol = true }, - ).err(); + agent_handle, + controller_handle, + std.meta.activeTag(attributes), + )) { + .success => return if (attributes == .test_protocol) null else ptr, + .unsupported => return if (attributes == .test_protocol) error.Unsupported else null, + .access_denied => return error.AccessDenied, + .already_started => return error.AlreadyStarted, + else => |status| return uefi.unexpectedStatus(status), + } + } - return ptr.?; + pub fn closeProtocol( + self: *BootServices, + handle: Handle, + Protocol: type, + agent: Handle, + controller: ?Handle, + ) CloseProtocolError!void { + if (!@hasDecl(Protocol, "guid")) + @compileError("protocol is missing guid: " ++ @typeName(Protocol)); + + switch (self._closeProtocol( + handle, + &Protocol.guid, + agent, + controller, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .not_found => return error.NotFound, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn openProtocolInformation( + self: *const BootServices, + handle: Handle, + Protocol: type, + ) OpenProtocolInformationError!?[]ProtocolInformationEntry { + var entries: [*]ProtocolInformationEntry = undefined; + var len: usize = undefined; + + switch (self._openProtocolInformation( + handle, + &Protocol.guid, + &entries, + &len, + )) { + .success => return entries[0..len], + .not_found => return null, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn protocolsPerHandle( + self: *const BootServices, + handle: Handle, + ) ProtocolsPerHandleError![]*const Guid { + var guids: [*]*const Guid = undefined; + var len: usize = undefined; + + switch (self._protocolsPerHandle( + handle, + &guids, + &len, + )) { + .success => return guids[0..len], + .invalid_parameter => return error.InvalidParameter, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn locateHandleBuffer( + self: *const BootServices, + search: LocateSearch, + ) LocateHandleBufferError!?[]Handle { + var handles: [*]Handle = undefined; + var len: usize = undefined; + + switch (self._locateHandleBuffer( + std.meta.activeTag(search), + if (search == .by_protocol) search.by_protocol else null, + if (search == .by_register_notify) search.by_register_notify else null, + &len, + &handles, + )) { + .success => return handles[0..len], + .invalid_parameter => return error.InvalidParameter, + .not_found => return null, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn locateProtocol( + self: *const BootServices, + Protocol: type, + registration: ?EventRegistration, + ) LocateProtocolError!?*Protocol { + var interface: *Protocol = undefined; + + switch (self._locateProtocol( + &Protocol.guid, + registration, + @ptrCast(&interface), + )) { + .success => return interface, + .not_found => return null, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Installs a set of protocol interfaces into the boot services environment. + /// + /// This function's final argument should be a tuple of pointers to protocol + /// interfaces. For example: + /// + /// ``` + /// const handle = try boot_services.installProtocolInterfaces(null, .{ + /// &my_interface_1, + /// &my_interface_2, + /// }); + /// ``` + /// + /// The underlying function accepts a vararg list of pairs of Guid pointers + /// and opaque pointers to the interface. To provide a guid, the interface + /// types should declare a `guid` constant like so: + /// + /// ``` + /// pub const guid: uefi.Guid = .{ ... }; + /// ``` + /// + /// See `std.os.uefi.protocol` for examples of protocol type definitions. + pub fn installProtocolInterfaces( + self: *BootServices, + handle: ?Handle, + interfaces: anytype, + ) InstallProtocolInterfacesError!Handle { + var hdl: ?Handle = handle; + const args_tuple = protocolInterfaces(&hdl, interfaces); + + switch (@call( + .auto, + self._installMultipleProtocolInterfaces, + args_tuple, + )) { + .success => return hdl.?, + .already_started => return error.AlreadyStarted, + .out_of_resources => return error.OutOfResources, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn uninstallProtocolInterfaces( + self: *BootServices, + handle: Handle, + interfaces: anytype, + ) UninstallProtocolInterfacesError!void { + const args_tuple = protocolInterfaces(handle, interfaces); + + switch (@call( + .auto, + self._uninstallMultipleProtocolInterfaces, + args_tuple, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn calculateCrc32( + self: *const BootServices, + data: []const u8, + ) CalculateCrc32Error!u32 { + var value: u32 = undefined; + switch (self._calculateCrc32(data.ptr, data.len, &value)) { + .success => return value, + .invalid_parameter => return error.InvalidParameter, + else => |status| return uefi.unexpectedStatus(status), + } } pub const signature: u64 = 0x56524553544f4f42; - pub const event_timer: u32 = 0x80000000; - pub const event_runtime: u32 = 0x40000000; - pub const event_notify_wait: u32 = 0x00000100; - pub const event_notify_signal: u32 = 0x00000200; - pub const event_signal_exit_boot_services: u32 = 0x00000201; - pub const event_signal_virtual_address_change: u32 = 0x00000202; + pub const NotifyOpts = struct { + tpl: TaskPriorityLevel = .application, + function: ?*const fn (Event, ?*anyopaque) callconv(cc) void = null, + context: ?*anyopaque = null, + }; - pub const tpl_application: usize = 4; - pub const tpl_callback: usize = 8; - pub const tpl_notify: usize = 16; - pub const tpl_high_level: usize = 31; + pub const TaskPriorityLevel = enum(usize) { + application = 4, + callback = 8, + notify = 16, + high_level = 31, + _, + }; + + pub const ImageExitData = struct { + code: Status, + description: ?[:0]const u16, + data: ?[]const u16, + }; }; + +fn protocolInterfaces( + handle_arg: anytype, + interfaces: anytype, +) ProtocolInterfaces(@TypeOf(handle_arg), @TypeOf(interfaces)) { + var result: ProtocolInterfaces( + @TypeOf(handle_arg), + @TypeOf(interfaces), + ) = undefined; + result[0] = handle_arg; + + var idx: usize = 1; + inline for (interfaces) |interface| { + const InterfacePtr = @TypeOf(interface); + const Interface = switch (@typeInfo(InterfacePtr)) { + .pointer => |pointer| pointer.child, + else => @compileError("expected tuple of '*const Protocol', got " ++ @typeName(InterfacePtr)), + }; + + if (!@hasDecl(Interface, "guid")) + @compileError("protocol interface '" ++ @typeName(Interface) ++ + "' does not declare a 'const guid: uefi.Guid'."); + + switch (@typeInfo(Interface)) { + .@"struct" => |struct_info| if (struct_info.layout != .@"extern") + @compileLog("protocol interface '" ++ @typeName(Interface) ++ + "' is not extern - this is likely a mistake"), + else => @compileError("protocol interface must be a struct, got " ++ @typeName(Interface)), + } + + result[idx] = &Interface.guid; + result[idx + 1] = @ptrCast(interface); + idx += 2; + } + + return result; +} + +fn ProtocolInterfaces(HandleType: type, Interfaces: type) type { + const interfaces_type_info = @typeInfo(Interfaces); + if (interfaces_type_info != .@"struct" or !interfaces_type_info.@"struct".is_tuple) + @compileError("expected tuple of protocol interfaces, got " ++ @typeName(Interfaces)); + const interfaces_info = interfaces_type_info.@"struct"; + + var tuple_types: [interfaces_info.fields.len * 2 + 1]type = undefined; + tuple_types[0] = HandleType; + var idx = 1; + while (idx < tuple_types.len) : (idx += 2) { + tuple_types[idx] = *const Guid; + tuple_types[idx + 1] = *const anyopaque; + } + + return std.meta.Tuple(tuple_types[0..]); +} diff --git a/lib/std/os/uefi/tables/configuration_table.zig b/lib/std/os/uefi/tables/configuration_table.zig index 75ea2a215b..9c87ee2437 100644 --- a/lib/std/os/uefi/tables/configuration_table.zig +++ b/lib/std/os/uefi/tables/configuration_table.zig @@ -5,7 +5,7 @@ pub const ConfigurationTable = extern struct { vendor_guid: Guid, vendor_table: *anyopaque, - pub const acpi_20_table_guid align(8) = Guid{ + pub const acpi_20_table_guid: Guid = .{ .time_low = 0x8868e871, .time_mid = 0xe4f1, .time_high_and_version = 0x11d3, @@ -13,7 +13,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0x22, .node = [_]u8{ 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81 }, }; - pub const acpi_10_table_guid align(8) = Guid{ + pub const acpi_10_table_guid: Guid = .{ .time_low = 0xeb9d2d30, .time_mid = 0x2d88, .time_high_and_version = 0x11d3, @@ -21,7 +21,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0x16, .node = [_]u8{ 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }, }; - pub const sal_system_table_guid align(8) = Guid{ + pub const sal_system_table_guid: Guid = .{ .time_low = 0xeb9d2d32, .time_mid = 0x2d88, .time_high_and_version = 0x113d, @@ -29,7 +29,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0x16, .node = [_]u8{ 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }, }; - pub const smbios_table_guid align(8) = Guid{ + pub const smbios_table_guid: Guid = .{ .time_low = 0xeb9d2d31, .time_mid = 0x2d88, .time_high_and_version = 0x11d3, @@ -37,7 +37,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0x16, .node = [_]u8{ 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }, }; - pub const smbios3_table_guid align(8) = Guid{ + pub const smbios3_table_guid: Guid = .{ .time_low = 0xf2fd1544, .time_mid = 0x9794, .time_high_and_version = 0x4a2c, @@ -45,7 +45,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0x2e, .node = [_]u8{ 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94 }, }; - pub const mps_table_guid align(8) = Guid{ + pub const mps_table_guid: Guid = .{ .time_low = 0xeb9d2d2f, .time_mid = 0x2d88, .time_high_and_version = 0x11d3, @@ -53,7 +53,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0x16, .node = [_]u8{ 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }, }; - pub const json_config_data_table_guid align(8) = Guid{ + pub const json_config_data_table_guid: Guid = .{ .time_low = 0x87367f87, .time_mid = 0x1119, .time_high_and_version = 0x41ce, @@ -61,7 +61,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0xec, .node = [_]u8{ 0x8b, 0xe0, 0x11, 0x1f, 0x55, 0x8a }, }; - pub const json_capsule_data_table_guid align(8) = Guid{ + pub const json_capsule_data_table_guid: Guid = .{ .time_low = 0x35e7a725, .time_mid = 0x8dd2, .time_high_and_version = 0x4cac, @@ -69,7 +69,7 @@ pub const ConfigurationTable = extern struct { .clock_seq_low = 0x11, .node = [_]u8{ 0x33, 0xcd, 0xa8, 0x10, 0x90, 0x56 }, }; - pub const json_capsule_result_table_guid align(8) = Guid{ + pub const json_capsule_result_table_guid: Guid = .{ .time_low = 0xdbc461c3, .time_mid = 0xb3de, .time_high_and_version = 0x422a, diff --git a/lib/std/os/uefi/tables/runtime_services.zig b/lib/std/os/uefi/tables/runtime_services.zig index 2beae3b5c5..63bc9ad531 100644 --- a/lib/std/os/uefi/tables/runtime_services.zig +++ b/lib/std/os/uefi/tables/runtime_services.zig @@ -6,10 +6,12 @@ const Time = uefi.Time; const TimeCapabilities = uefi.TimeCapabilities; const Status = uefi.Status; const MemoryDescriptor = uefi.tables.MemoryDescriptor; +const MemoryMapSlice = uefi.tables.MemoryMapSlice; const ResetType = uefi.tables.ResetType; const CapsuleHeader = uefi.tables.CapsuleHeader; const PhysicalAddress = uefi.tables.PhysicalAddress; const cc = uefi.cc; +const Error = Status.Error; /// Runtime services are provided by the firmware before and after exitBootServices has been called. /// @@ -23,50 +25,511 @@ pub const RuntimeServices = extern struct { hdr: TableHeader, /// Returns the current time and date information, and the time-keeping capabilities of the hardware platform. - getTime: *const fn (time: *uefi.Time, capabilities: ?*TimeCapabilities) callconv(cc) Status, + _getTime: *const fn (time: *Time, capabilities: ?*TimeCapabilities) callconv(cc) Status, /// Sets the current local time and date information - setTime: *const fn (time: *uefi.Time) callconv(cc) Status, + _setTime: *const fn (time: *const Time) callconv(cc) Status, /// Returns the current wakeup alarm clock setting - getWakeupTime: *const fn (enabled: *bool, pending: *bool, time: *uefi.Time) callconv(cc) Status, + _getWakeupTime: *const fn (enabled: *bool, pending: *bool, time: *Time) callconv(cc) Status, /// Sets the system wakeup alarm clock time - setWakeupTime: *const fn (enable: *bool, time: ?*uefi.Time) callconv(cc) Status, + _setWakeupTime: *const fn (enable: bool, time: ?*const Time) callconv(cc) Status, /// Changes the runtime addressing mode of EFI firmware from physical to virtual. - setVirtualAddressMap: *const fn (mmap_size: usize, descriptor_size: usize, descriptor_version: u32, virtual_map: [*]MemoryDescriptor) callconv(cc) Status, + _setVirtualAddressMap: *const fn (mmap_size: usize, descriptor_size: usize, descriptor_version: u32, virtual_map: [*]align(@alignOf(MemoryDescriptor)) u8) callconv(cc) Status, /// Determines the new virtual address that is to be used on subsequent memory accesses. - convertPointer: *const fn (debug_disposition: usize, address: **anyopaque) callconv(cc) Status, + _convertPointer: *const fn (debug_disposition: DebugDisposition, address: *?*anyopaque) callconv(cc) Status, /// Returns the value of a variable. - getVariable: *const fn (var_name: [*:0]const u16, vendor_guid: *align(8) const Guid, attributes: ?*u32, data_size: *usize, data: ?*anyopaque) callconv(cc) Status, + _getVariable: *const fn (var_name: [*:0]const u16, vendor_guid: *const Guid, attributes: ?*VariableAttributes, data_size: *usize, data: ?*anyopaque) callconv(cc) Status, /// Enumerates the current variable names. - getNextVariableName: *const fn (var_name_size: *usize, var_name: [*:0]u16, vendor_guid: *align(8) Guid) callconv(cc) Status, + _getNextVariableName: *const fn (var_name_size: *usize, var_name: ?[*:0]const u16, vendor_guid: *Guid) callconv(cc) Status, /// Sets the value of a variable. - setVariable: *const fn (var_name: [*:0]const u16, vendor_guid: *align(8) const Guid, attributes: u32, data_size: usize, data: *anyopaque) callconv(cc) Status, + _setVariable: *const fn (var_name: [*:0]const u16, vendor_guid: *const Guid, attributes: VariableAttributes, data_size: usize, data: [*]const u8) callconv(cc) Status, /// Return the next high 32 bits of the platform's monotonic counter - getNextHighMonotonicCount: *const fn (high_count: *u32) callconv(cc) Status, + _getNextHighMonotonicCount: *const fn (high_count: *u32) callconv(cc) Status, /// Resets the entire platform. - resetSystem: *const fn (reset_type: ResetType, reset_status: Status, data_size: usize, reset_data: ?*const anyopaque) callconv(cc) noreturn, + _resetSystem: *const fn (reset_type: ResetType, reset_status: Status, data_size: usize, reset_data: ?[*]const u16) callconv(cc) noreturn, /// Passes capsules to the firmware with both virtual and physical mapping. /// Depending on the intended consumption, the firmware may process the capsule immediately. /// If the payload should persist across a system reset, the reset value returned from /// `queryCapsuleCapabilities` must be passed into resetSystem and will cause the capsule /// to be processed by the firmware as part of the reset process. - updateCapsule: *const fn (capsule_header_array: **CapsuleHeader, capsule_count: usize, scatter_gather_list: PhysicalAddress) callconv(cc) Status, + _updateCapsule: *const fn (capsule_header_array: [*]*const CapsuleHeader, capsule_count: usize, scatter_gather_list: PhysicalAddress) callconv(cc) Status, /// Returns if the capsule can be supported via `updateCapsule` - queryCapsuleCapabilities: *const fn (capsule_header_array: **CapsuleHeader, capsule_count: usize, maximum_capsule_size: *usize, reset_type: ResetType) callconv(cc) Status, + _queryCapsuleCapabilities: *const fn (capsule_header_array: [*]*const CapsuleHeader, capsule_count: usize, maximum_capsule_size: *usize, reset_type: *ResetType) callconv(cc) Status, /// Returns information about the EFI variables - queryVariableInfo: *const fn (attributes: *u32, maximum_variable_storage_size: *u64, remaining_variable_storage_size: *u64, maximum_variable_size: *u64) callconv(cc) Status, + _queryVariableInfo: *const fn (attributes: VariableAttributes, maximum_variable_storage_size: *u64, remaining_variable_storage_size: *u64, maximum_variable_size: *u64) callconv(cc) Status, + + pub const GetTimeError = uefi.UnexpectedError || error{ + DeviceError, + Unsupported, + }; + + pub const SetTimeError = uefi.UnexpectedError || error{ + DeviceError, + Unsupported, + }; + + pub const GetWakeupTimeError = uefi.UnexpectedError || error{ + DeviceError, + Unsupported, + }; + + pub const SetWakeupTimeError = uefi.UnexpectedError || error{ + InvalidParameter, + DeviceError, + Unsupported, + }; + + pub const SetVirtualAddressMapError = uefi.UnexpectedError || error{ + Unsupported, + NoMapping, + NotFound, + }; + + pub const ConvertPointerError = uefi.UnexpectedError || error{ + InvalidParameter, + Unsupported, + }; + + pub const GetVariableSizeError = uefi.UnexpectedError || error{ + DeviceError, + Unsupported, + }; + + pub const GetVariableError = GetVariableSizeError || error{ + BufferTooSmall, + }; + + pub const SetVariableError = uefi.UnexpectedError || error{ + InvalidParameter, + OutOfResources, + DeviceError, + WriteProtected, + SecurityViolation, + NotFound, + Unsupported, + }; + + pub const GetNextHighMonotonicCountError = uefi.UnexpectedError || error{ + DeviceError, + Unsupported, + }; + + pub const UpdateCapsuleError = uefi.UnexpectedError || error{ + InvalidParameter, + DeviceError, + Unsupported, + OutOfResources, + }; + + pub const QueryCapsuleCapabilitiesError = uefi.UnexpectedError || error{ + Unsupported, + OutOfResources, + }; + + pub const QueryVariableInfoError = uefi.UnexpectedError || error{ + InvalidParameter, + Unsupported, + }; + + /// Returns the current time and the time capabilities of the platform. + pub fn getTime( + self: *const RuntimeServices, + ) GetTimeError!struct { Time, TimeCapabilities } { + var time: Time = undefined; + var capabilities: TimeCapabilities = undefined; + + switch (self._getTime(&time, &capabilities)) { + .success => return .{ time, capabilities }, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn setTime(self: *RuntimeServices, time: *const Time) SetTimeError!void { + switch (self._setTime(time)) { + .success => {}, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub const GetWakeupTime = struct { + enabled: bool, + pending: bool, + time: Time, + }; + + pub fn getWakeupTime( + self: *const RuntimeServices, + ) GetWakeupTimeError!GetWakeupTime { + var result: GetWakeupTime = undefined; + switch (self._getWakeupTime( + &result.enabled, + &result.pending, + &result.time, + )) { + .success => return result, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub const SetWakeupTime = union(enum) { + enabled: *const Time, + disabled, + }; + + pub fn setWakeupTime( + self: *RuntimeServices, + set: SetWakeupTime, + ) SetWakeupTimeError!void { + switch (self._setWakeupTime( + set != .disabled, + if (set == .enabled) set.enabled else null, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn setVirtualAddressMap( + self: *RuntimeServices, + map: MemoryMapSlice, + ) SetVirtualAddressMapError!void { + switch (self._setVirtualAddressMap( + map.info.len * map.info.descriptor_size, + map.info.descriptor_size, + map.info.descriptor_version, + @ptrCast(map.ptr), + )) { + .success => {}, + .unsupported => return error.Unsupported, + .no_mapping => return error.NoMapping, + .not_found => return error.NotFound, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn convertPointer( + self: *const RuntimeServices, + comptime disposition: DebugDisposition, + cvt: @FieldType(PointerConversion, @tagName(disposition)), + ) ConvertPointerError!?@FieldType(PointerConversion, @tagName(disposition)) { + var pointer = cvt; + + switch (self._convertPointer(disposition, @ptrCast(&pointer))) { + .success => return pointer, + .not_found => return null, + .invalid_parameter => return error.InvalidParameter, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Returns the length of the variable's data and its attributes. + pub fn getVariableSize( + self: *const RuntimeServices, + name: [*:0]const u16, + guid: *const Guid, + ) GetVariableSizeError!?struct { usize, VariableAttributes } { + var size: usize = 0; + var attrs: VariableAttributes = undefined; + + switch (self._getVariable( + name, + guid, + &attrs, + &size, + null, + )) { + .buffer_too_small => return .{ size, attrs }, + .not_found => return null, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// To determine the minimum necessary buffer size for the variable, call + /// `getVariableSize` first. + pub fn getVariable( + self: *const RuntimeServices, + name: [*:0]const u16, + guid: *const Guid, + buffer: []u8, + ) GetVariableError!?struct { []u8, VariableAttributes } { + var attrs: VariableAttributes = undefined; + var len = buffer.len; + + switch (self._getVariable( + name, + guid, + &attrs, + &len, + buffer.ptr, + )) { + .success => return .{ buffer[0..len], attrs }, + .not_found => return null, + .buffer_too_small => return error.BufferTooSmall, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn variableNameIterator( + self: *const RuntimeServices, + buffer: []u16, + ) VariableNameIterator { + buffer[0] = 0; + return .{ + .services = self, + .buffer = buffer, + .guid = undefined, + }; + } + + pub fn setVariable( + self: *RuntimeServices, + name: [*:0]const u16, + guid: *const Guid, + attributes: VariableAttributes, + data: []const u8, + ) SetVariableError!void { + switch (self._setVariable( + name, + guid, + attributes, + data.len, + data.ptr, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .out_of_resources => return error.OutOfResources, + .device_error => return error.DeviceError, + .write_protected => return error.WriteProtected, + .security_violation => return error.SecurityViolation, + .not_found => return error.NotFound, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn getNextHighMonotonicCount(self: *const RuntimeServices) GetNextHighMonotonicCountError!u32 { + var cnt: u32 = undefined; + switch (self._getNextHighMonotonicCount(&cnt)) { + .success => return cnt, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn resetSystem( + self: *RuntimeServices, + reset_type: ResetType, + reset_status: Status, + data: ?[]align(2) const u8, + ) noreturn { + self._resetSystem( + reset_type, + reset_status, + if (data) |d| d.len else 0, + if (data) |d| @alignCast(@ptrCast(d.ptr)) else null, + ); + } + + pub fn updateCapsule( + self: *RuntimeServices, + capsules: []*const CapsuleHeader, + scatter_gather_list: PhysicalAddress, + ) UpdateCapsuleError!void { + switch (self._updateCapsule( + capsules.ptr, + capsules.len, + scatter_gather_list, + )) { + .success => {}, + .invalid_parameter => return error.InvalidParameter, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn queryCapsuleCapabilities( + self: *const RuntimeServices, + capsules: []*const CapsuleHeader, + ) QueryCapsuleCapabilitiesError!struct { u64, ResetType } { + var max_capsule_size: u64 = undefined; + var reset_type: ResetType = undefined; + + switch (self._queryCapsuleCapabilities( + capsules.ptr, + capsules.len, + &max_capsule_size, + &reset_type, + )) { + .success => return .{ max_capsule_size, reset_type }, + .unsupported => return error.Unsupported, + .out_of_resources => return error.OutOfResources, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub fn queryVariableInfo( + self: *const RuntimeServices, + // Note: .append_write is ignored + attributes: VariableAttributes, + ) QueryVariableInfoError!VariableInfo { + var res: VariableInfo = undefined; + + switch (self._queryVariableInfo( + attributes, + &res.max_variable_storage_size, + &res.remaining_variable_storage_size, + &res.max_variable_size, + )) { + .success => return res, + .invalid_parameter => return error.InvalidParameter, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + pub const DebugDisposition = enum(usize) { + const Bits = packed struct(usize) { + optional_ptr: bool = false, + _pad: std.meta.Int(.unsigned, @bitSizeOf(usize) - 1) = 0, + }; + + pointer = @bitCast(Bits{}), + optional = @bitCast(Bits{ .optional_ptr = true }), + _, + }; + + pub const PointerConversion = union(DebugDisposition) { + pointer: *anyopaque, + optional: ?*anyopaque, + }; + + pub const VariableAttributes = packed struct(u32) { + non_volatile: bool = false, + bootservice_access: bool = false, + runtime_access: bool = false, + hardware_error_record: bool = false, + /// Note: deprecated and should be considered reserved. + authenticated_write_access: bool = false, + time_based_authenticated_write_access: bool = false, + append_write: bool = false, + /// Indicates that the variable payload begins with a EFI_VARIABLE_AUTHENTICATION_3 + /// structure, and potentially more structures as indicated by fields of + /// this structure. + enhanced_authenticated_access: bool = false, + _pad: u24 = 0, + }; + + pub const VariableAuthentication3 = extern struct { + version: u8 = 1, + type: Type, + metadata_size: u32, + flags: Flags, + + pub fn payloadConst(self: *const VariableAuthentication3) []const u8 { + return @constCast(self).payload(); + } + + pub fn payload(self: *VariableAuthentication3) []u8 { + var ptr: [*]u8 = @ptrCast(self); + return ptr[@sizeOf(VariableAuthentication3)..self.metadata_size]; + } + + pub const Flags = packed struct(u32) { + update_cert: bool = false, + _pad: u31 = 0, + }; + + pub const Type = enum(u8) { + timestamp = 1, + nonce = 2, + _, + }; + }; + + pub const VariableInfo = struct { + max_variable_storage_size: u64, + remaining_variable_storage_size: u64, + max_variable_size: u64, + }; + + pub const VariableNameIterator = struct { + pub const NextSizeError = uefi.UnexpectedError || error{ + DeviceError, + Unsupported, + }; + + pub const IterateVariableNameError = NextSizeError || error{ + BufferTooSmall, + }; + + services: *const RuntimeServices, + buffer: []u16, + guid: Guid, + + pub fn nextSize(self: *VariableNameIterator) NextSizeError!?usize { + var len: usize = 0; + switch (self.services._getNextVariableName( + &len, + null, + &self.guid, + )) { + .buffer_too_small => return len, + .not_found => return null, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + + /// Call `nextSize` to get the length of the next variable name and check + /// if `buffer` is large enough to hold the name. + pub fn next( + self: *VariableNameIterator, + ) IterateVariableNameError!?[:0]const u16 { + var len = self.buffer.len; + switch (self.services._getNextVariableName( + &len, + @ptrCast(self.buffer.ptr), + &self.guid, + )) { + .success => return self.buffer[0 .. len - 1 :0], + .not_found => return null, + .buffer_too_small => return error.BufferTooSmall, + .device_error => return error.DeviceError, + .unsupported => return error.Unsupported, + else => |status| return uefi.unexpectedStatus(status), + } + } + }; pub const signature: u64 = 0x56524553544e5552; }; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index f46cb94e0e..df5837a995 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -772,12 +772,12 @@ pub fn exit(status: u8) noreturn { if (native_os == .uefi) { const uefi = std.os.uefi; // exit() is only available if exitBootServices() has not been called yet. - // This call to exit should not fail, so we don't care about its return value. + // This call to exit should not fail, so we catch-ignore errors. if (uefi.system_table.boot_services) |bs| { - _ = bs.exit(uefi.handle, @enumFromInt(status), 0, null); + bs.exit(uefi.handle, @enumFromInt(status), null) catch {}; } // If we can't exit, reboot the system instead. - uefi.system_table.runtime_services.resetSystem(.reset_cold, @enumFromInt(status), 0, null); + uefi.system_table.runtime_services.resetSystem(.cold, @enumFromInt(status), null); } system.exit(status); } diff --git a/lib/std/time.zig b/lib/std/time.zig index af93c060d9..504257a852 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -56,9 +56,7 @@ pub fn nanoTimestamp() i128 { return ns; }, .uefi => { - var value: std.os.uefi.Time = undefined; - const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); - assert(status == .success); + const value, _ = std.os.uefi.system_table.runtime_services.getTime() catch return 0; return value.toEpoch(); }, else => { @@ -141,9 +139,7 @@ pub const Instant = struct { return .{ .timestamp = ns }; }, .uefi => { - var value: std.os.uefi.Time = undefined; - const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); - if (status != .success) return error.Unsupported; + const value, _ = std.os.uefi.system_table.runtime_services.getTime() catch return error.Unsupported; return .{ .timestamp = value.toEpoch() }; }, // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while