-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
std.os.uefi.tables: ziggify boot and runtime services #23441
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
base: master
Are you sure you want to change the base?
std.os.uefi.tables: ziggify boot and runtime services #23441
Conversation
It is appreciated! :^) |
ee41a75
to
e4487d5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a thorough review, LGTM at a high level
|
||
/// 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: [*]MemoryDescriptor, map_key: *MemoryMapKey, descriptor_size: *usize, descriptor_version: *u32) callconv(cc) Status, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i figured it's safer to obscure map_key
values since as far as i can tell it's meant to be assigned by the system (only retrievable via getMemoryMap
)
interfaces: anytype, | ||
) InstallProtocolInterfacesError!Handle { | ||
var hdl: ?Handle = handle; | ||
const args_tuple = protocolInterfaces(&hdl, interfaces); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i couldn't find an easier way to manage dynamically constructing a tuple than this helper :/
}; | ||
|
||
fn protocolInterfaces( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function is kinda gross :/ suggestions are welcome
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor nitpicks, but since we're overhauling boot services it's time we remove [*]MemoryDescriptor
and []MemoryDescriptor
from std.os.uefi, they are never valid because the size of the zig struct is almost never the size of the descriptor the firmware returns.
e4487d5
to
5ad806e
Compare
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't make sense to define wrappers for these functions imo
|
||
/// 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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function is now basically the handleProtocol
function
50823da
to
0733531
Compare
@@ -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); |
There was a problem hiding this comment.
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
i feel like this is ready for a final review :) i haven't tried it with my own project yet though, i'll tackle that later |
@@ -134,7 +134,7 @@ pub const BootServices = extern struct { | |||
_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: ?[*:null]Handle, remaining_device_path: ?*const DevicePathProtocol, recursive: bool) callconv(cc) Status, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
really feels like [*:null]*anyopaque
should work :(
haven't forgotten or abandoned this, I'm dealing with immigration stuffs :) I'll pick it up again soon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/std/os/uefi.zig
Outdated
/// A handle to an event structure. | ||
pub const Event = *opaque {}; | ||
|
||
pub const EventRegistration = opaque {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to not keep this as *const opaque{}
since it might be nice to have the namespace eventually? idk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually feel like it'd be handy to do that for Event
eventually 🙃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The standard describes an event registration as a VOID*
, and the standard provides absolutely no methods to operate on the registration value other than passing around the VOID*
, so I say it makes no sense to change this from a *opaque{}
.
If for some reason someone finds a good reason to make this a namespace that would be a better time to make this change.
Co-authored-by: linusg <mail@linusgroh.de>
lib/std/os/uefi.zig
Outdated
/// A handle to an event structure. | ||
pub const Event = *opaque {}; | ||
|
||
pub const EventRegistration = opaque {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The standard describes an event registration as a VOID*
, and the standard provides absolutely no methods to operate on the registration value other than passing around the VOID*
, so I say it makes no sense to change this from a *opaque{}
.
If for some reason someone finds a good reason to make this a namespace that would be a better time to make this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
decided to include a pattern that i've been experimenting with in another PR i'm working on for std.elf, wdyt?
lib/std/os/uefi/tables.zig
Outdated
pub const InvalidValue = math.IntFittingRange( | ||
@intFromEnum(MemoryType.invalid_start), | ||
@intFromEnum(MemoryType.invalid_end), | ||
); | ||
pub const OemValue = math.IntFittingRange( | ||
@intFromEnum(MemoryType.oem_start), | ||
@intFromEnum(MemoryType.oem_end), | ||
); | ||
pub const VendorValue = math.IntFittingRange( | ||
@intFromEnum(MemoryType.vendor_start), | ||
@intFromEnum(MemoryType.vendor_end), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- define
IntFittingRange
for the relevant value range
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh wait the start
should be 0 😅 and the end
should be _start - _end
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_end - _start
*
lib/std/os/uefi/tables.zig
Outdated
pub fn invalid(value: InvalidValue) MemoryType { | ||
const invalid_start = @intFromEnum(MemoryType.invalid_start); | ||
return @enumFromInt(invalid_start + value); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- function for constructing such values ie
pub const my_invalid: MemoryType = .invalid(0x2);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we constructing invalid memory types?
lib/std/os/uefi/tables.zig
Outdated
pub fn getInvalid(memtype: MemoryType) ?InvalidValue { | ||
const as_int = @intFromEnum(memtype); | ||
const invalid_start = @intFromEnum(MemoryType.invalid_start); | ||
if (as_int < invalid_start) return null; | ||
if (as_int > @intFromEnum(MemoryType.invalid_end)) return null; | ||
return @truncate(as_int - invalid_start); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- function for retrieving the value relative to the range's start
IMO, it is also a good idea to use |
Not that that's a bad idea for the special case of exactly 4K alignment, I definitely would need a source to back up that |
I don't have an exact source to back this up, but testing on some hardware I have available I found that allocatePage is significantly slower than allocatePool. |
tbh i think that's a better change for #22818 which is doing a lot more work to integrate uefi's allocator api into zig's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good to me.
Yeah, it's good that the spec mentions that only 4k page sizes are used. |
now #23600 btw :) |
friendly ping :) @linusg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@kouosi / @truemedian any final thoughts? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The memory type abstracting the separate invalid/vendor/oem fields into their own thing kind of confuses me; invalid in particular because you should never have any reason to construct or know which invalid field you have outside of "it's invalid".
lib/std/os/uefi/tables.zig
Outdated
pub fn invalid(value: InvalidValue) MemoryType { | ||
const invalid_start = @intFromEnum(MemoryType.invalid_start); | ||
return @enumFromInt(invalid_start + value); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we constructing invalid memory types?
i can remove i just wrote |
The concern there is that the vendor or oem's memory type is most certainly not going to be an offset from the offset of vendor/oem types. It's going to just be a single hex value that you could (also it should be noted that a wide variety of UEFI firmware derived from Tianocore struggles to handle vendor memory types) |
A decent number of the constants I refactored were using offsets from oem/vendor types. While that alone isn't a good argument, it becomes a lot more apparent to the user what exactly they're referencing, which I think is an easy win for readability. Of course enumFromInt will still exist for people who are using a cImport or plain don't want to.
Can you expand on this? Are you saying that UEFI bootloaders developed in a Tianocore environment don't handle real-world scenarios? |
It's a bug in at least the QEMU firmware and a few other virtual machine platforms and people have encountered it in the wild on hardware. Essentially the firmware reads an array out of bounds when it encounters a memory type it doesn't know, including the user-defined range. rust-osdev/uefi-rs#1375 |
i think the conclusions in those links are reasonable - that bug is platform-specific and independent of the design of this api. if you use a non-buggy UEFI firmware, then it's reasonable to expect to be able to use custom memory types, and these functions help with that (besides |
this PR updates
BootServices
andRuntimeServices
to an idiomatic interface similar to the pattern used instd.os.uefi.protocol
. Part of my crusade towards makingstd.os.uefi
more idiomatic