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: implement basic io and improve alloc in uefi #22226

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

RossComputerGuy
Copy link
Contributor

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 init example build.

I tested this in QEMU by loading in OVMF, entering the UEFI shell, loading FS0:\bin\$.efi. The zig-out can be shared by adding: -drive file=fat:rw:zig-out.
image

@@ -1,5 +1,7 @@
const std = @import("../std.zig");

pub const posix = @import("uefi/posix.zig");
Copy link
Member

Choose a reason for hiding this comment

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

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

@@ -66,7 +66,7 @@ pub fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
Impl.wake(ptr, max_waiters);
}

const Impl = if (builtin.single_threaded)
const Impl = if (builtin.single_threaded or builtin.os.tag == .uefi)
Copy link
Member

Choose a reason for hiding this comment

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

Should UEFI be treated as a proper single-threaded target?

zig/src/target.zig

Lines 66 to 81 in 82f35c5

pub fn alwaysSingleThreaded(target: std.Target) bool {
_ = target;
return false;
}
pub fn defaultSingleThreaded(target: std.Target) bool {
switch (target.cpu.arch) {
.wasm32, .wasm64 => return true,
else => {},
}
switch (target.os.tag) {
.haiku => return true,
else => {},
}
return false;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I just didn't know where the source was which defined that heh.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've updated this and changed the defaultSingleThreaded for UEFI.

Copy link
Contributor

Choose a reason for hiding this comment

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

Out of curiosity, is UEFI not always single threaded instead of just default single threaded?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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.

lib/std/fs.zig Outdated
@@ -52,7 +52,7 @@ pub const MAX_PATH_BYTES = @compileError("deprecated; renamed to max_path_bytes"
/// * On other platforms, `[]u8` file paths are opaque sequences of bytes with
/// 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,
Copy link
Collaborator

@linusg linusg Dec 19, 2024

Choose a reason for hiding this comment

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

This looks wrong, I'd assume it needs to use similar logic as windows

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this ties in with #22226 (comment)


pub fn next(self: *Self) Error!?Entry {
_ = self;
return null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Magic sleep needs a comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This part of the code is based on what @truemedian did. I'm not sure either so CC'ing them for some information.

Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I likely will remove it and it'll be up to the program to do that if they want to.

Copy link
Collaborator

@linusg linusg left a comment

Choose a reason for hiding this comment

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

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().

@RossComputerGuy
Copy link
Contributor Author

RossComputerGuy commented Feb 8, 2025

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.

Will do

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().

Welll, kinda. The UEFI PI spec does mention a way to do multiprocessing which in turn can lead to multithreading.

@linusg
Copy link
Collaborator

linusg commented Feb 8, 2025

Welll, kinda. The UEFI PI spec does mention a way to do multiprocessing which in turn can lead to multithreading.

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
Copy link
Contributor Author

I found out while slicing this PR that we actually do have to mock threading.

/home/ross/zig/lib/std/Thread.zig:558:9: error: Unsupported operating system uefi
        @compileError("Unsupported operating system " ++ @tagName(native_os));
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    getCurrentId: /home/ross/zig/lib/std/Thread.zig:533:27
    getCurrentId: /home/ross/zig/lib/std/Thread.zig:354:29
    lock: /home/ross/zig/lib/std/Thread/Mutex/Recursive.zig:50:54
    lockStdErr: /home/ross/zig/lib/std/Progress.zig:545:22
    lockStdErr: /home/ross/zig/lib/std/debug.zig:198:28
    print__anon_1981: /home/ross/zig/lib/std/debug.zig:208:15
    main: src/main.zig:7:20
    EfiMain: /home/ross/zig/lib/std/start.zig:223:37
    comptime: /home/ross/zig/lib/std/start.zig:75:58
    start: /home/ross/zig/lib/std/std.zig:97:27
    comptime: /home/ross/zig/lib/std/std.zig:168:9

@RossComputerGuy
Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants