Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/std/Thread.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/std/debug.zig
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,8 @@ pub fn defaultPanic(
// 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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

afaict this was a bug 😬 exit_data.len for number of u16s but the spec says it's the number of bytes

const bytes: [*]const u8 = @ptrCast(exit_data.ptr);
bs.exit(uefi.handle, .aborted, bytes[0 .. exit_data.len * 2]);
}
@trap();
},
Expand Down
42 changes: 42 additions & 0 deletions lib/std/os/uefi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,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 anyopaque;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An event registration is not arbitrary data (as opposed to say, a notify context which is user-controlled), it refers to a specific opaque structure

Suggested change
pub const EventRegistration = *const anyopaque;
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 = .{} },
Expand Down
24 changes: 14 additions & 10 deletions lib/std/os/uefi/pool_allocator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
};

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;
}
160 changes: 145 additions & 15 deletions lib/std/os/uefi/tables.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,59 @@ 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) {
/// 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,
// per https://github.com/tianocore/edk2/blob/59805c7697c801be8b08e5169bc2300d821c419d/MdePkg/Include/Uefi/UefiSpec.h#L191-L195
max_invalid_memory_type = 0x6FFFFFFF,
oem_start = 0x70000000,
oem_end = 0x7FFFFFFF,
vendor_start = 0x80000000,
vendor_end = 0xFFFFFFFF,
_,

pub fn isInvalid(self: MemoryType) bool {
const as_int = @intFromEnum(self);
return as_int >= @intFromEnum(MemoryType.max_memory_type) and
as_int <= @intFromEnum(MemoryType.max_invalid_memory_type);
}

pub fn isOem(self: MemoryType) bool {
const as_int = @intFromEnum(self);
return as_int >= @intFromEnum(MemoryType.oem_start) and
as_int <= @intFromEnum(MemoryType.oem_end);
}

pub fn isVendor(self: MemoryType) bool {
const as_int = @intFromEnum(self);
return as_int >= @intFromEnum(MemoryType.vendor_start) and
as_int <= @intFromEnum(MemoryType.vendor_end);
}
};

pub const MemoryDescriptorAttribute = packed struct(u64) {
Expand All @@ -51,6 +81,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,
Expand All @@ -59,20 +91,111 @@ pub const MemoryDescriptor = extern struct {
attribute: MemoryDescriptorAttribute,
};

pub const MemoryMapInfo = struct {
key: MemoryMapKey,
descriptor_size: usize,
descriptor_version: u32,
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.descriptor_size >= 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: *align(8) 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 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 {
Expand All @@ -86,10 +209,17 @@ pub const InterfaceType = enum(u32) {
efi_native_interface,
};

pub const AllocateLocation = union(AllocateType) {
allocate_any_pages,
allocate_max_address: [*]align(4096) uefi.Page,
allocate_address: [*]align(4096) uefi.Page,
};

pub const AllocateType = enum(u32) {
allocate_any_pages,
allocate_max_address,
allocate_address,
_,
};

pub const PhysicalAddress = u64;
Expand All @@ -110,10 +240,10 @@ 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{
Expand Down
Loading