Uh oh!
There was an error while loading. Please reload this page.
std: implement basic io for UEFI - #22226
Conversation
| @@ -1,5 +1,7 @@ | |||
| const std = @import("../std.zig"); | |||
| pub const posix = @import("uefi/posix.zig"); | |||
There was a problem hiding this comment.
Last I talked with Andrew about the future of std.posix, one thing we agreed on is that probably all Windows code in std.posix should be deleted. I would expect that the exact same thing is true of UEFI, as it is also not a POSIX platform in any meaningful sense?
There was a problem hiding this comment.
Yeah, I agree. The UEFI POSIX stuff is just a POSIX wrapper around the UEFI protocols. We can always ditch it. UEFI is close to Windows so I think whatever we do for Windows and POSIX, it would apply to UEFI.
| } | ||
| const Impl = if (builtin.single_threaded) | ||
| const Impl = if (builtin.single_threaded or builtin.os.tag == .uefi) |
There was a problem hiding this comment.
Should UEFI be treated as a proper single-threaded target?
Lines 66 to 81 in 82f35c5
There was a problem hiding this comment.
Yes, I just didn't know where the source was which defined that heh.
There was a problem hiding this comment.
I've updated this and changed the defaultSingleThreaded for UEFI.
There was a problem hiding this comment.
Out of curiosity, is UEFI not always single threaded instead of just default single threaded?
There was a problem hiding this comment.
You technically could implement a form of multithreading, just like with wasm. It's just builtin.single_threaded will default to true when single_threaded is not changed in your build. You probably could write a custom scheduler + mutltithreading stuff and use root.os stuff to get it working but no implementation will be made for upstream.
There was a problem hiding this comment.
I learned somewhat recently that UEFI does have EFI_MP_SERVICES_PROTOCOL so there is a way to do multiprocessing but not multithreading but likely is still out of the scope.
87beafb to
2a2c072Compare| /// no particular encoding. | ||
| pub const max_path_bytes = switch (native_os) { | ||
| .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten, .wasi => posix.PATH_MAX, | ||
| .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten, .wasi, .uefi => posix.PATH_MAX, |
There was a problem hiding this comment.
This looks wrong, I'd assume it needs to use similar logic as windows
| pub fn next(self: *Self) Error!?Entry { | ||
| _ = self; | ||
| return null; |
There was a problem hiding this comment.
Not sure if turning a compile error into a non-functional stub is that useful? Most people will assume the standard library functions work for a certain target if they compile.
There was a problem hiding this comment.
Yeah, I hadn't figured out how to implement it just yet. I'll leave it out of this PR and work on it later on.
| /// Allocates memory in pages. | ||
| /// | ||
| /// This allocator is backed by `allocatePages` and is therefore only suitable for usage when Boot Services are available. | ||
| pub const PageAllocator = struct { |
There was a problem hiding this comment.
std.os.uefi.allocator.{Page,Pool}Allocator violates https://ziglang.org/documentation/master/#Avoid-Redundant-Names-in-Fully-Qualified-Namespaces
There was a problem hiding this comment.
I have changed it now, hopefully it doesn't violate the fully qualified namespace stuff.
| if (@errorReturnTrace()) |trace| { | ||
| std.debug.dumpStackTrace(trace.*); | ||
| } | ||
| std.time.sleep(5 * std.time.ns_per_s); |
There was a problem hiding this comment.
This part of the code is based on what @truemedian did. I'm not sure either so CC'ing them for some information.
There was a problem hiding this comment.
Magic sleep here was a debugging aid to prevent the uefi firmware from clearing the screen before I could read the any output because once EfiMain returns it will go to the next boot loader. Not necessary at all, unless that window to eead the error is desired.
There was a problem hiding this comment.
Ok, I likely will remove it and it'll be up to the program to do that if they want to.
cb18f91 to
74b7013Compare372c006 to
ce7569cComparece7569c to
ce1a302Compare
linusg
left a comment
There was a problem hiding this comment.
I'll have to ask you to split this up even more - there are number of changes in here that need more discussion or simply won't be merged as-is. I think the allocator improvements should be their own PR, not touching anything unrelated.
The changes around threading are nonsensical - UEFI is by definition single threaded, so alwaysSingleThreaded() ought to be the function that gets modified, which in turn means the panic handler doesn't need a stub for std.Thread.getCurrentId().
Will do
Welll, kinda. The UEFI PI spec does mention a way to do multiprocessing which in turn can lead to multithreading. |
linusg
commented
Feb 8, 2025
You don't even need to run code on another core for "threading", but from the standard library's point of view we always have a single thread. Preparing for a scenario that is unlikely to ever be useful and only creates the need for more stub code isn't desirable, let's start small :) |
RossComputerGuy
commented
Feb 8, 2025
I found out while slicing this PR that we actually do have to mock threading. |
31ebae5 to
3d3cfa3CompareRossComputerGuy
commented
Feb 8, 2025
Ok, things are split up now. I went through and dropped changes which were not necessary to make this work: $ zig init
$ zig build -Dtarget=$(uname -m)-uefi |
9220214 to
dad2aa7CompareRossComputerGuy
commented
Feb 8, 2025
Going to do this right now since the UEFI allocators fix PR got merged. |
dad2aa7 to
4e3e840CompareRossComputerGuy
commented
Feb 8, 2025
Allocator stuff is now in #22818 |
Uh oh!
There was an error while loading. Please reload this page.
| // rudimentary utf16 writer | ||
| var index: usize = 0; | ||
| var utf16: [256]u16 = undefined; | ||
| while (iter.nextCodepoint()) |rune| { |
There was a problem hiding this comment.
Probably better to avoid the term rune.
There was a problem hiding this comment.
Is codepoint a better name?
There was a problem hiding this comment.
Renamed to codepoint
There was a problem hiding this comment.
codepoint is a good name, it is coming from nextCodepoint after all.
Uh oh!
There was an error while loading. Please reload this page.
a3461b7 to
5208154CompareRossComputerGuy
commented
Feb 10, 2025
Rebased |
5208154 to
451dc36Compare2ce0eb2 to
2234f1dCompare2234f1d to
d5c9358CompareRossComputerGuy
commented
Apr 10, 2025
Rebased |
d5c9358 to
5d41a4fCompare
dotcarmen
left a comment
There was a problem hiding this comment.
lgtm on first glance, my primary concern is around the change to OpenMode. haven't otherwise read through the posix implementation details which i'll do soon
| .handle = blk: { | ||
| if (is_windows) break :blk windows.peb().ProcessParameters.hStdOutput; | ||
| if (is_uefi) break :blk .{ .simple_output = std.os.uefi.system_table.con_out.? }; | ||
| break :blk posix.STDOUT_FILENO; | ||
| }, |
There was a problem hiding this comment.
nitpicky but can you do this instead and below:
| .handle=blk: { | |
| if (is_windows) break :blkwindows.peb().ProcessParameters.hStdOutput; | |
| if (is_uefi) break :blk .{ .simple_output=std.os.uefi.system_table.con_out.? }; | |
| break :blkposix.STDOUT_FILENO; | |
| }, | |
| .handle=if (is_windows) | |
| windows.peb().ProcessParameters.hStdOutput | |
| elseif (is_uefi) | |
| .{ .simple_output=std.os.uefi.system_table.con_out.? } | |
| else | |
| posix.STDOUT_FILENO, |
it simplifies the control flow and is easier to read
| fn unexpectedError(err: anyerror) error{Unexpected} { | ||
| std.log.err("unexpected error: {}\n", .{err}); | ||
| return error.Unexpected; | ||
| } |
There was a problem hiding this comment.
can you colocate this with std.os.uefi.unexpectedStatus and change the param to err: uefi.Error?
| // 0x8000000000000000 | ||
| create: bool = false, | ||
| }; | ||
| pub const OpenMode = packed struct(u64) { |
There was a problem hiding this comment.
OpenMode only selects 3 options according to UEFI which is why it's an enum. why lift the bits struct?
alternatively you can make this enum open (_) and add toBits and fromBits methods. IMO that's preferred because it's a signal to the user "this is an unsupported value, you probably don't want this"
| const fd = p.open(path_buffer[0..len :0], .{ | ||
| .read = true, | ||
| .write = flags.CREAT or flags.TRUNC or flags.ACCMODE != .RDONLY, | ||
| .create = flags.CREAT, | ||
| }, .{}) catch |err| switch (err) { |
There was a problem hiding this comment.
to avoid creating an invalid OpenMode you can do something like:
constopen_mode: OpenMode=if (flags.CREAT) mode: {
if (!flags.TRUNCorflags.ACCMODE==.RDONLY) {
// UEFI doesn't allow create without write flagsreturnerror.PermissionDenied;
break :mode.read_write_create;
} elseif (flags.TRUNCorflags.ACCMODE!=.RDONLY)
.read_writeelse.read;There was a problem hiding this comment.
so alwaysSingleThreaded() ought to be the function that gets modified, which in turn means the panic handler doesn't need a stub for std.Thread.getCurrentId().
do we need this stub still?
| switch (@TypeOf(result)) { | ||
| void => return 0, | ||
| u8, usize => { | ||
| return result; | ||
| }, | ||
| uefi.Status => { | ||
| return @intFromEnum(result); | ||
| }, | ||
| else => @compileError(bad_efi_main_ret), | ||
| } |
There was a problem hiding this comment.
We're not reintroducing untyped u8/usize return values. Keep this as is and only touch the existing switch (err) {}.
There was a problem hiding this comment.
Fundamentally opposed to this, anything that uses std.posix as a generic fallback impl should have a dedicated UEFI impl instead. std.posix's days are numbered anyway: #24329 (comment)
| return path; | ||
| } | ||
| pub fn realpath(pathname: []const u8, out_buffer: *[PATH_MAX]u8) std.posix.RealPathError![]u8 { |
andrewrk
commented
Jul 14, 2025
Self-requesting a review to block this from landing until I take a look at it. I think this will likely need to be redone differently depending on how this Io interface shakes out. |
andrewrk
commented
Nov 27, 2025
This pull request is not ready for review because:
Since we have moved development to Codeberg, please open your pull request there if you would like to continue these efforts. |
Largely based on #19486, this PR marks me starting to work on UEFI support in Zig again. This time, there will be multiple PR's instead of one so it becomes easier to manage. This PR simply makes the
zig initexample build.I tested this in QEMU by loading in OVMF, entering the UEFI shell, loading

FS0:\bin\$.efi. Thezig-outcan be shared by adding:-drive file=fat:rw:zig-out.