esp-hal/xtensa-lx-rt/src/exception/context.rs
Dániel Buga be9dc0e0b7
Fix restoring of CPENABLE (#2315)
* Add tests Cp0Disabled issue

* Fix saving CPENABLE on context switch

* Fix position shift of registers

* Clean up
2024-10-09 10:33:31 +00:00

454 lines
12 KiB
Rust

use core::arch::asm;
use super::ExceptionCause;
/// State of the CPU saved when entering exception or interrupt
///
/// Must be aligned with assembly frame format in asm.rs
#[repr(C)]
#[allow(non_snake_case)]
#[derive(Debug, Clone, Copy)]
pub struct Context {
pub PC: u32,
pub PS: u32,
pub A0: u32,
pub A1: u32,
pub A2: u32,
pub A3: u32,
pub A4: u32,
pub A5: u32,
pub A6: u32,
pub A7: u32,
pub A8: u32,
pub A9: u32,
pub A10: u32,
pub A11: u32,
pub A12: u32,
pub A13: u32,
pub A14: u32,
pub A15: u32,
pub SAR: u32,
pub EXCCAUSE: u32,
pub EXCVADDR: u32,
pub LBEG: u32,
pub LEND: u32,
pub LCOUNT: u32,
pub THREADPTR: u32,
pub SCOMPARE1: u32,
pub BR: u32,
pub ACCLO: u32,
pub ACCHI: u32,
pub M0: u32,
pub M1: u32,
pub M2: u32,
pub M3: u32,
#[cfg(XCHAL_HAVE_CP)]
// Either F64R_LO or CPENABLE depending on `float-save-restore`.
pub F64R_LO_CPENABLE: u32,
// F64R_HI is only meaningful with cfg!(XCHAL_HAVE_DFP_ACCEL) but it's present in the stack
// frame unconditionally
#[cfg(feature = "float-save-restore")]
pub F64R_HI: u32,
// F64S is only meaningful with cfg!(XCHAL_HAVE_DFP_ACCEL) but it's present in the stack frame
// unconditionally
#[cfg(feature = "float-save-restore")]
pub F64S: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub FCR: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub FSR: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F0: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F1: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F2: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F3: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F4: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F5: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F6: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F7: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F8: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F9: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F10: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F11: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F12: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F13: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F14: u32,
#[cfg(all(feature = "float-save-restore", XCHAL_HAVE_FP))]
pub F15: u32,
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
impl Context {
/// Creates a new, zeroed out context.
pub const fn new() -> Self {
unsafe { core::mem::zeroed() }
}
}
extern "Rust" {
/// The exception assembly jumps here once registers have been spilled
fn __exception(cause: ExceptionCause, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[exception]`
fn __user_exception(cause: ExceptionCause, save_frame: &mut Context);
/// No attribute is supplied for this symbol as the double exception can
/// hardly occur
fn __double_exception(cause: ExceptionCause, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[interrupt(1)]`
fn __level_1_interrupt(level: u32, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[interrupt(2)]`
fn __level_2_interrupt(level: u32, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[interrupt(3)]`
fn __level_3_interrupt(level: u32, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[interrupt(4)]`
fn __level_4_interrupt(level: u32, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[interrupt(5)]`
fn __level_5_interrupt(level: u32, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[interrupt(6)]`
fn __level_6_interrupt(level: u32, save_frame: &mut Context);
/// This symbol will be provided by the user via `#[interrupt(7)]`
fn __level_7_interrupt(level: u32, save_frame: &mut Context);
}
#[no_mangle]
#[link_section = ".rwtext"]
unsafe extern "C" fn __default_exception(cause: ExceptionCause, save_frame: &mut Context) {
__user_exception(cause, save_frame)
}
#[no_mangle]
#[link_section = ".rwtext"]
extern "C" fn __default_user_exception(cause: ExceptionCause, save_frame: &Context) {
#[cfg(any(feature = "esp32", feature = "esp32s3"))]
if cause == ExceptionCause::Cp0Disabled {
panic!(
"Access to the floating point coprocessor is not allowed. You may want to enable the `float-save-restore` feature of the `xtensa-lx-rt` crate. {:08x?}",
save_frame
)
}
panic!("Exception: {:?}, {:08x?}", cause, save_frame)
}
#[no_mangle]
#[link_section = ".rwtext"]
extern "C" fn __default_interrupt(level: u32, save_frame: &Context) {
panic!("Interrupt: {:?}, {:08x?}", level, save_frame)
}
#[no_mangle]
#[link_section = ".rwtext"]
extern "C" fn __default_double_exception(cause: ExceptionCause, save_frame: &Context) {
panic!("Double Exception: {:?}, {:08x?}", cause, save_frame)
}
// Raw vector handlers
//
// The interrupt handlers all use special return instructions.
// rust still generates a ret.w instruction, which will never be reached.
// generation of the ret.w can be prevented by using
// core::intrinsics::unreachable, but then a break 15,1 will be generated (which
// takes 3 bytes instead of 2) or a 'loop {}', but then a jump to own address
// will be generated which is also 3 bytes. No way found yet to prevent this
// generation altogether.
#[naked]
#[no_mangle]
#[link_section = ".KernelExceptionVector.text"]
unsafe extern "C" fn _KernelExceptionVector() {
asm!(
"
wsr a0, EXCSAVE1 // preserve a0
rsr a0, EXCCAUSE // get exception cause
beqi a0, 5, .AllocAException
call0 __naked_kernel_exception
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".UserExceptionVector.text"]
unsafe extern "C" fn _UserExceptionVector() {
asm!(
"
wsr a0, EXCSAVE1 // preserve a0
rsr a0, EXCCAUSE // get exception cause
beqi a0, 5, .AllocAException
call0 __naked_user_exception
.AllocAException:
call0 _AllocAException
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".DoubleExceptionVector.text"]
unsafe extern "C" fn _DoubleExceptionVector() {
asm!(
"
wsr a0, EXCSAVE1 // preserve a0 (EXCSAVE1 can be reused as long as there
// is no double exception in the first exception until
// EXCSAVE1 is stored to the stack.)
call0 __naked_double_exception // used as long jump
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".Level2InterruptVector.text"]
unsafe extern "C" fn _Level2InterruptVector() {
asm!(
"
wsr a0, EXCSAVE2 // preserve a0
call0 __naked_level_2_interrupt // used as long jump
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".Level3InterruptVector.text"]
unsafe extern "C" fn _Level3InterruptVector() {
asm!(
"
wsr a0, EXCSAVE3 // preserve a0
call0 __naked_level_3_interrupt // used as long jump
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".Level4InterruptVector.text"]
unsafe extern "C" fn _Level4InterruptVector() {
asm!(
"
wsr a0, EXCSAVE4 // preserve a0
call0 __naked_level_4_interrupt // used as long jump
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".Level5InterruptVector.text"]
unsafe extern "C" fn _Level5InterruptVector() {
asm!(
"
wsr a0, EXCSAVE5 // preserve a0
call0 __naked_level_5_interrupt // used as long jump
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".DebugExceptionVector.text"]
unsafe extern "C" fn _Level6InterruptVector() {
asm!(
"
wsr a0, EXCSAVE6 // preserve a0
call0 __naked_level_6_interrupt // used as long jump
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".NMIExceptionVector.text"]
unsafe extern "C" fn _Level7InterruptVector() {
asm!(
"
wsr a0, EXCSAVE7 // preserve a0
call0 __naked_level_7_interrupt // used as long jump
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".WindowOverflow4.text"]
unsafe extern "C" fn _WindowOverflow4() {
asm!(
"
s32e a0, a5, -16
s32e a1, a5, -12
s32e a2, a5, -8
s32e a3, a5, -4
rfwo
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".WindowUnderflow4.text"]
unsafe extern "C" fn _WindowUnderflow4() {
asm!(
"
l32e a0, a5, -16
l32e a1, a5, -12
l32e a2, a5, -8
l32e a3, a5, -4
rfwu
// inline the _AllocAException saves on the ret.w for WindowUnderflow4
// this makes that it just fits, which is needed for the bbci instructions
.align 4
_AllocAException:
rsr a0, WINDOWBASE // grab WINDOWBASE before rotw changes it
rotw -1 // WINDOWBASE goes to a4, new a0-a3 are scratch
rsr a2, PS
extui a3, a2, 8, 4 // XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS
xor a3, a3, a4 // bits changed from old to current windowbase
rsr a4, EXCSAVE1 // restore original a0 (now in a4)
slli a3, a3, 8 // XCHAL_PS_OWB_SHIFT
xor a2, a2, a3 // flip changed bits in old window base
wsr a2, PS // update PS.OWB to new window base
rsync
bbci a4, 31, _WindowUnderflow4
rotw -1 // original a0 goes to a8
bbci a8, 30, _WindowUnderflow8
rotw -1
j _WindowUnderflow12
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".WindowOverflow8.text"]
unsafe extern "C" fn _WindowOverflow8() {
asm!(
"
s32e a0, a9, -16
l32e a0, a1, -12
s32e a1, a9, -12
s32e a2, a9, -8
s32e a3, a9, -4
s32e a4, a0, -32
s32e a5, a0, -28
s32e a6, a0, -24
s32e a7, a0, -20
rfwo
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".WindowUnderflow8.text"]
unsafe extern "C" fn _WindowUnderflow8() {
asm!(
"
l32e a0, a9, -16
l32e a1, a9, -12
l32e a2, a9, -8
l32e a7, a1, -12
l32e a3, a9, -4
l32e a4, a7, -32
l32e a5, a7, -28
l32e a6, a7, -24
l32e a7, a7, -20
rfwu
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".WindowOverflow12.text"]
unsafe extern "C" fn _WindowOverflow12() {
asm!(
"
s32e a0, a13, -16
l32e a0, a1, -12
s32e a1, a13, -12
s32e a2, a13, -8
s32e a3, a13, -4
s32e a4, a0, -48
s32e a5, a0, -44
s32e a6, a0, -40
s32e a7, a0, -36
s32e a8, a0, -32
s32e a9, a0, -28
s32e a10, a0, -24
s32e a11, a0, -20
rfwo
",
options(noreturn)
);
}
#[naked]
#[no_mangle]
#[link_section = ".WindowUnderflow12.text"]
unsafe extern "C" fn _WindowUnderflow12() {
asm!(
"
l32e a0, a13, -16
l32e a1, a13, -12
l32e a2, a13, -8
l32e a11, a1, -12
l32e a3, a13, -4
l32e a4, a11, -48
l32e a5, a11, -44
l32e a6, a11, -40
l32e a7, a11, -36
l32e a8, a11, -32
l32e a9, a11, -28
l32e a10, a11, -24
l32e a11, a11, -20
rfwu
",
options(noreturn)
);
}