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

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 *)&regs->reg0, 32*4, 0);
+			ptr += 32*8;
+			hex2mem(ptr, (char *)&regs->cp0_status, 6*4, 0);
+			ptr += 6*8;
+			hex2mem(ptr, (char *)&regs->fpr0, 32*4, 0);
+			ptr += 32*8;
+			hex2mem(ptr, (char *)&regs->cp1_fsr, 2*4, 0);
+			ptr += 2*8;
+			hex2mem(ptr, (char *)&regs->frame_ptr, 2*4, 0);
+			ptr += 2*8;
+			hex2mem(ptr, (char *)&regs->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)