c18fe9a046
This patch was hanging around for some time while we were waiting for the compiler situation to improve.. now that all is well again, finally merge it. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
2103 lines
49 KiB
ArmAsm
2103 lines
49 KiB
ArmAsm
/*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* arch/sh64/kernel/entry.S
|
|
*
|
|
* Copyright (C) 2000, 2001 Paolo Alberelli
|
|
* Copyright (C) 2004, 2005 Paul Mundt
|
|
* Copyright (C) 2003, 2004 Richard Curnow
|
|
*
|
|
*/
|
|
|
|
#include <linux/errno.h>
|
|
#include <linux/sys.h>
|
|
|
|
#include <asm/processor.h>
|
|
#include <asm/registers.h>
|
|
#include <asm/unistd.h>
|
|
#include <asm/thread_info.h>
|
|
#include <asm/asm-offsets.h>
|
|
|
|
/*
|
|
* SR fields.
|
|
*/
|
|
#define SR_ASID_MASK 0x00ff0000
|
|
#define SR_FD_MASK 0x00008000
|
|
#define SR_SS 0x08000000
|
|
#define SR_BL 0x10000000
|
|
#define SR_MD 0x40000000
|
|
|
|
/*
|
|
* Event code.
|
|
*/
|
|
#define EVENT_INTERRUPT 0
|
|
#define EVENT_FAULT_TLB 1
|
|
#define EVENT_FAULT_NOT_TLB 2
|
|
#define EVENT_DEBUG 3
|
|
|
|
/* EXPEVT values */
|
|
#define RESET_CAUSE 0x20
|
|
#define DEBUGSS_CAUSE 0x980
|
|
|
|
/*
|
|
* Frame layout. Quad index.
|
|
*/
|
|
#define FRAME_T(x) FRAME_TBASE+(x*8)
|
|
#define FRAME_R(x) FRAME_RBASE+(x*8)
|
|
#define FRAME_S(x) FRAME_SBASE+(x*8)
|
|
#define FSPC 0
|
|
#define FSSR 1
|
|
#define FSYSCALL_ID 2
|
|
|
|
/* Arrange the save frame to be a multiple of 32 bytes long */
|
|
#define FRAME_SBASE 0
|
|
#define FRAME_RBASE (FRAME_SBASE+(3*8)) /* SYSCALL_ID - SSR - SPC */
|
|
#define FRAME_TBASE (FRAME_RBASE+(63*8)) /* r0 - r62 */
|
|
#define FRAME_PBASE (FRAME_TBASE+(8*8)) /* tr0 -tr7 */
|
|
#define FRAME_SIZE (FRAME_PBASE+(2*8)) /* pad0-pad1 */
|
|
|
|
#define FP_FRAME_SIZE FP_FRAME_BASE+(33*8) /* dr0 - dr31 + fpscr */
|
|
#define FP_FRAME_BASE 0
|
|
|
|
#define SAVED_R2 0*8
|
|
#define SAVED_R3 1*8
|
|
#define SAVED_R4 2*8
|
|
#define SAVED_R5 3*8
|
|
#define SAVED_R18 4*8
|
|
#define SAVED_R6 5*8
|
|
#define SAVED_TR0 6*8
|
|
|
|
/* These are the registers saved in the TLB path that aren't saved in the first
|
|
level of the normal one. */
|
|
#define TLB_SAVED_R25 7*8
|
|
#define TLB_SAVED_TR1 8*8
|
|
#define TLB_SAVED_TR2 9*8
|
|
#define TLB_SAVED_TR3 10*8
|
|
#define TLB_SAVED_TR4 11*8
|
|
/* Save R0/R1 : PT-migrating compiler currently dishounours -ffixed-r0 and -ffixed-r1 causing
|
|
breakage otherwise. */
|
|
#define TLB_SAVED_R0 12*8
|
|
#define TLB_SAVED_R1 13*8
|
|
|
|
#define CLI() \
|
|
getcon SR, r6; \
|
|
ori r6, 0xf0, r6; \
|
|
putcon r6, SR;
|
|
|
|
#define STI() \
|
|
getcon SR, r6; \
|
|
andi r6, ~0xf0, r6; \
|
|
putcon r6, SR;
|
|
|
|
#ifdef CONFIG_PREEMPT
|
|
# define preempt_stop() CLI()
|
|
#else
|
|
# define preempt_stop()
|
|
# define resume_kernel restore_all
|
|
#endif
|
|
|
|
.section .data, "aw"
|
|
|
|
#define FAST_TLBMISS_STACK_CACHELINES 4
|
|
#define FAST_TLBMISS_STACK_QUADWORDS (4*FAST_TLBMISS_STACK_CACHELINES)
|
|
|
|
/* Register back-up area for all exceptions */
|
|
.balign 32
|
|
/* Allow for 16 quadwords to be pushed by fast tlbmiss handling
|
|
* register saves etc. */
|
|
.fill FAST_TLBMISS_STACK_QUADWORDS, 8, 0x0
|
|
/* This is 32 byte aligned by construction */
|
|
/* Register back-up area for all exceptions */
|
|
reg_save_area:
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
|
|
.quad 0
|
|
.quad 0
|
|
|
|
/* Save area for RESVEC exceptions. We cannot use reg_save_area because of
|
|
* reentrancy. Note this area may be accessed via physical address.
|
|
* Align so this fits a whole single cache line, for ease of purging.
|
|
*/
|
|
.balign 32,0,32
|
|
resvec_save_area:
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
.quad 0
|
|
.balign 32,0,32
|
|
|
|
/* Jump table of 3rd level handlers */
|
|
trap_jtable:
|
|
.long do_exception_error /* 0x000 */
|
|
.long do_exception_error /* 0x020 */
|
|
.long tlb_miss_load /* 0x040 */
|
|
.long tlb_miss_store /* 0x060 */
|
|
! ARTIFICIAL pseudo-EXPEVT setting
|
|
.long do_debug_interrupt /* 0x080 */
|
|
.long tlb_miss_load /* 0x0A0 */
|
|
.long tlb_miss_store /* 0x0C0 */
|
|
.long do_address_error_load /* 0x0E0 */
|
|
.long do_address_error_store /* 0x100 */
|
|
#ifdef CONFIG_SH_FPU
|
|
.long do_fpu_error /* 0x120 */
|
|
#else
|
|
.long do_exception_error /* 0x120 */
|
|
#endif
|
|
.long do_exception_error /* 0x140 */
|
|
.long system_call /* 0x160 */
|
|
.long do_reserved_inst /* 0x180 */
|
|
.long do_illegal_slot_inst /* 0x1A0 */
|
|
.long do_NMI /* 0x1C0 */
|
|
.long do_exception_error /* 0x1E0 */
|
|
.rept 15
|
|
.long do_IRQ /* 0x200 - 0x3C0 */
|
|
.endr
|
|
.long do_exception_error /* 0x3E0 */
|
|
.rept 32
|
|
.long do_IRQ /* 0x400 - 0x7E0 */
|
|
.endr
|
|
.long fpu_error_or_IRQA /* 0x800 */
|
|
.long fpu_error_or_IRQB /* 0x820 */
|
|
.long do_IRQ /* 0x840 */
|
|
.long do_IRQ /* 0x860 */
|
|
.rept 6
|
|
.long do_exception_error /* 0x880 - 0x920 */
|
|
.endr
|
|
.long do_software_break_point /* 0x940 */
|
|
.long do_exception_error /* 0x960 */
|
|
.long do_single_step /* 0x980 */
|
|
|
|
.rept 3
|
|
.long do_exception_error /* 0x9A0 - 0x9E0 */
|
|
.endr
|
|
.long do_IRQ /* 0xA00 */
|
|
.long do_IRQ /* 0xA20 */
|
|
.long itlb_miss_or_IRQ /* 0xA40 */
|
|
.long do_IRQ /* 0xA60 */
|
|
.long do_IRQ /* 0xA80 */
|
|
.long itlb_miss_or_IRQ /* 0xAA0 */
|
|
.long do_exception_error /* 0xAC0 */
|
|
.long do_address_error_exec /* 0xAE0 */
|
|
.rept 8
|
|
.long do_exception_error /* 0xB00 - 0xBE0 */
|
|
.endr
|
|
.rept 18
|
|
.long do_IRQ /* 0xC00 - 0xE20 */
|
|
.endr
|
|
|
|
.section .text64, "ax"
|
|
|
|
/*
|
|
* --- Exception/Interrupt/Event Handling Section
|
|
*/
|
|
|
|
/*
|
|
* VBR and RESVEC blocks.
|
|
*
|
|
* First level handler for VBR-based exceptions.
|
|
*
|
|
* To avoid waste of space, align to the maximum text block size.
|
|
* This is assumed to be at most 128 bytes or 32 instructions.
|
|
* DO NOT EXCEED 32 instructions on the first level handlers !
|
|
*
|
|
* Also note that RESVEC is contained within the VBR block
|
|
* where the room left (1KB - TEXT_SIZE) allows placing
|
|
* the RESVEC block (at most 512B + TEXT_SIZE).
|
|
*
|
|
* So first (and only) level handler for RESVEC-based exceptions.
|
|
*
|
|
* Where the fault/interrupt is handled (not_a_tlb_miss, tlb_miss
|
|
* and interrupt) we are a lot tight with register space until
|
|
* saving onto the stack frame, which is done in handle_exception().
|
|
*
|
|
*/
|
|
|
|
#define TEXT_SIZE 128
|
|
#define BLOCK_SIZE 1664 /* Dynamic check, 13*128 */
|
|
|
|
.balign TEXT_SIZE
|
|
LVBR_block:
|
|
.space 256, 0 /* Power-on class handler, */
|
|
/* not required here */
|
|
not_a_tlb_miss:
|
|
synco /* TAKum03020 (but probably a good idea anyway.) */
|
|
/* Save original stack pointer into KCR1 */
|
|
putcon SP, KCR1
|
|
|
|
/* Save other original registers into reg_save_area */
|
|
movi reg_save_area, SP
|
|
st.q SP, SAVED_R2, r2
|
|
st.q SP, SAVED_R3, r3
|
|
st.q SP, SAVED_R4, r4
|
|
st.q SP, SAVED_R5, r5
|
|
st.q SP, SAVED_R6, r6
|
|
st.q SP, SAVED_R18, r18
|
|
gettr tr0, r3
|
|
st.q SP, SAVED_TR0, r3
|
|
|
|
/* Set args for Non-debug, Not a TLB miss class handler */
|
|
getcon EXPEVT, r2
|
|
movi ret_from_exception, r3
|
|
ori r3, 1, r3
|
|
movi EVENT_FAULT_NOT_TLB, r4
|
|
or SP, ZERO, r5
|
|
getcon KCR1, SP
|
|
pta handle_exception, tr0
|
|
blink tr0, ZERO
|
|
|
|
.balign 256
|
|
! VBR+0x200
|
|
nop
|
|
.balign 256
|
|
! VBR+0x300
|
|
nop
|
|
.balign 256
|
|
/*
|
|
* Instead of the natural .balign 1024 place RESVEC here
|
|
* respecting the final 1KB alignment.
|
|
*/
|
|
.balign TEXT_SIZE
|
|
/*
|
|
* Instead of '.space 1024-TEXT_SIZE' place the RESVEC
|
|
* block making sure the final alignment is correct.
|
|
*/
|
|
tlb_miss:
|
|
synco /* TAKum03020 (but probably a good idea anyway.) */
|
|
putcon SP, KCR1
|
|
movi reg_save_area, SP
|
|
/* SP is guaranteed 32-byte aligned. */
|
|
st.q SP, TLB_SAVED_R0 , r0
|
|
st.q SP, TLB_SAVED_R1 , r1
|
|
st.q SP, SAVED_R2 , r2
|
|
st.q SP, SAVED_R3 , r3
|
|
st.q SP, SAVED_R4 , r4
|
|
st.q SP, SAVED_R5 , r5
|
|
st.q SP, SAVED_R6 , r6
|
|
st.q SP, SAVED_R18, r18
|
|
|
|
/* Save R25 for safety; as/ld may want to use it to achieve the call to
|
|
* the code in mm/tlbmiss.c */
|
|
st.q SP, TLB_SAVED_R25, r25
|
|
gettr tr0, r2
|
|
gettr tr1, r3
|
|
gettr tr2, r4
|
|
gettr tr3, r5
|
|
gettr tr4, r18
|
|
st.q SP, SAVED_TR0 , r2
|
|
st.q SP, TLB_SAVED_TR1 , r3
|
|
st.q SP, TLB_SAVED_TR2 , r4
|
|
st.q SP, TLB_SAVED_TR3 , r5
|
|
st.q SP, TLB_SAVED_TR4 , r18
|
|
|
|
pt do_fast_page_fault, tr0
|
|
getcon SSR, r2
|
|
getcon EXPEVT, r3
|
|
getcon TEA, r4
|
|
shlri r2, 30, r2
|
|
andi r2, 1, r2 /* r2 = SSR.MD */
|
|
blink tr0, LINK
|
|
|
|
pt fixup_to_invoke_general_handler, tr1
|
|
|
|
/* If the fast path handler fixed the fault, just drop through quickly
|
|
to the restore code right away to return to the excepting context.
|
|
*/
|
|
beqi/u r2, 0, tr1
|
|
|
|
fast_tlb_miss_restore:
|
|
ld.q SP, SAVED_TR0, r2
|
|
ld.q SP, TLB_SAVED_TR1, r3
|
|
ld.q SP, TLB_SAVED_TR2, r4
|
|
|
|
ld.q SP, TLB_SAVED_TR3, r5
|
|
ld.q SP, TLB_SAVED_TR4, r18
|
|
|
|
ptabs r2, tr0
|
|
ptabs r3, tr1
|
|
ptabs r4, tr2
|
|
ptabs r5, tr3
|
|
ptabs r18, tr4
|
|
|
|
ld.q SP, TLB_SAVED_R0, r0
|
|
ld.q SP, TLB_SAVED_R1, r1
|
|
ld.q SP, SAVED_R2, r2
|
|
ld.q SP, SAVED_R3, r3
|
|
ld.q SP, SAVED_R4, r4
|
|
ld.q SP, SAVED_R5, r5
|
|
ld.q SP, SAVED_R6, r6
|
|
ld.q SP, SAVED_R18, r18
|
|
ld.q SP, TLB_SAVED_R25, r25
|
|
|
|
getcon KCR1, SP
|
|
rte
|
|
nop /* for safety, in case the code is run on sh5-101 cut1.x */
|
|
|
|
fixup_to_invoke_general_handler:
|
|
|
|
/* OK, new method. Restore stuff that's not expected to get saved into
|
|
the 'first-level' reg save area, then just fall through to setting
|
|
up the registers and calling the second-level handler. */
|
|
|
|
/* 2nd level expects r2,3,4,5,6,18,tr0 to be saved. So we must restore
|
|
r25,tr1-4 and save r6 to get into the right state. */
|
|
|
|
ld.q SP, TLB_SAVED_TR1, r3
|
|
ld.q SP, TLB_SAVED_TR2, r4
|
|
ld.q SP, TLB_SAVED_TR3, r5
|
|
ld.q SP, TLB_SAVED_TR4, r18
|
|
ld.q SP, TLB_SAVED_R25, r25
|
|
|
|
ld.q SP, TLB_SAVED_R0, r0
|
|
ld.q SP, TLB_SAVED_R1, r1
|
|
|
|
ptabs/u r3, tr1
|
|
ptabs/u r4, tr2
|
|
ptabs/u r5, tr3
|
|
ptabs/u r18, tr4
|
|
|
|
/* Set args for Non-debug, TLB miss class handler */
|
|
getcon EXPEVT, r2
|
|
movi ret_from_exception, r3
|
|
ori r3, 1, r3
|
|
movi EVENT_FAULT_TLB, r4
|
|
or SP, ZERO, r5
|
|
getcon KCR1, SP
|
|
pta handle_exception, tr0
|
|
blink tr0, ZERO
|
|
|
|
/* NB TAKE GREAT CARE HERE TO ENSURE THAT THE INTERRUPT CODE
|
|
DOES END UP AT VBR+0x600 */
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
|
|
.balign 256
|
|
/* VBR + 0x600 */
|
|
|
|
interrupt:
|
|
synco /* TAKum03020 (but probably a good idea anyway.) */
|
|
/* Save original stack pointer into KCR1 */
|
|
putcon SP, KCR1
|
|
|
|
/* Save other original registers into reg_save_area */
|
|
movi reg_save_area, SP
|
|
st.q SP, SAVED_R2, r2
|
|
st.q SP, SAVED_R3, r3
|
|
st.q SP, SAVED_R4, r4
|
|
st.q SP, SAVED_R5, r5
|
|
st.q SP, SAVED_R6, r6
|
|
st.q SP, SAVED_R18, r18
|
|
gettr tr0, r3
|
|
st.q SP, SAVED_TR0, r3
|
|
|
|
/* Set args for interrupt class handler */
|
|
getcon INTEVT, r2
|
|
movi ret_from_irq, r3
|
|
ori r3, 1, r3
|
|
movi EVENT_INTERRUPT, r4
|
|
or SP, ZERO, r5
|
|
getcon KCR1, SP
|
|
pta handle_exception, tr0
|
|
blink tr0, ZERO
|
|
.balign TEXT_SIZE /* let's waste the bare minimum */
|
|
|
|
LVBR_block_end: /* Marker. Used for total checking */
|
|
|
|
.balign 256
|
|
LRESVEC_block:
|
|
/* Panic handler. Called with MMU off. Possible causes/actions:
|
|
* - Reset: Jump to program start.
|
|
* - Single Step: Turn off Single Step & return.
|
|
* - Others: Call panic handler, passing PC as arg.
|
|
* (this may need to be extended...)
|
|
*/
|
|
reset_or_panic:
|
|
synco /* TAKum03020 (but probably a good idea anyway.) */
|
|
putcon SP, DCR
|
|
/* First save r0-1 and tr0, as we need to use these */
|
|
movi resvec_save_area-CONFIG_CACHED_MEMORY_OFFSET, SP
|
|
st.q SP, 0, r0
|
|
st.q SP, 8, r1
|
|
gettr tr0, r0
|
|
st.q SP, 32, r0
|
|
|
|
/* Check cause */
|
|
getcon EXPEVT, r0
|
|
movi RESET_CAUSE, r1
|
|
sub r1, r0, r1 /* r1=0 if reset */
|
|
movi _stext-CONFIG_CACHED_MEMORY_OFFSET, r0
|
|
ori r0, 1, r0
|
|
ptabs r0, tr0
|
|
beqi r1, 0, tr0 /* Jump to start address if reset */
|
|
|
|
getcon EXPEVT, r0
|
|
movi DEBUGSS_CAUSE, r1
|
|
sub r1, r0, r1 /* r1=0 if single step */
|
|
pta single_step_panic, tr0
|
|
beqi r1, 0, tr0 /* jump if single step */
|
|
|
|
/* Now jump to where we save the registers. */
|
|
movi panic_stash_regs-CONFIG_CACHED_MEMORY_OFFSET, r1
|
|
ptabs r1, tr0
|
|
blink tr0, r63
|
|
|
|
single_step_panic:
|
|
/* We are in a handler with Single Step set. We need to resume the
|
|
* handler, by turning on MMU & turning off Single Step. */
|
|
getcon SSR, r0
|
|
movi SR_MMU, r1
|
|
or r0, r1, r0
|
|
movi ~SR_SS, r1
|
|
and r0, r1, r0
|
|
putcon r0, SSR
|
|
/* Restore EXPEVT, as the rte won't do this */
|
|
getcon PEXPEVT, r0
|
|
putcon r0, EXPEVT
|
|
/* Restore regs */
|
|
ld.q SP, 32, r0
|
|
ptabs r0, tr0
|
|
ld.q SP, 0, r0
|
|
ld.q SP, 8, r1
|
|
getcon DCR, SP
|
|
synco
|
|
rte
|
|
|
|
|
|
.balign 256
|
|
debug_exception:
|
|
synco /* TAKum03020 (but probably a good idea anyway.) */
|
|
/*
|
|
* Single step/software_break_point first level handler.
|
|
* Called with MMU off, so the first thing we do is enable it
|
|
* by doing an rte with appropriate SSR.
|
|
*/
|
|
putcon SP, DCR
|
|
/* Save SSR & SPC, together with R0 & R1, as we need to use 2 regs. */
|
|
movi resvec_save_area-CONFIG_CACHED_MEMORY_OFFSET, SP
|
|
|
|
/* With the MMU off, we are bypassing the cache, so purge any
|
|
* data that will be made stale by the following stores.
|
|
*/
|
|
ocbp SP, 0
|
|
synco
|
|
|
|
st.q SP, 0, r0
|
|
st.q SP, 8, r1
|
|
getcon SPC, r0
|
|
st.q SP, 16, r0
|
|
getcon SSR, r0
|
|
st.q SP, 24, r0
|
|
|
|
/* Enable MMU, block exceptions, set priv mode, disable single step */
|
|
movi SR_MMU | SR_BL | SR_MD, r1
|
|
or r0, r1, r0
|
|
movi ~SR_SS, r1
|
|
and r0, r1, r0
|
|
putcon r0, SSR
|
|
/* Force control to debug_exception_2 when rte is executed */
|
|
movi debug_exeception_2, r0
|
|
ori r0, 1, r0 /* force SHmedia, just in case */
|
|
putcon r0, SPC
|
|
getcon DCR, SP
|
|
synco
|
|
rte
|
|
debug_exeception_2:
|
|
/* Restore saved regs */
|
|
putcon SP, KCR1
|
|
movi resvec_save_area, SP
|
|
ld.q SP, 24, r0
|
|
putcon r0, SSR
|
|
ld.q SP, 16, r0
|
|
putcon r0, SPC
|
|
ld.q SP, 0, r0
|
|
ld.q SP, 8, r1
|
|
|
|
/* Save other original registers into reg_save_area */
|
|
movi reg_save_area, SP
|
|
st.q SP, SAVED_R2, r2
|
|
st.q SP, SAVED_R3, r3
|
|
st.q SP, SAVED_R4, r4
|
|
st.q SP, SAVED_R5, r5
|
|
st.q SP, SAVED_R6, r6
|
|
st.q SP, SAVED_R18, r18
|
|
gettr tr0, r3
|
|
st.q SP, SAVED_TR0, r3
|
|
|
|
/* Set args for debug class handler */
|
|
getcon EXPEVT, r2
|
|
movi ret_from_exception, r3
|
|
ori r3, 1, r3
|
|
movi EVENT_DEBUG, r4
|
|
or SP, ZERO, r5
|
|
getcon KCR1, SP
|
|
pta handle_exception, tr0
|
|
blink tr0, ZERO
|
|
|
|
.balign 256
|
|
debug_interrupt:
|
|
/* !!! WE COME HERE IN REAL MODE !!! */
|
|
/* Hook-up debug interrupt to allow various debugging options to be
|
|
* hooked into its handler. */
|
|
/* Save original stack pointer into KCR1 */
|
|
synco
|
|
putcon SP, KCR1
|
|
movi resvec_save_area-CONFIG_CACHED_MEMORY_OFFSET, SP
|
|
ocbp SP, 0
|
|
ocbp SP, 32
|
|
synco
|
|
|
|
/* Save other original registers into reg_save_area thru real addresses */
|
|
st.q SP, SAVED_R2, r2
|
|
st.q SP, SAVED_R3, r3
|
|
st.q SP, SAVED_R4, r4
|
|
st.q SP, SAVED_R5, r5
|
|
st.q SP, SAVED_R6, r6
|
|
st.q SP, SAVED_R18, r18
|
|
gettr tr0, r3
|
|
st.q SP, SAVED_TR0, r3
|
|
|
|
/* move (spc,ssr)->(pspc,pssr). The rte will shift
|
|
them back again, so that they look like the originals
|
|
as far as the real handler code is concerned. */
|
|
getcon spc, r6
|
|
putcon r6, pspc
|
|
getcon ssr, r6
|
|
putcon r6, pssr
|
|
|
|
! construct useful SR for handle_exception
|
|
movi 3, r6
|
|
shlli r6, 30, r6
|
|
getcon sr, r18
|
|
or r18, r6, r6
|
|
putcon r6, ssr
|
|
|
|
! SSR is now the current SR with the MD and MMU bits set
|
|
! i.e. the rte will switch back to priv mode and put
|
|
! the mmu back on
|
|
|
|
! construct spc
|
|
movi handle_exception, r18
|
|
ori r18, 1, r18 ! for safety (do we need this?)
|
|
putcon r18, spc
|
|
|
|
/* Set args for Non-debug, Not a TLB miss class handler */
|
|
|
|
! EXPEVT==0x80 is unused, so 'steal' this value to put the
|
|
! debug interrupt handler in the vectoring table
|
|
movi 0x80, r2
|
|
movi ret_from_exception, r3
|
|
ori r3, 1, r3
|
|
movi EVENT_FAULT_NOT_TLB, r4
|
|
|
|
or SP, ZERO, r5
|
|
movi CONFIG_CACHED_MEMORY_OFFSET, r6
|
|
add r6, r5, r5
|
|
getcon KCR1, SP
|
|
|
|
synco ! for safety
|
|
rte ! -> handle_exception, switch back to priv mode again
|
|
|
|
LRESVEC_block_end: /* Marker. Unused. */
|
|
|
|
.balign TEXT_SIZE
|
|
|
|
/*
|
|
* Second level handler for VBR-based exceptions. Pre-handler.
|
|
* In common to all stack-frame sensitive handlers.
|
|
*
|
|
* Inputs:
|
|
* (KCR0) Current [current task union]
|
|
* (KCR1) Original SP
|
|
* (r2) INTEVT/EXPEVT
|
|
* (r3) appropriate return address
|
|
* (r4) Event (0 = interrupt, 1 = TLB miss fault, 2 = Not TLB miss fault, 3=debug)
|
|
* (r5) Pointer to reg_save_area
|
|
* (SP) Original SP
|
|
*
|
|
* Available registers:
|
|
* (r6)
|
|
* (r18)
|
|
* (tr0)
|
|
*
|
|
*/
|
|
handle_exception:
|
|
/* Common 2nd level handler. */
|
|
|
|
/* First thing we need an appropriate stack pointer */
|
|
getcon SSR, r6
|
|
shlri r6, 30, r6
|
|
andi r6, 1, r6
|
|
pta stack_ok, tr0
|
|
bne r6, ZERO, tr0 /* Original stack pointer is fine */
|
|
|
|
/* Set stack pointer for user fault */
|
|
getcon KCR0, SP
|
|
movi THREAD_SIZE, r6 /* Point to the end */
|
|
add SP, r6, SP
|
|
|
|
stack_ok:
|
|
|
|
/* DEBUG : check for underflow/overflow of the kernel stack */
|
|
pta no_underflow, tr0
|
|
getcon KCR0, r6
|
|
movi 1024, r18
|
|
add r6, r18, r6
|
|
bge SP, r6, tr0 ! ? below 1k from bottom of stack : danger zone
|
|
|
|
/* Just panic to cause a crash. */
|
|
bad_sp:
|
|
ld.b r63, 0, r6
|
|
nop
|
|
|
|
no_underflow:
|
|
pta bad_sp, tr0
|
|
getcon kcr0, r6
|
|
movi THREAD_SIZE, r18
|
|
add r18, r6, r6
|
|
bgt SP, r6, tr0 ! sp above the stack
|
|
|
|
/* Make some room for the BASIC frame. */
|
|
movi -(FRAME_SIZE), r6
|
|
add SP, r6, SP
|
|
|
|
/* Could do this with no stalling if we had another spare register, but the
|
|
code below will be OK. */
|
|
ld.q r5, SAVED_R2, r6
|
|
ld.q r5, SAVED_R3, r18
|
|
st.q SP, FRAME_R(2), r6
|
|
ld.q r5, SAVED_R4, r6
|
|
st.q SP, FRAME_R(3), r18
|
|
ld.q r5, SAVED_R5, r18
|
|
st.q SP, FRAME_R(4), r6
|
|
ld.q r5, SAVED_R6, r6
|
|
st.q SP, FRAME_R(5), r18
|
|
ld.q r5, SAVED_R18, r18
|
|
st.q SP, FRAME_R(6), r6
|
|
ld.q r5, SAVED_TR0, r6
|
|
st.q SP, FRAME_R(18), r18
|
|
st.q SP, FRAME_T(0), r6
|
|
|
|
/* Keep old SP around */
|
|
getcon KCR1, r6
|
|
|
|
/* Save the rest of the general purpose registers */
|
|
st.q SP, FRAME_R(0), r0
|
|
st.q SP, FRAME_R(1), r1
|
|
st.q SP, FRAME_R(7), r7
|
|
st.q SP, FRAME_R(8), r8
|
|
st.q SP, FRAME_R(9), r9
|
|
st.q SP, FRAME_R(10), r10
|
|
st.q SP, FRAME_R(11), r11
|
|
st.q SP, FRAME_R(12), r12
|
|
st.q SP, FRAME_R(13), r13
|
|
st.q SP, FRAME_R(14), r14
|
|
|
|
/* SP is somewhere else */
|
|
st.q SP, FRAME_R(15), r6
|
|
|
|
st.q SP, FRAME_R(16), r16
|
|
st.q SP, FRAME_R(17), r17
|
|
/* r18 is saved earlier. */
|
|
st.q SP, FRAME_R(19), r19
|
|
st.q SP, FRAME_R(20), r20
|
|
st.q SP, FRAME_R(21), r21
|
|
st.q SP, FRAME_R(22), r22
|
|
st.q SP, FRAME_R(23), r23
|
|
st.q SP, FRAME_R(24), r24
|
|
st.q SP, FRAME_R(25), r25
|
|
st.q SP, FRAME_R(26), r26
|
|
st.q SP, FRAME_R(27), r27
|
|
st.q SP, FRAME_R(28), r28
|
|
st.q SP, FRAME_R(29), r29
|
|
st.q SP, FRAME_R(30), r30
|
|
st.q SP, FRAME_R(31), r31
|
|
st.q SP, FRAME_R(32), r32
|
|
st.q SP, FRAME_R(33), r33
|
|
st.q SP, FRAME_R(34), r34
|
|
st.q SP, FRAME_R(35), r35
|
|
st.q SP, FRAME_R(36), r36
|
|
st.q SP, FRAME_R(37), r37
|
|
st.q SP, FRAME_R(38), r38
|
|
st.q SP, FRAME_R(39), r39
|
|
st.q SP, FRAME_R(40), r40
|
|
st.q SP, FRAME_R(41), r41
|
|
st.q SP, FRAME_R(42), r42
|
|
st.q SP, FRAME_R(43), r43
|
|
st.q SP, FRAME_R(44), r44
|
|
st.q SP, FRAME_R(45), r45
|
|
st.q SP, FRAME_R(46), r46
|
|
st.q SP, FRAME_R(47), r47
|
|
st.q SP, FRAME_R(48), r48
|
|
st.q SP, FRAME_R(49), r49
|
|
st.q SP, FRAME_R(50), r50
|
|
st.q SP, FRAME_R(51), r51
|
|
st.q SP, FRAME_R(52), r52
|
|
st.q SP, FRAME_R(53), r53
|
|
st.q SP, FRAME_R(54), r54
|
|
st.q SP, FRAME_R(55), r55
|
|
st.q SP, FRAME_R(56), r56
|
|
st.q SP, FRAME_R(57), r57
|
|
st.q SP, FRAME_R(58), r58
|
|
st.q SP, FRAME_R(59), r59
|
|
st.q SP, FRAME_R(60), r60
|
|
st.q SP, FRAME_R(61), r61
|
|
st.q SP, FRAME_R(62), r62
|
|
|
|
/*
|
|
* Save the S* registers.
|
|
*/
|
|
getcon SSR, r61
|
|
st.q SP, FRAME_S(FSSR), r61
|
|
getcon SPC, r62
|
|
st.q SP, FRAME_S(FSPC), r62
|
|
movi -1, r62 /* Reset syscall_nr */
|
|
st.q SP, FRAME_S(FSYSCALL_ID), r62
|
|
|
|
/* Save the rest of the target registers */
|
|
gettr tr1, r6
|
|
st.q SP, FRAME_T(1), r6
|
|
gettr tr2, r6
|
|
st.q SP, FRAME_T(2), r6
|
|
gettr tr3, r6
|
|
st.q SP, FRAME_T(3), r6
|
|
gettr tr4, r6
|
|
st.q SP, FRAME_T(4), r6
|
|
gettr tr5, r6
|
|
st.q SP, FRAME_T(5), r6
|
|
gettr tr6, r6
|
|
st.q SP, FRAME_T(6), r6
|
|
gettr tr7, r6
|
|
st.q SP, FRAME_T(7), r6
|
|
|
|
! setup FP so that unwinder can wind back through nested kernel mode
|
|
! exceptions
|
|
add SP, ZERO, r14
|
|
|
|
#ifdef CONFIG_POOR_MANS_STRACE
|
|
/* We've pushed all the registers now, so only r2-r4 hold anything
|
|
* useful. Move them into callee save registers */
|
|
or r2, ZERO, r28
|
|
or r3, ZERO, r29
|
|
or r4, ZERO, r30
|
|
|
|
/* Preserve r2 as the event code */
|
|
movi evt_debug, r3
|
|
ori r3, 1, r3
|
|
ptabs r3, tr0
|
|
|
|
or SP, ZERO, r6
|
|
getcon TRA, r5
|
|
blink tr0, LINK
|
|
|
|
or r28, ZERO, r2
|
|
or r29, ZERO, r3
|
|
or r30, ZERO, r4
|
|
#endif
|
|
|
|
/* For syscall and debug race condition, get TRA now */
|
|
getcon TRA, r5
|
|
|
|
/* We are in a safe position to turn SR.BL off, but set IMASK=0xf
|
|
* Also set FD, to catch FPU usage in the kernel.
|
|
*
|
|
* benedict.gaster@superh.com 29/07/2002
|
|
*
|
|
* On all SH5-101 revisions it is unsafe to raise the IMASK and at the
|
|
* same time change BL from 1->0, as any pending interrupt of a level
|
|
* higher than he previous value of IMASK will leak through and be
|
|
* taken unexpectedly.
|
|
*
|
|
* To avoid this we raise the IMASK and then issue another PUTCON to
|
|
* enable interrupts.
|
|
*/
|
|
getcon SR, r6
|
|
movi SR_IMASK | SR_FD, r7
|
|
or r6, r7, r6
|
|
putcon r6, SR
|
|
movi SR_UNBLOCK_EXC, r7
|
|
and r6, r7, r6
|
|
putcon r6, SR
|
|
|
|
|
|
/* Now call the appropriate 3rd level handler */
|
|
or r3, ZERO, LINK
|
|
movi trap_jtable, r3
|
|
shlri r2, 3, r2
|
|
ldx.l r2, r3, r3
|
|
shlri r2, 2, r2
|
|
ptabs r3, tr0
|
|
or SP, ZERO, r3
|
|
blink tr0, ZERO
|
|
|
|
/*
|
|
* Second level handler for VBR-based exceptions. Post-handlers.
|
|
*
|
|
* Post-handlers for interrupts (ret_from_irq), exceptions
|
|
* (ret_from_exception) and common reentrance doors (restore_all
|
|
* to get back to the original context, ret_from_syscall loop to
|
|
* check kernel exiting).
|
|
*
|
|
* ret_with_reschedule and work_notifysig are an inner lables of
|
|
* the ret_from_syscall loop.
|
|
*
|
|
* In common to all stack-frame sensitive handlers.
|
|
*
|
|
* Inputs:
|
|
* (SP) struct pt_regs *, original register's frame pointer (basic)
|
|
*
|
|
*/
|
|
.global ret_from_irq
|
|
ret_from_irq:
|
|
#ifdef CONFIG_POOR_MANS_STRACE
|
|
pta evt_debug_ret_from_irq, tr0
|
|
ori SP, 0, r2
|
|
blink tr0, LINK
|
|
#endif
|
|
ld.q SP, FRAME_S(FSSR), r6
|
|
shlri r6, 30, r6
|
|
andi r6, 1, r6
|
|
pta resume_kernel, tr0
|
|
bne r6, ZERO, tr0 /* no further checks */
|
|
STI()
|
|
pta ret_with_reschedule, tr0
|
|
blink tr0, ZERO /* Do not check softirqs */
|
|
|
|
.global ret_from_exception
|
|
ret_from_exception:
|
|
preempt_stop()
|
|
|
|
#ifdef CONFIG_POOR_MANS_STRACE
|
|
pta evt_debug_ret_from_exc, tr0
|
|
ori SP, 0, r2
|
|
blink tr0, LINK
|
|
#endif
|
|
|
|
ld.q SP, FRAME_S(FSSR), r6
|
|
shlri r6, 30, r6
|
|
andi r6, 1, r6
|
|
pta resume_kernel, tr0
|
|
bne r6, ZERO, tr0 /* no further checks */
|
|
|
|
/* Check softirqs */
|
|
|
|
#ifdef CONFIG_PREEMPT
|
|
pta ret_from_syscall, tr0
|
|
blink tr0, ZERO
|
|
|
|
resume_kernel:
|
|
pta restore_all, tr0
|
|
|
|
getcon KCR0, r6
|
|
ld.l r6, TI_PRE_COUNT, r7
|
|
beq/u r7, ZERO, tr0
|
|
|
|
need_resched:
|
|
ld.l r6, TI_FLAGS, r7
|
|
movi (1 << TIF_NEED_RESCHED), r8
|
|
and r8, r7, r8
|
|
bne r8, ZERO, tr0
|
|
|
|
getcon SR, r7
|
|
andi r7, 0xf0, r7
|
|
bne r7, ZERO, tr0
|
|
|
|
movi ((PREEMPT_ACTIVE >> 16) & 65535), r8
|
|
shori (PREEMPT_ACTIVE & 65535), r8
|
|
st.l r6, TI_PRE_COUNT, r8
|
|
|
|
STI()
|
|
movi schedule, r7
|
|
ori r7, 1, r7
|
|
ptabs r7, tr1
|
|
blink tr1, LINK
|
|
|
|
st.l r6, TI_PRE_COUNT, ZERO
|
|
CLI()
|
|
|
|
pta need_resched, tr1
|
|
blink tr1, ZERO
|
|
#endif
|
|
|
|
.global ret_from_syscall
|
|
ret_from_syscall:
|
|
|
|
ret_with_reschedule:
|
|
getcon KCR0, r6 ! r6 contains current_thread_info
|
|
ld.l r6, TI_FLAGS, r7 ! r7 contains current_thread_info->flags
|
|
|
|
! FIXME:!!!
|
|
! no handling of TIF_SYSCALL_TRACE yet!!
|
|
|
|
movi _TIF_NEED_RESCHED, r8
|
|
and r8, r7, r8
|
|
pta work_resched, tr0
|
|
bne r8, ZERO, tr0
|
|
|
|
pta restore_all, tr1
|
|
|
|
movi (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r8
|
|
and r8, r7, r8
|
|
pta work_notifysig, tr0
|
|
bne r8, ZERO, tr0
|
|
|
|
blink tr1, ZERO
|
|
|
|
work_resched:
|
|
pta ret_from_syscall, tr0
|
|
gettr tr0, LINK
|
|
movi schedule, r6
|
|
ptabs r6, tr0
|
|
blink tr0, ZERO /* Call schedule(), return on top */
|
|
|
|
work_notifysig:
|
|
gettr tr1, LINK
|
|
|
|
movi do_signal, r6
|
|
ptabs r6, tr0
|
|
or SP, ZERO, r2
|
|
or ZERO, ZERO, r3
|
|
blink tr0, LINK /* Call do_signal(regs, 0), return here */
|
|
|
|
restore_all:
|
|
/* Do prefetches */
|
|
|
|
ld.q SP, FRAME_T(0), r6
|
|
ld.q SP, FRAME_T(1), r7
|
|
ld.q SP, FRAME_T(2), r8
|
|
ld.q SP, FRAME_T(3), r9
|
|
ptabs r6, tr0
|
|
ptabs r7, tr1
|
|
ptabs r8, tr2
|
|
ptabs r9, tr3
|
|
ld.q SP, FRAME_T(4), r6
|
|
ld.q SP, FRAME_T(5), r7
|
|
ld.q SP, FRAME_T(6), r8
|
|
ld.q SP, FRAME_T(7), r9
|
|
ptabs r6, tr4
|
|
ptabs r7, tr5
|
|
ptabs r8, tr6
|
|
ptabs r9, tr7
|
|
|
|
ld.q SP, FRAME_R(0), r0
|
|
ld.q SP, FRAME_R(1), r1
|
|
ld.q SP, FRAME_R(2), r2
|
|
ld.q SP, FRAME_R(3), r3
|
|
ld.q SP, FRAME_R(4), r4
|
|
ld.q SP, FRAME_R(5), r5
|
|
ld.q SP, FRAME_R(6), r6
|
|
ld.q SP, FRAME_R(7), r7
|
|
ld.q SP, FRAME_R(8), r8
|
|
ld.q SP, FRAME_R(9), r9
|
|
ld.q SP, FRAME_R(10), r10
|
|
ld.q SP, FRAME_R(11), r11
|
|
ld.q SP, FRAME_R(12), r12
|
|
ld.q SP, FRAME_R(13), r13
|
|
ld.q SP, FRAME_R(14), r14
|
|
|
|
ld.q SP, FRAME_R(16), r16
|
|
ld.q SP, FRAME_R(17), r17
|
|
ld.q SP, FRAME_R(18), r18
|
|
ld.q SP, FRAME_R(19), r19
|
|
ld.q SP, FRAME_R(20), r20
|
|
ld.q SP, FRAME_R(21), r21
|
|
ld.q SP, FRAME_R(22), r22
|
|
ld.q SP, FRAME_R(23), r23
|
|
ld.q SP, FRAME_R(24), r24
|
|
ld.q SP, FRAME_R(25), r25
|
|
ld.q SP, FRAME_R(26), r26
|
|
ld.q SP, FRAME_R(27), r27
|
|
ld.q SP, FRAME_R(28), r28
|
|
ld.q SP, FRAME_R(29), r29
|
|
ld.q SP, FRAME_R(30), r30
|
|
ld.q SP, FRAME_R(31), r31
|
|
ld.q SP, FRAME_R(32), r32
|
|
ld.q SP, FRAME_R(33), r33
|
|
ld.q SP, FRAME_R(34), r34
|
|
ld.q SP, FRAME_R(35), r35
|
|
ld.q SP, FRAME_R(36), r36
|
|
ld.q SP, FRAME_R(37), r37
|
|
ld.q SP, FRAME_R(38), r38
|
|
ld.q SP, FRAME_R(39), r39
|
|
ld.q SP, FRAME_R(40), r40
|
|
ld.q SP, FRAME_R(41), r41
|
|
ld.q SP, FRAME_R(42), r42
|
|
ld.q SP, FRAME_R(43), r43
|
|
ld.q SP, FRAME_R(44), r44
|
|
ld.q SP, FRAME_R(45), r45
|
|
ld.q SP, FRAME_R(46), r46
|
|
ld.q SP, FRAME_R(47), r47
|
|
ld.q SP, FRAME_R(48), r48
|
|
ld.q SP, FRAME_R(49), r49
|
|
ld.q SP, FRAME_R(50), r50
|
|
ld.q SP, FRAME_R(51), r51
|
|
ld.q SP, FRAME_R(52), r52
|
|
ld.q SP, FRAME_R(53), r53
|
|
ld.q SP, FRAME_R(54), r54
|
|
ld.q SP, FRAME_R(55), r55
|
|
ld.q SP, FRAME_R(56), r56
|
|
ld.q SP, FRAME_R(57), r57
|
|
ld.q SP, FRAME_R(58), r58
|
|
|
|
getcon SR, r59
|
|
movi SR_BLOCK_EXC, r60
|
|
or r59, r60, r59
|
|
putcon r59, SR /* SR.BL = 1, keep nesting out */
|
|
ld.q SP, FRAME_S(FSSR), r61
|
|
ld.q SP, FRAME_S(FSPC), r62
|
|
movi SR_ASID_MASK, r60
|
|
and r59, r60, r59
|
|
andc r61, r60, r61 /* Clear out older ASID */
|
|
or r59, r61, r61 /* Retain current ASID */
|
|
putcon r61, SSR
|
|
putcon r62, SPC
|
|
|
|
/* Ignore FSYSCALL_ID */
|
|
|
|
ld.q SP, FRAME_R(59), r59
|
|
ld.q SP, FRAME_R(60), r60
|
|
ld.q SP, FRAME_R(61), r61
|
|
ld.q SP, FRAME_R(62), r62
|
|
|
|
/* Last touch */
|
|
ld.q SP, FRAME_R(15), SP
|
|
rte
|
|
nop
|
|
|
|
/*
|
|
* Third level handlers for VBR-based exceptions. Adapting args to
|
|
* and/or deflecting to fourth level handlers.
|
|
*
|
|
* Fourth level handlers interface.
|
|
* Most are C-coded handlers directly pointed by the trap_jtable.
|
|
* (Third = Fourth level)
|
|
* Inputs:
|
|
* (r2) fault/interrupt code, entry number (e.g. NMI = 14,
|
|
* IRL0-3 (0000) = 16, RTLBMISS = 2, SYSCALL = 11, etc ...)
|
|
* (r3) struct pt_regs *, original register's frame pointer
|
|
* (r4) Event (0 = interrupt, 1 = TLB miss fault, 2 = Not TLB miss fault)
|
|
* (r5) TRA control register (for syscall/debug benefit only)
|
|
* (LINK) return address
|
|
* (SP) = r3
|
|
*
|
|
* Kernel TLB fault handlers will get a slightly different interface.
|
|
* (r2) struct pt_regs *, original register's frame pointer
|
|
* (r3) writeaccess, whether it's a store fault as opposed to load fault
|
|
* (r4) execaccess, whether it's a ITLB fault as opposed to DTLB fault
|
|
* (r5) Effective Address of fault
|
|
* (LINK) return address
|
|
* (SP) = r2
|
|
*
|
|
* fpu_error_or_IRQ? is a helper to deflect to the right cause.
|
|
*
|
|
*/
|
|
tlb_miss_load:
|
|
or SP, ZERO, r2
|
|
or ZERO, ZERO, r3 /* Read */
|
|
or ZERO, ZERO, r4 /* Data */
|
|
getcon TEA, r5
|
|
pta call_do_page_fault, tr0
|
|
beq ZERO, ZERO, tr0
|
|
|
|
tlb_miss_store:
|
|
or SP, ZERO, r2
|
|
movi 1, r3 /* Write */
|
|
or ZERO, ZERO, r4 /* Data */
|
|
getcon TEA, r5
|
|
pta call_do_page_fault, tr0
|
|
beq ZERO, ZERO, tr0
|
|
|
|
itlb_miss_or_IRQ:
|
|
pta its_IRQ, tr0
|
|
beqi/u r4, EVENT_INTERRUPT, tr0
|
|
or SP, ZERO, r2
|
|
or ZERO, ZERO, r3 /* Read */
|
|
movi 1, r4 /* Text */
|
|
getcon TEA, r5
|
|
/* Fall through */
|
|
|
|
call_do_page_fault:
|
|
movi do_page_fault, r6
|
|
ptabs r6, tr0
|
|
blink tr0, ZERO
|
|
|
|
fpu_error_or_IRQA:
|
|
pta its_IRQ, tr0
|
|
beqi/l r4, EVENT_INTERRUPT, tr0
|
|
#ifdef CONFIG_SH_FPU
|
|
movi do_fpu_state_restore, r6
|
|
#else
|
|
movi do_exception_error, r6
|
|
#endif
|
|
ptabs r6, tr0
|
|
blink tr0, ZERO
|
|
|
|
fpu_error_or_IRQB:
|
|
pta its_IRQ, tr0
|
|
beqi/l r4, EVENT_INTERRUPT, tr0
|
|
#ifdef CONFIG_SH_FPU
|
|
movi do_fpu_state_restore, r6
|
|
#else
|
|
movi do_exception_error, r6
|
|
#endif
|
|
ptabs r6, tr0
|
|
blink tr0, ZERO
|
|
|
|
its_IRQ:
|
|
movi do_IRQ, r6
|
|
ptabs r6, tr0
|
|
blink tr0, ZERO
|
|
|
|
/*
|
|
* system_call/unknown_trap third level handler:
|
|
*
|
|
* Inputs:
|
|
* (r2) fault/interrupt code, entry number (TRAP = 11)
|
|
* (r3) struct pt_regs *, original register's frame pointer
|
|
* (r4) Not used. Event (0=interrupt, 1=TLB miss fault, 2=Not TLB miss fault)
|
|
* (r5) TRA Control Reg (0x00xyzzzz: x=1 SYSCALL, y = #args, z=nr)
|
|
* (SP) = r3
|
|
* (LINK) return address: ret_from_exception
|
|
* (*r3) Syscall parms: SC#, arg0, arg1, ..., arg5 in order (Saved r2/r7)
|
|
*
|
|
* Outputs:
|
|
* (*r3) Syscall reply (Saved r2)
|
|
* (LINK) In case of syscall only it can be scrapped.
|
|
* Common second level post handler will be ret_from_syscall.
|
|
* Common (non-trace) exit point to that is syscall_ret (saving
|
|
* result to r2). Common bad exit point is syscall_bad (returning
|
|
* ENOSYS then saved to r2).
|
|
*
|
|
*/
|
|
|
|
unknown_trap:
|
|
/* Unknown Trap or User Trace */
|
|
movi do_unknown_trapa, r6
|
|
ptabs r6, tr0
|
|
ld.q r3, FRAME_R(9), r2 /* r2 = #arg << 16 | syscall # */
|
|
andi r2, 0x1ff, r2 /* r2 = syscall # */
|
|
blink tr0, LINK
|
|
|
|
pta syscall_ret, tr0
|
|
blink tr0, ZERO
|
|
|
|
/* New syscall implementation*/
|
|
system_call:
|
|
pta unknown_trap, tr0
|
|
or r5, ZERO, r4 /* TRA (=r5) -> r4 */
|
|
shlri r4, 20, r4
|
|
bnei r4, 1, tr0 /* unknown_trap if not 0x1yzzzz */
|
|
|
|
/* It's a system call */
|
|
st.q r3, FRAME_S(FSYSCALL_ID), r5 /* ID (0x1yzzzz) -> stack */
|
|
andi r5, 0x1ff, r5 /* syscall # -> r5 */
|
|
|
|
STI()
|
|
|
|
pta syscall_allowed, tr0
|
|
movi NR_syscalls - 1, r4 /* Last valid */
|
|
bgeu/l r4, r5, tr0
|
|
|
|
syscall_bad:
|
|
/* Return ENOSYS ! */
|
|
movi -(ENOSYS), r2 /* Fall-through */
|
|
|
|
.global syscall_ret
|
|
syscall_ret:
|
|
st.q SP, FRAME_R(9), r2 /* Expecting SP back to BASIC frame */
|
|
|
|
#ifdef CONFIG_POOR_MANS_STRACE
|
|
/* nothing useful in registers at this point */
|
|
|
|
movi evt_debug2, r5
|
|
ori r5, 1, r5
|
|
ptabs r5, tr0
|
|
ld.q SP, FRAME_R(9), r2
|
|
or SP, ZERO, r3
|
|
blink tr0, LINK
|
|
#endif
|
|
|
|
ld.q SP, FRAME_S(FSPC), r2
|
|
addi r2, 4, r2 /* Move PC, being pre-execution event */
|
|
st.q SP, FRAME_S(FSPC), r2
|
|
pta ret_from_syscall, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
/* A different return path for ret_from_fork, because we now need
|
|
* to call schedule_tail with the later kernels. Because prev is
|
|
* loaded into r2 by switch_to() means we can just call it straight away
|
|
*/
|
|
|
|
.global ret_from_fork
|
|
ret_from_fork:
|
|
|
|
movi schedule_tail,r5
|
|
ori r5, 1, r5
|
|
ptabs r5, tr0
|
|
blink tr0, LINK
|
|
|
|
#ifdef CONFIG_POOR_MANS_STRACE
|
|
/* nothing useful in registers at this point */
|
|
|
|
movi evt_debug2, r5
|
|
ori r5, 1, r5
|
|
ptabs r5, tr0
|
|
ld.q SP, FRAME_R(9), r2
|
|
or SP, ZERO, r3
|
|
blink tr0, LINK
|
|
#endif
|
|
|
|
ld.q SP, FRAME_S(FSPC), r2
|
|
addi r2, 4, r2 /* Move PC, being pre-execution event */
|
|
st.q SP, FRAME_S(FSPC), r2
|
|
pta ret_from_syscall, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
|
|
syscall_allowed:
|
|
/* Use LINK to deflect the exit point, default is syscall_ret */
|
|
pta syscall_ret, tr0
|
|
gettr tr0, LINK
|
|
pta syscall_notrace, tr0
|
|
|
|
getcon KCR0, r2
|
|
ld.l r2, TI_FLAGS, r4
|
|
movi (1 << TIF_SYSCALL_TRACE), r6
|
|
and r6, r4, r6
|
|
beq/l r6, ZERO, tr0
|
|
|
|
/* Trace it by calling syscall_trace before and after */
|
|
movi syscall_trace, r4
|
|
ptabs r4, tr0
|
|
blink tr0, LINK
|
|
/* Reload syscall number as r5 is trashed by syscall_trace */
|
|
ld.q SP, FRAME_S(FSYSCALL_ID), r5
|
|
andi r5, 0x1ff, r5
|
|
|
|
pta syscall_ret_trace, tr0
|
|
gettr tr0, LINK
|
|
|
|
syscall_notrace:
|
|
/* Now point to the appropriate 4th level syscall handler */
|
|
movi sys_call_table, r4
|
|
shlli r5, 2, r5
|
|
ldx.l r4, r5, r5
|
|
ptabs r5, tr0
|
|
|
|
/* Prepare original args */
|
|
ld.q SP, FRAME_R(2), r2
|
|
ld.q SP, FRAME_R(3), r3
|
|
ld.q SP, FRAME_R(4), r4
|
|
ld.q SP, FRAME_R(5), r5
|
|
ld.q SP, FRAME_R(6), r6
|
|
ld.q SP, FRAME_R(7), r7
|
|
|
|
/* And now the trick for those syscalls requiring regs * ! */
|
|
or SP, ZERO, r8
|
|
|
|
/* Call it */
|
|
blink tr0, ZERO /* LINK is already properly set */
|
|
|
|
syscall_ret_trace:
|
|
/* We get back here only if under trace */
|
|
st.q SP, FRAME_R(9), r2 /* Save return value */
|
|
|
|
movi syscall_trace, LINK
|
|
ptabs LINK, tr0
|
|
blink tr0, LINK
|
|
|
|
/* This needs to be done after any syscall tracing */
|
|
ld.q SP, FRAME_S(FSPC), r2
|
|
addi r2, 4, r2 /* Move PC, being pre-execution event */
|
|
st.q SP, FRAME_S(FSPC), r2
|
|
|
|
pta ret_from_syscall, tr0
|
|
blink tr0, ZERO /* Resume normal return sequence */
|
|
|
|
/*
|
|
* --- Switch to running under a particular ASID and return the previous ASID value
|
|
* --- The caller is assumed to have done a cli before calling this.
|
|
*
|
|
* Input r2 : new ASID
|
|
* Output r2 : old ASID
|
|
*/
|
|
|
|
.global switch_and_save_asid
|
|
switch_and_save_asid:
|
|
getcon sr, r0
|
|
movi 255, r4
|
|
shlli r4, 16, r4 /* r4 = mask to select ASID */
|
|
and r0, r4, r3 /* r3 = shifted old ASID */
|
|
andi r2, 255, r2 /* mask down new ASID */
|
|
shlli r2, 16, r2 /* align new ASID against SR.ASID */
|
|
andc r0, r4, r0 /* efface old ASID from SR */
|
|
or r0, r2, r0 /* insert the new ASID */
|
|
putcon r0, ssr
|
|
movi 1f, r0
|
|
putcon r0, spc
|
|
rte
|
|
nop
|
|
1:
|
|
ptabs LINK, tr0
|
|
shlri r3, 16, r2 /* r2 = old ASID */
|
|
blink tr0, r63
|
|
|
|
.global route_to_panic_handler
|
|
route_to_panic_handler:
|
|
/* Switch to real mode, goto panic_handler, don't return. Useful for
|
|
last-chance debugging, e.g. if no output wants to go to the console.
|
|
*/
|
|
|
|
movi panic_handler - CONFIG_CACHED_MEMORY_OFFSET, r1
|
|
ptabs r1, tr0
|
|
pta 1f, tr1
|
|
gettr tr1, r0
|
|
putcon r0, spc
|
|
getcon sr, r0
|
|
movi 1, r1
|
|
shlli r1, 31, r1
|
|
andc r0, r1, r0
|
|
putcon r0, ssr
|
|
rte
|
|
nop
|
|
1: /* Now in real mode */
|
|
blink tr0, r63
|
|
nop
|
|
|
|
.global peek_real_address_q
|
|
peek_real_address_q:
|
|
/* Two args:
|
|
r2 : real mode address to peek
|
|
r2(out) : result quadword
|
|
|
|
This is provided as a cheapskate way of manipulating device
|
|
registers for debugging (to avoid the need to onchip_remap the debug
|
|
module, and to avoid the need to onchip_remap the watchpoint
|
|
controller in a way that identity maps sufficient bits to avoid the
|
|
SH5-101 cut2 silicon defect).
|
|
|
|
This code is not performance critical
|
|
*/
|
|
|
|
add.l r2, r63, r2 /* sign extend address */
|
|
getcon sr, r0 /* r0 = saved original SR */
|
|
movi 1, r1
|
|
shlli r1, 28, r1
|
|
or r0, r1, r1 /* r0 with block bit set */
|
|
putcon r1, sr /* now in critical section */
|
|
movi 1, r36
|
|
shlli r36, 31, r36
|
|
andc r1, r36, r1 /* turn sr.mmu off in real mode section */
|
|
|
|
putcon r1, ssr
|
|
movi .peek0 - CONFIG_CACHED_MEMORY_OFFSET, r36 /* real mode target address */
|
|
movi 1f, r37 /* virtual mode return addr */
|
|
putcon r36, spc
|
|
|
|
synco
|
|
rte
|
|
nop
|
|
|
|
.peek0: /* come here in real mode, don't touch caches!!
|
|
still in critical section (sr.bl==1) */
|
|
putcon r0, ssr
|
|
putcon r37, spc
|
|
/* Here's the actual peek. If the address is bad, all bets are now off
|
|
* what will happen (handlers invoked in real-mode = bad news) */
|
|
ld.q r2, 0, r2
|
|
synco
|
|
rte /* Back to virtual mode */
|
|
nop
|
|
|
|
1:
|
|
ptabs LINK, tr0
|
|
blink tr0, r63
|
|
|
|
.global poke_real_address_q
|
|
poke_real_address_q:
|
|
/* Two args:
|
|
r2 : real mode address to poke
|
|
r3 : quadword value to write.
|
|
|
|
This is provided as a cheapskate way of manipulating device
|
|
registers for debugging (to avoid the need to onchip_remap the debug
|
|
module, and to avoid the need to onchip_remap the watchpoint
|
|
controller in a way that identity maps sufficient bits to avoid the
|
|
SH5-101 cut2 silicon defect).
|
|
|
|
This code is not performance critical
|
|
*/
|
|
|
|
add.l r2, r63, r2 /* sign extend address */
|
|
getcon sr, r0 /* r0 = saved original SR */
|
|
movi 1, r1
|
|
shlli r1, 28, r1
|
|
or r0, r1, r1 /* r0 with block bit set */
|
|
putcon r1, sr /* now in critical section */
|
|
movi 1, r36
|
|
shlli r36, 31, r36
|
|
andc r1, r36, r1 /* turn sr.mmu off in real mode section */
|
|
|
|
putcon r1, ssr
|
|
movi .poke0-CONFIG_CACHED_MEMORY_OFFSET, r36 /* real mode target address */
|
|
movi 1f, r37 /* virtual mode return addr */
|
|
putcon r36, spc
|
|
|
|
synco
|
|
rte
|
|
nop
|
|
|
|
.poke0: /* come here in real mode, don't touch caches!!
|
|
still in critical section (sr.bl==1) */
|
|
putcon r0, ssr
|
|
putcon r37, spc
|
|
/* Here's the actual poke. If the address is bad, all bets are now off
|
|
* what will happen (handlers invoked in real-mode = bad news) */
|
|
st.q r2, 0, r3
|
|
synco
|
|
rte /* Back to virtual mode */
|
|
nop
|
|
|
|
1:
|
|
ptabs LINK, tr0
|
|
blink tr0, r63
|
|
|
|
/*
|
|
* --- User Access Handling Section
|
|
*/
|
|
|
|
/*
|
|
* User Access support. It all moved to non inlined Assembler
|
|
* functions in here.
|
|
*
|
|
* __kernel_size_t __copy_user(void *__to, const void *__from,
|
|
* __kernel_size_t __n)
|
|
*
|
|
* Inputs:
|
|
* (r2) target address
|
|
* (r3) source address
|
|
* (r4) size in bytes
|
|
*
|
|
* Ouputs:
|
|
* (*r2) target data
|
|
* (r2) non-copied bytes
|
|
*
|
|
* If a fault occurs on the user pointer, bail out early and return the
|
|
* number of bytes not copied in r2.
|
|
* Strategy : for large blocks, call a real memcpy function which can
|
|
* move >1 byte at a time using unaligned ld/st instructions, and can
|
|
* manipulate the cache using prefetch + alloco to improve the speed
|
|
* further. If a fault occurs in that function, just revert to the
|
|
* byte-by-byte approach used for small blocks; this is rare so the
|
|
* performance hit for that case does not matter.
|
|
*
|
|
* For small blocks it's not worth the overhead of setting up and calling
|
|
* the memcpy routine; do the copy a byte at a time.
|
|
*
|
|
*/
|
|
.global __copy_user
|
|
__copy_user:
|
|
pta __copy_user_byte_by_byte, tr1
|
|
movi 16, r0 ! this value is a best guess, should tune it by benchmarking
|
|
bge/u r0, r4, tr1
|
|
pta copy_user_memcpy, tr0
|
|
addi SP, -32, SP
|
|
/* Save arguments in case we have to fix-up unhandled page fault */
|
|
st.q SP, 0, r2
|
|
st.q SP, 8, r3
|
|
st.q SP, 16, r4
|
|
st.q SP, 24, r35 ! r35 is callee-save
|
|
/* Save LINK in a register to reduce RTS time later (otherwise
|
|
ld SP,*,LINK;ptabs LINK;trn;blink trn,r63 becomes a critical path) */
|
|
ori LINK, 0, r35
|
|
blink tr0, LINK
|
|
|
|
/* Copy completed normally if we get back here */
|
|
ptabs r35, tr0
|
|
ld.q SP, 24, r35
|
|
/* don't restore r2-r4, pointless */
|
|
/* set result=r2 to zero as the copy must have succeeded. */
|
|
or r63, r63, r2
|
|
addi SP, 32, SP
|
|
blink tr0, r63 ! RTS
|
|
|
|
.global __copy_user_fixup
|
|
__copy_user_fixup:
|
|
/* Restore stack frame */
|
|
ori r35, 0, LINK
|
|
ld.q SP, 24, r35
|
|
ld.q SP, 16, r4
|
|
ld.q SP, 8, r3
|
|
ld.q SP, 0, r2
|
|
addi SP, 32, SP
|
|
/* Fall through to original code, in the 'same' state we entered with */
|
|
|
|
/* The slow byte-by-byte method is used if the fast copy traps due to a bad
|
|
user address. In that rare case, the speed drop can be tolerated. */
|
|
__copy_user_byte_by_byte:
|
|
pta ___copy_user_exit, tr1
|
|
pta ___copy_user1, tr0
|
|
beq/u r4, r63, tr1 /* early exit for zero length copy */
|
|
sub r2, r3, r0
|
|
addi r0, -1, r0
|
|
|
|
___copy_user1:
|
|
ld.b r3, 0, r5 /* Fault address 1 */
|
|
|
|
/* Could rewrite this to use just 1 add, but the second comes 'free'
|
|
due to load latency */
|
|
addi r3, 1, r3
|
|
addi r4, -1, r4 /* No real fixup required */
|
|
___copy_user2:
|
|
stx.b r3, r0, r5 /* Fault address 2 */
|
|
bne r4, ZERO, tr0
|
|
|
|
___copy_user_exit:
|
|
or r4, ZERO, r2
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
/*
|
|
* __kernel_size_t __clear_user(void *addr, __kernel_size_t size)
|
|
*
|
|
* Inputs:
|
|
* (r2) target address
|
|
* (r3) size in bytes
|
|
*
|
|
* Ouputs:
|
|
* (*r2) zero-ed target data
|
|
* (r2) non-zero-ed bytes
|
|
*/
|
|
.global __clear_user
|
|
__clear_user:
|
|
pta ___clear_user_exit, tr1
|
|
pta ___clear_user1, tr0
|
|
beq/u r3, r63, tr1
|
|
|
|
___clear_user1:
|
|
st.b r2, 0, ZERO /* Fault address */
|
|
addi r2, 1, r2
|
|
addi r3, -1, r3 /* No real fixup required */
|
|
bne r3, ZERO, tr0
|
|
|
|
___clear_user_exit:
|
|
or r3, ZERO, r2
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
/*
|
|
* int __strncpy_from_user(unsigned long __dest, unsigned long __src,
|
|
* int __count)
|
|
*
|
|
* Inputs:
|
|
* (r2) target address
|
|
* (r3) source address
|
|
* (r4) maximum size in bytes
|
|
*
|
|
* Ouputs:
|
|
* (*r2) copied data
|
|
* (r2) -EFAULT (in case of faulting)
|
|
* copied data (otherwise)
|
|
*/
|
|
.global __strncpy_from_user
|
|
__strncpy_from_user:
|
|
pta ___strncpy_from_user1, tr0
|
|
pta ___strncpy_from_user_done, tr1
|
|
or r4, ZERO, r5 /* r5 = original count */
|
|
beq/u r4, r63, tr1 /* early exit if r4==0 */
|
|
movi -(EFAULT), r6 /* r6 = reply, no real fixup */
|
|
or ZERO, ZERO, r7 /* r7 = data, clear top byte of data */
|
|
|
|
___strncpy_from_user1:
|
|
ld.b r3, 0, r7 /* Fault address: only in reading */
|
|
st.b r2, 0, r7
|
|
addi r2, 1, r2
|
|
addi r3, 1, r3
|
|
beq/u ZERO, r7, tr1
|
|
addi r4, -1, r4 /* return real number of copied bytes */
|
|
bne/l ZERO, r4, tr0
|
|
|
|
___strncpy_from_user_done:
|
|
sub r5, r4, r6 /* If done, return copied */
|
|
|
|
___strncpy_from_user_exit:
|
|
or r6, ZERO, r2
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
/*
|
|
* extern long __strnlen_user(const char *__s, long __n)
|
|
*
|
|
* Inputs:
|
|
* (r2) source address
|
|
* (r3) source size in bytes
|
|
*
|
|
* Ouputs:
|
|
* (r2) -EFAULT (in case of faulting)
|
|
* string length (otherwise)
|
|
*/
|
|
.global __strnlen_user
|
|
__strnlen_user:
|
|
pta ___strnlen_user_set_reply, tr0
|
|
pta ___strnlen_user1, tr1
|
|
or ZERO, ZERO, r5 /* r5 = counter */
|
|
movi -(EFAULT), r6 /* r6 = reply, no real fixup */
|
|
or ZERO, ZERO, r7 /* r7 = data, clear top byte of data */
|
|
beq r3, ZERO, tr0
|
|
|
|
___strnlen_user1:
|
|
ldx.b r2, r5, r7 /* Fault address: only in reading */
|
|
addi r3, -1, r3 /* No real fixup */
|
|
addi r5, 1, r5
|
|
beq r3, ZERO, tr0
|
|
bne r7, ZERO, tr1
|
|
! The line below used to be active. This meant led to a junk byte lying between each pair
|
|
! of entries in the argv & envp structures in memory. Whilst the program saw the right data
|
|
! via the argv and envp arguments to main, it meant the 'flat' representation visible through
|
|
! /proc/$pid/cmdline was corrupt, causing trouble with ps, for example.
|
|
! addi r5, 1, r5 /* Include '\0' */
|
|
|
|
___strnlen_user_set_reply:
|
|
or r5, ZERO, r6 /* If done, return counter */
|
|
|
|
___strnlen_user_exit:
|
|
or r6, ZERO, r2
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
/*
|
|
* extern long __get_user_asm_?(void *val, long addr)
|
|
*
|
|
* Inputs:
|
|
* (r2) dest address
|
|
* (r3) source address (in User Space)
|
|
*
|
|
* Ouputs:
|
|
* (r2) -EFAULT (faulting)
|
|
* 0 (not faulting)
|
|
*/
|
|
.global __get_user_asm_b
|
|
__get_user_asm_b:
|
|
or r2, ZERO, r4
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___get_user_asm_b1:
|
|
ld.b r3, 0, r5 /* r5 = data */
|
|
st.b r4, 0, r5
|
|
or ZERO, ZERO, r2
|
|
|
|
___get_user_asm_b_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
.global __get_user_asm_w
|
|
__get_user_asm_w:
|
|
or r2, ZERO, r4
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___get_user_asm_w1:
|
|
ld.w r3, 0, r5 /* r5 = data */
|
|
st.w r4, 0, r5
|
|
or ZERO, ZERO, r2
|
|
|
|
___get_user_asm_w_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
.global __get_user_asm_l
|
|
__get_user_asm_l:
|
|
or r2, ZERO, r4
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___get_user_asm_l1:
|
|
ld.l r3, 0, r5 /* r5 = data */
|
|
st.l r4, 0, r5
|
|
or ZERO, ZERO, r2
|
|
|
|
___get_user_asm_l_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
.global __get_user_asm_q
|
|
__get_user_asm_q:
|
|
or r2, ZERO, r4
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___get_user_asm_q1:
|
|
ld.q r3, 0, r5 /* r5 = data */
|
|
st.q r4, 0, r5
|
|
or ZERO, ZERO, r2
|
|
|
|
___get_user_asm_q_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
/*
|
|
* extern long __put_user_asm_?(void *pval, long addr)
|
|
*
|
|
* Inputs:
|
|
* (r2) kernel pointer to value
|
|
* (r3) dest address (in User Space)
|
|
*
|
|
* Ouputs:
|
|
* (r2) -EFAULT (faulting)
|
|
* 0 (not faulting)
|
|
*/
|
|
.global __put_user_asm_b
|
|
__put_user_asm_b:
|
|
ld.b r2, 0, r4 /* r4 = data */
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___put_user_asm_b1:
|
|
st.b r3, 0, r4
|
|
or ZERO, ZERO, r2
|
|
|
|
___put_user_asm_b_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
.global __put_user_asm_w
|
|
__put_user_asm_w:
|
|
ld.w r2, 0, r4 /* r4 = data */
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___put_user_asm_w1:
|
|
st.w r3, 0, r4
|
|
or ZERO, ZERO, r2
|
|
|
|
___put_user_asm_w_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
.global __put_user_asm_l
|
|
__put_user_asm_l:
|
|
ld.l r2, 0, r4 /* r4 = data */
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___put_user_asm_l1:
|
|
st.l r3, 0, r4
|
|
or ZERO, ZERO, r2
|
|
|
|
___put_user_asm_l_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
|
|
.global __put_user_asm_q
|
|
__put_user_asm_q:
|
|
ld.q r2, 0, r4 /* r4 = data */
|
|
movi -(EFAULT), r2 /* r2 = reply, no real fixup */
|
|
|
|
___put_user_asm_q1:
|
|
st.q r3, 0, r4
|
|
or ZERO, ZERO, r2
|
|
|
|
___put_user_asm_q_exit:
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|
|
panic_stash_regs:
|
|
/* The idea is : when we get an unhandled panic, we dump the registers
|
|
to a known memory location, the just sit in a tight loop.
|
|
This allows the human to look at the memory region through the GDB
|
|
session (assuming the debug module's SHwy initiator isn't locked up
|
|
or anything), to hopefully analyze the cause of the panic. */
|
|
|
|
/* On entry, former r15 (SP) is in DCR
|
|
former r0 is at resvec_saved_area + 0
|
|
former r1 is at resvec_saved_area + 8
|
|
former tr0 is at resvec_saved_area + 32
|
|
DCR is the only register whose value is lost altogether.
|
|
*/
|
|
|
|
movi 0xffffffff80000000, r0 ! phy of dump area
|
|
ld.q SP, 0x000, r1 ! former r0
|
|
st.q r0, 0x000, r1
|
|
ld.q SP, 0x008, r1 ! former r1
|
|
st.q r0, 0x008, r1
|
|
st.q r0, 0x010, r2
|
|
st.q r0, 0x018, r3
|
|
st.q r0, 0x020, r4
|
|
st.q r0, 0x028, r5
|
|
st.q r0, 0x030, r6
|
|
st.q r0, 0x038, r7
|
|
st.q r0, 0x040, r8
|
|
st.q r0, 0x048, r9
|
|
st.q r0, 0x050, r10
|
|
st.q r0, 0x058, r11
|
|
st.q r0, 0x060, r12
|
|
st.q r0, 0x068, r13
|
|
st.q r0, 0x070, r14
|
|
getcon dcr, r14
|
|
st.q r0, 0x078, r14
|
|
st.q r0, 0x080, r16
|
|
st.q r0, 0x088, r17
|
|
st.q r0, 0x090, r18
|
|
st.q r0, 0x098, r19
|
|
st.q r0, 0x0a0, r20
|
|
st.q r0, 0x0a8, r21
|
|
st.q r0, 0x0b0, r22
|
|
st.q r0, 0x0b8, r23
|
|
st.q r0, 0x0c0, r24
|
|
st.q r0, 0x0c8, r25
|
|
st.q r0, 0x0d0, r26
|
|
st.q r0, 0x0d8, r27
|
|
st.q r0, 0x0e0, r28
|
|
st.q r0, 0x0e8, r29
|
|
st.q r0, 0x0f0, r30
|
|
st.q r0, 0x0f8, r31
|
|
st.q r0, 0x100, r32
|
|
st.q r0, 0x108, r33
|
|
st.q r0, 0x110, r34
|
|
st.q r0, 0x118, r35
|
|
st.q r0, 0x120, r36
|
|
st.q r0, 0x128, r37
|
|
st.q r0, 0x130, r38
|
|
st.q r0, 0x138, r39
|
|
st.q r0, 0x140, r40
|
|
st.q r0, 0x148, r41
|
|
st.q r0, 0x150, r42
|
|
st.q r0, 0x158, r43
|
|
st.q r0, 0x160, r44
|
|
st.q r0, 0x168, r45
|
|
st.q r0, 0x170, r46
|
|
st.q r0, 0x178, r47
|
|
st.q r0, 0x180, r48
|
|
st.q r0, 0x188, r49
|
|
st.q r0, 0x190, r50
|
|
st.q r0, 0x198, r51
|
|
st.q r0, 0x1a0, r52
|
|
st.q r0, 0x1a8, r53
|
|
st.q r0, 0x1b0, r54
|
|
st.q r0, 0x1b8, r55
|
|
st.q r0, 0x1c0, r56
|
|
st.q r0, 0x1c8, r57
|
|
st.q r0, 0x1d0, r58
|
|
st.q r0, 0x1d8, r59
|
|
st.q r0, 0x1e0, r60
|
|
st.q r0, 0x1e8, r61
|
|
st.q r0, 0x1f0, r62
|
|
st.q r0, 0x1f8, r63 ! bogus, but for consistency's sake...
|
|
|
|
ld.q SP, 0x020, r1 ! former tr0
|
|
st.q r0, 0x200, r1
|
|
gettr tr1, r1
|
|
st.q r0, 0x208, r1
|
|
gettr tr2, r1
|
|
st.q r0, 0x210, r1
|
|
gettr tr3, r1
|
|
st.q r0, 0x218, r1
|
|
gettr tr4, r1
|
|
st.q r0, 0x220, r1
|
|
gettr tr5, r1
|
|
st.q r0, 0x228, r1
|
|
gettr tr6, r1
|
|
st.q r0, 0x230, r1
|
|
gettr tr7, r1
|
|
st.q r0, 0x238, r1
|
|
|
|
getcon sr, r1
|
|
getcon ssr, r2
|
|
getcon pssr, r3
|
|
getcon spc, r4
|
|
getcon pspc, r5
|
|
getcon intevt, r6
|
|
getcon expevt, r7
|
|
getcon pexpevt, r8
|
|
getcon tra, r9
|
|
getcon tea, r10
|
|
getcon kcr0, r11
|
|
getcon kcr1, r12
|
|
getcon vbr, r13
|
|
getcon resvec, r14
|
|
|
|
st.q r0, 0x240, r1
|
|
st.q r0, 0x248, r2
|
|
st.q r0, 0x250, r3
|
|
st.q r0, 0x258, r4
|
|
st.q r0, 0x260, r5
|
|
st.q r0, 0x268, r6
|
|
st.q r0, 0x270, r7
|
|
st.q r0, 0x278, r8
|
|
st.q r0, 0x280, r9
|
|
st.q r0, 0x288, r10
|
|
st.q r0, 0x290, r11
|
|
st.q r0, 0x298, r12
|
|
st.q r0, 0x2a0, r13
|
|
st.q r0, 0x2a8, r14
|
|
|
|
getcon SPC,r2
|
|
getcon SSR,r3
|
|
getcon EXPEVT,r4
|
|
/* Prepare to jump to C - physical address */
|
|
movi panic_handler-CONFIG_CACHED_MEMORY_OFFSET, r1
|
|
ori r1, 1, r1
|
|
ptabs r1, tr0
|
|
getcon DCR, SP
|
|
blink tr0, ZERO
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
|
|
|
|
|
|
|
|
/*
|
|
* --- Signal Handling Section
|
|
*/
|
|
|
|
/*
|
|
* extern long long _sa_default_rt_restorer
|
|
* extern long long _sa_default_restorer
|
|
*
|
|
* or, better,
|
|
*
|
|
* extern void _sa_default_rt_restorer(void)
|
|
* extern void _sa_default_restorer(void)
|
|
*
|
|
* Code prototypes to do a sys_rt_sigreturn() or sys_sysreturn()
|
|
* from user space. Copied into user space by signal management.
|
|
* Both must be quad aligned and 2 quad long (4 instructions).
|
|
*
|
|
*/
|
|
.balign 8
|
|
.global sa_default_rt_restorer
|
|
sa_default_rt_restorer:
|
|
movi 0x10, r9
|
|
shori __NR_rt_sigreturn, r9
|
|
trapa r9
|
|
nop
|
|
|
|
.balign 8
|
|
.global sa_default_restorer
|
|
sa_default_restorer:
|
|
movi 0x10, r9
|
|
shori __NR_sigreturn, r9
|
|
trapa r9
|
|
nop
|
|
|
|
/*
|
|
* --- __ex_table Section
|
|
*/
|
|
|
|
/*
|
|
* User Access Exception Table.
|
|
*/
|
|
.section __ex_table, "a"
|
|
|
|
.global asm_uaccess_start /* Just a marker */
|
|
asm_uaccess_start:
|
|
|
|
.long ___copy_user1, ___copy_user_exit
|
|
.long ___copy_user2, ___copy_user_exit
|
|
.long ___clear_user1, ___clear_user_exit
|
|
.long ___strncpy_from_user1, ___strncpy_from_user_exit
|
|
.long ___strnlen_user1, ___strnlen_user_exit
|
|
.long ___get_user_asm_b1, ___get_user_asm_b_exit
|
|
.long ___get_user_asm_w1, ___get_user_asm_w_exit
|
|
.long ___get_user_asm_l1, ___get_user_asm_l_exit
|
|
.long ___get_user_asm_q1, ___get_user_asm_q_exit
|
|
.long ___put_user_asm_b1, ___put_user_asm_b_exit
|
|
.long ___put_user_asm_w1, ___put_user_asm_w_exit
|
|
.long ___put_user_asm_l1, ___put_user_asm_l_exit
|
|
.long ___put_user_asm_q1, ___put_user_asm_q_exit
|
|
|
|
.global asm_uaccess_end /* Just a marker */
|
|
asm_uaccess_end:
|
|
|
|
|
|
|
|
|
|
/*
|
|
* --- .text.init Section
|
|
*/
|
|
|
|
.section .text.init, "ax"
|
|
|
|
/*
|
|
* void trap_init (void)
|
|
*
|
|
*/
|
|
.global trap_init
|
|
trap_init:
|
|
addi SP, -24, SP /* Room to save r28/r29/r30 */
|
|
st.q SP, 0, r28
|
|
st.q SP, 8, r29
|
|
st.q SP, 16, r30
|
|
|
|
/* Set VBR and RESVEC */
|
|
movi LVBR_block, r19
|
|
andi r19, -4, r19 /* reset MMUOFF + reserved */
|
|
/* For RESVEC exceptions we force the MMU off, which means we need the
|
|
physical address. */
|
|
movi LRESVEC_block-CONFIG_CACHED_MEMORY_OFFSET, r20
|
|
andi r20, -4, r20 /* reset reserved */
|
|
ori r20, 1, r20 /* set MMUOFF */
|
|
putcon r19, VBR
|
|
putcon r20, RESVEC
|
|
|
|
/* Sanity check */
|
|
movi LVBR_block_end, r21
|
|
andi r21, -4, r21
|
|
movi BLOCK_SIZE, r29 /* r29 = expected size */
|
|
or r19, ZERO, r30
|
|
add r19, r29, r19
|
|
|
|
/*
|
|
* Ugly, but better loop forever now than crash afterwards.
|
|
* We should print a message, but if we touch LVBR or
|
|
* LRESVEC blocks we should not be surprised if we get stuck
|
|
* in trap_init().
|
|
*/
|
|
pta trap_init_loop, tr1
|
|
gettr tr1, r28 /* r28 = trap_init_loop */
|
|
sub r21, r30, r30 /* r30 = actual size */
|
|
|
|
/*
|
|
* VBR/RESVEC handlers overlap by being bigger than
|
|
* allowed. Very bad. Just loop forever.
|
|
* (r28) panic/loop address
|
|
* (r29) expected size
|
|
* (r30) actual size
|
|
*/
|
|
trap_init_loop:
|
|
bne r19, r21, tr1
|
|
|
|
/* Now that exception vectors are set up reset SR.BL */
|
|
getcon SR, r22
|
|
movi SR_UNBLOCK_EXC, r23
|
|
and r22, r23, r22
|
|
putcon r22, SR
|
|
|
|
addi SP, 24, SP
|
|
ptabs LINK, tr0
|
|
blink tr0, ZERO
|
|
|