Skip to content

Commit 82c98b9

Browse files
committed
fix: 🐛 Move reset function to assembly for chainboot
1 parent 88c088b commit 82c98b9

File tree

4 files changed

+77
-81
lines changed

4 files changed

+77
-81
lines changed

bin/chainboot/src/boot.rs

Lines changed: 3 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -28,81 +28,8 @@ pub unsafe extern "C" fn _start() -> ! {
2828
unsafe { reset() };
2929
}
3030

31-
#[unsafe(no_mangle)]
32-
#[unsafe(link_section = ".text.chainboot")]
33-
pub unsafe extern "C" fn reset() -> ! {
34-
use core::{
35-
cell::UnsafeCell,
36-
sync::{atomic, atomic::Ordering},
37-
};
38-
39-
// These are a problem, because they are not interpreted as constants here.
40-
// Subsequently, this code tries to read values from not-yet-existing data locations.
41-
unsafe extern "Rust" {
42-
// Boundaries of the .bss section, provided by the linker script
43-
static __BSS_START: UnsafeCell<()>;
44-
static __BSS_SIZE_U64S: UnsafeCell<()>;
45-
// Load address of the kernel binary
46-
static __binary_nonzero_lma: UnsafeCell<()>;
47-
// Address to relocate to and image size
48-
static __binary_nonzero_vma: UnsafeCell<()>;
49-
static __binary_nonzero_vma_end_exclusive: UnsafeCell<()>;
50-
// Stack top
51-
static __boot_core_stack_end_exclusive: UnsafeCell<()>;
52-
}
53-
54-
// This tries to call memcpy() at a wrong linked address - the function is in relocated area!
55-
56-
// Relocate the code.
57-
// Emulate
58-
// core::ptr::copy_nonoverlapping(
59-
// __binary_nonzero_lma.get() as *const u64,
60-
// __binary_nonzero_vma.get() as *mut u64,
61-
// __binary_nonzero_vma_end_exclusive.get() as usize - __binary_nonzero_vma.get() as usize,
62-
// );
63-
let binary_size = unsafe { __binary_nonzero_vma_end_exclusive.get() } as usize
64-
- unsafe { __binary_nonzero_vma.get() } as usize;
65-
unsafe {
66-
local_memcpy(
67-
__binary_nonzero_vma.get() as *mut u8,
68-
__binary_nonzero_lma.get() as *const u8,
69-
binary_size,
70-
)
71-
};
72-
73-
// This tries to call memset() at a wrong linked address - the function is in relocated area!
74-
75-
// Zeroes the .bss section
76-
// Emulate
77-
// crate::stdmem::local_memset(__bss_start.get() as *mut u8, 0u8, __bss_size.get() as usize);
78-
let bss = unsafe {
79-
core::slice::from_raw_parts_mut(
80-
__BSS_START.get() as *mut u64,
81-
__BSS_SIZE_U64S.get() as usize,
82-
)
83-
};
84-
for i in bss {
85-
*i = 0;
86-
}
87-
88-
// Don't cross this line with loads and stores. The initializations
89-
// done above could be "invisible" to the compiler, because we write to the
90-
// same memory location that is used by statics after this point.
91-
// Additionally, we assume that no statics are accessed before this point.
92-
atomic::compiler_fence(Ordering::SeqCst);
93-
94-
let max_kernel_size = unsafe { __binary_nonzero_vma.get() } as u64
95-
- unsafe { __boot_core_stack_end_exclusive.get() } as u64;
96-
unsafe { crate::kernel_init(max_kernel_size) }
31+
unsafe extern "Rust" {
32+
fn reset() -> !;
9733
}
9834

99-
#[inline(always)]
100-
#[unsafe(link_section = ".text.chainboot")]
101-
unsafe fn local_memcpy(mut dest: *mut u8, mut src: *const u8, n: usize) {
102-
let dest_end = unsafe { dest.add(n) };
103-
while dest < dest_end {
104-
unsafe { *dest = *src };
105-
dest = unsafe { dest.add(1) };
106-
src = unsafe { src.add(1) };
107-
}
108-
}
35+
core::arch::global_asm!(include_str!("boot.s"));

bin/chainboot/src/boot.s

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Pre-boot code.
3+
* Used only because Rust's AM considers UB any access to statics before statics
4+
* have been initialized. This is exactly the case for the boot code.
5+
* So we avoid referencing any statics in the Rust code, and delegate the
6+
* task to assembly piece instead.
7+
*/
8+
9+
// Load the address of a symbol into a register, PC-relative.
10+
//
11+
// The symbol must lie within +/- 4 GiB of the Program Counter.
12+
//
13+
// # Resources
14+
//
15+
// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html
16+
.macro ADR_REL register, symbol
17+
adrp \register, \symbol
18+
add \register, \register, #:lo12:\symbol
19+
.endm
20+
21+
// .section .text.chainboot.entry
22+
23+
.section .text.chainboot
24+
/// Reset function.
25+
///
26+
/// Initializes the bss section before calling into the user's `main()`.
27+
///
28+
/// # Safety
29+
///
30+
/// We assume that no statics are accessed before transition to main from this function.
31+
///
32+
/// We are guaranteed to be in EL1 non-secure mode here.
33+
reset:
34+
ADR_REL x0, __boot_core_stack_end_exclusive
35+
mov sp, x0
36+
37+
// Relocate the code from __binary_nonzero_lma to __binary_nonzero_vma
38+
ADR_REL x1, __binary_nonzero_lma // Load address of the kernel binary
39+
ADR_REL x2, __binary_nonzero_vma // Address to relocate to
40+
ADR_REL x3, __binary_nonzero_vma_end_exclusive // To calculate image size
41+
42+
sub x0, x2, x0 // max loadable kernel size = VMA - SP
43+
44+
// Relocate the code.
45+
sub x4, x3, x2 // x4 = Image size
46+
47+
.L__relocate_loop:
48+
ldp x5, x6, [x1], #16
49+
stp x5, x6, [x2], #16
50+
sub x4, x4, #16
51+
b.nz .L__relocate_loop
52+
53+
// Initialize BSS
54+
// Assumptions: BSS start is u64-aligned, BSS end is u128-aligned.
55+
// __BSS_START and __BSS_END are defined in linker script
56+
ADR_REL x1, __BSS_START
57+
ADR_REL x2, __BSS_END
58+
.L__bss_init_loop:
59+
stp xzr, xzr, [x1], #16
60+
cmp x1, x2
61+
b.lt .L__bss_init_loop
62+
63+
// max_kernel_size is already in x0 here
64+
b kernel_init
65+
66+
.size reset, . - reset
67+
.type reset, function
68+
.global reset

bin/chainboot/src/link.ld

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ SECTIONS
5555
*(.text.chainboot)
5656
} :segment_start_code
5757

