uefi-rs icon indicating copy to clipboard operation
uefi-rs copied to clipboard

RISCV support

Open NathanMcMillan54 opened this issue 3 years ago • 28 comments

From skimming through this and not looking up anything else, I thought of starting a UEFI application in Rust for RISCV. Could or should this library make support RISCV?

NathanMcMillan54 avatar Jun 17 '21 20:06 NathanMcMillan54

Hello. Definitely, uefi-rs aims to support UEFI systems on all possible architectures! The core library and our build/test runner script already has support for multiple architectures (x86_64 + AArch64).

The only possible issues with adding support for RISCV are:

  • bugs in the upstream Rust compiler (these might've been fixed already)
  • experience with the platform (I haven't personally used this arch before).

If you're familiar with RISCV and want to help add support for it to this project, you're more than welcome to contribute :smile:

GabrielMajeri avatar Jun 18 '21 16:06 GabrielMajeri

Lol no I've never done anything with RISCV, I was just wondering if it was possible. But I guess I will fork it and try to do something for RISCV.

NathanMcMillan54 avatar Jun 19 '21 14:06 NathanMcMillan54

I was going through the commits for Aarch64 support 5e7a615 and f02a899, and it looks like the only thing that changed was uefi-test-runner and the documentation. Are those the only things that need to change for a new architecture?

NathanMcMillan54 avatar Jun 19 '21 17:06 NathanMcMillan54

@NathanMcMillan54 assuming the upstream Rust compiler supports the UEFI calling convention on that architecture, yes. It was also necessary to define a target .json file for AArch64 since the compiler doesn't yet natively understand how to generate binaries for that architecture.

GabrielMajeri avatar Jun 21 '21 10:06 GabrielMajeri

Rust doesn't have a riscv64-unknown-windows (like x86_64) or a riscv64-pc-windows-msvc (like Aarch64) for a llvm target. So I guess it's just not possible to compile for UEFI right now.

NathanMcMillan54 avatar Jun 21 '21 11:06 NathanMcMillan54

Same, I haven't done anything with RISCV so this is kinda a bug project for me.

NathanMcMillan54 avatar Jun 22 '21 10:06 NathanMcMillan54

@orvij the build issue is probably related to #241. The src/proto/shim/mod.rs file has to be commented/removed temporarily, until we get it fixed.

GabrielMajeri avatar Jun 23 '21 10:06 GabrielMajeri

@orvij I don't care if you make RISCV support and I don't. This issue was really just a question.

NathanMcMillan54 avatar Jun 23 '21 14:06 NathanMcMillan54

My summer break from school starts at the end of today so I could do literally nothing but work on RISCV support, but you can decide since you seem like you're already working on it.

NathanMcMillan54 avatar Jun 23 '21 14:06 NathanMcMillan54

@orvij I meant #242, but it's fixed now.

GabrielMajeri avatar Jun 24 '21 08:06 GabrielMajeri

Would you not need to modify LLVM for it to generate PE binaries for UEFI? A target file will work but you'll still output ELF. And converting ELF to PE is hacky.

ethindp avatar Jun 26 '21 04:06 ethindp

I've faced some serious issues with LLVM and generating riscv PE/COFF binaries for LLVM, so it would be interesting if it works with Rust. Basically, no matter what I would do llvm would generate an ELF object file for riscv64 uefi and of course fail to link it as a UEFI binary. I've faced this issue both with clang and zig. If someone gets something working, please let me know what you did.

sreehax avatar Jul 01 '21 03:07 sreehax

given my experience your last point is most definitely true: I've never gotten any lang or compiler that uses LLVM to output a riscv PE object. the only solution I've seen employed in the wild is making a custom crt0 in assembly that fakes a PE/COFF header and calls into your code compiled for freestanding, and the whole thing is objcopy'd to a flat binary to expose the PE header. It's not ideal, but it does work

sreehax avatar Jul 21 '21 04:07 sreehax

PE/COFF does support RISC-V (32/64/128). You want to generate a binary for riscv64gc/riscv32gc/riscv128gc, not riscv32/riscv64/riscv128. I don't think LLVM supports this though. A way around this would be to use something like cargo-make to add a post-build step. After the ELF image is generated, use something like goblin to "convert" the image to a valid EFI PE/COFF image. That would be really involved but it might work, and I think its effectively what objcopy does.

ethindp avatar Jul 21 '21 17:07 ethindp

object now has a simple elftoefi example that could be used as a basis for this. The example is untested other than checking that the resulting PE file can be parsed. It's missing some relocation support that EDK2 does, but I haven't looked into whether that is required.

philipc avatar Sep 30 '21 02:09 philipc

Any update on this? I'm using the bootloader crate from the rust-osdev org and if we can get RISC-V EFI support making that bootloader work with RISC-V would be trivial to do. EFI in general would make life so much easier -- no need to write hacky and nasty asm at all.

ethindp avatar Jan 02 '22 08:01 ethindp

I didn't do any further work on the elftoefi example. Building uefi-test-runner for riscv64gc-unknown-none-elf gives an ELF that doesn't have any relocations, so I'm hopeful it might work as is, but I don't know how to test it. Looks like to test it with qemu I need some OVMF files?

philipc avatar Jan 03 '22 05:01 philipc

@philipc Yeah you might need to build OVMF. I can't seem to find OvMF for RISC-V in the AUR so I don't know if any other distros have it or not. And I've never built OvMF for anything but x86 so I'd need to poke around but I can probably get you an OVMF image. Maybe. Lol

ethindp avatar Jan 03 '22 05:01 ethindp

I don't think OVMF has been ported for RISC-V yet. At least, https://github.com/riscv-collab/riscv-edk2/tree/master/OvmfPkg doesn't have any relevant commits.

It might be possible to use the bootefi command in u-boot? I don't know enough about this stuff to know if that makes sense.

~Also need to investigate why this has nothing besides a bit of .eh_frame:~ (needed a custom target for entry point)

   text	   data	    bss	    dec	    hex	filename
     28	      0	      0	     28	     1c	target/riscv64gc-unknown-none-elf/debug/uefi-test-runner

philipc avatar Jan 04 '22 09:01 philipc

My current work is at https://github.com/philipc/uefi-rs/commit/a8d6c6b6e8406978f0ca1a5a8e46cac094ce8715. This includes an ELF target for rustc. The linker options and linker script are derived from EDK2. build.py is modified to use EDK2's GenFw to convert the ELF to an EFI (~object's elftoefi doesnt work yet but I'll fix that later~ edit: fixed elftoefi so it uses that now instead of GenFw). I can then test this under Ubuntu 21.10 with:

sudo apt install qemu-system-misc u-boot-qemu opensbi

./build.py --target riscv64 build

qemu-system-riscv64 \
    -machine virt \
    -cpu rv64 \
    -m 1G \
    -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf \
    -kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \
    -nographic \
    -device ich9-ahci,id=ahci \
    -drive if=none,file=fat:rw:target/riscv64-unknown-uefi/debug/esp/,format=raw,id=mydisk \
    -device ide-hd,drive=mydisk,bus=ahci.0

load scsi 0:1 ${kernel_addr_r} EFI/Boot/BootRV64.efi
bootefi ${kernel_addr_r}

Those last two commands are in u-boot. It looks like this setup doesn't provide everything that uefi-test-runner requires, but it at least runs something.

This is all very rough, and I'll clean it up more when I have time, but it'll probably require someone else to do the work to get it integrated here.

philipc avatar Jan 06 '22 09:01 philipc

Very nice. It appears that you can't build oVMF for RISCV yet (sad). Using U-boot is an option though.

ethindp avatar Jan 06 '22 22:01 ethindp

I tried to add a target configuration to rustc itself with llvm_target values riscv64-unknown-windows, but it looks like LLVM doesn't recognize it as PE format. It shows like:

Building stage1 library artifacts (x86_64-unknown-linux-gnu -> riscv64imac-unknown-uefi)
   Compiling compiler_builtins v0.1.87
   Compiling core v0.0.0 (/home/luojia/UefiRiscv/rust/library/core)
   Compiling rustc-std-workspace-core v1.99.0 (/home/luojia/UefiRiscv/rust/library/rustc-std-workspace-core)
error: unknown flag
  |
note: instantiated into assembly here
 --> <inline asm>:1:21
  |
1 | .section .llvmbc,"n"
  |                     ^

error: unknown flag
  |
note: instantiated into assembly here
 --> <inline asm>:3:22
  |
3 | .section .llvmcmd,"n"
  |                      ^

error: could not compile `rustc-std-workspace-core` due to 2 previous errors
warning: build failed, waiting for other jobs to finish...
error: could not compile `core` due to 2 previous errors
error: could not compile `compiler_builtins` due to 2 previous errors

where "n" section flag would exist under PE. I tried not to build core library and fill in #![no_core] language items manually. But it shows that crate object won't accept Riscv64 as PE target.

thread 'rustc' panicked at 'called `Result::unwrap()` on an `Err` value: Error("unimplemented architecture Riscv64")', compiler/rustc_codegen_ssa/src/back/link.rs:1785:53
stack backtrace:
   0:     0x7f073bafeb97 - std::backtrace_rs::backtrace::trace_unsynchronized::h8317dca8f1569d65
   1:     0x7f073bab13c7 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hf47fa547ae6f40de
   2:     0x7f073bb55247 - core::fmt::write::h485466f956dc1ae8
   3:     0x7f073babd3f1 - std::io::Write::write_fmt::hee9856a9efbff71e
   4:     0x7f073bab122b - std::sys_common::backtrace::print::h5738de0db63ef2a1
   5:     0x7f073bac8be7 - std::panicking::default_hook::{{closure}}::h885f531b4751288f
   6:     0x7f073bac898f - std::panicking::default_hook::h272acffc233193d2
   7:     0x7f073c4bd17f - rustc_driver_impl[8a5bf62dccc24c17]::DEFAULT_HOOK::{closure#0}::{closure#0}
   8:     0x7f073bac9154 - std::panicking::rust_panic_with_hook::h27b56f6e5ce0ef73
   9:     0x7f073bad3923 - std::panicking::begin_panic_handler::{{closure}}::h65ad7d4b3d4fcd12
  10:     0x7f073bad385c - std::sys_common::backtrace::__rust_end_short_backtrace::h5f5e0232bacf8eba
  11:     0x7f073bac8cc1 - rust_begin_unwind
  12:     0x7f073bab0843 - core::panicking::panic_fmt::h840d3096004ea8aa
  13:     0x7f073bab0cc3 - core::result::unwrap_failed::h6d3762b511f72022
  14:     0x7f073df93530 - rustc_codegen_ssa[715d2fe257d5e31e]::back::link::linker_with_args
  15:     0x7f073df87fce - rustc_codegen_ssa[715d2fe257d5e31e]::back::link::link_natively
  16:     0x7f073df852d2 - rustc_codegen_ssa[715d2fe257d5e31e]::back::link::link_binary
  17:     0x7f073c636924 - <rustc_codegen_llvm[fe2ddc96e13bf5f]::LlvmCodegenBackend as rustc_codegen_ssa[715d2fe257d5e31e]::traits::backend::CodegenBackend>::link
  18:     0x7f073c55b72a - <rustc_interface[1f091050b014c472]::queries::Linker>::link
  19:     0x7f073c53822a - rustc_span[9ba7f977b36937e2]::with_source_map::<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_interface[1f091050b014c472]::interface::run_compiler<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_driver_impl[8a5bf62dccc24c17]::run_compiler::{closure#1}>::{closure#0}::{closure#0}>
  20:     0x7f073c52498b - <scoped_tls[b966ac58386d5624]::ScopedKey<rustc_span[9ba7f977b36937e2]::SessionGlobals>>::set::<rustc_interface[1f091050b014c472]::interface::run_compiler<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_driver_impl[8a5bf62dccc24c17]::run_compiler::{closure#1}>::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>
  21:     0x7f073c540ec7 - std[3bf57732816ce612]::sys_common::backtrace::__rust_begin_short_backtrace::<rustc_interface[1f091050b014c472]::util::run_in_thread_pool_with_globals<rustc_interface[1f091050b014c472]::interface::run_compiler<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_driver_impl[8a5bf62dccc24c17]::run_compiler::{closure#1}>::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>
  22:     0x7f073c53cbc6 - <core[78a717fb536b413e]::panic::unwind_safe::AssertUnwindSafe<<std[3bf57732816ce612]::thread::Builder>::spawn_unchecked_<rustc_interface[1f091050b014c472]::util::run_in_thread_pool_with_globals<rustc_interface[1f091050b014c472]::interface::run_compiler<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_driver_impl[8a5bf62dccc24c17]::run_compiler::{closure#1}>::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#1}::{closure#0}> as core[78a717fb536b413e]::ops::function::FnOnce<()>>::call_once
  23:     0x7f073c53626e - std[3bf57732816ce612]::panicking::try::<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, core[78a717fb536b413e]::panic::unwind_safe::AssertUnwindSafe<<std[3bf57732816ce612]::thread::Builder>::spawn_unchecked_<rustc_interface[1f091050b014c472]::util::run_in_thread_pool_with_globals<rustc_interface[1f091050b014c472]::interface::run_compiler<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_driver_impl[8a5bf62dccc24c17]::run_compiler::{closure#1}>::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#1}::{closure#0}>>
  24:     0x7f073c4b67c9 - std[3bf57732816ce612]::panic::catch_unwind::<core[78a717fb536b413e]::panic::unwind_safe::AssertUnwindSafe<<std[3bf57732816ce612]::thread::Builder>::spawn_unchecked_<rustc_interface[1f091050b014c472]::util::run_in_thread_pool_with_globals<rustc_interface[1f091050b014c472]::interface::run_compiler<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_driver_impl[8a5bf62dccc24c17]::run_compiler::{closure#1}>::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#1}::{closure#0}>, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>
  25:     0x7f073c53de54 - <<std[3bf57732816ce612]::thread::Builder>::spawn_unchecked_<rustc_interface[1f091050b014c472]::util::run_in_thread_pool_with_globals<rustc_interface[1f091050b014c472]::interface::run_compiler<core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>, rustc_driver_impl[8a5bf62dccc24c17]::run_compiler::{closure#1}>::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[78a717fb536b413e]::result::Result<(), rustc_span[9ba7f977b36937e2]::ErrorGuaranteed>>::{closure#1} as core[78a717fb536b413e]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
  26:     0x7f073bb06958 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::hfa8d67098fec2471
  27:     0x7f073bad498a - std::sys::unix::thread::Thread::new::thread_start::hb51be3c5a4172ca9
  28:     0x7f073b89bb43 - start_thread
                               at ./nptl/./nptl/pthread_create.c:442:8
  29:     0x7f073b92da00 - clone3
                               at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
  30:                0x0 - <unknown>

error: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: rustc 1.69.0-dev running on x86_64-unknown-linux-gnu

note: compiler flags: --crate-type bin -C embed-bitcode=no -C debuginfo=2 -C incremental=[REDACTED]

note: some of the compiler flags provided by cargo are hidden

Any further ideas?

luojia65 avatar Mar 02 '23 09:03 luojia65

But it shows that crate object won't accept Riscv64 as PE target.

To be pedantic, object would need to support Riscv64 for writing Windows COFF object files. That's never going to happen though, not even LLVM can do that. Instead, you need to compile to ELF and then convert the ELF to a EFI, which was the same approach that EDK2 used last time I looked. See https://github.com/rust-osdev/uefi-rs/issues/241#issuecomment-1006426837 for a proof of concept of doing that.

philipc avatar Mar 02 '23 10:03 philipc

To be pedantic, object would need to support Riscv64 for writing Windows COFF object files. That's never going to happen though, not even LLVM can do that.

I'm not clear on why that would never happen. I think adding that support for riscv64+COFF to LLVM is indeed what's needed here, and then adding a riscv64-uefi target to rustc would presumably be reasonably straightforward.

nicholasbishop avatar Mar 03 '23 02:03 nicholasbishop

Fair enough, never is too strong, but it's a lot of work to add riscv64+COFF support to all the places that are required, and that work would need to start somewhere other than in the object crate, whereas compiling to ELF and converting ELF to EFI is something that can be done right now.

philipc avatar Mar 03 '23 02:03 philipc

As to where you would start with riscv64+COFF support, it would probably be by adding COFF support in this directory: https://github.com/llvm/llvm-project/tree/main/llvm/lib/Target/RISCV/MCTargetDesc. See other targets for examples. You would need to make your own RISCV COFF relocation definitions, since I don't think anyone has ever used RISCV with COFF before.

philipc avatar Mar 03 '23 03:03 philipc

FWIW, in NixOS, I am adding support for OVMF "properly": https://github.com/NixOS/nixpkgs/pull/226485 in order to compile https://github.com/nix-community/lanzaboote for RISCV64GC.

I am planning to take a look to adding RISCV COFF relocation defs if I have time (I never did that though in my life.)

RaitoBezarius avatar Apr 16 '23 15:04 RaitoBezarius

I'm not sure that adding RISCV COFF relocation defs is an option. @luojia65 started on a LLVM patch, and one of the LLVM reviewers said:

This isn't actionable unless Microsoft gain an interest in Windows on RISC-V, so I don't think this is something we want to have a comment for as it's not possible to fix

philipc avatar Apr 17 '23 12:04 philipc