-
Notifications
You must be signed in to change notification settings - Fork 9
Implement Linux bootable image support #20
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -120,6 +120,35 @@ cache fills. | |
| Adds the `start_core` function to start another CPU core via a PSCI `CPU_ON` call. This adds a | ||
| dependency on the `smccc` crate. | ||
|
|
||
| ### `relocate` | ||
|
|
||
| Relocates binary symbols to the memory offset where the binary was loaded. | ||
| This allows for building binaries that can be loaded at arbitrary locations by | ||
| the bootloader/hypervisor. | ||
|
|
||
| To perform relocations, the linker must be instructed to generate a position | ||
| independent executable and to generate relocation entries for text relocations. | ||
|
|
||
| These options can be passed to the linker by `rustc` through some manner, e.g. | ||
| a `.cargo/config.toml` file: | ||
|
|
||
| ```toml | ||
| [target."aarch64-unknown-none"] | ||
| rustflags = [ | ||
| "-C", "relocation-model=pie", | ||
| "-C", "link-args=-z notext", | ||
| ] | ||
| ``` | ||
|
|
||
| Also make sure your image's origin is set to zero in your linker script: | ||
|
|
||
| ```ld | ||
| MEMORY | ||
| { | ||
| image : ORIGIN = 0x0, LENGTH = 2M | ||
| } | ||
| ``` | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document that the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not necessarily, you can use
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah okay, let's document that then.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that actually valid? Talking to some folks at Arm, it sounds like this might not work, because if a device is incorrectly mapped as normal memory then the CPU might speculatively access it, which can cause the device to perform arbitrary unwanted operations.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack, will write it as incompatible then. Thanks! |
||
| ## License | ||
|
|
||
| Licensed under either of | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,19 +6,82 @@ | |
|
|
||
| use core::arch::naked_asm; | ||
|
|
||
| /// This is a generic entry point for an image. It carries out the operations required to prepare the | ||
| /// loaded image to be run. Specifically, it zeroes the bss section using registers x25 and above, | ||
| /// prepares the stack, enables floating point, and sets up the exception vector. It preserves x0-x3 | ||
| /// for the Rust entry point, as these may contain boot parameters. | ||
| use crate::rust_entry; | ||
|
|
||
| /// This is a generic entry point for an image that calls [`entry_early_prepare`]. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// This function is marked unsafe because it should never be called by anyone. The linker is | ||
| /// responsible for setting it as the entry function. | ||
| #[cfg(not(feature = "relocate"))] | ||
| #[unsafe(naked)] | ||
| #[unsafe(link_section = ".init.entry")] | ||
| #[unsafe(export_name = "entry")] | ||
| unsafe extern "C" fn entry() -> ! { | ||
| naked_asm!( | ||
| "b {entry_early_prepare}", | ||
| entry_early_prepare = sym entry_early_prepare | ||
| ) | ||
| } | ||
|
|
||
| /// This is a generic entry point for an image prefixed with an [AArch64 Linux kernel boot | ||
| /// header](https://docs.kernel.org/arch/arm64/booting.html) that calls [`entry_early_prepare`]. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// This function is marked unsafe because it should never be called by anyone. The linker is | ||
| /// responsible for setting it as the entry function. | ||
| #[cfg(feature = "relocate")] | ||
| #[unsafe(naked)] | ||
| #[unsafe(link_section = ".init.entry")] | ||
| #[unsafe(export_name = "entry")] | ||
| unsafe extern "C" fn entry() -> ! { | ||
| const HEADER_FLAG_ENDIANNESS: u64 = cfg!(target_endian = "big") as u64; | ||
| // 0 - Unspecified, 1 - 4K, 2 - 16K, 3 - 64K | ||
| const HEADER_FLAG_PAGE_SIZE: u64 = 1; | ||
| const HEADER_FLAG_PHYSICAL_PLACEMENT: u64 = 1; | ||
| const HEADER_FLAGS: u64 = HEADER_FLAG_ENDIANNESS | ||
| | (HEADER_FLAG_PAGE_SIZE << 1) | ||
| | (HEADER_FLAG_PHYSICAL_PLACEMENT << 3); | ||
|
|
||
| naked_asm!( | ||
| // code0 | ||
| "b {entry_early_prepare}", | ||
| // code1 | ||
| "nop", | ||
|
|
||
| // text_offset | ||
| ".quad 0x0", | ||
| // image_size | ||
| ".quad bin_end - entry", | ||
| // flags | ||
| ".quad {HEADER_FLAGS}", | ||
| // res2 | ||
| ".quad 0", | ||
| // res3 | ||
| ".quad 0", | ||
| // res4 | ||
| ".quad 0", | ||
|
|
||
| // "ARM\x64" magic number | ||
| ".long 0x644d5241", | ||
| // res5 | ||
| ".long 0", | ||
| ".align 3", | ||
| entry_early_prepare = sym entry_early_prepare, | ||
| HEADER_FLAGS = const HEADER_FLAGS.to_le(), | ||
| ) | ||
| } | ||
|
|
||
| /// Early entry point preparations. | ||
| /// | ||
| /// It carries out the operations required to prepare the loaded image to be run. Specifically, it | ||
| /// zeroes the bss section using registers x25 and above, prepares the stack, enables floating | ||
| /// point, and sets up the exception vector. It preserves x0-x3 for the Rust entry point, as these | ||
| /// may contain boot parameters. | ||
| #[unsafe(naked)] | ||
| unsafe extern "C" fn entry_early_prepare() -> ! { | ||
| naked_asm!( | ||
| ".macro adr_l, reg:req, sym:req", | ||
| r"adrp \reg, \sym", | ||
|
|
@@ -42,9 +105,66 @@ unsafe extern "C" fn entry() -> ! { | |
| // Prepare the stack. | ||
| "adr_l x30, boot_stack_end", | ||
| "mov sp, x30", | ||
| // Perform final Rust entrypoint setup | ||
| "b {entry_prepare_image}", | ||
| ".purgem adr_l", | ||
| entry_prepare_image = sym entry_prepare_image | ||
| ) | ||
| } | ||
|
|
||
| #[cfg(not(feature = "relocate"))] | ||
| #[unsafe(naked)] | ||
| unsafe extern "C" fn entry_prepare_image() -> ! { | ||
| naked_asm!( | ||
| // Call into Rust code. | ||
| "b {rust_entry}", | ||
| rust_entry = sym rust_entry | ||
| ) | ||
| } | ||
|
|
||
| #[cfg(feature = "relocate")] | ||
| #[unsafe(naked)] | ||
| unsafe extern "C" fn entry_prepare_image() -> ! { | ||
| naked_asm!( | ||
| ".macro adr_l, reg:req, sym:req", | ||
| r"adrp \reg, \sym", | ||
| r"add \reg, \reg, :lo12:\sym", | ||
| ".endm", | ||
| // Where the image was loaded | ||
| "adr_l x7, text_begin", | ||
| // Start relocating. | ||
| // let mut rela: *mut Elf64Rela = __rela_start; | ||
| "adr_l x9, __rela_start", | ||
| // let rela_end: *mut Elf64Rela = __rela_end; | ||
| "adr_l x8, __rela_end", | ||
| "b 1f", | ||
| "2:", | ||
| // rela = rela.wrapping_offset(1); | ||
| "add x9, x9, #24", | ||
| "1:", | ||
| // while rela < rela_end { | ||
| "cmp x9, x8", | ||
| "b.hs 3f", | ||
| // let r_type = unsafe { *rela }.r_info & 0xffff_ffff; | ||
| "ldr w10, [x9, #8]", | ||
| // if r_type != R_AARCH64_RELATIVE { continue }; | ||
| "cmp w10, #1027", | ||
| "b.ne 2b", | ||
| // let mut new_ptr = unsafe { *rela }.r_addend; | ||
| "ldr x10, [x9, #16]", | ||
| // let r_offset = unsafe { *rela }.r_offset; | ||
| "ldr x11, [x9]", | ||
| // new_ptr += offset; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can simplify this a bit, and remove the need for the jump at the start of the loop, by doing something like ldp x10, x11, [x9], #24
ldr x12, [x9, #-8]Note that
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do we know that no other RELA types can occur? |
||
| "add x10, x10, x7", | ||
| // unsafe { *(offset + r_offset) } = new_ptr; | ||
| "str x10, [x7, x11]", | ||
| "b 2b", | ||
| "3:", | ||
| // End of relocations. | ||
| // Call into Rust code. | ||
| "b {rust_entry}", | ||
| rust_entry = sym crate::rust_entry, | ||
| ".purgem adr_l", | ||
| rust_entry = sym rust_entry, | ||
| ) | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Higher up in this README we recommend passing the linker script in
build.rsviacargo:rustc-link-arg=, so let's suggest the same thing here for these flags if that's possible.