|  | /* | 
|  | *  Copyright (C) 2004 Axis Communications AB | 
|  | * | 
|  | * Code for handling break 8, hardware breakpoint, single step, and serial | 
|  | * port exceptions for kernel debugging purposes. | 
|  | */ | 
|  |  | 
|  | #include <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 |