fd4e66499c
This commit re-implements the SPARC V8 ABI "Flush windows" software trap. The trap is generated by C++ compilers for exceptions and also by the C standard library function longjmp(). There were two issues with the previous implementation: 1. It did reads and writes via the stack pointer of the trap window, which is not defined. 2. It executed with traps enabled but without the processor run-time state set to safely handle traps. In particular there was no valid stack for trap processing. Even though interrupt priority was set to highest level, the behavior at other traps was not deterministic. For example non-maskable interrupt (15) trap or bus error trap for instruction fetch. This new implementation does not store backup copies of CPU registers to the stack, and it executes with traps disabled. Fixes #63901 Signed-off-by: Martin Åberg <martin.aberg@gaisler.com>
168 lines
3.7 KiB
ArmAsm
168 lines
3.7 KiB
ArmAsm
/*
|
|
* Copyright (c) 2019-2020 Cobham Gaisler AB
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/*
|
|
* This file contains standard handlers for the SPARC V8 window overflow and
|
|
* underflow traps. It also implements the handler for SPARC-ABI
|
|
* "Flush windows" which is used for example by longjmp() and C++ exceptions.
|
|
*/
|
|
|
|
#include <zephyr/toolchain.h>
|
|
#include <zephyr/linker/sections.h>
|
|
#include <zephyr/arch/sparc/sparc.h>
|
|
|
|
GTEXT(__sparc_trap_window_overflow)
|
|
GTEXT(__sparc_trap_window_underflow)
|
|
GTEXT(__sparc_trap_flush_windows)
|
|
|
|
SECTION_FUNC(TEXT, __sparc_trap_window_overflow)
|
|
/* Enter the window to be stored. */
|
|
save
|
|
/* Save local register set. */
|
|
std %l0, [%sp + 0x00]
|
|
std %l2, [%sp + 0x08]
|
|
std %l4, [%sp + 0x10]
|
|
rd %wim, %l3
|
|
std %l6, [%sp + 0x18]
|
|
/* l2 := WIM << (NWIN-1) */
|
|
sll %l3, (CONFIG_SPARC_NWIN-1), %l2
|
|
/* Save input register set. */
|
|
std %i0, [%sp + 0x20]
|
|
/* l3 := WIM >> 1 */
|
|
srl %l3, 1, %l3
|
|
std %i2, [%sp + 0x28]
|
|
/* WIM := (WIM >> 1) ^ (WIM << (NWIN-1)) */
|
|
wr %l3, %l2, %wim
|
|
/* NOTE: 3 instruction before restore (delayed write instruction) */
|
|
std %i4, [%sp + 0x30]
|
|
nop
|
|
std %i6, [%sp + 0x38]
|
|
/* Go back to trap window. */
|
|
restore
|
|
/* Re-execute save. */
|
|
jmp %l1
|
|
rett %l2
|
|
|
|
SECTION_FUNC(TEXT, __sparc_trap_window_underflow)
|
|
rd %wim, %l3
|
|
/* l4 := WIM << 1 */
|
|
sll %l3, 1, %l4
|
|
/* l5 := WIM >> (NWIN-1) */
|
|
srl %l3, (CONFIG_SPARC_NWIN-1), %l5
|
|
/* WIM := (WIM << 1) ^ (WIM >> (NWIN-1)) */
|
|
wr %l4, %l5, %wim
|
|
/* WIM is implicitly read so nops are needed. */
|
|
nop
|
|
nop
|
|
nop
|
|
|
|
/* Enter the window to restore requires two restore instructions. */
|
|
restore
|
|
restore
|
|
ldd [%sp + 0x00], %l0
|
|
ldd [%sp + 0x08], %l2
|
|
ldd [%sp + 0x10], %l4
|
|
ldd [%sp + 0x18], %l6
|
|
ldd [%sp + 0x20], %i0
|
|
ldd [%sp + 0x28], %i2
|
|
ldd [%sp + 0x30], %i4
|
|
ldd [%sp + 0x38], %i6
|
|
/* Go back to the trap window. */
|
|
save
|
|
save
|
|
/* Re-execute restore. */
|
|
jmp %l1
|
|
rett %l2
|
|
|
|
/*
|
|
* Handler for SPARC trap 0x83: trap_instruction, defined as "Flush windows" by
|
|
* SPARC-ABI:
|
|
* "By executing a type 3 trap, a process asks the system to flush all its
|
|
* register windows to the stack."
|
|
*
|
|
* On entry:
|
|
* %l0: psr
|
|
* %l1: pc
|
|
* %l2: npc
|
|
*/
|
|
SECTION_FUNC(TEXT, __sparc_trap_flush_windows)
|
|
/* Save global registers used by the routine */
|
|
mov %g3, %l3
|
|
mov %g4, %l4
|
|
mov %g5, %l5
|
|
mov %g1, %l6
|
|
mov %g2, %l7
|
|
|
|
/* Uses g3=psr, g4=1, g2=wim, g1,g5=scratch */
|
|
mov %l0, %g3
|
|
set 1, %g4
|
|
rd %wim, %g2
|
|
|
|
/*
|
|
* We can always restore the previous window. Check if we can restore
|
|
* the window after that.
|
|
*/
|
|
and %l0, PSR_CWP, %g1
|
|
add %g1, 2, %g1
|
|
ba .LcheckNextWindow
|
|
restore
|
|
|
|
/* Flush window to stack */
|
|
.LflushWindow:
|
|
std %l0, [%sp + 0x00]
|
|
std %l2, [%sp + 0x08]
|
|
std %l4, [%sp + 0x10]
|
|
std %l6, [%sp + 0x18]
|
|
std %i0, [%sp + 0x20]
|
|
std %i2, [%sp + 0x28]
|
|
std %i4, [%sp + 0x30]
|
|
std %i6, [%sp + 0x38]
|
|
|
|
/*
|
|
* Check if next window is invalid by comparing
|
|
* (1 << ((cwp + 1) % NWIN)) with WIM
|
|
*/
|
|
.LcheckNextWindow:
|
|
set CONFIG_SPARC_NWIN, %g5
|
|
cmp %g1, %g5
|
|
bge,a .Lnowrap
|
|
sub %g1, %g5, %g1
|
|
.Lnowrap:
|
|
sll %g4, %g1, %g5
|
|
cmp %g5, %g2
|
|
be .LflushWindowDone
|
|
inc %g1
|
|
|
|
/* We need to flush the next window */
|
|
ba .LflushWindow
|
|
restore
|
|
|
|
/*
|
|
* All used windows have been flushed. Set WIM to cause trap for CWP+2.
|
|
* When we return from this trap it will be CWP+1 that will trap, that
|
|
* is, the next restore or rett.
|
|
*/
|
|
.LflushWindowDone:
|
|
/* We can not restore %psr from %l0 because we may be in any window. */
|
|
wr %g3, %psr
|
|
and %g3, PSR_CWP, %g1
|
|
add %g1, 2, %g1
|
|
set CONFIG_SPARC_NWIN, %g5
|
|
/* We are now back in the trap window. */
|
|
cmp %g1, %g5
|
|
bge,a .Lnowrap2
|
|
sub %g1, %g5, %g1
|
|
.Lnowrap2:
|
|
sll %g4, %g1, %g1
|
|
wr %g1, %wim
|
|
mov %l3, %g3
|
|
mov %l4, %g4
|
|
mov %l5, %g5
|
|
mov %l6, %g1
|
|
mov %l7, %g2
|
|
jmp %l2
|
|
rett %l2 + 4
|