552 lines
12 KiB
ArmAsm
552 lines
12 KiB
ArmAsm
/*
|
|
* Copyright (C) 2004 Axis Communications AB
|
|
*
|
|
* Code for handling break 8, hardware breakpoint, single step, and serial
|
|
* port exceptions for kernel debugging purposes.
|
|
*/
|
|
|
|
#include <arch/hwregs/intr_vect.h>
|
|
|
|
;; Exported functions.
|
|
.globl kgdb_handle_exception
|
|
|
|
kgdb_handle_exception:
|
|
|
|
;; Create a register image of the caller.
|
|
;;
|
|
;; First of all, save the ACR on the stack since we need it for address calculations.
|
|
;; We put it into the register struct later.
|
|
|
|
subq 4, $sp
|
|
move.d $acr, [$sp]
|
|
|
|
;; Now we are free to use ACR all we want.
|
|
;; If we were running this handler with interrupts on, we would have to be careful
|
|
;; to save and restore CCS manually, but since we aren't we treat it like every other
|
|
;; register.
|
|
|
|
move.d reg, $acr
|
|
move.d $r0, [$acr] ; Save R0 (start of register struct)
|
|
addq 4, $acr
|
|
move.d $r1, [$acr] ; Save R1
|
|
addq 4, $acr
|
|
move.d $r2, [$acr] ; Save R2
|
|
addq 4, $acr
|
|
move.d $r3, [$acr] ; Save R3
|
|
addq 4, $acr
|
|
move.d $r4, [$acr] ; Save R4
|
|
addq 4, $acr
|
|
move.d $r5, [$acr] ; Save R5
|
|
addq 4, $acr
|
|
move.d $r6, [$acr] ; Save R6
|
|
addq 4, $acr
|
|
move.d $r7, [$acr] ; Save R7
|
|
addq 4, $acr
|
|
move.d $r8, [$acr] ; Save R8
|
|
addq 4, $acr
|
|
move.d $r9, [$acr] ; Save R9
|
|
addq 4, $acr
|
|
move.d $r10, [$acr] ; Save R10
|
|
addq 4, $acr
|
|
move.d $r11, [$acr] ; Save R11
|
|
addq 4, $acr
|
|
move.d $r12, [$acr] ; Save R12
|
|
addq 4, $acr
|
|
move.d $r13, [$acr] ; Save R13
|
|
addq 4, $acr
|
|
move.d $sp, [$acr] ; Save SP (R14)
|
|
addq 4, $acr
|
|
|
|
;; The ACR register is already saved on the stack, so pop it from there.
|
|
move.d [$sp],$r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
|
|
move $bz, [$acr]
|
|
addq 1, $acr
|
|
move $vr, [$acr]
|
|
addq 1, $acr
|
|
move $pid, [$acr]
|
|
addq 4, $acr
|
|
move $srs, [$acr]
|
|
addq 1, $acr
|
|
move $wz, [$acr]
|
|
addq 2, $acr
|
|
move $exs, [$acr]
|
|
addq 4, $acr
|
|
move $eda, [$acr]
|
|
addq 4, $acr
|
|
move $mof, [$acr]
|
|
addq 4, $acr
|
|
move $dz, [$acr]
|
|
addq 4, $acr
|
|
move $ebp, [$acr]
|
|
addq 4, $acr
|
|
move $erp, [$acr]
|
|
addq 4, $acr
|
|
move $srp, [$acr]
|
|
addq 4, $acr
|
|
move $nrp, [$acr]
|
|
addq 4, $acr
|
|
move $ccs, [$acr]
|
|
addq 4, $acr
|
|
move $usp, [$acr]
|
|
addq 4, $acr
|
|
move $spc, [$acr]
|
|
addq 4, $acr
|
|
|
|
;; Skip the pseudo-PC.
|
|
addq 4, $acr
|
|
|
|
;; Save the support registers in bank 0 - 3.
|
|
clear.d $r1 ; Bank counter
|
|
move.d sreg, $acr
|
|
|
|
;; Bank 0
|
|
move $r1, $srs
|
|
nop
|
|
nop
|
|
nop
|
|
move $s0, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s1, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s2, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s3, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s4, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s5, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s6, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s7, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s8, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s9, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s10, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s11, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s12, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
|
|
;; Nothing in S13 - S15, bank 0
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
|
|
;; Bank 1 and bank 2 have the same layout, hence the loop.
|
|
addq 1, $r1
|
|
1:
|
|
move $r1, $srs
|
|
nop
|
|
nop
|
|
nop
|
|
move $s0, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s1, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s2, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s3, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s4, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s5, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s6, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
|
|
;; Nothing in S7 - S15, bank 1 and 2
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
|
|
addq 1, $r1
|
|
cmpq 3, $r1
|
|
bne 1b
|
|
nop
|
|
|
|
;; Bank 3
|
|
move $r1, $srs
|
|
nop
|
|
nop
|
|
nop
|
|
move $s0, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s1, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s2, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s3, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s4, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s5, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s6, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s7, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s8, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s9, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s10, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s11, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s12, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s13, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
move $s14, $r0
|
|
move.d $r0, [$acr]
|
|
addq 4, $acr
|
|
;; Nothing in S15, bank 3
|
|
clear.d [$acr]
|
|
addq 4, $acr
|
|
|
|
;; Check what got us here: get IDX field of EXS.
|
|
move $exs, $r10
|
|
and.d 0xff00, $r10
|
|
lsrq 8, $r10
|
|
#if defined(CONFIG_ETRAX_KGDB_PORT0)
|
|
cmp.d SER0_INTR_VECT, $r10 ; IRQ for serial port 0
|
|
beq sigint
|
|
nop
|
|
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
|
|
cmp.d SER1_INTR_VECT, $r10 ; IRQ for serial port 1
|
|
beq sigint
|
|
nop
|
|
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
|
|
cmp.d SER2_INTR_VECT, $r10 ; IRQ for serial port 2
|
|
beq sigint
|
|
nop
|
|
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
|
|
cmp.d SER3_INTR_VECT, $r10 ; IRQ for serial port 3
|
|
beq sigint
|
|
nop
|
|
#endif
|
|
;; Multiple interrupt must be due to serial break.
|
|
cmp.d 0x30, $r10 ; Multiple interrupt
|
|
beq sigint
|
|
nop
|
|
;; Neither of those? Then it's a sigtrap.
|
|
ba handle_comm
|
|
moveq 5, $r10 ; Set SIGTRAP (delay slot)
|
|
|
|
sigint:
|
|
;; Serial interrupt; get character
|
|
jsr getDebugChar
|
|
nop ; Delay slot
|
|
cmp.b 3, $r10 ; \003 (Ctrl-C)?
|
|
bne return ; No, get out of here
|
|
nop
|
|
moveq 2, $r10 ; Set SIGINT
|
|
|
|
;;
|
|
;; Handle the communication
|
|
;;
|
|
handle_comm:
|
|
move.d internal_stack+1020, $sp ; Use the internal stack which grows upwards
|
|
jsr handle_exception ; Interactive routine
|
|
nop
|
|
|
|
;;
|
|
;; Return to the caller
|
|
;;
|
|
return:
|
|
|
|
;; First of all, write the support registers.
|
|
clear.d $r1 ; Bank counter
|
|
move.d sreg, $acr
|
|
|
|
;; Bank 0
|
|
move $r1, $srs
|
|
nop
|
|
nop
|
|
nop
|
|
move.d [$acr], $r0
|
|
move $r0, $s0
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s1
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s2
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s3
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s4
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s5
|
|
addq 4, $acr
|
|
|
|
;; Nothing in S6 - S7, bank 0.
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
|
|
move.d [$acr], $r0
|
|
move $r0, $s8
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s9
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s10
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s11
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s12
|
|
addq 4, $acr
|
|
|
|
;; Nothing in S13 - S15, bank 0
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
|
|
;; Bank 1 and bank 2 have the same layout, hence the loop.
|
|
addq 1, $r1
|
|
2:
|
|
move $r1, $srs
|
|
nop
|
|
nop
|
|
nop
|
|
move.d [$acr], $r0
|
|
move $r0, $s0
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s1
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s2
|
|
addq 4, $acr
|
|
|
|
;; S3 (MM_CAUSE) is read-only.
|
|
addq 4, $acr
|
|
|
|
move.d [$acr], $r0
|
|
move $r0, $s4
|
|
addq 4, $acr
|
|
|
|
;; FIXME: Actually write S5/S6? (Affects MM_CAUSE.)
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
|
|
;; Nothing in S7 - S15, bank 1 and 2
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
addq 4, $acr
|
|
|
|
addq 1, $r1
|
|
cmpq 3, $r1
|
|
bne 2b
|
|
nop
|
|
|
|
;; Bank 3
|
|
move $r1, $srs
|
|
nop
|
|
nop
|
|
nop
|
|
move.d [$acr], $r0
|
|
move $r0, $s0
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s1
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s2
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s3
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s4
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s5
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s6
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s7
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s8
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s9
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s10
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s11
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s12
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s13
|
|
addq 4, $acr
|
|
move.d [$acr], $r0
|
|
move $r0, $s14
|
|
addq 4, $acr
|
|
|
|
;; Nothing in S15, bank 3
|
|
addq 4, $acr
|
|
|
|
;; Now, move on to the regular register restoration process.
|
|
|
|
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
|
|
move.d [$acr], $r0 ; Restore R0
|
|
addq 4, $acr
|
|
move.d [$acr], $r1 ; Restore R1
|
|
addq 4, $acr
|
|
move.d [$acr], $r2 ; Restore R2
|
|
addq 4, $acr
|
|
move.d [$acr], $r3 ; Restore R3
|
|
addq 4, $acr
|
|
move.d [$acr], $r4 ; Restore R4
|
|
addq 4, $acr
|
|
move.d [$acr], $r5 ; Restore R5
|
|
addq 4, $acr
|
|
move.d [$acr], $r6 ; Restore R6
|
|
addq 4, $acr
|
|
move.d [$acr], $r7 ; Restore R7
|
|
addq 4, $acr
|
|
move.d [$acr], $r8 ; Restore R8
|
|
addq 4, $acr
|
|
move.d [$acr], $r9 ; Restore R9
|
|
addq 4, $acr
|
|
move.d [$acr], $r10 ; Restore R10
|
|
addq 4, $acr
|
|
move.d [$acr], $r11 ; Restore R11
|
|
addq 4, $acr
|
|
move.d [$acr], $r12 ; Restore R12
|
|
addq 4, $acr
|
|
move.d [$acr], $r13 ; Restore R13
|
|
|
|
;;
|
|
;; We restore all registers, even though some of them probably haven't changed.
|
|
;;
|
|
|
|
addq 4, $acr
|
|
move.d [$acr], $sp ; Restore SP (R14)
|
|
|
|
;; ACR cannot be restored just yet.
|
|
addq 8, $acr
|
|
|
|
;; Skip BZ, VR.
|
|
addq 2, $acr
|
|
|
|
move [$acr], $pid ; Restore PID
|
|
addq 4, $acr
|
|
move [$acr], $srs ; Restore SRS
|
|
nop
|
|
nop
|
|
nop
|
|
addq 1, $acr
|
|
|
|
;; Skip WZ.
|
|
addq 2, $acr
|
|
|
|
move [$acr], $exs ; Restore EXS.
|
|
addq 4, $acr
|
|
move [$acr], $eda ; Restore EDA.
|
|
addq 4, $acr
|
|
move [$acr], $mof ; Restore MOF.
|
|
|
|
;; Skip DZ.
|
|
addq 8, $acr
|
|
|
|
move [$acr], $ebp ; Restore EBP.
|
|
addq 4, $acr
|
|
move [$acr], $erp ; Restore ERP.
|
|
addq 4, $acr
|
|
move [$acr], $srp ; Restore SRP.
|
|
addq 4, $acr
|
|
move [$acr], $nrp ; Restore NRP.
|
|
addq 4, $acr
|
|
move [$acr], $ccs ; Restore CCS like an ordinary register.
|
|
addq 4, $acr
|
|
move [$acr], $usp ; Restore USP
|
|
addq 4, $acr
|
|
move [$acr], $spc ; Restore SPC
|
|
; No restoration of pseudo-PC of course.
|
|
|
|
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
|
|
add.d 15*4, $acr
|
|
move.d [$acr], $acr ; Finally, restore ACR.
|
|
rete ; Same as jump ERP
|
|
rfe ; Shifts CCS
|