use core::arch::global_asm; /// Parse cfg attributes inside a global_asm call. macro_rules! cfg_global_asm { {@inner, [$($x:tt)*], } => { global_asm!{$($x)*} }; (@inner, [$($x:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => { #[cfg($meta)] cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*} #[cfg(not($meta))] cfg_global_asm!{@inner, [$($x)*], $($rest)*} }; {@inner, [$($x:tt)*], $asm:literal, $($rest:tt)*} => { cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*} }; {$($asms:tt)*} => { cfg_global_asm!{@inner, [], $($asms)*} }; } // Entry point of all programs (_start). It initializes DWARF call frame information, // the stack pointer, the frame pointer (needed for closures to work in start_rust) // and the global pointer. Then it calls _start_rust. cfg_global_asm!( ".section .init, \"ax\" .global _start _start:", #[cfg(riscv32)] "lui ra, %hi(_abs_start) jr %lo(_abs_start)(ra)", #[cfg(riscv64)] ".option push .option norelax // to prevent an unsupported R_RISCV_ALIGN relocation from being generated 1: auipc ra, %pcrel_hi(1f) ld ra, %pcrel_lo(1b)(ra) jr ra .align 3 1: .dword _abs_start .option pop", " _abs_start: .option norelax .cfi_startproc .cfi_undefined ra", #[cfg(feature = "s-mode")] "csrw sie, 0 csrw sip, 0", #[cfg(not(feature = "s-mode"))] "csrw mie, 0 csrw mip, 0", "li x1, 0 li x2, 0 li x3, 0 li x4, 0 li x5, 0 li x6, 0 li x7, 0 li x8, 0 li x9, 0 // a0..a2 (x10..x12) skipped li x13, 0 li x14, 0 li x15, 0 li x16, 0 li x17, 0 li x18, 0 li x19, 0 li x20, 0 li x21, 0 li x22, 0 li x23, 0 li x24, 0 li x25, 0 li x26, 0 li x27, 0 li x28, 0 li x29, 0 li x30, 0 li x31, 0 .option push .option norelax la gp, __global_pointer$ .option pop // Allocate stacks", #[cfg(all(not(feature = "single-hart"), feature = "s-mode"))] "mv t2, a0 // the hartid is passed as parameter by SMODE", #[cfg(all(not(feature = "single-hart"), not(feature = "s-mode")))] "csrr t2, mhartid", #[cfg(not(feature = "single-hart"))] "lui t0, %hi(_max_hart_id) add t0, t0, %lo(_max_hart_id) bgtu t2, t0, abort lui t0, %hi(_hart_stack_size) add t0, t0, %lo(_hart_stack_size)", #[cfg(all(not(feature = "single-hart"), riscvm))] "mul t0, t2, t0", #[cfg(all(not(feature = "single-hart"), not(riscvm)))] "beqz t2, 2f // Jump if single-hart mv t1, t2 mv t3, t0 1: add t0, t0, t3 addi t1, t1, -1 bnez t1, 1b 2: ", "la t1, _stack_start", #[cfg(not(feature = "single-hart"))] "sub t1, t1, t0", "andi sp, t1, -16 // Force 16-byte alignment // Set frame pointer add s0, sp, zero jal zero, _start_rust .cfi_endproc", ); /// Trap entry point (_start_trap). It saves caller saved registers, calls /// _start_trap_rust, restores caller saved registers and then returns. /// /// # Usage /// /// The macro takes 5 arguments: /// - `$STORE`: the instruction used to store a register in the stack (e.g. `sd` for riscv64) /// - `$LOAD`: the instruction used to load a register from the stack (e.g. `ld` for riscv64) /// - `$BYTES`: the number of bytes used to store a register (e.g. 8 for riscv64) /// - `$TRAP_SIZE`: the number of registers to store in the stack (e.g. 32 for all the user registers) /// - list of tuples of the form `($REG, $LOCATION)`, where: /// - `$REG`: the register to store/load /// - `$LOCATION`: the location in the stack where to store/load the register #[rustfmt::skip] macro_rules! trap_handler { ($STORE:ident, $LOAD:ident, $BYTES:literal, $TRAP_SIZE:literal, [$(($REG:ident, $LOCATION:literal)),*]) => { // ensure we do not break that sp is 16-byte aligned const _: () = assert!(($TRAP_SIZE * $BYTES) % 16 == 0); global_asm!( " .section .trap, \"ax\" .global default_start_trap default_start_trap:", // save space for trap handler in stack concat!("addi sp, sp, -", stringify!($TRAP_SIZE * $BYTES)), // save registers in the desired order $(concat!(stringify!($STORE), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)* // call rust trap handler "add a0, sp, zero jal ra, _start_trap_rust", // restore registers in the desired order $(concat!(stringify!($LOAD), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)* // free stack concat!("addi sp, sp, ", stringify!($TRAP_SIZE * $BYTES)), ); cfg_global_asm!( // return from trap #[cfg(feature = "s-mode")] "sret", #[cfg(not(feature = "s-mode"))] "mret", ); }; } #[rustfmt::skip] #[cfg(riscv32)] trap_handler!( sw, lw, 4, 16, [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7), (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)] ); #[rustfmt::skip] #[cfg(riscv64)] trap_handler!( sd, ld, 8, 16, [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7), (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)] ); // Make sure there is an abort when linking global_asm!( ".section .text.abort .globl abort abort: j abort" );