std.os.uefi.tables: ziggify boot and runtime services (#23441)

* std.os.uefi.tables: ziggify boot and runtime services

* avoid T{} syntax

Co-authored-by: linusg <mail@linusgroh.de>

* misc fixes

* work

* self-review quickfixes

* dont make MemoryMapSlice generic

* more review fixes, work

* more work

* more work

* review fixes

* update boot/runtime services references throughout codebase

* self-review fixes

* couple of fixes i forgot to commit earlier

* fixes from integrating in my own project

* fixes from refAllDeclsRecursive

* Apply suggestions from code review

Co-authored-by: truemedian <truemedian@gmail.com>

* more fixes from review

* fixes from project integration

* make natural alignment of Guid align-8

* EventRegistration is a new opaque type

* fix getNextHighMonotonicCount

* fix locateProtocol

* fix exit

* partly revert 7372d65

* oops exit data_len is num of bytes

* fixes from project integration

* MapInfo consistency, MemoryType update per review

* turn EventRegistration back into a pointer

* forgot to finish updating MemoryType methods

* fix IntFittingRange calls

* set uefi.Page nat alignment

* Back out "set uefi.Page nat alignment"

This backs out commit cdd9bd6f7f5fb763f994b8fbe3e1a1c2996a2393.

* get rid of some error.NotFound-s

* fix .exit call in panic

* review comments, add format method

* fix resetSystem data alignment

* oops, didnt do a final refAllDeclsRecursive i guess

* review comments

* writergate update MemoryType.format

* fix rename

---------

Co-authored-by: linusg <mail@linusgroh.de>
Co-authored-by: truemedian <truemedian@gmail.com>
This commit is contained in:
Carmen 2025-07-12 10:18:53 -07:00 committed by GitHub
parent bd97b66186
commit 5b4e982169
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 1897 additions and 144 deletions

View File

@ -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;
}

View File

@ -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();
},

View File

@ -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 events NotificationFunction will be queued at the events NotifyTpl
/// whenever the event is being waited on via EFI_BOOT_SERVICES.WaitForEvent()
/// or EFI_BOOT_SERVICES.CheckEvent() .
wait: bool = false,
/// The events 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 events 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

View File

@ -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;
}

View File

@ -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 drivers 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
/// drivers 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;

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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