* Add tests Cp0Disabled issue * Fix saving CPENABLE on context switch * Fix position shift of registers * Clean up
454 lines
12 KiB
Rust
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)
|
|
);
|
|
}
|