#![no_std]
#[cfg(target_arch = "arm")]
use cortex_ar::register::{cpsr::ProcessorMode, Cpsr};
#[cfg(arm_architecture = "v8-r")]
use cortex_ar::register::Hactlr;
pub use cortex_ar_rt_macros::{entry, exception, irq};
#[no_mangle]
pub extern "C" fn _default_handler() {
loop {
core::hint::spin_loop();
}
}
#[cfg(target_arch = "arm")]
core::arch::global_asm!(
r#"
.section .vector_table,"ax",%progbits
.global _vector_table
.type _vector_table, %function
_vector_table:
ldr pc, =_start
ldr pc, =_asm_undefined_handler
ldr pc, =_asm_svc_handler
ldr pc, =_asm_prefetch_abort_handler
ldr pc, =_asm_data_abort_handler
nop
ldr pc, =_asm_irq_handler
ldr pc, =_asm_fiq_handler
.size _vector_table, . - _vector_table
"#
);
#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))]
macro_rules! save_context {
() => {
r#"
// save preserved registers (and gives us some working area)
push {{r0-r3}}
// align SP down to eight byte boundary
mov r0, sp
and r0, r0, 7
sub sp, r0
// push alignment amount, and final preserved register
push {{r0, r12}}
"#
};
}
#[cfg(not(any(target_abi = "eabihf", feature = "eabi-fpu")))]
macro_rules! restore_context {
() => {
r#"
// restore alignment amount, and preserved register
pop {{r0, r12}}
// restore pre-alignment SP
add sp, r0
// restore more preserved registers
pop {{r0-r3}}
"#
};
}
#[cfg(any(target_abi = "eabihf", feature = "eabi-fpu"))]
macro_rules! save_context {
() => {
r#"
// save preserved registers (and gives us some working area)
push {{r0-r3}}
// save FPU context
vpush {{d0-d7}}
vmrs r0, FPSCR
vmrs r1, FPEXC
push {{r0-r1}}
// align SP down to eight byte boundary
mov r0, sp
and r0, r0, 7
sub sp, r0
// push alignment amount, and final preserved register
push {{r0, r12}}
"#
};
}
#[cfg(any(target_abi = "eabihf", feature = "eabi-fpu"))]
macro_rules! restore_context {
() => {
r#"
// restore alignment amount, and preserved register
pop {{r0, r12}}
// restore pre-alignment SP
add sp, r0
// pop FPU state
pop {{r0-r1}}
vmsr FPEXC, r1
vmsr FPSCR, r0
vpop {{d0-d7}}
// restore more preserved registers
pop {{r0-r3}}
"#
};
}
#[cfg(target_arch = "arm")]
core::arch::global_asm!(
r#"
// Work around https://github.com/rust-lang/rust/issues/127269
.fpu vfp3-d16
// Called from the vector table when we have an undefined exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _undefined_handler(addr: usize) -> usize;`
// or
// `extern "C" fn _undefined_handler(addr: usize) -> !;`
.section .text._asm_default_undefined_handler
.global _asm_default_undefined_handler
.type _asm_default_undefined_handler, %function
_asm_default_undefined_handler:
// state save from compiled code
srsfd sp!, #{und_mode}
// to work out what mode we're in, we need R0
push {{r0}}
// First adjust LR for two purposes: Passing the faulting instruction to the C handler,
// and to return to the failing instruction after the C handler returns.
// Load processor status for the calling code
mrs r0, spsr
// Was the code that triggered the exception in Thumb state?
tst r0, {t_bit}
// Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual.
ite eq
subeq lr, lr, #4
subne lr, lr, #2
// now do our standard exception save (which saves the 'wrong' R0)
"#,
save_context!(),
r#"
// Pass the faulting instruction address to the handler.
mov r0, lr
// call C handler
bl _undefined_handler
// if we get back here, assume they returned a new LR in r0
mov lr, r0
// do our standard restore (with the 'wrong' R0)
"#,
restore_context!(),
r#"
// get the R0 we saved early
pop {{r0}}
// overwrite the saved LR with the one from the C handler
str lr, [sp]
// Return from the asm handler
rfefd sp!
.size _asm_default_undefined_handler, . - _asm_default_undefined_handler
.section .text._asm_default_svc_handler
// Called from the vector table when we have an software interrupt.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _svc_handler(svc: u32);`
.global _asm_default_svc_handler
.type _asm_default_svc_handler, %function
_asm_default_svc_handler:
srsfd sp!, #{svc_mode}
"#,
save_context!(),
r#"
mrs r0, cpsr // Load processor status
tst r0, {t_bit} // Occurred in Thumb state?
ldrhne r0, [lr,#-2] // Yes: Load halfword and...
bicne r0, r0, #0xFF00 // ...extract comment field
ldreq r0, [lr,#-4] // No: Load word and...
biceq r0, r0, #0xFF000000 // ...extract comment field
// r0 now contains SVC number
bl _svc_handler
"#,
restore_context!(),
r#"
rfefd sp!
.size _asm_default_svc_handler, . - _asm_default_svc_handler
.section .text._asm_default_data_abort_handler
// Called from the vector table when we have an undefined exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _data_abort_handler(addr: usize);`
.global _asm_default_data_abort_handler
.type _asm_default_data_abort_handler, %function
_asm_default_data_abort_handler:
// Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual.
subs lr, lr, #8
// state save from compiled code
srsfd sp!, #{abt_mode}
"#,
save_context!(),
r#"
// Pass the faulting instruction address to the handler.
mov r0, lr
// call C handler
bl _data_abort_handler
// if we get back here, assume they returned a new LR in r0
mov lr, r0
"#,
restore_context!(),
r#"
// overwrite the saved LR with the one from the C handler
str lr, [sp]
// Return from the asm handler
rfefd sp!
.size _asm_default_data_abort_handler, . - _asm_default_data_abort_handler
.section .text._asm_default_prefetch_abort_handler
// Called from the vector table when we have a prefetch abort.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _prefetch_abort_handler(addr: usize);`
.global _asm_default_prefetch_abort_handler
.type _asm_default_prefetch_abort_handler, %function
_asm_default_prefetch_abort_handler:
// Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual.
subs lr, lr, #4
// state save from compiled code
srsfd sp!, #{abt_mode}
"#,
save_context!(),
r#"
// Pass the faulting instruction address to the handler.
mov r0, lr
// call C handler
bl _prefetch_abort_handler
// if we get back here, assume they returned a new LR in r0
mov lr, r0
"#,
restore_context!(),
r#"
// overwrite the saved LR with the one from the C handler
str lr, [sp]
// Return from the asm handler
rfefd sp!
.size _asm_default_prefetch_abort_handler, . - _asm_default_prefetch_abort_handler
.section .text._asm_default_irq_handler
// Called from the vector table when we have an interrupt.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _irq_handler();`
.global _asm_default_irq_handler
.type _asm_default_irq_handler, %function
_asm_default_irq_handler:
// make sure we jump back to the right place
sub lr, lr, 4
// The hardware has copied CPSR to SPSR_irq and LR to LR_irq for us.
// Now push SPSR_irq and LR_irq to the SYS stack.
srsfd sp!, #{sys_mode}
// switch to system mode
cps #{sys_mode}
// we also need to save LR, so we can be re-entrant
push {{lr}}
// save state to the system stack (adjusting SP for alignment)
"#,
save_context!(),
r#"
// call C handler
bl _irq_handler
// restore from the system stack
"#,
restore_context!(),
r#"
// restore LR
pop {{lr}}
// pop CPSR and LR from the stack (which also restores the mode)
rfefd sp!
.size _asm_default_irq_handler, . - _asm_default_irq_handler
.section .text._asm_default_fiq_handler
// Our default FIQ handler
.global _asm_default_fiq_handler
.type _asm_default_fiq_handler, %function
_asm_default_fiq_handler:
b _asm_default_fiq_handler
.size _asm_default_fiq_handler, . - _asm_default_fiq_handler
"#,
svc_mode = const ProcessorMode::Svc as u8,
und_mode = const ProcessorMode::Und as u8,
abt_mode = const ProcessorMode::Abt as u8,
sys_mode = const ProcessorMode::Sys as u8,
t_bit = const {
Cpsr::new_with_raw_value(0)
.with_t(true)
.raw_value()
},
);
#[cfg(all(
any(arm_architecture = "v7-r", arm_architecture = "v8-r"),
any(target_abi = "eabihf", feature = "eabi-fpu")
))]
macro_rules! fpu_enable {
() => {
r#"
// Allow VFP coprocessor access
mrc p15, 0, r0, c1, c0, 2
orr r0, r0, #0xF00000
mcr p15, 0, r0, c1, c0, 2
// Enable VFP
mov r0, #0x40000000
vmsr fpexc, r0
"#
};
}
#[cfg(all(
any(arm_architecture = "v7-r", arm_architecture = "v8-r"),
not(any(target_abi = "eabihf", feature = "eabi-fpu"))
))]
macro_rules! fpu_enable {
() => {
r#"
// no FPU - do nothing
"#
};
}
#[cfg(target_arch = "arm")]
core::arch::global_asm!(
r#"
// Work around https://github.com/rust-lang/rust/issues/127269
.fpu vfp3-d16
// Configure a stack for every mode. Leaves you in sys mode.
//
// Pass in stack top in r0.
.section .text._stack_setup
.global _stack_setup
.type _stack_setup, %function
_stack_setup:
// Save LR from whatever mode we're currently in
mov r2, lr
// (we might not be in the same mode when we return).
// Set stack pointer (right after) and mask interrupts for for UND mode (Mode 0x1B)
msr cpsr, {und_mode}
mov sp, r0
ldr r1, =_und_stack_size
sub r0, r0, r1
// Set stack pointer (right after) and mask interrupts for for SVC mode (Mode 0x13)
msr cpsr, {svc_mode}
mov sp, r0
ldr r1, =_svc_stack_size
sub r0, r0, r1
// Set stack pointer (right after) and mask interrupts for for ABT mode (Mode 0x17)
msr cpsr, {abt_mode}
mov sp, r0
ldr r1, =_abt_stack_size
sub r0, r0, r1
// Set stack pointer (right after) and mask interrupts for for IRQ mode (Mode 0x12)
msr cpsr, {irq_mode}
mov sp, r0
ldr r1, =_irq_stack_size
sub r0, r0, r1
// Set stack pointer (right after) and mask interrupts for for FIQ mode (Mode 0x11)
msr cpsr, {fiq_mode}
mov sp, r0
ldr r1, =_fiq_stack_size
sub r0, r0, r1
// Set stack pointer (right after) and mask interrupts for for System mode (Mode 0x1F)
msr cpsr, {sys_mode}
mov sp, r0
// Clear the Thumb Exception bit because all our targets are currently
// for Arm (A32) mode
mrc p15, 0, r1, c1, c0, 0
bic r1, #{te_bit}
mcr p15, 0, r1, c1, c0, 0
bx r2
.size _stack_setup, . - _stack_setup
// Initialises stacks, .data and .bss
.section .text._init_segments
.global _init_segments
.type _init_segments, %function
_init_segments:
// Initialise .bss
ldr r0, =__sbss
ldr r1, =__ebss
mov r2, 0
0:
cmp r1, r0
beq 1f
stm r0!, {{r2}}
b 0b
1:
// Initialise .data
ldr r0, =__sdata
ldr r1, =__edata
ldr r2, =__sidata
0:
cmp r1, r0
beq 1f
ldm r2!, {{r3}}
stm r0!, {{r3}}
b 0b
1:
bx lr
.size _init_segments, . - _init_segments
"#,
und_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Und)
.with_i(true)
.with_f(true)
.raw_value()
},
svc_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Svc)
.with_i(true)
.with_f(true)
.raw_value()
},
abt_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Abt)
.with_i(true)
.with_f(true)
.raw_value()
},
fiq_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Fiq)
.with_i(true)
.with_f(true)
.raw_value()
},
irq_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Irq)
.with_i(true)
.with_f(true)
.raw_value()
},
sys_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Sys)
.with_i(true)
.with_f(true)
.raw_value()
},
te_bit = const {
cortex_ar::register::Sctlr::new_with_raw_value(0)
.with_te(true)
.raw_value()
}
);
#[cfg(arm_architecture = "v7-r")]
core::arch::global_asm!(
r#"
// Work around https://github.com/rust-lang/rust/issues/127269
.fpu vfp3-d16
.section .text.default_start
.global _default_start
.type _default_start, %function
_default_start:
// Set up stacks.
ldr r0, =_stack_top
bl _stack_setup
// Init .data and .bss
bl _init_segments
"#,
fpu_enable!(),
r#"
// Zero all registers before calling kmain
mov r0, 0
mov r1, 0
mov r2, 0
mov r3, 0
mov r4, 0
mov r5, 0
mov r6, 0
mov r7, 0
mov r8, 0
mov r9, 0
mov r10, 0
mov r11, 0
mov r12, 0
// Jump to application
bl kmain
// In case the application returns, loop forever
b .
.size _default_start, . - _default_start
"#
);
#[cfg(arm_architecture = "v8-r")]
core::arch::global_asm!(
r#"
// Work around https://github.com/rust-lang/rust/issues/127269
.fpu vfp3-d16
.section .text.default_start
.global _default_start
.type _default_start, %function
_default_start:
// Are we in EL2? If not, skip the EL2 setup portion
mrs r0, cpsr
and r0, r0, 0x1F
cmp r0, {cpsr_mode_hyp}
bne 1f
// Set stack pointer
ldr r0, =_stack_top
mov sp, r0
ldr r1, =_hyp_stack_size
sub r0, r0, r1
// Set the HVBAR (for EL2) to _vector_table
ldr r1, =_vector_table
mcr p15, 4, r1, c12, c0, 0
// Configure HACTLR to let us enter EL1
mrc p15, 4, r1, c1, c0, 1
mov r2, {hactlr_bits}
orr r1, r1, r2
mcr p15, 4, r1, c1, c0, 1
// Program the SPSR - enter system mode (0x1F) in Arm mode with IRQ, FIQ masked
mov r1, {sys_mode}
msr spsr_hyp, r1
adr r1, 1f
msr elr_hyp, r1
dsb
isb
eret
1:
// Set up stacks. r0 points to the bottom of the hyp stack.
bl _stack_setup
// Set the VBAR (for EL1) to _vector_table. NB: This isn't required on
// Armv7-R because that only supports 'low' (default) or 'high'.
ldr r0, =_vector_table
mcr p15, 0, r0, c12, c0, 0
// Init .data and .bss
bl _init_segments
"#,
fpu_enable!(),
r#"
// Zero all registers before calling kmain
mov r0, 0
mov r1, 0
mov r2, 0
mov r3, 0
mov r4, 0
mov r5, 0
mov r6, 0
mov r7, 0
mov r8, 0
mov r9, 0
mov r10, 0
mov r11, 0
mov r12, 0
// Jump to application
bl kmain
// In case the application returns, loop forever
b .
.size _default_start, . - _default_start
"#,
cpsr_mode_hyp = const ProcessorMode::Hyp as u8,
hactlr_bits = const {
Hactlr::new_with_raw_value(0)
.with_cpuactlr(true)
.with_cdbgdci(true)
.with_flashifregionr(true)
.with_periphpregionr(true)
.with_qosr(true)
.with_bustimeoutr(true)
.with_intmonr(true)
.with_err(true)
.with_testr1(true)
.raw_value()
},
sys_mode = const {
Cpsr::new_with_raw_value(0)
.with_mode(ProcessorMode::Sys)
.with_i(true)
.with_f(true)
.raw_value()
}
);