esp-hal/esp-riscv-rt/src/lib.rs
Björn Quentin 8c5fda2d53
Fix zero/copy data in esp-riscv-rt (#952)
* Fix zero/copy data in esp-riscv-rt

* CHANGELOG.md
2023-11-17 10:49:40 +00:00

916 lines
19 KiB
Rust

//! Minimal startup / runtime for RISC-V CPUs from Espressif
//!
//! # Minimum Supported Rust Version (MSRV)
//!
//! This crate is guaranteed to compile on stable Rust 1.60 and up. It *might*
//! compile with older versions but that may change in any new patch release.
//!
//! # Features
//!
//! This crate provides:
//!
//! - Before main initialization of the `.bss` and `.data` sections controlled
//! by features
//! - `#[entry]` to declare the entry point of the program
// NOTE: Adapted from riscv-rt/src/lib.rs
#![no_std]
use core::arch::global_asm;
pub use riscv;
use riscv::register::{
mcause,
mtvec::{self, TrapMode},
};
pub use riscv_rt_macros::{entry, pre_init};
pub use self::Interrupt as interrupt;
#[export_name = "error: esp-riscv-rt appears more than once in the dependency graph"]
#[doc(hidden)]
pub static __ONCE__: () = ();
extern "C" {
// Boundaries of the .bss section
static mut _bss_end: u32;
static mut _bss_start: u32;
// Boundaries of the .data section
static mut _data_end: u32;
static mut _data_start: u32;
// Initial values of the .data section (stored in Flash)
static _sidata: u32;
}
/// Rust entry point (_start_rust)
///
/// Zeros bss section, initializes data section and calls main. This function
/// never returns.
///
/// # Safety
///
/// This function should not be called directly by the user, and should instead
/// be invoked by the runtime implicitly.
#[link_section = ".init.rust"]
#[export_name = "_start_rust"]
pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! {
extern "Rust" {
// This symbol will be provided by the user via `#[entry]`
fn main(a0: usize, a1: usize, a2: usize) -> !;
fn __post_init();
fn _setup_interrupts();
}
__post_init();
_setup_interrupts();
main(a0, a1, a2);
}
/// Registers saved in trap handler
#[allow(missing_docs)]
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct TrapFrame {
pub ra: usize,
pub t0: usize,
pub t1: usize,
pub t2: usize,
pub t3: usize,
pub t4: usize,
pub t5: usize,
pub t6: usize,
pub a0: usize,
pub a1: usize,
pub a2: usize,
pub a3: usize,
pub a4: usize,
pub a5: usize,
pub a6: usize,
pub a7: usize,
pub s0: usize,
pub s1: usize,
pub s2: usize,
pub s3: usize,
pub s4: usize,
pub s5: usize,
pub s6: usize,
pub s7: usize,
pub s8: usize,
pub s9: usize,
pub s10: usize,
pub s11: usize,
pub gp: usize,
pub tp: usize,
pub sp: usize,
pub pc: usize,
pub mstatus: usize,
pub mcause: usize,
pub mtval: usize,
}
/// Trap entry point rust (_start_trap_rust)
///
/// `scause`/`mcause` is read to determine the cause of the trap. XLEN-1 bit
/// indicates if it's an interrupt or an exception. The result is examined and
/// ExceptionHandler or one of the core interrupt handlers is called.
///
/// # Safety
///
/// This function should not be called directly by the user, and should instead
/// be invoked by the runtime implicitly.
#[link_section = ".trap.rust"]
#[export_name = "_start_trap_rust"]
pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) {
extern "C" {
fn ExceptionHandler(trap_frame: &TrapFrame);
fn DefaultHandler();
}
unsafe {
let cause = mcause::read();
if cause.is_exception() {
ExceptionHandler(&*trap_frame)
} else if cause.code() < __INTERRUPTS.len() {
let h = &__INTERRUPTS[cause.code()];
if h.reserved == 0 {
DefaultHandler();
} else {
(h.handler)();
}
} else {
DefaultHandler();
}
}
}
#[doc(hidden)]
#[no_mangle]
#[allow(unused_variables, non_snake_case)]
pub fn DefaultExceptionHandler(trap_frame: &TrapFrame) -> ! {
loop {
// Prevent this from turning into a UDF instruction
// see rust-lang/rust#28728 for details
continue;
}
}
#[doc(hidden)]
#[no_mangle]
#[allow(unused_variables, non_snake_case)]
pub fn DefaultInterruptHandler() {
loop {
// Prevent this from turning into a UDF instruction
// see rust-lang/rust#28728 for details
continue;
}
}
// Interrupts
#[doc(hidden)]
pub enum Interrupt {
UserSoft,
SupervisorSoft,
MachineSoft,
UserTimer,
SupervisorTimer,
MachineTimer,
UserExternal,
SupervisorExternal,
MachineExternal,
}
extern "C" {
fn UserSoft();
fn SupervisorSoft();
fn MachineSoft();
fn UserTimer();
fn SupervisorTimer();
fn MachineTimer();
fn UserExternal();
fn SupervisorExternal();
fn MachineExternal();
}
#[doc(hidden)]
pub union Vector {
pub handler: unsafe extern "C" fn(),
pub reserved: usize,
}
#[doc(hidden)]
#[no_mangle]
pub static __INTERRUPTS: [Vector; 12] = [
Vector { handler: UserSoft },
Vector {
handler: SupervisorSoft,
},
Vector { reserved: 0 },
Vector {
handler: MachineSoft,
},
Vector { handler: UserTimer },
Vector {
handler: SupervisorTimer,
},
Vector { reserved: 0 },
Vector {
handler: MachineTimer,
},
Vector {
handler: UserExternal,
},
Vector {
handler: SupervisorExternal,
},
Vector { reserved: 0 },
Vector {
handler: MachineExternal,
},
];
#[doc(hidden)]
#[no_mangle]
#[rustfmt::skip]
pub unsafe extern "Rust" fn default_post_init() {}
/// Default implementation of `_setup_interrupts` that sets `mtvec`/`stvec` to a
/// trap handler address.
#[doc(hidden)]
#[no_mangle]
#[rustfmt::skip]
pub unsafe extern "Rust" fn default_setup_interrupts() {
extern "C" {
fn _start_trap();
}
mtvec::write(_start_trap as usize, TrapMode::Direct);
}
/// 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)*}
};
}
cfg_global_asm! {
r#"
/*
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.
*/
.section .init, "ax"
.global _start
_start:
/* Jump to the absolute address defined by the linker script. */
lui ra, %hi(_abs_start)
jr %lo(_abs_start)(ra)
_abs_start:
.option norelax
.cfi_startproc
.cfi_undefined ra
"#,
#[cfg(feature = "has-mie-mip")]
r#"
csrw mie, 0
csrw mip, 0
"#,
#[cfg(feature = "zero-bss")]
r#"
la a0, _bss_start
la a1, _bss_end
bge a0, a1, 2f
mv a3, x0
1:
sw a3, 0(a0)
addi a0, a0, 4
blt a0, a1, 1b
2:
"#,
#[cfg(feature = "zero-rtc-fast-bss")]
r#"
la a0, _rtc_fast_bss_start
la a1, _rtc_fast_bss_end
bge a0, a1, 2f
mv a3, x0
1:
sw a3, 0(a0)
addi a0, a0, 4
blt a0, a1, 1b
2:
"#,
#[cfg(feature = "init-data")]
r#"
la a0, _data_start
la a1, _data_end
bge a0, a1, 2f
la a2, _sidata
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
2:
"#,
#[cfg(feature = "init-rw-text")]
r#"
la a0, _srwtext
la a1, _erwtext
bge a0, a1, 2f
la a2, _irwtext
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
2:
"#,
#[cfg(feature = "init-rtc-fast-data")]
r#"
la a0, _rtc_fast_data_start
la a1, _rtc_fast_data_end
bge a0, a1, 2f
la a2, _irtc_fast_data
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
2:
"#,
#[cfg(feature = "init-rtc-fast-text")]
r#"
la a0, _srtc_fast_text
la a1, _ertc_fast_text
bge a0, a1, 2f
la a2, _irtc_fast_text
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
2:
"#,
r#"
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
li x10,0
li x11,0
li x12,0
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
// Check hart ID
csrr t2, mhartid
lui t0, %hi(_max_hart_id)
add t0, t0, %lo(_max_hart_id)
bgtu t2, t0, abort
// Allocate stack
la sp, _stack_start
lui t0, 16
sub sp, sp, t0
// Set frame pointer
add s0, sp, zero
jal zero, _start_rust
.cfi_endproc
/*
Trap entry points (_start_trap, _start_trapN for N in 1..=31)
The default implementation saves all registers to the stack and calls
_start_trap_rust, then restores all saved registers before `mret`
*/
.section .trap, "ax"
.weak _start_trap
.weak _start_trap1
.weak _start_trap2
.weak _start_trap3
.weak _start_trap4
.weak _start_trap5
.weak _start_trap6
.weak _start_trap7
.weak _start_trap8
.weak _start_trap9
.weak _start_trap10
.weak _start_trap11
.weak _start_trap12
.weak _start_trap13
.weak _start_trap14
.weak _start_trap15
.weak _start_trap16
.weak _start_trap17
.weak _start_trap18
.weak _start_trap19
.weak _start_trap20
.weak _start_trap21
.weak _start_trap22
.weak _start_trap23
.weak _start_trap24
.weak _start_trap25
.weak _start_trap26
.weak _start_trap27
.weak _start_trap28
.weak _start_trap29
.weak _start_trap30
.weak _start_trap31
"#,
#[cfg(feature="direct-vectoring")]
r#"
_start_trap1:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_1_handler
j _start_trap_direct
_start_trap2:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_2_handler
j _start_trap_direct
_start_trap3:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_3_handler
j _start_trap_direct
_start_trap4:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_4_handler
j _start_trap_direct
_start_trap5:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_5_handler
j _start_trap_direct
_start_trap6:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_6_handler
j _start_trap_direct
_start_trap7:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_7_handler
j _start_trap_direct
_start_trap8:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_8_handler
j _start_trap_direct
_start_trap9:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_9_handler
j _start_trap_direct
_start_trap10:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_10_handler
j _start_trap_direct
_start_trap11:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_11_handler
j _start_trap_direct
_start_trap12:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_12_handler
j _start_trap_direct
_start_trap13:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_13_handler
j _start_trap_direct
_start_trap14:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_14_handler
j _start_trap_direct
_start_trap15:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_15_handler
j _start_trap_direct
_start_trap16:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_16_handler
j _start_trap_direct
_start_trap17:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_17_handler
j _start_trap_direct
_start_trap18:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_18_handler
j _start_trap_direct
_start_trap19:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_19_handler
j _start_trap_direct
_start_trap20:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_20_handler
j _start_trap_direct
_start_trap21:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_21_handler
j _start_trap_direct
_start_trap22:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_22_handler
j _start_trap_direct
_start_trap23:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_23_handler
j _start_trap_direct
_start_trap24:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_24_handler
j _start_trap_direct
_start_trap25:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_25_handler
j _start_trap_direct
_start_trap26:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_26_handler
j _start_trap_direct
_start_trap27:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_27_handler
j _start_trap_direct
_start_trap28:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_28_handler
j _start_trap_direct
_start_trap29:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_29_handler
j _start_trap_direct
_start_trap30:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_30_handler
j _start_trap_direct
_start_trap31:
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, cpu_int_31_handler
j _start_trap_direct
"#,
#[cfg(not(feature="direct-vectoring"))]
r#"
_start_trap1:
_start_trap2:
_start_trap3:
_start_trap4:
_start_trap5:
_start_trap6:
_start_trap7:
_start_trap8:
_start_trap9:
_start_trap10:
_start_trap11:
_start_trap12:
_start_trap13:
_start_trap14:
_start_trap15:
_start_trap16:
_start_trap17:
_start_trap18:
_start_trap19:
_start_trap20:
_start_trap21:
_start_trap22:
_start_trap23:
_start_trap24:
_start_trap25:
_start_trap26:
_start_trap27:
_start_trap28:
_start_trap29:
_start_trap30:
_start_trap31:
"#,
r#"
_start_trap:
addi sp, sp, -40*4
sw ra, 0*4(sp)"#,
#[cfg(feature="direct-vectoring")] //for the directly vectored handlers the above is stacked beforehand
r#"
la ra, _start_trap_rust_hal #this runs on exception, use regular fault handler
_start_trap_direct:
"#,
r#"
sw t0, 1*4(sp)
sw t1, 2*4(sp)
sw t2, 3*4(sp)
sw t3, 4*4(sp)
sw t4, 5*4(sp)
sw t5, 6*4(sp)
sw t6, 7*4(sp)
sw a0, 8*4(sp)
sw a1, 9*4(sp)
sw a2, 10*4(sp)
sw a3, 11*4(sp)
sw a4, 12*4(sp)
sw a5, 13*4(sp)
sw a6, 14*4(sp)
sw a7, 15*4(sp)
sw s0, 16*4(sp)
sw s1, 17*4(sp)
sw s2, 18*4(sp)
sw s3, 19*4(sp)
sw s4, 20*4(sp)
sw s5, 21*4(sp)
sw s6, 22*4(sp)
sw s7, 23*4(sp)
sw s8, 24*4(sp)
sw s9, 25*4(sp)
sw s10, 26*4(sp)
sw s11, 27*4(sp)
sw gp, 28*4(sp)
sw tp, 29*4(sp)
csrrs t1, mepc, x0
sw t1, 31*4(sp)
csrrs t1, mstatus, x0
sw t1, 32*4(sp)
csrrs t1, mcause, x0
sw t1, 33*4(sp)
csrrs t1, mtval, x0
sw t1, 34*4(sp)
addi s0, sp, 40*4
sw s0, 30*4(sp)
add a0, sp, zero
"#,
#[cfg(all(feature="interrupt-preemption", feature="direct-vectoring"))] //store current priority, set threshold, enable interrupts
r#"
addi sp, sp, -4 #build stack
sw ra, 0(sp)
jal ra, _handle_priority
lw ra, 0(sp)
sw a0, 0(sp) #reuse old stack, a0 is return of _handle_priority
addi a0, sp, 4 #the proper stack pointer is an argument to the HAL handler
"#,
#[cfg(not(feature="direct-vectoring"))] //jump to HAL handler
r#"
jal ra, _start_trap_rust_hal
"#,
#[cfg(feature="direct-vectoring")] //jump to handler loaded in direct handler
r#"
jalr ra, ra #jump to label loaded in _start_trapx
"#,
#[cfg(all(feature="interrupt-preemption", feature="direct-vectoring"))] //restore threshold
r#"
lw a0, 0(sp) #load stored priority
jal ra, _restore_priority
addi sp, sp, 4 #pop
"#,
r#"
lw t1, 31*4(sp)
csrrw x0, mepc, t1
lw t1, 32*4(sp)
csrrw x0, mstatus, t1
lw ra, 0*4(sp)
lw t0, 1*4(sp)
lw t1, 2*4(sp)
lw t2, 3*4(sp)
lw t3, 4*4(sp)
lw t4, 5*4(sp)
lw t5, 6*4(sp)
lw t6, 7*4(sp)
lw a0, 8*4(sp)
lw a1, 9*4(sp)
lw a2, 10*4(sp)
lw a3, 11*4(sp)
lw a4, 12*4(sp)
lw a5, 13*4(sp)
lw a6, 14*4(sp)
lw a7, 15*4(sp)
lw s0, 16*4(sp)
lw s1, 17*4(sp)
lw s2, 18*4(sp)
lw s3, 19*4(sp)
lw s4, 20*4(sp)
lw s5, 21*4(sp)
lw s6, 22*4(sp)
lw s7, 23*4(sp)
lw s8, 24*4(sp)
lw s9, 25*4(sp)
lw s10, 26*4(sp)
lw s11, 27*4(sp)
lw gp, 28*4(sp)
lw tp, 29*4(sp)
lw sp, 30*4(sp)
# SP was restored from the original SP
mret
/* Make sure there is an abort when linking */
.section .text.abort
.globl abort
abort:
j abort
/*
Interrupt vector table (_vector_table)
*/
.section .trap, "ax"
.weak _vector_table
.type _vector_table, @function
.option push
.balign 0x100
.option norelax
.option norvc
_vector_table:
j _start_trap
j _start_trap1
j _start_trap2
j _start_trap3
j _start_trap4
j _start_trap5
j _start_trap6
j _start_trap7
j _start_trap8
j _start_trap9
j _start_trap10
j _start_trap11
j _start_trap12
j _start_trap13
j _start_trap14
j _start_trap15
j _start_trap16
j _start_trap17
j _start_trap18
j _start_trap19
j _start_trap20
j _start_trap21
j _start_trap22
j _start_trap23
j _start_trap24
j _start_trap25
j _start_trap26
j _start_trap27
j _start_trap28
j _start_trap29
j _start_trap30
j _start_trap31
.option pop
"#,
#[cfg(feature="direct-vectoring")]
r#"
#this is required for the linking step, these symbols for in-use interrupts should always be overwritten by the user.
.section .trap, "ax"
.weak cpu_int_1_handler
.weak cpu_int_2_handler
.weak cpu_int_3_handler
.weak cpu_int_4_handler
.weak cpu_int_5_handler
.weak cpu_int_6_handler
.weak cpu_int_7_handler
.weak cpu_int_8_handler
.weak cpu_int_9_handler
.weak cpu_int_10_handler
.weak cpu_int_11_handler
.weak cpu_int_12_handler
.weak cpu_int_13_handler
.weak cpu_int_14_handler
.weak cpu_int_15_handler
.weak cpu_int_16_handler
.weak cpu_int_17_handler
.weak cpu_int_18_handler
.weak cpu_int_19_handler
.weak cpu_int_20_handler
.weak cpu_int_21_handler
.weak cpu_int_22_handler
.weak cpu_int_23_handler
.weak cpu_int_24_handler
.weak cpu_int_25_handler
.weak cpu_int_26_handler
.weak cpu_int_27_handler
.weak cpu_int_28_handler
.weak cpu_int_29_handler
.weak cpu_int_30_handler
.weak cpu_int_31_handler
cpu_int_1_handler:
cpu_int_2_handler:
cpu_int_3_handler:
cpu_int_4_handler:
cpu_int_5_handler:
cpu_int_6_handler:
cpu_int_7_handler:
cpu_int_8_handler:
cpu_int_9_handler:
cpu_int_10_handler:
cpu_int_11_handler:
cpu_int_12_handler:
cpu_int_13_handler:
cpu_int_14_handler:
cpu_int_15_handler:
cpu_int_16_handler:
cpu_int_17_handler:
cpu_int_18_handler:
cpu_int_19_handler:
cpu_int_20_handler:
cpu_int_21_handler:
cpu_int_22_handler:
cpu_int_23_handler:
cpu_int_24_handler:
cpu_int_25_handler:
cpu_int_26_handler:
cpu_int_27_handler:
cpu_int_28_handler:
cpu_int_29_handler:
cpu_int_30_handler:
cpu_int_31_handler:
la ra, abort #abort since proper handler is not defined, this could also just load the default _start_trap_rust_hal address and let the hal handle it.
jr ra
"#,
}