58-
/* Align to 8 bytes, b/c relocating the binary is done in u64 chunks */
59-
. = ALIGN(8);
58+
/* Align to 16 bytes, b/c relocating the binary is done in u128 chunks */
59+
. = ALIGN(16);
6060

6161
__binary_nonzero_lma = .;
6262

@@ -88,8 +88,8 @@ SECTIONS
8888
***********************************************************************************************/
8989
.data : { *(.data*) } :segment_data
9090

91-
/* Fill up to 8 bytes, b/c relocating the binary is done in u64 chunks */
92-
. = ALIGN(8);
91+
/* Fill up to 16 bytes, b/c relocating the binary is done in u128 chunks */
92+
. = ALIGN(16);
9393
__binary_nonzero_vma_end_exclusive = .;
9494

9595
/* Section is zeroed in pairs of u64. Align start and end to 16 bytes at least */
@@ -99,7 +99,7 @@ SECTIONS
9999
*(.bss .bss.*)
100100
*(COMMON)
101101
. = ALIGN(16);
102-
__BSS_SIZE_U64S = (. - __BSS_START) / 8;
102+
__BSS_END = .;
103103
} :segment_data
104104

105105
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) *(.text.boot*)}

bin/chainboot/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod boot;
2323
///
2424
/// - Only a single core must be active and running this function.
2525
/// - The init calls in this function must appear in the correct order.
26+
#[unsafe(no_mangle)]
2627
unsafe fn kernel_init(max_kernel_size: u64) -> ! {
2728
#[cfg(feature = "jtag")]
2829
machine::debug::jtag::wait_debugger();

0 commit comments

Comments
 (0)