patch-2.4.22 linux-2.4.22/arch/mips/kernel/gdb-stub.c
Next file: linux-2.4.22/arch/mips/kernel/head.S
Previous file: linux-2.4.22/arch/mips/kernel/gdb-low.S
Back to the patch index
Back to the overall index
- Lines: 439
- Date:
2003-08-25 04:44:40.000000000 -0700
- Orig file:
linux-2.4.21/arch/mips/kernel/gdb-stub.c
- Orig date:
2002-11-28 15:53:10.000000000 -0800
diff -urN linux-2.4.21/arch/mips/kernel/gdb-stub.c linux-2.4.22/arch/mips/kernel/gdb-stub.c
@@ -11,6 +11,9 @@
* Send complaints, suggestions etc. to <andy@waldorf-gmbh.de>
*
* Copyright (C) 1995 Andreas Busse
+ *
+ * Copyright (C) 2003 MontaVista Software Inc
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
*/
/*
@@ -99,7 +102,7 @@
* the kernel running. It will promptly halt and wait
* for the host gdb session to connect. It does this
* since the "Kernel Hacking" option has defined
- * CONFIG_REMOTE_DEBUG which in turn enables your calls
+ * CONFIG_KGDB which in turn enables your calls
* to:
* set_debug_traps();
* breakpoint();
@@ -127,6 +130,10 @@
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/reboot.h>
#include <asm/asm.h>
#include <asm/mipsregs.h>
@@ -135,6 +142,8 @@
#include <asm/gdb-stub.h>
#include <asm/inst.h>
+#include <asm/debug.h>
+
/*
* external low-level support routines
*/
@@ -148,6 +157,8 @@
*/
extern void breakpoint(void);
extern void breakinst(void);
+extern void async_breakpoint(void);
+extern void async_breakinst(void);
extern void adel(void);
/*
@@ -163,6 +174,12 @@
void handle_exception(struct gdb_regs *regs);
/*
+ * spin locks for smp case
+ */
+static spinlock_t kgdb_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t kgdb_cpulock[NR_CPUS] = { [0 ... NR_CPUS-1] = SPIN_LOCK_UNLOCKED};
+
+/*
* BUFMAX defines the maximum number of characters in inbound/outbound buffers
* at least NUMREGBYTES*2 are needed for register packets
*/
@@ -171,12 +188,13 @@
static char input_buffer[BUFMAX];
static char output_buffer[BUFMAX];
static int initialized; /* !0 means we've been initialized */
+static int kgdb_started;
static const char hexchars[]="0123456789abcdef";
/* Used to prevent crashes in memory access. Note that they'll crash anyway if
we haven't set up fault handlers yet... */
-int kgdb_read_byte(unsigned *address, unsigned *dest);
-int kgdb_write_byte(unsigned val, unsigned *dest);
+int kgdb_read_byte(unsigned char *address, unsigned char *dest);
+int kgdb_write_byte(unsigned char val, unsigned char *dest);
/*
* Convert ch from a hex digit to an int
@@ -394,6 +412,17 @@
restore_flags(flags);
}
+void restore_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ save_and_cli(flags);
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ set_except_vector(ht->tt, saved_vectors[ht->tt]);
+ restore_flags(flags);
+}
+
/*
* Convert the MIPS hardware trap type code to a Unix signal number.
*/
@@ -572,12 +601,39 @@
*/
static struct gdb_bp_save async_bp;
-void set_async_breakpoint(unsigned int epc)
+/*
+ * Swap the interrupted EPC with our asynchronous breakpoint routine.
+ * This is safer than stuffing the breakpoint in-place, since no cache
+ * flushes (or resulting smp_call_functions) are required. The
+ * assumption is that only one CPU will be handling asynchronous bp's,
+ * and only one can be active at a time.
+ */
+extern spinlock_t smp_call_lock;
+void set_async_breakpoint(unsigned long *epc)
{
- async_bp.addr = epc;
- async_bp.val = *(unsigned *)epc;
- *(unsigned *)epc = BP;
- __flush_cache_all();
+ /* skip breaking into userland */
+ if ((*epc & 0x80000000) == 0)
+ return;
+
+ /* avoid deadlock if someone is make IPC */
+ if (spin_is_locked(&smp_call_lock))
+ return;
+
+ async_bp.addr = *epc;
+ *epc = (unsigned long)async_breakpoint;
+}
+
+void kgdb_wait(void *arg)
+{
+ unsigned flags;
+ int cpu = smp_processor_id();
+
+ local_irq_save(flags);
+
+ spin_lock(&kgdb_cpulock[cpu]);
+ spin_unlock(&kgdb_cpulock[cpu]);
+
+ local_irq_restore(flags);
}
@@ -594,31 +650,45 @@
int length;
char *ptr;
unsigned long *stack;
+ int i;
-#if 0
- printk("in handle_exception()\n");
- show_gdbregs(regs);
-#endif
+ kgdb_started = 1;
/*
- * First check trap type. If this is CPU_UNUSABLE and CPU_ID is 1,
- * the simply switch the FPU on and return since this is no error
- * condition. kernel/traps.c does the same.
- * FIXME: This doesn't work yet, so we don't catch CPU_UNUSABLE
- * traps for now.
+ * acquire the big kgdb spinlock
*/
- trap = (regs->cp0_cause & 0x7c) >> 2;
-/* printk("trap=%d\n",trap); */
- if (trap == 11) {
- if (((regs->cp0_cause >> CAUSEB_CE) & 3) == 1) {
- regs->cp0_status |= ST0_CU1;
- return;
- }
+ if (!spin_trylock(&kgdb_lock)) {
+ /*
+ * some other CPU has the lock, we should go back to
+ * receive the gdb_wait IPC
+ */
+ return;
}
/*
+ * If we're in async_breakpoint(), restore the real EPC from
+ * the breakpoint.
+ */
+ if (regs->cp0_epc == (unsigned long)async_breakinst) {
+ regs->cp0_epc = async_bp.addr;
+ async_bp.addr = 0;
+ }
+
+ /*
+ * acquire the CPU spinlocks
+ */
+ for (i=0; i< smp_num_cpus; i++)
+ db_verify(spin_trylock(&kgdb_cpulock[i]), != 0);
+
+ /*
+ * force other cpus to enter kgdb
+ */
+ smp_call_function(kgdb_wait, NULL, 0, 0);
+
+ /*
* If we're in breakpoint() increment the PC
*/
+ trap = (regs->cp0_cause & 0x7c) >> 2;
if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst)
regs->cp0_epc += 4;
@@ -636,16 +706,6 @@
}
}
- /*
- * If we were interrupted asynchronously by gdb, then a
- * breakpoint was set at the EPC of the interrupt so
- * that we'd wind up here with an interesting stack frame.
- */
- if (async_bp.addr) {
- *(unsigned *)async_bp.addr = async_bp.val;
- async_bp.addr = 0;
- }
-
stack = (long *)regs->reg29; /* stack ptr */
sigval = computeSignal(trap);
@@ -707,6 +767,14 @@
output_buffer[3] = 0;
break;
+ /*
+ * Detach debugger; let CPU run
+ */
+ case 'D':
+ putpacket(output_buffer);
+ goto finish_kgdb;
+ break;
+
case 'd':
/* toggle debug flag */
break;
@@ -726,26 +794,21 @@
/*
* set the value of the CPU registers - return OK
- * FIXME: Needs to be written
*/
case 'G':
{
-#if 0
- unsigned long *newsp, psr;
-
ptr = &input_buffer[1];
- hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */
-
- /*
- * See if the stack pointer has moved. If so, then copy the
- * saved locals and ins to the new location.
- */
-
- newsp = (unsigned long *)registers[SP];
- if (sp != newsp)
- sp = memcpy(newsp, sp, 16 * 4);
-
-#endif
+ hex2mem(ptr, (char *)®s->reg0, 32*4, 0);
+ ptr += 32*8;
+ hex2mem(ptr, (char *)®s->cp0_status, 6*4, 0);
+ ptr += 6*8;
+ hex2mem(ptr, (char *)®s->fpr0, 32*4, 0);
+ ptr += 32*8;
+ hex2mem(ptr, (char *)®s->cp1_fsr, 2*4, 0);
+ ptr += 2*8;
+ hex2mem(ptr, (char *)®s->frame_ptr, 2*4, 0);
+ ptr += 2*8;
+ hex2mem(ptr, (char *)®s->cp0_index, 16*4, 0);
strcpy(output_buffer,"OK");
}
break;
@@ -794,36 +857,19 @@
ptr = &input_buffer[1];
if (hexToInt(&ptr, &addr))
regs->cp0_epc = addr;
-
- /*
- * 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.
- * NB: We flush both caches, just to be sure...
- */
-
- __flush_cache_all();
- return;
- /* NOTREACHED */
+
+ goto exit_kgdb_exception;
break;
-
- /*
- * kill the program
- */
- case 'k' :
- break; /* do nothing */
-
-
/*
- * Reset the whole machine (FIXME: system dependent)
+ * kill the program; let us try to restart the machine
+ * Reset the whole machine.
*/
+ case 'k':
case 'r':
+ machine_restart("kgdb restarts machine");
break;
-
/*
* Step to next instruction
*/
@@ -833,9 +879,9 @@
* use breakpoints and continue, instead.
*/
single_step(regs);
- __flush_cache_all();
- return;
+ goto exit_kgdb_exception;
/* NOTREACHED */
+ break;
/*
* Set baud rate (bBB)
@@ -890,6 +936,20 @@
putpacket(output_buffer);
} /* while */
+
+ return;
+
+finish_kgdb:
+ restore_debug_traps();
+
+exit_kgdb_exception:
+ /* release locks so other CPUs can go */
+ for (i=0; i < smp_num_cpus; i++)
+ spin_unlock(&kgdb_cpulock[i]);
+ spin_unlock(&kgdb_lock);
+
+ __flush_cache_all();
+ return;
}
/*
@@ -903,23 +963,50 @@
if (!initialized)
return;
- __asm__ __volatile__("
- .globl breakinst
- .set noreorder
- nop
-breakinst: break
- nop
- .set reorder
- ");
+ __asm__ __volatile__(
+ ".globl breakinst\n\t"
+ ".set\tnoreorder\n\t"
+ "nop\n\t"
+ "breakinst:\tbreak\n\t"
+ "nop\n\t"
+ ".set\treorder"
+ );
+}
+
+/* Nothing but the break; don't pollute any registers */
+void async_breakpoint(void)
+{
+ __asm__ __volatile__(
+ ".globl async_breakinst\n\t"
+ ".set\tnoreorder\n\t"
+ "nop\n\t"
+ "async_breakinst:\tbreak\n\t"
+ "nop\n\t"
+ ".set\treorder"
+ );
}
void adel(void)
{
- __asm__ __volatile__("
- .globl adel
- la $8,0x80000001
- lw $9,0($8)
- ");
+ __asm__ __volatile__(
+ ".globl\tadel\n\t"
+ "la\t$8,0x80000001\n\t"
+ "lw\t$9,0($8)\n\t"
+ );
+}
+
+/*
+ * malloc is needed by gdb client in "call func()", even a private one
+ * will make gdb happy
+ */
+static void *malloc(size_t size)
+{
+ return kmalloc(size, GFP_ATOMIC);
+}
+
+static void free(void *where)
+{
+ kfree(where);
}
#ifdef CONFIG_GDB_CONSOLE
@@ -928,6 +1015,9 @@
{
char outbuf[18];
+ if (!kgdb_started)
+ return;
+
outbuf[0]='O';
while(l) {
@@ -951,11 +1041,11 @@
}
static struct console gdb_console = {
- name: "gdb",
- write: gdb_console_write,
- device: gdb_console_dev,
- flags: CON_PRINTBUFFER,
- index: -1
+ .name = "gdb",
+ .write = gdb_console_write,
+ .device = gdb_console_dev,
+ .flags = CON_PRINTBUFFER,
+ .index = -1
};
__init void register_gdb_console(void)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)