diff options
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r-- | arch/sparc/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/sparc/kernel/entry.S | 125 | ||||
-rw-r--r-- | arch/sparc/kernel/head.S | 9 | ||||
-rw-r--r-- | arch/sparc/kernel/kgdb.c | 164 | ||||
-rw-r--r-- | arch/sparc/kernel/sparc-stub.c | 724 |
5 files changed, 214 insertions, 809 deletions
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 59700aaaae9..6e03a2a7863 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_PCI) += ebus.o obj-$(CONFIG_SUN_PM) += apc.o pmc.o obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o obj-$(CONFIG_SPARC_LED) += led.o +obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 484c83d23ee..57d1bbdd0bd 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -12,7 +12,6 @@ #include <asm/head.h> #include <asm/asi.h> #include <asm/smp.h> -#include <asm/kgdb.h> #include <asm/contregs.h> #include <asm/ptrace.h> #include <asm/asm-offsets.h> @@ -45,91 +44,20 @@ _SV; _SV; _SV; _SV; _SV; _SV; _SV; \ _RS; _RS; _RS; _RS; _RS; _RS; _RS; -/* First, KGDB low level things. This is a rewrite - * of the routines found in the sparc-stub.c asm() statement - * from the gdb distribution. This is also dual-purpose - * as a software trap for userlevel programs. - */ - .data - .align 4 - -in_trap_handler: - .word 0 - .text - .align 4 -#if 0 /* kgdb is dropped from 2.5.33 */ -! This function is called when any SPARC trap (except window overflow or -! underflow) occurs. It makes sure that the invalid register window is still -! available before jumping into C code. It will also restore the world if you -! return from handle_exception. - - .globl trap_low -trap_low: - rd %wim, %l3 - SAVE_ALL - - sethi %hi(in_trap_handler), %l4 - ld [%lo(in_trap_handler) + %l4], %l5 - inc %l5 - st %l5, [%lo(in_trap_handler) + %l4] - - /* Make sure kgdb sees the same state we just saved. */ - LOAD_PT_GLOBALS(sp) - LOAD_PT_INS(sp) - ld [%sp + STACKFRAME_SZ + PT_Y], %l4 - ld [%sp + STACKFRAME_SZ + PT_WIM], %l3 - ld [%sp + STACKFRAME_SZ + PT_PSR], %l0 - ld [%sp + STACKFRAME_SZ + PT_PC], %l1 - ld [%sp + STACKFRAME_SZ + PT_NPC], %l2 - rd %tbr, %l5 /* Never changes... */ - - /* Make kgdb exception frame. */ - sub %sp,(16+1+6+1+72)*4,%sp ! Make room for input & locals - ! + hidden arg + arg spill - ! + doubleword alignment - ! + registers[72] local var - SAVE_KGDB_GLOBALS(sp) - SAVE_KGDB_INS(sp) - SAVE_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2) - - /* We are increasing PIL, so two writes. */ - or %l0, PSR_PIL, %l0 - wr %l0, 0, %psr - WRITE_PAUSE - wr %l0, PSR_ET, %psr - WRITE_PAUSE - - call handle_exception - add %sp, STACKFRAME_SZ, %o0 ! Pass address of registers - - /* Load new kgdb register set. */ - LOAD_KGDB_GLOBALS(sp) - LOAD_KGDB_INS(sp) - LOAD_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2) - wr %l4, 0x0, %y - - sethi %hi(in_trap_handler), %l4 - ld [%lo(in_trap_handler) + %l4], %l5 - dec %l5 - st %l5, [%lo(in_trap_handler) + %l4] - - add %sp,(16+1+6+1+72)*4,%sp ! Undo the kgdb trap frame. - - /* Now take what kgdb did and place it into the pt_regs - * frame which SparcLinux RESTORE_ALL understands., - */ - STORE_PT_INS(sp) - STORE_PT_GLOBALS(sp) - STORE_PT_YREG(sp, g2) - STORE_PT_PRIV(sp, l0, l1, l2) - - RESTORE_ALL +#ifdef CONFIG_KGDB + .align 4 + .globl arch_kgdb_breakpoint + .type arch_kgdb_breakpoint,#function +arch_kgdb_breakpoint: + ta 0x7d + retl + nop + .size arch_kgdb_breakpoint,.-arch_kgdb_breakpoint #endif #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE) - .text .align 4 .globl floppy_hardint floppy_hardint: @@ -1596,6 +1524,23 @@ breakpoint_trap: RESTORE_ALL +#ifdef CONFIG_KGDB + .align 4 + .globl kgdb_trap_low + .type kgdb_trap_low,#function +kgdb_trap_low: + rd %wim,%l3 + SAVE_ALL + wr %l0, PSR_ET, %psr + WRITE_PAUSE + + call kgdb_trap + add %sp, STACKFRAME_SZ, %o0 + + RESTORE_ALL + .size kgdb_trap_low,.-kgdb_trap_low +#endif + .align 4 .globl __handle_exception, flush_patch_exception __handle_exception: @@ -1698,4 +1643,22 @@ pcic_nmi_trap_patch: #endif /* CONFIG_PCI */ + .globl flushw_all +flushw_all: + save %sp, -0x40, %sp + save %sp, -0x40, %sp + save %sp, -0x40, %sp + save %sp, -0x40, %sp + save %sp, -0x40, %sp + save %sp, -0x40, %sp + save %sp, -0x40, %sp + restore + restore + restore + restore + restore + restore + ret + restore + /* End of entry.S */ diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index b7f1e81c8ff..8bec05fa579 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -191,7 +191,8 @@ t_bade8:BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xe t_baded:BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) t_badf2:BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) t_badf7:BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) -t_badfc:BAD_TRAP(0xfc) BAD_TRAP(0xfd) +t_badfc:BAD_TRAP(0xfc) +t_kgdb: KGDB_TRAP(0xfd) dbtrap: BAD_TRAP(0xfe) /* Debugger/PROM breakpoint #1 */ dbtrap2:BAD_TRAP(0xff) /* Debugger/PROM breakpoint #2 */ @@ -267,7 +268,7 @@ trapbase_cpu1: BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) - BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) + BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) trapbase_cpu2: BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction) @@ -335,7 +336,7 @@ trapbase_cpu2: BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) - BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) + BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) trapbase_cpu3: BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction) @@ -403,7 +404,7 @@ trapbase_cpu3: BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) - BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) + BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) #endif .align PAGE_SIZE diff --git a/arch/sparc/kernel/kgdb.c b/arch/sparc/kernel/kgdb.c new file mode 100644 index 00000000000..757805ce02e --- /dev/null +++ b/arch/sparc/kernel/kgdb.c @@ -0,0 +1,164 @@ +/* kgdb.c: KGDB support for 32-bit sparc. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kgdb.h> +#include <linux/kdebug.h> + +#include <asm/kdebug.h> +#include <asm/ptrace.h> +#include <asm/irq.h> + +extern unsigned long trapbase; + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + struct reg_window *win; + int i; + + gdb_regs[GDB_G0] = 0; + for (i = 0; i < 15; i++) + gdb_regs[GDB_G1 + i] = regs->u_regs[UREG_G1 + i]; + + win = (struct reg_window *) regs->u_regs[UREG_FP]; + for (i = 0; i < 8; i++) + gdb_regs[GDB_L0 + i] = win->locals[i]; + for (i = 0; i < 8; i++) + gdb_regs[GDB_I0 + i] = win->ins[i]; + + for (i = GDB_F0; i <= GDB_F31; i++) + gdb_regs[i] = 0; + + gdb_regs[GDB_Y] = regs->y; + gdb_regs[GDB_PSR] = regs->psr; + gdb_regs[GDB_WIM] = 0; + gdb_regs[GDB_TBR] = (unsigned long) &trapbase; + gdb_regs[GDB_PC] = regs->pc; + gdb_regs[GDB_NPC] = regs->npc; + gdb_regs[GDB_FSR] = 0; + gdb_regs[GDB_CSR] = 0; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + struct thread_info *t = task_thread_info(p); + struct reg_window *win; + int i; + + for (i = GDB_G0; i < GDB_G6; i++) + gdb_regs[i] = 0; + gdb_regs[GDB_G6] = (unsigned long) t; + gdb_regs[GDB_G7] = 0; + for (i = GDB_O0; i < GDB_SP; i++) + gdb_regs[i] = 0; + gdb_regs[GDB_SP] = t->ksp; + gdb_regs[GDB_O7] = 0; + + win = (struct reg_window *) t->ksp; + for (i = 0; i < 8; i++) + gdb_regs[GDB_L0 + i] = win->locals[i]; + for (i = 0; i < 8; i++) + gdb_regs[GDB_I0 + i] = win->ins[i]; + + for (i = GDB_F0; i <= GDB_F31; i++) + gdb_regs[i] = 0; + + gdb_regs[GDB_Y] = 0; + + gdb_regs[GDB_PSR] = t->kpsr; + gdb_regs[GDB_WIM] = t->kwim; + gdb_regs[GDB_TBR] = (unsigned long) &trapbase; + gdb_regs[GDB_PC] = t->kpc; + gdb_regs[GDB_NPC] = t->kpc + 4; + gdb_regs[GDB_FSR] = 0; + gdb_regs[GDB_CSR] = 0; +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + struct reg_window *win; + int i; + + for (i = 0; i < 15; i++) + regs->u_regs[UREG_G1 + i] = gdb_regs[GDB_G1 + i]; + + /* If the PSR register is changing, we have to preserve + * the CWP field, otherwise window save/restore explodes. + */ + if (regs->psr != gdb_regs[GDB_PSR]) { + unsigned long cwp = regs->psr & PSR_CWP; + + regs->psr = (gdb_regs[GDB_PSR] & ~PSR_CWP) | cwp; + } + + regs->pc = gdb_regs[GDB_PC]; + regs->npc = gdb_regs[GDB_NPC]; + regs->y = gdb_regs[GDB_Y]; + + win = (struct reg_window *) regs->u_regs[UREG_FP]; + for (i = 0; i < 8; i++) + win->locals[i] = gdb_regs[GDB_L0 + i]; + for (i = 0; i < 8; i++) + win->ins[i] = gdb_regs[GDB_I0 + i]; +} + +int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, + char *remcomInBuffer, char *remcomOutBuffer, + struct pt_regs *linux_regs) +{ + unsigned long addr; + char *ptr; + + switch (remcomInBuffer[0]) { + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (kgdb_hex2long(&ptr, &addr)) { + linux_regs->pc = addr; + linux_regs->npc = addr + 4; + } + /* fallthru */ + + case 'D': + case 'k': + if (linux_regs->pc == (unsigned long) arch_kgdb_breakpoint) { + linux_regs->pc = linux_regs->npc; + linux_regs->npc += 4; + } + return 0; + } + return -1; +} + +extern void do_hw_interrupt(struct pt_regs *regs, unsigned long type); + +asmlinkage void kgdb_trap(struct pt_regs *regs) +{ + unsigned long flags; + + if (user_mode(regs)) { + do_hw_interrupt(regs, 0xfd); + return; + } + + flushw_all(); + + local_irq_save(flags); + kgdb_handle_exception(0x172, SIGTRAP, 0, regs); + local_irq_restore(flags); +} + +int kgdb_arch_init(void) +{ + return 0; +} + +void kgdb_arch_exit(void) +{ +} + +struct kgdb_arch arch_kgdb_ops = { + /* Breakpoint instruction: ta 0x7d */ + .gdb_bpt_instr = { 0x91, 0xd0, 0x20, 0x7d }, +}; diff --git a/arch/sparc/kernel/sparc-stub.c b/arch/sparc/kernel/sparc-stub.c deleted file mode 100644 index e84f815e690..00000000000 --- a/arch/sparc/kernel/sparc-stub.c +++ /dev/null @@ -1,724 +0,0 @@ -/* $Id: sparc-stub.c,v 1.28 2001/10/30 04:54:21 davem Exp $ - * sparc-stub.c: KGDB support for the Linux kernel. - * - * Modifications to run under Linux - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * - * This file originally came from the gdb sources, and the - * copyright notices have been retained below. - */ - -/**************************************************************************** - - THIS SOFTWARE IS NOT COPYRIGHTED - - HP offers the following for use in the public domain. HP makes no - warranty with regard to the software or its performance and the - user accepts the software "AS IS" with all faults. - - HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD - TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - -****************************************************************************/ - -/**************************************************************************** - * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ - * - * Module name: remcom.c $ - * Revision: 1.34 $ - * Date: 91/03/09 12:29:49 $ - * Contributor: Lake Stevens Instrument Division$ - * - * Description: low level support for gdb debugger. $ - * - * Considerations: only works on target hardware $ - * - * Written by: Glenn Engel $ - * ModuleState: Experimental $ - * - * NOTES: See Below $ - * - * Modified for SPARC by Stu Grossman, Cygnus Support. - * - * This code has been extensively tested on the Fujitsu SPARClite demo board. - * - * To enable debugger support, two things need to happen. One, a - * call to set_debug_traps() is necessary in order to allow any breakpoints - * or error conditions to be properly intercepted and reported to gdb. - * Two, a breakpoint needs to be generated to begin communication. This - * is most easily accomplished by a call to breakpoint(). Breakpoint() - * simulates a breakpoint by executing a trap #1. - * - ************* - * - * The following gdb commands are supported: - * - * command function Return value - * - * g return the value of the CPU registers hex data or ENN - * G set the value of the CPU registers OK or ENN - * - * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN - * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN - * - * c Resume at current address SNN ( signal NN) - * cAA..AA Continue at address AA..AA SNN - * - * s Step one instruction SNN - * sAA..AA Step one instruction from AA..AA SNN - * - * k kill - * - * ? What was the last sigval ? SNN (signal NN) - * - * bBB..BB Set baud rate to BB..BB OK or BNN, then sets - * baud rate - * - * All commands and responses are sent with a packet which includes a - * checksum. A packet consists of - * - * $<packet info>#<checksum>. - * - * where - * <packet info> :: <characters representing the command or response> - * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> - * - * When a packet is received, it is first acknowledged with either '+' or '-'. - * '+' indicates a successful transfer. '-' indicates a failed transfer. - * - * Example: - * - * Host: Reply: - * $m0,10#2a +$00010203040506070809101112131415#42 - * - ****************************************************************************/ - -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/smp.h> -#include <linux/smp_lock.h> - -#include <asm/system.h> -#include <asm/signal.h> -#include <asm/oplib.h> -#include <asm/head.h> -#include <asm/traps.h> -#include <asm/vac-ops.h> -#include <asm/kgdb.h> -#include <asm/pgalloc.h> -#include <asm/pgtable.h> -#include <asm/cacheflush.h> - -/* - * - * external low-level support routines - */ - -extern void putDebugChar(char); /* write a single character */ -extern char getDebugChar(void); /* read and return a single char */ - -/* - * BUFMAX defines the maximum number of characters in inbound/outbound buffers - * at least NUMREGBYTES*2 are needed for register packets - */ -#define BUFMAX 2048 - -static int initialized; /* !0 means we've been initialized */ - -static const char hexchars[]="0123456789abcdef"; - -#define NUMREGS 72 - -/* Number of bytes of registers. */ -#define NUMREGBYTES (NUMREGS * 4) -enum regnames {G0, G1, G2, G3, G4, G5, G6, G7, - O0, O1, O2, O3, O4, O5, SP, O7, - L0, L1, L2, L3, L4, L5, L6, L7, - I0, I1, I2, I3, I4, I5, FP, I7, - - F0, F1, F2, F3, F4, F5, F6, F7, - F8, F9, F10, F11, F12, F13, F14, F15, - F16, F17, F18, F19, F20, F21, F22, F23, - F24, F25, F26, F27, F28, F29, F30, F31, - Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR }; - - -extern void trap_low(void); /* In arch/sparc/kernel/entry.S */ - -unsigned long get_sun4cpte(unsigned long addr) -{ - unsigned long entry; - - __asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : - "=r" (entry) : - "r" (addr), "i" (ASI_PTE)); - return entry; -} - -unsigned long get_sun4csegmap(unsigned long addr) -{ - unsigned long entry; - - __asm__ __volatile__("\n\tlduba [%1] %2, %0\n\t" : - "=r" (entry) : - "r" (addr), "i" (ASI_SEGMAP)); - return entry; -} - -#if 0 -/* Have to sort this out. This cannot be done after initialization. */ -static void flush_cache_all_nop(void) {} -#endif - -/* Place where we save old trap entries for restoration */ -struct tt_entry kgdb_savettable[256]; -typedef void (*trapfunc_t)(void); - -/* Helper routine for manipulation of kgdb_savettable */ -static inline void copy_ttentry(struct tt_entry *src, struct tt_entry *dest) -{ - dest->inst_one = src->inst_one; - dest->inst_two = src->inst_two; - dest->inst_three = src->inst_three; - dest->inst_four = src->inst_four; -} - -/* Initialize the kgdb_savettable so that debugging can commence */ -static void eh_init(void) -{ - int i; - - for(i=0; i < 256; i++) - copy_ttentry(&sparc_ttable[i], &kgdb_savettable[i]); -} - -/* Install an exception handler for kgdb */ -static void exceptionHandler(int tnum, trapfunc_t trap_entry) -{ - unsigned long te_addr = (unsigned long) trap_entry; - - /* Make new vector */ - sparc_ttable[tnum].inst_one = - SPARC_BRANCH((unsigned long) te_addr, - (unsigned long) &sparc_ttable[tnum].inst_one); - sparc_ttable[tnum].inst_two = SPARC_RD_PSR_L0; - sparc_ttable[tnum].inst_three = SPARC_NOP; - sparc_ttable[tnum].inst_four = SPARC_NOP; -} - -/* Convert ch from a hex digit to an int */ -static int -hex(unsigned char ch) -{ - if (ch >= 'a' && ch <= 'f') - return ch-'a'+10; - if (ch >= '0' && ch <= '9') - return ch-'0'; - if (ch >= 'A' && ch <= 'F') - return ch-'A'+10; - return -1; -} - -/* scan for the sequence $<data>#<checksum> */ -static void -getpacket(char *buffer) -{ - unsigned char checksum; - unsigned char xmitcsum; - int i; - int count; - unsigned char ch; - - do { - /* wait around for the start character, ignore all other characters */ - while ((ch = (getDebugChar() & 0x7f)) != '$') ; - - checksum = 0; - xmitcsum = -1; - - count = 0; - - /* now, read until a # or end of buffer is found */ - while (count < BUFMAX) { - ch = getDebugChar() & 0x7f; - if (ch == '#') - break; - checksum = checksum + ch; - buffer[count] = ch; - count = count + 1; - } - - if (count >= BUFMAX) - continue; - - buffer[count] = 0; - - if (ch == '#') { - xmitcsum = hex(getDebugChar() & 0x7f) << 4; - xmitcsum |= hex(getDebugChar() & 0x7f); - if (checksum != xmitcsum) - putDebugChar('-'); /* failed checksum */ - else { - putDebugChar('+'); /* successful transfer */ - /* if a sequence char is present, reply the ID */ - if (buffer[2] == ':') { - putDebugChar(buffer[0]); - putDebugChar(buffer[1]); - /* remove sequence chars from buffer */ - count = strlen(buffer); - for (i=3; i <= count; i++) - buffer[i-3] = buffer[i]; - } - } - } - } while (checksum != xmitcsum); -} - -/* send the packet in buffer. */ - -static void -putpacket(unsigned char *buffer) -{ - unsigned char checksum; - int count; - unsigned char ch, recv; - - /* $<packet info>#<checksum>. */ - do { - putDebugChar('$'); - checksum = 0; - count = 0; - - while ((ch = buffer[count])) { - putDebugChar(ch); - checksum += ch; - count += 1; - } - - putDebugChar('#'); - putDebugChar(hexchars[checksum >> 4]); - putDebugChar(hexchars[checksum & 0xf]); - recv = getDebugChar(); - } while ((recv & 0x7f) != '+'); -} - -static char remcomInBuffer[BUFMAX]; -static char remcomOutBuffer[BUFMAX]; - -/* Convert the memory pointed to by mem into hex, placing result in buf. - * Return a pointer to the last char put in buf (null), in case of mem fault, - * return 0. - */ - -static unsigned char * -mem2hex(char *mem, char *buf, int count) -{ - unsigned char ch; - - while (count-- > 0) { - /* This assembler code is basically: ch = *mem++; - * except that we use the SPARC/Linux exception table - * mechanism (see how "fixup" works in kernel_mna_trap_fault) - * to arrange for a "return 0" upon a memory fault - */ - __asm__( - "\n1:\n\t" - "ldub [%0], %1\n\t" - "inc %0\n\t" - ".section .fixup,#alloc,#execinstr\n\t" - ".align 4\n" - "2:\n\t" - "retl\n\t" - " mov 0, %%o0\n\t" - ".section __ex_table, #alloc\n\t" - ".align 4\n\t" - ".word 1b, 2b\n\t" - ".text\n" - : "=r" (mem), "=r" (ch) : "0" (mem)); - *buf++ = hexchars[ch >> 4]; - *buf++ = hexchars[ch & 0xf]; - } - - *buf = 0; - return buf; -} - -/* convert the hex array pointed to by buf into binary to be placed in mem - * return a pointer to the character AFTER the last byte written. -*/ -static char * -hex2mem(char *buf, char *mem, int count) -{ - int i; - unsigned char ch; - - for (i=0; i<count; i++) { - - ch = hex(*buf++) << 4; - ch |= hex(*buf++); - /* Assembler code is *mem++ = ch; with return 0 on fault */ - __asm__( - "\n1:\n\t" - "stb %1, [%0]\n\t" - "inc %0\n\t" - ".section .fixup,#alloc,#execinstr\n\t" - ".align 4\n" - "2:\n\t" - "retl\n\t" - " mov 0, %%o0\n\t" - ".section __ex_table, #alloc\n\t" - ".align 4\n\t" - ".word 1b, 2b\n\t" - ".text\n" - : "=r" (mem) : "r" (ch) , "0" (mem)); - } - return mem; -} - -/* This table contains the mapping between SPARC hardware trap types, and - signals, which are primarily what GDB understands. It also indicates - which hardware traps we need to commandeer when initializing the stub. */ - -static struct hard_trap_info -{ - unsigned char tt; /* Trap type code for SPARC */ - unsigned char signo; /* Signal that we map this trap into */ -} hard_trap_info[] = { - {SP_TRAP_SBPT, SIGTRAP}, /* ta 1 - Linux/KGDB software breakpoint */ - {0, 0} /* Must be last */ -}; - -/* Set up exception handlers for tracing and breakpoints */ - -void -set_debug_traps(void) -{ - struct hard_trap_info *ht; - unsigned long flags; - - local_irq_save(flags); -#if 0 -/* Have to sort this out. This cannot be done after initialization. */ - BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP); -#endif - - /* Initialize our copy of the Linux Sparc trap table */ - eh_init(); - - for (ht = hard_trap_info; ht->tt && ht->signo; ht++) { - /* Only if it doesn't destroy our fault handlers */ - if((ht->tt != SP_TRAP_TFLT) && - (ht->tt != SP_TRAP_DFLT)) - exceptionHandler(ht->tt, trap_low); - } - - /* In case GDB is started before us, ack any packets (presumably - * "$?#xx") sitting there. - * - * I've found this code causes more problems than it solves, - * so that's why it's commented out. GDB seems to work fine - * now starting either before or after the kernel -bwb - */ -#if 0 - while((c = getDebugChar()) != '$'); - while((c = getDebugChar()) != '#'); - c = getDebugChar(); /* eat first csum byte */ - c = getDebugChar(); /* eat second csum byte */ - putDebugChar('+'); /* ack it */ -#endif - - initialized = 1; /* connect! */ - local_irq_restore(flags); -} - -/* Convert the SPARC hardware trap type code to a unix signal number. */ - -static int -computeSignal(int tt) -{ - struct hard_trap_info *ht; - - for (ht = hard_trap_info; ht->tt && ht->signo; ht++) - if (ht->tt == tt) - return ht->signo; - - return SIGHUP; /* default for things we don't know about */ -} - -/* - * While we find nice hex chars, build an int. - * Return number of chars processed. - */ - -static int -hexToInt(char **ptr, int *intValue) -{ - int numChars = 0; - int hexValue; - - *intValue = 0; - - while (**ptr) { - hexValue = hex(**ptr); - if (hexValue < 0) - break; - - *intValue = (*intValue << 4) | hexValue; - numChars ++; - - (*ptr)++; - } - - return (numChars); -} - -/* - * This function does all command processing for interfacing to gdb. It - * returns 1 if you should skip the instruction at the trap address, 0 - * otherwise. - */ - -extern void breakinst(void); - -void -handle_exception (unsigned long *registers) -{ - int tt; /* Trap type */ - int sigval; - int addr; - int length; - char *ptr; - unsigned long *sp; - - /* First, we must force all of the windows to be spilled out */ - - asm("save %sp, -64, %sp\n\t" - "save %sp, -64, %sp\n\t" - "save %sp, -64, %sp\n\t" - "save %sp, -64, %sp\n\t" - "save %sp, -64, %sp\n\t" - "save %sp, -64, %sp\n\t" - "save %sp, -64, %sp\n\t" - "save %sp, -64, %sp\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t" - "restore\n\t"); - - lock_kernel(); - if (registers[PC] == (unsigned long)breakinst) { - /* Skip over breakpoint trap insn */ - registers[PC] = registers[NPC]; - registers[NPC] += 4; - } - - sp = (unsigned long *)registers[SP]; - - tt = (registers[TBR] >> 4) & 0xff; - - /* reply to host that an exception has occurred */ - sigval = computeSignal(tt); - ptr = remcomOutBuffer; - - *ptr++ = 'T'; - *ptr++ = hexchars[sigval >> 4]; - *ptr++ = hexchars[sigval & 0xf]; - - *ptr++ = hexchars[PC >> 4]; - *ptr++ = hexchars[PC & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®isters[PC], ptr, 4); - *ptr++ = ';'; - - *ptr++ = hexchars[FP >> 4]; - *ptr++ = hexchars[FP & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *) (sp + 8 + 6), ptr, 4); /* FP */ - *ptr++ = ';'; - - *ptr++ = hexchars[SP >> 4]; - *ptr++ = hexchars[SP & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)&sp, ptr, 4); - *ptr++ = ';'; - - *ptr++ = hexchars[NPC >> 4]; - *ptr++ = hexchars[NPC & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®isters[NPC], ptr, 4); - *ptr++ = ';'; - - *ptr++ = hexchars[O7 >> 4]; - *ptr++ = hexchars[O7 & 0xf]; - *ptr++ = ':'; - ptr = mem2hex((char *)®isters[O7], ptr, 4); - *ptr++ = ';'; - - *ptr++ = 0; - - putpacket(remcomOutBuffer); - - /* XXX We may want to add some features dealing with poking the - * XXX page tables, the real ones on the srmmu, and what is currently - * XXX loaded in the sun4/sun4c tlb at this point in time. But this - * XXX also required hacking to the gdb sources directly... - */ - - while (1) { - remcomOutBuffer[0] = 0; - - getpacket(remcomInBuffer); - switch (remcomInBuffer[0]) { - case '?': - remcomOutBuffer[0] = 'S'; - remcomOutBuffer[1] = hexchars[sigval >> 4]; - remcomOutBuffer[2] = hexchars[sigval & 0xf]; - remcomOutBuffer[3] = 0; - break; - - case 'd': - /* toggle debug flag */ - break; - - case 'g': /* return the value of the CPU registers */ - { - ptr = remcomOutBuffer; - /* G & O regs */ - ptr = mem2hex((char *)registers, ptr, 16 * 4); - /* L & I regs */ - ptr = mem2hex((char *) (sp + 0), ptr, 16 * 4); - /* Floating point */ - memset(ptr, '0', 32 * 8); - /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ - mem2hex((char *)®isters[Y], (ptr + 32 * 4 * 2), (8 * 4)); - } - break; - - case 'G': /* set the value of the CPU registers - return OK */ - { - unsigned long *newsp, psr; - - psr = registers[PSR]; - - ptr = &remcomInBuffer[1]; - /* G & O regs */ - hex2mem(ptr, (char *)registers, 16 * 4); - /* L & I regs */ - hex2mem(ptr + 16 * 4 * 2, (char *) (sp + 0), 16 * 4); - /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ - hex2mem(ptr + 64 * 4 * 2, (char *)®isters[Y], 8 * 4); - - /* See if the stack pointer has moved. If so, - * then copy the saved locals and ins to the - * new location. This keeps the window - * overflow and underflow routines happy. - */ - - newsp = (unsigned long *)registers[SP]; - if (sp != newsp) - sp = memcpy(newsp, sp, 16 * 4); - - /* Don't allow CWP to be modified. */ - - if (psr != registers[PSR]) - registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f); - - strcpy(remcomOutBuffer,"OK"); - } - break; - - case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ - /* Try to read %x,%x. */ - - ptr = &remcomInBuffer[1]; - - if (hexToInt(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length)) { - if (mem2hex((char *)addr, remcomOutBuffer, length)) - break; - - strcpy (remcomOutBuffer, "E03"); - } else { - strcpy(remcomOutBuffer,"E01"); - } - break; - - case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ - /* Try to read '%x,%x:'. */ - - ptr = &remcomInBuffer[1]; - - if (hexToInt(&ptr, &addr) - && *ptr++ == ',' - && hexToInt(&ptr, &length) - && *ptr++ == ':') { - if (hex2mem(ptr, (char *)addr, length)) { - strcpy(remcomOutBuffer, "OK"); - } else { - strcpy(remcomOutBuffer, "E03"); - } - } else { - strcpy(remcomOutBuffer, "E02"); - } - break; - - case 'c': /* cAA..AA Continue at address AA..AA(optional) */ - /* try to read optional parameter, pc unchanged if no parm */ - - ptr = &remcomInBuffer[1]; - if (hexToInt(&ptr, &addr)) { - registers[PC] = addr; - registers[NPC] = addr + 4; - } - -/* Need to flush the instruction cache here, as we may have deposited a - * breakpoint, and the icache probably has no way of knowing that a data ref to - * some location may have changed something that is in the instruction cache. - */ - flush_cache_all(); - unlock_kernel(); - return; - - /* kill the program */ - case 'k' : /* do nothing */ - break; - case 'r': /* Reset */ - asm ("call 0\n\t" - "nop\n\t"); - break; - } /* switch */ - - /* reply to the request */ - putpacket(remcomOutBuffer); - } /* while(1) */ -} - -/* This function will generate a breakpoint exception. It is used at the - beginning of a program to sync up with a debugger and can be used - otherwise as a quick means to stop program execution and "break" into - the debugger. */ - -void -breakpoint(void) -{ - if (!initialized) - return; - - /* Again, watch those c-prefixes for ELF kernels */ -#if defined(__svr4__) || defined(__ELF__) - asm(".globl breakinst\n" - "breakinst:\n\t" - "ta 1\n"); -#else - asm(".globl _breakinst\n" - "_breakinst:\n\t" - "ta 1\n"); -#endif -} |