rust-osdev / uefi-rs

Rusty wrapper for the Unified Extensible Firmware Interface (UEFI). This crate makes it easy to develop Rust software that leverages safe, convenient, and performant abstractions for UEFI functionality.
https://rust-osdev.com/uefi-book
Mozilla Public License 2.0
1.32k stars 159 forks source link

Allow building device paths as constants #983

Open nicholasbishop opened 1 year ago

nicholasbishop commented 1 year ago

Splitting this off from https://github.com/rust-osdev/uefi-rs/issues/970.

It would be nice to be able to build device paths as global constants as well. This would sidestep the use-after-free issues from nix-community/lanzaboote#194.

Originally posted by @blitz in https://github.com/rust-osdev/uefi-rs/issues/970#issuecomment-1775618414

nicholasbishop commented 1 year ago

I took a look at this, and I think it would be quite difficult to do right now with const types and functions. Many of the tools we want are available in unstable Rust, but it may be a while before it becomes straightforward to do in stable Rust. A couple specific things:

  1. There's no way to call trait methods from const fns (you can't make a const trait, or declare methods in a trait const, or create a const impl of a trait). So the current BuildNode interface wouldn't work at all. There is a fairly straightforward workaround for this one, albeit not super ergonomic: we could generate a big BuildNode enum containing all the node types.
  2. Mutable references are not allowed in const fns. Mutable pointers are, but you can't do much with them; writing and dereferencing are not allowed. So that means you can't pass a byte buffer into a const fn and write to it. The alternative here is to create the byte buffer within the const fn and return it. That works, but copying in the data gets pretty gnarly. You can't use copy_from_slice or anything like that. You could copy the bytes one at a time in a loop, but you can't even use a for loop in a const fn, so it all has to be done very manually and verbosely. EDIT: mut refs in const fns were stabilized in 1.83

Given the difficulties, and the fact that this will (probably) get easier in the future as more things get stabilized, and the fact that I suspect fully const/static device paths are rare, I recommend we not try to add a const device path builder yet. Some potential alternatives for projects that want this:

  1. Generate the device path in build.rs. You can add uefi-rs as a build dependency and use the regular DevicePathBuilder to create a path, then call as_bytes to get the raw bytes. Write those raw bytes out to a file and include_bytes! in your source. Or generate a bit of Rust code containing the raw bytes and include! that.
  2. Hardcode the raw bytes in the static, then have a unit test that creates the same path with DevicePathBuilder. Compare the byte slices and make sure they match.

Lastly, it's totally possible I missed something in my exploration of what const fns can do, and maybe it's not actually as hard to add a const device path builder as I think -- happy to hear if I missed anything!

phip1611 commented 1 year ago

The approach with build.rs might be a good solution. That's a good idea.