Device behaves erractically after calling boot::open_protocol_exclusive::<DevicePath>
Closed this issue · 4 comments
Thanks for giving us a nice API to UEFI! I have built a very simple demo app to test it, but my device (X12SPi-TF) behaves strangely after the app exits gracefully: I see the boot menu as expected, but I can neither start my app a second time, nor boot into the operating system on my NVMe (GRUB -> Ubuntu Server 2024). No matter what I select, after a brief glitch I get back to the boot menu. After a power reset the device works as expected again, and I can start the app once or boot into GRUB.
My app only attempts to open the DevicePath protocol on all handles that support the BlockIO protocol:
#[entry]
fn main() -> Status {
uefi::helpers::init().unwrap();
info!("uefitest starting up");
test_bug().unwrap();
boot::stall(10_000_000);
Status::SUCCESS
}
fn test_bug() -> Result {
info!("### Enumerating BlockIO handles ###");
let block_io_handles = boot::locate_handle_buffer(SearchType::ByProtocol(&BlockIO::GUID)).unwrap();
for handle in block_io_handles.iter() {
let Ok(_device_path_proto) = boot::open_protocol_exclusive::<DevicePath>(*handle) else {
continue;
};
info!("DevicePath proto opened");
}
Ok(())
}I am testing with uefi 0.35.0 with the logger, panic_handler, alloc, and global_allocator feature (rustc 1.88.0).
Is there something obvious which I am missing, or could this be a firmware bug? I anticipated that an UEFI app could be started a second time, and that I could boot into a mundane OS after running an app. Do I need to reset the environment somehow before exiting? If I omit the boot::open_protocol_exclusive::<DevicePath> call and just loop over the BlockIO handles, everything behaves as expected, and I can boot into GRUB as expected.
Opening the BlockIO protcol with the exclusiv attribute will disconnect the FAT driver from this handle. Therefore the firmware is unable to load the boot options from the filesystem.
If you really need exclusiv access to the BlockIO protocol, you might try to reconnect the FAT driver before your application returns. If this works will depend on your firmware implementation.
Thanks for the insights, I didn't know that reconnecting drivers without depending on device-specific behaviour is impossible.
I am calling open_protocol_exclusive only for the DevicePath protocol so far, are the side effects similar?
I have been trying to get non-exclusive access to a protocol as an app, but I don't understand the API yet - the osdev book says one should use boot::open_protocol[1], but I cannot use open_protocol because ByHandleProtocol would be the appropriate value for an app[2] (?), but it is not a variant of OpenProtocolAttributes. Docstring suggests I should use HandleProtocol (?), but I am unable to find more information on that. The changelog of 0.17.0 says handle_protocol is deprecated and one should use open_protocol_exclusive or open_protocol, and the changelog of 0.20.0 says handle_protocol was removed.
[1] https://rust-osdev.github.io/uefi-rs/how_to/protocols.html?search=HandleProtocol
[2] https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#efi-boot-services-openprotocol
I have been trying to get non-exclusive access to a protocol as an app, but I don't understand the API yet
Use boot::open_protocol with OpenProtocolAttributes::GetProtocol. Here's an example:
uefi-rs/uefi-test-runner/src/proto/media.rs
Lines 257 to 264 in d30f3c3
The documentation (and spec) are a little confusing since they say it's for drivers, but it works just as well with applications. (You can see in edk2 that BY_HANDLE_PROTOCOL and GET_PROTOCOL are handled the same way, and you can see example usages in popular UEFI applications such as shim and grub.) We should certainly update our docs to make this less confusing.