* [patch 2/8] KGDB support for i386
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
@ 2004-10-29 18:33 ` Tom Rini
2004-10-29 18:33 ` [patch 3/8] KGDB support for ppc32 Tom Rini
` (7 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:33 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, trini
This adds support for KGDB on the i386 architecture. The only slightly
related change is to make i386 parse early_params now as KGDB registers as
one. We also add an early_trap_init() call which does the parts of
trap_init() which need to be done for KGDB to be entered, and can be done
safely this early.
As an aside, I believe the hunk to arch/i386/kernel/signal.c can be dropped in
a post 2.6.10-rc1 tree as something similar using unlikey() has been put in.
---
linux-2.6.10-rc1-trini/arch/i386/kernel/Makefile | 1
linux-2.6.10-rc1-trini/arch/i386/kernel/irq.c | 3
linux-2.6.10-rc1-trini/arch/i386/kernel/kgdb.c | 300 +++++++++++++++++++++++
linux-2.6.10-rc1-trini/arch/i386/kernel/nmi.c | 11
linux-2.6.10-rc1-trini/arch/i386/kernel/setup.c | 3
linux-2.6.10-rc1-trini/arch/i386/kernel/signal.c | 3
linux-2.6.10-rc1-trini/arch/i386/kernel/traps.c | 31 +-
linux-2.6.10-rc1-trini/arch/i386/mm/fault.c | 10
linux-2.6.10-rc1-trini/include/asm-i386/kgdb.h | 48 +++
linux-2.6.10-rc1-trini/include/asm-i386/system.h | 10
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
11 files changed, 405 insertions(+), 17 deletions(-)
diff -puN arch/i386/kernel/Makefile~i386-lite arch/i386/kernel/Makefile
--- linux-2.6.10-rc1/arch/i386/kernel/Makefile~i386-lite 2004-10-29 11:26:44.461448834 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/Makefile 2004-10-29 11:26:44.481444139 -0700
@@ -32,6 +32,7 @@ obj-$(CONFIG_ACPI_SRAT) += srat.o
obj-$(CONFIG_HPET_TIMER) += time_hpet.o
obj-$(CONFIG_EFI) += efi.o efi_stub.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_KGDB) += kgdb.o
EXTRA_AFLAGS := -traditional
diff -puN arch/i386/kernel/irq.c~i386-lite arch/i386/kernel/irq.c
--- linux-2.6.10-rc1/arch/i386/kernel/irq.c~i386-lite 2004-10-29 11:26:44.463448364 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/irq.c 2004-10-29 11:26:44.482443904 -0700
@@ -15,6 +15,7 @@
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
+#include <linux/kgdb.h>
#ifndef CONFIG_X86_LOCAL_APIC
/*
@@ -103,6 +104,8 @@ asmlinkage unsigned int do_IRQ(struct pt
irq_exit();
+ kgdb_process_breakpoint();
+
return 1;
}
diff -puN /dev/null arch/i386/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/kgdb.c 2004-10-29 11:26:44.482443904 -0700
@@ -0,0 +1,300 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ */
+/*
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale<akale@veritas.com>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <dave@gcom.com>
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/vm86.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <asm/desc.h>
+#include <asm/kdebug.h>
+
+/* Put the error code here just in case the user cares. */
+int gdb_i386errcode;
+/* Likewise, the vector number here (since GDB only gets the signal
+ number through the usual means, and that's not very specific). */
+int gdb_i386vector = -1;
+
+extern atomic_t cpu_doing_single_step;
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ gdb_regs[_EAX] = regs->eax;
+ gdb_regs[_EBX] = regs->ebx;
+ gdb_regs[_ECX] = regs->ecx;
+ gdb_regs[_EDX] = regs->edx;
+ gdb_regs[_ESI] = regs->esi;
+ gdb_regs[_EDI] = regs->edi;
+ gdb_regs[_EBP] = regs->ebp;
+ gdb_regs[_DS] = regs->xds;
+ gdb_regs[_ES] = regs->xes;
+ gdb_regs[_PS] = regs->eflags;
+ gdb_regs[_CS] = regs->xcs;
+ gdb_regs[_PC] = regs->eip;
+ gdb_regs[_ESP] = (int)(®s->esp);
+ gdb_regs[_SS] = __KERNEL_DS;
+ gdb_regs[_FS] = 0xFFFF;
+ gdb_regs[_GS] = 0xFFFF;
+}
+
+/*
+ * Extracts ebp, esp and eip values understandable by gdb from the values
+ * saved by switch_to.
+ * thread.esp points to ebp. flags and ebp are pushed in switch_to hence esp
+ * prior to entering switch_to is 8 greater then the value that is saved.
+ * If switch_to changes, change following code appropriately.
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ gdb_regs[_EAX] = 0;
+ gdb_regs[_EBX] = 0;
+ gdb_regs[_ECX] = 0;
+ gdb_regs[_EDX] = 0;
+ gdb_regs[_ESI] = 0;
+ gdb_regs[_EDI] = 0;
+ gdb_regs[_EBP] = *(unsigned long *)p->thread.esp;
+ gdb_regs[_DS] = __KERNEL_DS;
+ gdb_regs[_ES] = __KERNEL_DS;
+ gdb_regs[_PS] = *(unsigned long *)(p->thread.esp + 4);
+ gdb_regs[_CS] = __KERNEL_CS;
+ gdb_regs[_PC] = p->thread.eip;
+ gdb_regs[_ESP] = p->thread.esp;
+ gdb_regs[_SS] = __KERNEL_DS;
+ gdb_regs[_FS] = 0xFFFF;
+ gdb_regs[_GS] = 0xFFFF;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ regs->eax = gdb_regs[_EAX];
+ regs->ebx = gdb_regs[_EBX];
+ regs->ecx = gdb_regs[_ECX];
+ regs->edx = gdb_regs[_EDX];
+ regs->esi = gdb_regs[_ESI];
+ regs->edi = gdb_regs[_EDI];
+ regs->ebp = gdb_regs[_EBP];
+ regs->xds = gdb_regs[_DS];
+ regs->xes = gdb_regs[_ES];
+ regs->eflags = gdb_regs[_PS];
+ regs->xcs = gdb_regs[_CS];
+ regs->eip = gdb_regs[_PC];
+#if 0 /* can't change these */
+ regs->esp = gdb_regs[_ESP];
+ regs->xss = gdb_regs[_SS];
+ regs->fs = gdb_regs[_FS];
+ regs->gs = gdb_regs[_GS];
+#endif
+
+}
+
+struct hw_breakpoint {
+ unsigned enabled;
+ unsigned type;
+ unsigned len;
+ unsigned addr;
+} breakinfo[4] = {
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+ { .enabled = 0 },
+};
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+ int correctit;
+ int breakbit;
+ unsigned dr7;
+
+ asm volatile ("movl %%db7, %0\n":"=r" (dr7)
+ :);
+ do {
+ unsigned addr0, addr1, addr2, addr3;
+ asm volatile ("movl %%db0, %0\n"
+ "movl %%db1, %1\n"
+ "movl %%db2, %2\n"
+ "movl %%db3, %3\n":"=r" (addr0), "=r"(addr1),
+ "=r"(addr2), "=r"(addr3):);
+ } while (0);
+ correctit = 0;
+ for (breakno = 0; breakno < 3; breakno++) {
+ breakbit = 2 << (breakno << 1);
+ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 |= breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ dr7 |= (((breakinfo[breakno].len << 2) |
+ breakinfo[breakno].type) << 16) <<
+ (breakno << 2);
+ switch (breakno) {
+ case 0:
+ asm volatile ("movl %0, %%dr0\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 1:
+ asm volatile ("movl %0, %%dr1\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 2:
+ asm volatile ("movl %0, %%dr2\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 3:
+ asm volatile ("movl %0, %%dr3\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+ }
+ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 &= ~breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ }
+ }
+ if (correctit) {
+ asm volatile ("movl %0, %%db7\n"::"r" (dr7));
+ }
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ /* Disable hardware debugging while we are in kgdb */
+ asm volatile ("movl %0,%%db7": /* no output */ :"r" (0));
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_i386vector = eVector;
+ gdb_i386errcode = err_code;
+}
+
+int kgdb_arch_handle_exception(int exceptionVector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ long addr;
+ long breakno;
+ char *ptr;
+ int newPC;
+ int dr6;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ case 's':
+ if (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(remcom_out_buffer, "E00");
+ break;
+ }
+
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr)) {
+ linux_regs->eip = addr;
+ }
+ newPC = linux_regs->eip;
+
+ /* clear the trace bit */
+ linux_regs->eflags &= ~TF_MASK;
+ atomic_set(&cpu_doing_single_step,-1);
+
+ /* set the trace bit if we're stepping */
+ if (remcom_in_buffer[0] == 's') {
+ linux_regs->eflags |= TF_MASK;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,smp_processor_id());
+ }
+
+ asm volatile ("movl %%db6, %0\n":"=r" (dr6));
+ if (!(dr6 & 0x4000)) {
+ for (breakno = 0; breakno < 4; ++breakno) {
+ if (dr6 & (1 << breakno)) {
+ if (breakinfo[breakno].type == 0) {
+ /* Set restore flag */
+ linux_regs->eflags |= X86_EFLAGS_RF;
+ break;
+ }
+ }
+ }
+ }
+ kgdb_correct_hw_break();
+ asm volatile ("movl %0, %%db6\n"::"r" (0));
+
+ return (0);
+ } /* switch */
+ return -1; /* this means that we do not want to exit from the handler */
+}
+
+/* Register KGDB with the i386die_chain so that we hook into all of the right
+ * spots. */
+static int kgdb_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *args = (struct die_args *)ptr;
+ struct pt_regs *regs = args->regs;
+
+ /* See if KGDB is interested. */
+ if (cmd == DIE_PAGE_FAULT)
+ return NOTIFY_DONE;
+ else if (user_mode(regs)) {
+ if (cmd == DIE_OOPS)
+ CHK_DEBUGGER(args->trapnr, args->signr, args->err,
+ args->regs,);
+ return NOTIFY_DONE;
+ } else if (cmd == DIE_NMI_IPI) {
+ if (atomic_read(&debugger_active)) {
+ debugger_nmihook(smp_processor_id(), ptr);
+ return NOTIFY_STOP;
+ }
+ }
+
+ CHK_DEBUGGER(args->trapnr, args->signr, args->err, args->regs,);
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_notify,
+};
+
+int kgdb_arch_init(void) {
+ notifier_chain_register(&i386die_chain, &kgdb_notifier);
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0xcc},
+};
diff -puN arch/i386/kernel/nmi.c~i386-lite arch/i386/kernel/nmi.c
--- linux-2.6.10-rc1/arch/i386/kernel/nmi.c~i386-lite 2004-10-29 11:26:44.465447895 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/nmi.c 2004-10-29 11:26:44.483443669 -0700
@@ -26,6 +26,7 @@
#include <linux/nmi.h>
#include <linux/sysdev.h>
#include <linux/sysctl.h>
+#include <linux/debugger.h>
#include <asm/smp.h>
#include <asm/mtrr.h>
@@ -481,7 +482,15 @@ void nmi_watchdog_tick (struct pt_regs *
sum = irq_stat[cpu].apic_timer_irqs;
- if (last_irq_sums[cpu] == sum) {
+ if (atomic_read(&debugger_active)) {
+ /*
+ * The machine is in debugger, hold this cpu if already
+ * not held.
+ */
+ debugger_nmihook(cpu, regs);
+ alert_counter[cpu] = 0;
+
+ } else if (last_irq_sums[cpu] == sum) {
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
diff -puN arch/i386/kernel/setup.c~i386-lite arch/i386/kernel/setup.c
--- linux-2.6.10-rc1/arch/i386/kernel/setup.c~i386-lite 2004-10-29 11:26:44.467447425 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/setup.c 2004-10-29 11:26:44.484443435 -0700
@@ -117,6 +117,7 @@ struct e820map e820;
unsigned char aux_device_present;
extern void early_cpu_init(void);
+extern void early_trap_init(void);
extern void dmi_scan_machine(void);
extern void generic_apic_probe(char *);
extern int root_mountflags;
@@ -1302,6 +1303,7 @@ void __init setup_arch(char **cmdline_p)
memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data));
pre_setup_arch_hook();
early_cpu_init();
+ early_trap_init();
/*
* FIXME: This isn't an official loader_type right
@@ -1358,6 +1360,7 @@ void __init setup_arch(char **cmdline_p)
data_resource.end = virt_to_phys(_edata)-1;
parse_cmdline_early(cmdline_p);
+ parse_early_param();
max_low_pfn = setup_memory();
diff -puN arch/i386/kernel/signal.c~i386-lite arch/i386/kernel/signal.c
--- linux-2.6.10-rc1/arch/i386/kernel/signal.c~i386-lite 2004-10-29 11:26:44.469446956 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/signal.c 2004-10-29 11:26:44.485443200 -0700
@@ -600,7 +600,8 @@ int fastcall do_signal(struct pt_regs *r
* have been cleared if the watchpoint triggered
* inside the kernel.
*/
- __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7]));
+ if (current->thread.debugreg[7])
+ __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7]));
/* Whee! Actually deliver the signal. */
handle_signal(signr, &info, &ka, oldset, regs);
diff -puN arch/i386/kernel/traps.c~i386-lite arch/i386/kernel/traps.c
--- linux-2.6.10-rc1/arch/i386/kernel/traps.c~i386-lite 2004-10-29 11:26:44.471446486 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/kernel/traps.c 2004-10-29 11:26:44.486442965 -0700
@@ -53,6 +53,7 @@
#include <linux/irq.h>
#include <linux/module.h>
+#include <linux/debugger.h>
#include "mach_traps.h"
@@ -332,7 +333,7 @@ void die(const char * str, struct pt_reg
#endif
if (nl)
printk("\n");
- notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
+ notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
show_registers(regs);
} else
printk(KERN_ERR "Recursive die() failure, output suppressed\n");
@@ -711,7 +712,7 @@ asmlinkage void do_debug(struct pt_regs
tsk->thread.debugreg[6] = condition;
/* Mask out spurious TF errors due to lazy TF clearing */
- if (condition & DR_STEP) {
+ if (condition & DR_STEP && !debugger_step) {
/*
* The TF error should be masked out only if the current
* process is not traced and if the TRAP flag has been set
@@ -733,12 +734,14 @@ asmlinkage void do_debug(struct pt_regs
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
-
- /* If this is a kernel mode trap, save the user PC on entry to
- * the kernel, that's what the debugger can make sense of.
- */
- info.si_addr = ((regs->xcs & 3) == 0) ? (void __user *)tsk->thread.eip
- : (void __user *)regs->eip;
+
+ /* If this is a kernel mode trap, we need to reset db7 to allow us
+ * to continue sanely */
+ if ((regs->xcs & 3) == 0)
+ goto clear_dr7;
+
+ info.si_addr = (void *)regs->eip;
+
force_sig_info(SIGTRAP, &info, tsk);
/* Disable additional traps. They'll be re-enabled when
@@ -748,6 +751,7 @@ clear_dr7:
__asm__("movl %0,%%db7"
: /* no output */
: "r" (0));
+ notify_die(DIE_DEBUG, "debug", regs, condition, error_code, SIGTRAP);
return;
debug_vm86:
@@ -1005,6 +1009,12 @@ static void __init set_task_gate(unsigne
_set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
}
+/* Some traps need to be set early. */
+void __init early_trap_init(void) {
+ set_intr_gate(1,&debug);
+ set_system_intr_gate(3, &int3); /* int3 can be called from all */
+ set_intr_gate(14,&page_fault);
+}
void __init trap_init(void)
{
@@ -1019,10 +1029,8 @@ void __init trap_init(void)
#endif
set_trap_gate(0,÷_error);
- set_intr_gate(1,&debug);
set_intr_gate(2,&nmi);
- set_system_intr_gate(3, &int3); /* int3-5 can be called from all */
- set_system_gate(4,&overflow);
+ set_system_gate(4,&overflow); /* int4/5 can be called from all */
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
@@ -1032,7 +1040,6 @@ void __init trap_init(void)
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
- set_intr_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
diff -puN arch/i386/mm/fault.c~i386-lite arch/i386/mm/fault.c
--- linux-2.6.10-rc1/arch/i386/mm/fault.c~i386-lite 2004-10-29 11:26:44.473446017 -0700
+++ linux-2.6.10-rc1-trini/arch/i386/mm/fault.c 2004-10-29 11:26:44.486442965 -0700
@@ -2,6 +2,11 @@
* linux/arch/i386/mm/fault.c
*
* Copyright (C) 1995 Linus Torvalds
+ *
+ * Change History
+ *
+ * Tigran Aivazian <tigran@sco.com> Remote debugging support.
+ *
*/
#include <linux/signal.h>
@@ -21,6 +26,7 @@
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <linux/highmem.h>
#include <linux/module.h>
+#include <linux/debugger.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -426,6 +432,10 @@ no_context:
if (is_prefetch(regs, address, error_code))
return;
+ if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
+ SIGSEGV) == NOTIFY_BAD)
+ return;
+
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
diff -puN /dev/null include/asm-i386/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-i386/kgdb.h 2004-10-29 11:26:44.487442730 -0700
@@ -0,0 +1,48 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/ptrace.h>
+
+/************************************************************************/
+/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
+/* at least NUMREGBYTES*2 are needed for register packets */
+/* Longer buffer is needed to list all threads */
+#define BUFMAX 1024
+
+/* Number of bytes of registers. */
+#define NUMREGBYTES 64
+/*
+ * Note that this register image is in a different order than
+ * the register image that Linux produces at interrupt time.
+ *
+ * Linux's register image is defined by struct pt_regs in ptrace.h.
+ * Just why GDB uses a different order is a historical mystery.
+ */
+enum regnames { _EAX, /* 0 */
+ _ECX, /* 1 */
+ _EDX, /* 2 */
+ _EBX, /* 3 */
+ _ESP, /* 4 */
+ _EBP, /* 5 */
+ _ESI, /* 6 */
+ _EDI, /* 7 */
+ _PC, /* 8 also known as eip */
+ _PS, /* 9 also known as eflags */
+ _CS, /* 10 */
+ _SS, /* 11 */
+ _DS, /* 12 */
+ _ES, /* 13 */
+ _FS, /* 14 */
+ _GS /* 15 */
+};
+
+#define BREAKPOINT() asm(" int $3");
+#define BREAK_INSTR_SIZE 1
+
+#define CHECK_EXCEPTION_STACK() 1
+
+#endif /* _ASM_KGDB_H_ */
diff -puN include/asm-i386/system.h~i386-lite include/asm-i386/system.h
--- linux-2.6.10-rc1/include/asm-i386/system.h~i386-lite 2004-10-29 11:26:44.476445313 -0700
+++ linux-2.6.10-rc1-trini/include/asm-i386/system.h 2004-10-29 11:26:44.487442730 -0700
@@ -12,9 +12,13 @@
struct task_struct; /* one of the stranger aspects of C forward declarations.. */
extern struct task_struct * FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next));
+/* sleeping_thread_to_gdb_regs depends on this code. Correct it if you change
+ * any of the following */
#define switch_to(prev,next,last) do { \
unsigned long esi,edi; \
- asm volatile("pushfl\n\t" \
+ asm volatile(".globl __switch_to_begin\n" \
+ "__switch_to_begin:" \
+ "pushfl\n\t" \
"pushl %%ebp\n\t" \
"movl %%esp,%0\n\t" /* save ESP */ \
"movl %5,%%esp\n\t" /* restore ESP */ \
@@ -23,7 +27,9 @@ extern struct task_struct * FASTCALL(__s
"jmp __switch_to\n" \
"1:\t" \
"popl %%ebp\n\t" \
- "popfl" \
+ "popfl\n" \
+ ".globl __switch_to_end\n" \
+ "__switch_to_end:\n" \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), \
"=a" (last),"=S" (esi),"=D" (edi) \
:"m" (next->thread.esp),"m" (next->thread.eip), \
diff -puN lib/Kconfig.debug~i386-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~i386-lite 2004-10-29 11:26:44.477445078 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:58.640565630 -0700
@@ -115,7 +115,7 @@ endif
config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL
+ depends on DEBUG_KERNEL && (X86)
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_
^ permalink raw reply [flat|nested] 12+ messages in thread* [patch 3/8] KGDB support for ppc32
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
2004-10-29 18:33 ` [patch 2/8] KGDB support for i386 Tom Rini
@ 2004-10-29 18:33 ` Tom Rini
2004-10-30 19:05 ` Tom Rini
2004-10-29 18:33 ` [patch 4/8] 8250 uart driver for KGDB Tom Rini
` (6 subsequent siblings)
8 siblings, 1 reply; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:33 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, trini, mporter
Cc: Matt Porter <mporter@kernel.crashing.org>
This adds KGDB support for ppc32 and was done by myself. Note that this
currently doesn't work on 40x || BOOKE, but that problem is more generic (the
current ppc stub doesn't work either) and Matt Porter is close to having a
tested solution now.
---
linux-2.6.10-rc1-trini/arch/ppc/Kconfig.debug | 36
linux-2.6.10-rc1-trini/arch/ppc/kernel/Makefile | 2
linux-2.6.10-rc1-trini/arch/ppc/kernel/irq.c | 5
linux-2.6.10-rc1-trini/arch/ppc/kernel/kgdb.c | 276 +++++
linux-2.6.10-rc1-trini/arch/ppc/kernel/setup.c | 16
linux-2.6.10-rc1-trini/arch/ppc/platforms/sandpoint.c | 11
linux-2.6.10-rc1-trini/arch/ppc/syslib/Makefile | 2
linux-2.6.10-rc1-trini/arch/ppc/syslib/ppc4xx_serial.c | 5
linux-2.6.10-rc1-trini/include/asm-ppc/kgdb.h | 20
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
linux-2.6.10-rc1/arch/ppc/kernel/ppc-stub.c | 879 -----------------
linux-2.6.10-rc1/arch/ppc/syslib/gen550_kgdb.c | 86 -
linux-2.6.10-rc1/arch/ppc/syslib/ppc4xx_kgdb.c | 124 --
13 files changed, 295 insertions(+), 1169 deletions(-)
diff -puN arch/ppc/Kconfig.debug~ppc-lite arch/ppc/Kconfig.debug
--- linux-2.6.10-rc1/arch/ppc/Kconfig.debug~ppc-lite 2004-10-29 11:26:44.705391556 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/Kconfig.debug 2004-10-29 11:26:44.729385922 -0700
@@ -2,42 +2,6 @@ menu "Kernel hacking"
source "lib/Kconfig.debug"
-config KGDB
- bool "Include kgdb kernel debugger"
- depends on DEBUG_KERNEL && (BROKEN || PPC_GEN550 || 4xx)
- select DEBUG_INFO
- help
- Include in-kernel hooks for kgdb, the Linux kernel source level
- debugger. See <http://kgdb.sourceforge.net/> for more information.
- Unless you are intending to debug the kernel, say N here.
-
-choice
- prompt "Serial Port"
- depends on KGDB
- default KGDB_TTYS1
-
-config KGDB_TTYS0
- bool "ttyS0"
-
-config KGDB_TTYS1
- bool "ttyS1"
-
-config KGDB_TTYS2
- bool "ttyS2"
-
-config KGDB_TTYS3
- bool "ttyS3"
-
-endchoice
-
-config KGDB_CONSOLE
- bool "Enable serial console thru kgdb port"
- depends on KGDB && 8xx || CPM2
- help
- If you enable this, all serial console messages will be sent
- over the gdb stub.
- If unsure, say N.
-
config XMON
bool "Include xmon kernel debugger"
depends on DEBUG_KERNEL
diff -puN arch/ppc/kernel/Makefile~ppc-lite arch/ppc/kernel/Makefile
--- linux-2.6.10-rc1/arch/ppc/kernel/Makefile~ppc-lite 2004-10-29 11:26:44.706391321 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/Makefile 2004-10-29 11:26:44.729385922 -0700
@@ -20,7 +20,7 @@ obj-$(CONFIG_POWER4) += cpu_setup_power
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-mapping.o
obj-$(CONFIG_PCI) += pci.o
-obj-$(CONFIG_KGDB) += ppc-stub.o
+obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_SMP) += smp.o smp-tbsync.o
obj-$(CONFIG_TAU) += temp.o
obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o
diff -puN arch/ppc/kernel/irq.c~ppc-lite arch/ppc/kernel/irq.c
--- linux-2.6.10-rc1/arch/ppc/kernel/irq.c~ppc-lite 2004-10-29 11:26:44.708390852 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/irq.c 2004-10-29 11:26:44.730385688 -0700
@@ -48,6 +48,7 @@
#include <linux/cpumask.h>
#include <linux/profile.h>
#include <linux/bitops.h>
+#include <linux/kgdb.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -155,7 +156,9 @@ void do_IRQ(struct pt_regs *regs)
if (irq != -2 && first)
/* That's not SMP safe ... but who cares ? */
ppc_spurious_interrupts++;
- irq_exit();
+ irq_exit();
+
+ kgdb_process_breakpoint();
}
void __init init_IRQ(void)
diff -puN /dev/null arch/ppc/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/kgdb.c 2004-10-29 11:26:44.730385688 -0700
@@ -0,0 +1,276 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * 1998 (c) Michael AK Tesch (tesch@cs.wisc.edu)
+ * Copyright (C) 2003 Timesys Corporation.
+ * KGDB for the PowerPC processor
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <linux/kgdb.h>
+#include <asm/current.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+
+/* Convert the hardware trap type code to a unix signal number. */
+/*
+ * This table contains the mapping between PowerPC hardware trap types, and
+ * signals, which are primarily what GDB understands.
+ */
+static struct hard_trap_info
+{
+ unsigned int tt; /* Trap type code for powerpc */
+ unsigned char signo; /* Signal that we map this trap into */
+} hard_trap_info[] = {
+#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
+ { 0x0100, 0x02 /* SIGINT */ }, /* critical input interrupt */
+ { 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */
+ { 0x0300, 0x0b /* SIGSEGV */ }, /* data storage */
+ { 0x0400, 0x0a /* SIGBUS */ }, /* instruction storage */
+ { 0x0500, 0x02 /* SIGINT */ }, /* interrupt */
+ { 0x0600, 0x0a /* SIGBUS */ }, /* alignment */
+ { 0x0700, 0x04 /* SIGILL */ }, /* program */
+ { 0x0800, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0900, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0a00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0b00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0c00, 0x14 /* SIGCHLD */ }, /* syscall */
+ { 0x0d00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0e00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0f00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x2000, 0x05 /* SIGTRAP */}, /* debug */
+#else
+ { 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */
+ { 0x0300, 0x0b /* SIGSEGV */ }, /* address error (store) */
+ { 0x0400, 0x0a /* SIGBUS */ }, /* instruction bus error */
+ { 0x0500, 0x02 /* SIGINT */ }, /* interrupt */
+ { 0x0600, 0x0a /* SIGBUS */ }, /* alingment */
+ { 0x0700, 0x05 /* SIGTRAP */ }, /* breakpoint trap */
+ { 0x0800, 0x08 /* SIGFPE */}, /* fpu unavail */
+ { 0x0900, 0x0e /* SIGALRM */ }, /* decrementer */
+ { 0x0a00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0b00, 0x04 /* SIGILL */ }, /* reserved */
+ { 0x0c00, 0x14 /* SIGCHLD */ }, /* syscall */
+ { 0x0d00, 0x05 /* SIGTRAP */ }, /* single-step/watch */
+ { 0x0e00, 0x08 /* SIGFPE */ }, /* fp assist */
+#endif
+ { 0x0000, 0x000 } /* Must be last */
+};
+
+extern atomic_t cpu_doing_single_step;
+
+static int computeSignal(unsigned 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 */
+}
+
+/*
+ * Routines
+ */
+static void kgdb_debugger (struct pt_regs *regs)
+{
+ (*linux_debug_hook) (0, computeSignal(regs->trap), 0, regs);
+}
+
+static int kgdb_breakpoint (struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, SIGTRAP, 0, regs);
+
+ if (atomic_read (&kgdb_setting_breakpoint))
+ regs->nip += 4;
+
+ return 1;
+}
+
+static int kgdb_singlestep (struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, SIGTRAP, 0, regs);
+ return 1;
+}
+
+int kgdb_iabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, computeSignal(regs->trap), 0, regs);
+ return 1;
+}
+
+int kgdb_dabr_match(struct pt_regs *regs)
+{
+ if (user_mode(regs))
+ return 0;
+
+ (*linux_debug_hook) (0, computeSignal(regs->trap), 0, regs);
+ return 1;
+}
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ memset(gdb_regs, 0, MAXREG*4);
+
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = regs->gpr[reg];
+
+ for (reg = 0; reg < 64; reg++)
+ *(ptr++) = 0;
+
+ *(ptr++) = regs->nip;
+ *(ptr++) = regs->msr;
+ *(ptr++) = regs->ccr;
+ *(ptr++) = regs->link;
+ *(ptr++) = regs->ctr;
+ *(ptr++) = regs->xer;
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ struct pt_regs *regs = (struct pt_regs *) (p->thread.ksp +
+ STACK_FRAME_OVERHEAD);
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ memset(gdb_regs, 0, MAXREG*4);
+
+ /* Regs GPR0-2 */
+ for (reg = 0; reg < 3; reg++)
+ *(ptr++) = regs->gpr[reg];
+
+ /* Regs GPR3-13 are not saved */
+ for (reg = 3; reg < 14; reg++)
+ *(ptr++) = 0;
+
+ /* Regs GPR14-31 */
+ for (reg = 14; reg < 32; reg++)
+ *(ptr++) = regs->gpr[reg];
+
+ for (reg = 0; reg < 64; reg++)
+ *(ptr++) = 0;
+
+ *(ptr++) = regs->nip;
+ *(ptr++) = regs->msr;
+ *(ptr++) = regs->ccr;
+ *(ptr++) = regs->link;
+ *(ptr++) = regs->ctr;
+ *(ptr++) = regs->xer;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ regs->gpr[reg] = *(ptr++);
+
+ for (reg = 0; reg < 64; reg++)
+ ptr++;
+
+ regs->nip = *(ptr++);
+ regs->msr = *(ptr++);
+ regs->ccr = *(ptr++);
+ regs->link = *(ptr++);
+ regs->ctr = *(ptr++);
+ regs->xer = *(ptr++);
+}
+
+/*
+ * This function does PoerPC specific procesing for interfacing to gdb.
+ */
+int kgdb_arch_handle_exception (int vector, int signo, int err_code,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ struct pt_regs *linux_regs)
+{
+ char *ptr;
+ unsigned long addr;
+
+ switch (remcom_in_buffer[0])
+ {
+ /*
+ * sAA..AA Step one instruction from AA..AA
+ * This will return an error to gdb ..
+ */
+ case 's':
+ case 'c':
+ if (kgdb_contthread && kgdb_contthread != current)
+ {
+ strcpy(remcom_out_buffer, "E00");
+ break;
+ }
+
+ /* handle the optional parameter */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long (&ptr, &addr))
+ linux_regs->nip = addr;
+
+ atomic_set(&cpu_doing_single_step,-1);
+ /* set the trace bit if we're stepping */
+ if (remcom_in_buffer[0] == 's')
+ {
+#if defined (CONFIG_40x) || defined(CONFIG_BOOKE)
+ linux_regs->msr |= MSR_DE;
+ current->thread.dbcr0 |= (DBCR0_IDM | DBCR0_IC);
+#else
+ linux_regs->msr |= MSR_SE;
+#endif
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,smp_processor_id());
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Global data
+ */
+struct kgdb_arch arch_kgdb_ops =
+{
+ .gdb_bpt_instr = { 0x7d, 0x82, 0x10, 0x08 },
+};
+
+int kgdb_arch_init (void)
+{
+ debugger = kgdb_debugger;
+ debugger_bpt = kgdb_breakpoint;
+ debugger_sstep = kgdb_singlestep;
+ debugger_iabr_match = kgdb_iabr_match;
+ debugger_dabr_match = kgdb_dabr_match;
+
+ return 0;
+}
+arch_initcall(kgdb_arch_init);
diff -L arch/ppc/kernel/ppc-stub.c -puN arch/ppc/kernel/ppc-stub.c~ppc-lite /dev/null
--- linux-2.6.10-rc1/arch/ppc/kernel/ppc-stub.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,879 +0,0 @@
-/*
- * ppc-stub.c: KGDB support for the Linux kernel.
- *
- * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC
- * some stuff borrowed from Paul Mackerras' xmon
- * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu)
- *
- * 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
- * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz
- *
- * 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/config.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
-#include <linux/sysrq.h>
-
-#include <asm/cacheflush.h>
-#include <asm/system.h>
-#include <asm/signal.h>
-#include <asm/kgdb.h>
-#include <asm/pgtable.h>
-#include <asm/ptrace.h>
-
-void breakinst(void);
-
-/*
- * BUFMAX defines the maximum number of characters in inbound/outbound buffers
- * at least NUMREGBYTES*2 are needed for register packets
- */
-#define BUFMAX 2048
-static char remcomInBuffer[BUFMAX];
-static char remcomOutBuffer[BUFMAX];
-
-static int initialized;
-static int kgdb_active;
-static int kgdb_started;
-static u_int fault_jmp_buf[100];
-static int kdebug;
-
-
-static const char hexchars[]="0123456789abcdef";
-
-/* Place where we save old trap entries for restoration - sparc*/
-/* struct tt_entry kgdb_savettable[256]; */
-/* typedef void (*trapfunc_t)(void); */
-
-static void kgdb_fault_handler(struct pt_regs *regs);
-static int handle_exception (struct pt_regs *regs);
-
-#if 0
-/* Install an exception handler for kgdb */
-static void exceptionHandler(int tnum, unsigned int *tfunc)
-{
- /* We are dorking with a live trap table, all irqs off */
-}
-#endif
-
-int
-kgdb_setjmp(long *buf)
-{
- asm ("mflr 0; stw 0,0(%0);"
- "stw 1,4(%0); stw 2,8(%0);"
- "mfcr 0; stw 0,12(%0);"
- "stmw 13,16(%0)"
- : : "r" (buf));
- /* XXX should save fp regs as well */
- return 0;
-}
-void
-kgdb_longjmp(long *buf, int val)
-{
- if (val == 0)
- val = 1;
- asm ("lmw 13,16(%0);"
- "lwz 0,12(%0); mtcrf 0x38,0;"
- "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);"
- "mtlr 0; mr 3,%1"
- : : "r" (buf), "r" (val));
-}
-/* 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;
-}
-
-/* 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(const char *mem, char *buf, int count)
-{
- unsigned char ch;
- unsigned short tmp_s;
- unsigned long tmp_l;
-
- if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
- debugger_fault_handler = kgdb_fault_handler;
-
- /* Accessing 16 bit and 32 bit objects in a single
- ** load instruction is required to avoid bad side
- ** effects for some IO registers.
- */
-
- if ((count == 2) && (((long)mem & 1) == 0)) {
- tmp_s = *(unsigned short *)mem;
- mem += 2;
- *buf++ = hexchars[(tmp_s >> 12) & 0xf];
- *buf++ = hexchars[(tmp_s >> 8) & 0xf];
- *buf++ = hexchars[(tmp_s >> 4) & 0xf];
- *buf++ = hexchars[tmp_s & 0xf];
-
- } else if ((count == 4) && (((long)mem & 3) == 0)) {
- tmp_l = *(unsigned int *)mem;
- mem += 4;
- *buf++ = hexchars[(tmp_l >> 28) & 0xf];
- *buf++ = hexchars[(tmp_l >> 24) & 0xf];
- *buf++ = hexchars[(tmp_l >> 20) & 0xf];
- *buf++ = hexchars[(tmp_l >> 16) & 0xf];
- *buf++ = hexchars[(tmp_l >> 12) & 0xf];
- *buf++ = hexchars[(tmp_l >> 8) & 0xf];
- *buf++ = hexchars[(tmp_l >> 4) & 0xf];
- *buf++ = hexchars[tmp_l & 0xf];
-
- } else {
- while (count-- > 0) {
- ch = *mem++;
- *buf++ = hexchars[ch >> 4];
- *buf++ = hexchars[ch & 0xf];
- }
- }
-
- } else {
- /* error condition */
- }
- debugger_fault_handler = NULL;
- *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)
-{
- unsigned char ch;
- int i;
- char *orig_mem;
- unsigned short tmp_s;
- unsigned long tmp_l;
-
- orig_mem = mem;
-
- if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
- debugger_fault_handler = kgdb_fault_handler;
-
- /* Accessing 16 bit and 32 bit objects in a single
- ** store instruction is required to avoid bad side
- ** effects for some IO registers.
- */
-
- if ((count == 2) && (((long)mem & 1) == 0)) {
- tmp_s = hex(*buf++) << 12;
- tmp_s |= hex(*buf++) << 8;
- tmp_s |= hex(*buf++) << 4;
- tmp_s |= hex(*buf++);
-
- *(unsigned short *)mem = tmp_s;
- mem += 2;
-
- } else if ((count == 4) && (((long)mem & 3) == 0)) {
- tmp_l = hex(*buf++) << 28;
- tmp_l |= hex(*buf++) << 24;
- tmp_l |= hex(*buf++) << 20;
- tmp_l |= hex(*buf++) << 16;
- tmp_l |= hex(*buf++) << 12;
- tmp_l |= hex(*buf++) << 8;
- tmp_l |= hex(*buf++) << 4;
- tmp_l |= hex(*buf++);
-
- *(unsigned long *)mem = tmp_l;
- mem += 4;
-
- } else {
- for (i=0; i<count; i++) {
- ch = hex(*buf++) << 4;
- ch |= hex(*buf++);
- *mem++ = ch;
- }
- }
-
-
- /*
- ** Flush the data cache, invalidate the instruction cache.
- */
- flush_icache_range((int)orig_mem, (int)orig_mem + count - 1);
-
- } else {
- /* error condition */
- }
- debugger_fault_handler = NULL;
- return mem;
-}
-
-/*
- * 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;
-
- if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
- debugger_fault_handler = kgdb_fault_handler;
- while (**ptr) {
- hexValue = hex(**ptr);
- if (hexValue < 0)
- break;
-
- *intValue = (*intValue << 4) | hexValue;
- numChars ++;
-
- (*ptr)++;
- }
- } else {
- /* error condition */
- }
- debugger_fault_handler = NULL;
-
- return (numChars);
-}
-
-/* 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 void kgdb_flush_cache_all(void)
-{
- flush_instruction_cache();
-}
-
-/* Set up exception handlers for tracing and breakpoints
- * [could be called kgdb_init()]
- */
-void set_debug_traps(void)
-{
-#if 0
- unsigned char c;
-
- save_and_cli(flags);
-
- /* 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
- */
-
- while((c = getDebugChar()) != '$');
- while((c = getDebugChar()) != '#');
- c = getDebugChar(); /* eat first csum byte */
- c = getDebugChar(); /* eat second csum byte */
- putDebugChar('+'); /* ack it */
-#endif
- debugger = kgdb;
- debugger_bpt = kgdb_bpt;
- debugger_sstep = kgdb_sstep;
- debugger_iabr_match = kgdb_iabr_match;
- debugger_dabr_match = kgdb_dabr_match;
-
- initialized = 1;
-}
-
-static void kgdb_fault_handler(struct pt_regs *regs)
-{
- kgdb_longjmp((long*)fault_jmp_buf, 1);
-}
-
-int kgdb_bpt(struct pt_regs *regs)
-{
- return handle_exception(regs);
-}
-
-int kgdb_sstep(struct pt_regs *regs)
-{
- return handle_exception(regs);
-}
-
-void kgdb(struct pt_regs *regs)
-{
- handle_exception(regs);
-}
-
-int kgdb_iabr_match(struct pt_regs *regs)
-{
- printk(KERN_ERR "kgdb doesn't support iabr, what?!?\n");
- return handle_exception(regs);
-}
-
-int kgdb_dabr_match(struct pt_regs *regs)
-{
- printk(KERN_ERR "kgdb doesn't support dabr, what?!?\n");
- return handle_exception(regs);
-}
-
-/* Convert the hardware trap type code to a unix signal number. */
-/*
- * This table contains the mapping between PowerPC hardware trap types, and
- * signals, which are primarily what GDB understands.
- */
-static struct hard_trap_info
-{
- unsigned int tt; /* Trap type code for powerpc */
- unsigned char signo; /* Signal that we map this trap into */
-} hard_trap_info[] = {
-#if defined(CONFIG_40x)
- { 0x100, SIGINT }, /* critical input interrupt */
- { 0x200, SIGSEGV }, /* machine check */
- { 0x300, SIGSEGV }, /* data storage */
- { 0x400, SIGBUS }, /* instruction storage */
- { 0x500, SIGINT }, /* interrupt */
- { 0x600, SIGBUS }, /* alignment */
- { 0x700, SIGILL }, /* program */
- { 0x800, SIGILL }, /* reserved */
- { 0x900, SIGILL }, /* reserved */
- { 0xa00, SIGILL }, /* reserved */
- { 0xb00, SIGILL }, /* reserved */
- { 0xc00, SIGCHLD }, /* syscall */
- { 0xd00, SIGILL }, /* reserved */
- { 0xe00, SIGILL }, /* reserved */
- { 0xf00, SIGILL }, /* reserved */
- /*
- ** 0x1000 PIT
- ** 0x1010 FIT
- ** 0x1020 watchdog
- ** 0x1100 data TLB miss
- ** 0x1200 instruction TLB miss
- */
- { 0x2000, SIGTRAP}, /* debug */
-#else
- { 0x200, SIGSEGV }, /* machine check */
- { 0x300, SIGSEGV }, /* address error (store) */
- { 0x400, SIGBUS }, /* instruction bus error */
- { 0x500, SIGINT }, /* interrupt */
- { 0x600, SIGBUS }, /* alingment */
- { 0x700, SIGTRAP }, /* breakpoint trap */
- { 0x800, SIGFPE }, /* fpu unavail */
- { 0x900, SIGALRM }, /* decrementer */
- { 0xa00, SIGILL }, /* reserved */
- { 0xb00, SIGILL }, /* reserved */
- { 0xc00, SIGCHLD }, /* syscall */
- { 0xd00, SIGTRAP }, /* single-step/watch */
- { 0xe00, SIGFPE }, /* fp assist */
-#endif
- { 0, 0} /* Must be last */
-
-};
-
-static int computeSignal(unsigned 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 */
-}
-
-#define PC_REGNUM 64
-#define SP_REGNUM 1
-
-/*
- * This function does all command processing for interfacing to gdb.
- */
-static int
-handle_exception (struct pt_regs *regs)
-{
- int sigval;
- int addr;
- int length;
- char *ptr;
- unsigned int msr;
-
- /* We don't handle user-mode breakpoints. */
- if (user_mode(regs))
- return 0;
-
- if (debugger_fault_handler) {
- debugger_fault_handler(regs);
- panic("kgdb longjump failed!\n");
- }
- if (kgdb_active) {
- printk(KERN_ERR "interrupt while in kgdb, returning\n");
- return 0;
- }
-
- kgdb_active = 1;
- kgdb_started = 1;
-
-#ifdef KGDB_DEBUG
- printk("kgdb: entering handle_exception; trap [0x%x]\n",
- (unsigned int)regs->trap);
-#endif
-
- kgdb_interruptible(0);
- lock_kernel();
- msr = mfmsr();
- mtmsr(msr & ~MSR_EE); /* disable interrupts */
-
- if (regs->nip == (unsigned long)breakinst) {
- /* Skip over breakpoint trap insn */
- regs->nip += 4;
- }
-
- /* reply to host that an exception has occurred */
- sigval = computeSignal(regs->trap);
- ptr = remcomOutBuffer;
-
-#if defined(CONFIG_40x)
- *ptr++ = 'S';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
-#else
- *ptr++ = 'T';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
- *ptr++ = hexchars[PC_REGNUM >> 4];
- *ptr++ = hexchars[PC_REGNUM & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®s->nip, ptr, 4);
- *ptr++ = ';';
- *ptr++ = hexchars[SP_REGNUM >> 4];
- *ptr++ = hexchars[SP_REGNUM & 0xf];
- *ptr++ = ':';
- ptr = mem2hex(((char *)regs) + SP_REGNUM*4, ptr, 4);
- *ptr++ = ';';
-#endif
-
- *ptr++ = 0;
-
- putpacket(remcomOutBuffer);
- if (kdebug)
- printk("remcomOutBuffer: %s\n", remcomOutBuffer);
-
- /* XXX We may want to add some features dealing with poking the
- * XXX page tables, ... (look at sparc-stub.c for more info)
- * XXX also required hacking to the gdb sources directly...
- */
-
- while (1) {
- remcomOutBuffer[0] = 0;
-
- getpacket(remcomInBuffer);
- switch (remcomInBuffer[0]) {
- case '?': /* report most recent signal */
- remcomOutBuffer[0] = 'S';
- remcomOutBuffer[1] = hexchars[sigval >> 4];
- remcomOutBuffer[2] = hexchars[sigval & 0xf];
- remcomOutBuffer[3] = 0;
- break;
-#if 0
- case 'q': /* this screws up gdb for some reason...*/
- {
- extern long _start, sdata, __bss_start;
-
- ptr = &remcomInBuffer[1];
- if (strncmp(ptr, "Offsets", 7) != 0)
- break;
-
- ptr = remcomOutBuffer;
- sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x",
- &_start, &sdata, &__bss_start);
- break;
- }
-#endif
- case 'd':
- /* toggle debug flag */
- kdebug ^= 1;
- break;
-
- case 'g': /* return the value of the CPU registers.
- * some of them are non-PowerPC names :(
- * they are stored in gdb like:
- * struct {
- * u32 gpr[32];
- * f64 fpr[32];
- * u32 pc, ps, cnd, lr; (ps=msr)
- * u32 cnt, xer, mq;
- * }
- */
- {
- int i;
- ptr = remcomOutBuffer;
- /* General Purpose Regs */
- ptr = mem2hex((char *)regs, ptr, 32 * 4);
- /* Floating Point Regs - FIXME */
- /*ptr = mem2hex((char *), ptr, 32 * 8);*/
- for(i=0; i<(32*8*2); i++) { /* 2chars/byte */
- ptr[i] = '0';
- }
- ptr += 32*8*2;
- /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
- ptr = mem2hex((char *)®s->nip, ptr, 4);
- ptr = mem2hex((char *)®s->msr, ptr, 4);
- ptr = mem2hex((char *)®s->ccr, ptr, 4);
- ptr = mem2hex((char *)®s->link, ptr, 4);
- ptr = mem2hex((char *)®s->ctr, ptr, 4);
- ptr = mem2hex((char *)®s->xer, ptr, 4);
- }
- break;
-
- case 'G': /* set the value of the CPU registers */
- {
- ptr = &remcomInBuffer[1];
-
- /*
- * If the stack pointer has moved, you should pray.
- * (cause only god can help you).
- */
-
- /* General Purpose Regs */
- hex2mem(ptr, (char *)regs, 32 * 4);
-
- /* Floating Point Regs - FIXME?? */
- /*ptr = hex2mem(ptr, ??, 32 * 8);*/
- ptr += 32*8*2;
-
- /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
- ptr = hex2mem(ptr, (char *)®s->nip, 4);
- ptr = hex2mem(ptr, (char *)®s->msr, 4);
- ptr = hex2mem(ptr, (char *)®s->ccr, 4);
- ptr = hex2mem(ptr, (char *)®s->link, 4);
- ptr = hex2mem(ptr, (char *)®s->ctr, 4);
- ptr = hex2mem(ptr, (char *)®s->xer, 4);
-
- strcpy(remcomOutBuffer,"OK");
- }
- break;
- case 'H':
- /* don't do anything, yet, just acknowledge */
- hexToInt(&ptr, &addr);
- 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");
- flush_icache_range(addr, addr+length);
- } else
- strcpy(remcomOutBuffer, "E02");
- break;
-
-
- case 'k': /* kill the program, actually just continue */
- case 'c': /* cAA..AA Continue; address AA..AA optional */
- /* try to read optional parameter, pc unchanged if no parm */
-
- ptr = &remcomInBuffer[1];
- if (hexToInt(&ptr, &addr))
- regs->nip = 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.
- */
- kgdb_flush_cache_all();
-#if defined(CONFIG_40x)
- strcpy(remcomOutBuffer, "OK");
- putpacket(remcomOutBuffer);
-#endif
- mtmsr(msr);
-
- kgdb_interruptible(1);
- unlock_kernel();
- kgdb_active = 0;
- if (kdebug) {
- printk("remcomInBuffer: %s\n", remcomInBuffer);
- printk("remcomOutBuffer: %s\n", remcomOutBuffer);
- }
- return 1;
-
- case 's':
- kgdb_flush_cache_all();
-#if defined(CONFIG_40x)
- regs->msr |= MSR_DE;
- regs->dbcr0 |= (DBCR0_IDM | DBCR0_IC);
- mtmsr(msr);
-#else
- regs->msr |= MSR_SE;
-#endif
- unlock_kernel();
- kgdb_active = 0;
- if (kdebug) {
- printk("remcomInBuffer: %s\n", remcomInBuffer);
- printk("remcomOutBuffer: %s\n", remcomOutBuffer);
- }
- return 1;
-
- case 'r': /* Reset (if user process..exit ???)*/
- panic("kgdb reset.");
- break;
- } /* switch */
- if (remcomOutBuffer[0] && kdebug) {
- printk("remcomInBuffer: %s\n", remcomInBuffer);
- printk("remcomOutBuffer: %s\n", remcomOutBuffer);
- }
- /* 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) {
- printk("breakpoint() called b4 kgdb init\n");
- return;
- }
-
- asm(" .globl breakinst \n\
- breakinst: .long 0x7d821008");
-}
-
-#ifdef CONFIG_KGDB_CONSOLE
-/* Output string in GDB O-packet format if GDB has connected. If nothing
- output, returns 0 (caller must then handle output). */
-int
-kgdb_output_string (const char* s, unsigned int count)
-{
- char buffer[512];
-
- if (!kgdb_started)
- return 0;
-
- count = (count <= (sizeof(buffer) / 2 - 2))
- ? count : (sizeof(buffer) / 2 - 2);
-
- buffer[0] = 'O';
- mem2hex (s, &buffer[1], count);
- putpacket(buffer);
-
- return 1;
-}
-#endif
-
-static void sysrq_handle_gdb(int key, struct pt_regs *pt_regs,
- struct tty_struct *tty)
-{
- printk("Entering GDB stub\n");
- breakpoint();
-}
-static struct sysrq_key_op sysrq_gdb_op = {
- .handler = sysrq_handle_gdb,
- .help_msg = "Gdb",
- .action_msg = "GDB",
-};
-
-static int gdb_register_sysrq(void)
-{
- printk("Registering GDB sysrq handler\n");
- register_sysrq_key('g', &sysrq_gdb_op);
- return 0;
-}
-module_init(gdb_register_sysrq);
diff -puN arch/ppc/kernel/setup.c~ppc-lite arch/ppc/kernel/setup.c
--- linux-2.6.10-rc1/arch/ppc/kernel/setup.c~ppc-lite 2004-10-29 11:26:44.713389678 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/kernel/setup.c 2004-10-29 11:26:44.733384983 -0700
@@ -40,10 +40,6 @@
#include <asm/xmon.h>
#include <asm/ocp.h>
-#if defined CONFIG_KGDB
-#include <asm/kgdb.h>
-#endif
-
extern void platform_init(unsigned long r3, unsigned long r4,
unsigned long r5, unsigned long r6, unsigned long r7);
extern void bootx_init(unsigned long r4, unsigned long phys);
@@ -699,18 +695,6 @@ void __init setup_arch(char **cmdline_p)
#endif /* CONFIG_XMON */
if ( ppc_md.progress ) ppc_md.progress("setup_arch: enter", 0x3eab);
-#if defined(CONFIG_KGDB)
- if (ppc_md.kgdb_map_scc)
- ppc_md.kgdb_map_scc();
- set_debug_traps();
- if (strstr(cmd_line, "gdb")) {
- if (ppc_md.progress)
- ppc_md.progress("setup_arch: kgdb breakpoint", 0x4000);
- printk("kgdb breakpoint activated\n");
- breakpoint();
- }
-#endif
-
/*
* Set cache line size based on type of cpu as a default.
* Systems with OF can look in the properties on the cpu node(s)
diff -puN arch/ppc/platforms/sandpoint.c~ppc-lite arch/ppc/platforms/sandpoint.c
--- linux-2.6.10-rc1/arch/ppc/platforms/sandpoint.c~ppc-lite 2004-10-29 11:26:44.715389209 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/platforms/sandpoint.c 2004-10-29 11:26:44.733384983 -0700
@@ -252,8 +252,7 @@ sandpoint_find_bridges(void)
return;
}
-#if defined(CONFIG_SERIAL_8250) && \
- (defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG))
+#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG)
static void __init
sandpoint_early_serial_map(void)
{
@@ -689,16 +688,10 @@ platform_init(unsigned long r3, unsigned
ppc_md.nvram_read_val = todc_mc146818_read_val;
ppc_md.nvram_write_val = todc_mc146818_write_val;
-#if defined(CONFIG_SERIAL_8250) && \
- (defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG))
+#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG)
sandpoint_early_serial_map();
-#ifdef CONFIG_KGDB
- ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
-#endif
-#ifdef CONFIG_SERIAL_TEXT_DEBUG
ppc_md.progress = gen550_progress;
#endif
-#endif
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
ppc_ide_md.default_irq = sandpoint_ide_default_irq;
diff -puN arch/ppc/syslib/Makefile~ppc-lite arch/ppc/syslib/Makefile
--- linux-2.6.10-rc1/arch/ppc/syslib/Makefile~ppc-lite 2004-10-29 11:26:44.716388974 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/syslib/Makefile 2004-10-29 11:26:44.734384749 -0700
@@ -25,7 +25,6 @@ obj-$(CONFIG_GEN_RTC) += todc_time.o
obj-$(CONFIG_PPC4xx_DMA) += ppc4xx_dma.o
obj-$(CONFIG_PPC4xx_EDMA) += ppc4xx_sgdma.o
ifeq ($(CONFIG_40x),y)
-obj-$(CONFIG_KGDB) += ppc4xx_kgdb.o
obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o ppc405_pci.o
endif
endif
@@ -72,7 +71,6 @@ obj-$(CONFIG_PCI_8260) += m8260_pci.o i
obj-$(CONFIG_8260_PCI9) += m8260_pci_erratum9.o
obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o
ifeq ($(CONFIG_PPC_GEN550),y)
-obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o
obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o
endif
obj-$(CONFIG_BOOTX_TEXT) += btext.o
diff -L arch/ppc/syslib/gen550_kgdb.c -puN arch/ppc/syslib/gen550_kgdb.c~ppc-lite /dev/null
--- linux-2.6.10-rc1/arch/ppc/syslib/gen550_kgdb.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,86 +0,0 @@
-/*
- * arch/ppc/syslib/gen550_kgdb.c
- *
- * Generic 16550 kgdb support intended to be useful on a variety
- * of platforms. To enable this support, it is necessary to set
- * the CONFIG_GEN550 option. Any virtual mapping of the serial
- * port(s) to be used can be accomplished by setting
- * ppc_md.early_serial_map to a platform-specific mapping function.
- *
- * Adapted from ppc4xx_kgdb.c.
- *
- * Author: Matt Porter <mporter@kernel.crashing.org>
- *
- * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#include <asm/machdep.h>
-
-extern unsigned long serial_init(int, void *);
-extern unsigned long serial_getc(unsigned long);
-extern unsigned long serial_putc(unsigned long, unsigned char);
-
-#if defined(CONFIG_KGDB_TTYS0)
-#define KGDB_PORT 0
-#elif defined(CONFIG_KGDB_TTYS1)
-#define KGDB_PORT 1
-#elif defined(CONFIG_KGDB_TTYS2)
-#define KGDB_PORT 2
-#elif defined(CONFIG_KGDB_TTYS3)
-#define KGDB_PORT 3
-#else
-#error "invalid kgdb_tty port"
-#endif
-
-static volatile unsigned int kgdb_debugport;
-
-void putDebugChar(unsigned char c)
-{
- if (kgdb_debugport == 0)
- kgdb_debugport = serial_init(KGDB_PORT, NULL);
-
- serial_putc(kgdb_debugport, c);
-}
-
-int getDebugChar(void)
-{
- if (kgdb_debugport == 0)
- kgdb_debugport = serial_init(KGDB_PORT, NULL);
-
- return(serial_getc(kgdb_debugport));
-}
-
-void kgdb_interruptible(int enable)
-{
- return;
-}
-
-void putDebugString(char* str)
-{
- while (*str != '\0') {
- putDebugChar(*str);
- str++;
- }
- putDebugChar('\r');
- return;
-}
-
-/*
- * Note: gen550_init() must be called already on the port we are going
- * to use.
- */
-void
-gen550_kgdb_map_scc(void)
-{
- printk(KERN_DEBUG "kgdb init\n");
- if (ppc_md.early_serial_map)
- ppc_md.early_serial_map();
- kgdb_debugport = serial_init(KGDB_PORT, NULL);
-}
diff -L arch/ppc/syslib/ppc4xx_kgdb.c -puN arch/ppc/syslib/ppc4xx_kgdb.c~ppc-lite /dev/null
--- linux-2.6.10-rc1/arch/ppc/syslib/ppc4xx_kgdb.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,124 +0,0 @@
-#include <linux/config.h>
-#include <linux/types.h>
-#include <asm/ibm4xx.h>
-#include <linux/kernel.h>
-
-
-
-#define LSR_DR 0x01 /* Data ready */
-#define LSR_OE 0x02 /* Overrun */
-#define LSR_PE 0x04 /* Parity error */
-#define LSR_FE 0x08 /* Framing error */
-#define LSR_BI 0x10 /* Break */
-#define LSR_THRE 0x20 /* Xmit holding register empty */
-#define LSR_TEMT 0x40 /* Xmitter empty */
-#define LSR_ERR 0x80 /* Error */
-
-#include <platforms/4xx/ibm_ocp.h>
-
-extern struct NS16550* COM_PORTS[];
-#ifndef NULL
-#define NULL 0x00
-#endif
-
-static volatile struct NS16550 *kgdb_debugport = NULL;
-
-volatile struct NS16550 *
-NS16550_init(int chan)
-{
- volatile struct NS16550 *com_port;
- int quot;
-#ifdef BASE_BAUD
- quot = BASE_BAUD / 9600;
-#else
- quot = 0x000c; /* 0xc = 9600 baud (on a pc) */
-#endif
-
- com_port = (struct NS16550 *) COM_PORTS[chan];
-
- com_port->lcr = 0x00;
- com_port->ier = 0xFF;
- com_port->ier = 0x00;
- com_port->lcr = com_port->lcr | 0x80; /* Access baud rate */
- com_port->dll = ( quot & 0x00ff ); /* 0xc = 9600 baud */
- com_port->dlm = ( quot & 0xff00 ) >> 8;
- com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */
- com_port->mcr = 0x00; /* RTS/DTR */
- com_port->fcr = 0x07; /* Clear & enable FIFOs */
-
- return( com_port );
-}
-
-
-void
-NS16550_putc(volatile struct NS16550 *com_port, unsigned char c)
-{
- while ((com_port->lsr & LSR_THRE) == 0)
- ;
- com_port->thr = c;
- return;
-}
-
-unsigned char
-NS16550_getc(volatile struct NS16550 *com_port)
-{
- while ((com_port->lsr & LSR_DR) == 0)
- ;
- return (com_port->rbr);
-}
-
-unsigned char
-NS16550_tstc(volatile struct NS16550 *com_port)
-{
- return ((com_port->lsr & LSR_DR) != 0);
-}
-
-
-#if defined(CONFIG_KGDB_TTYS0)
-#define KGDB_PORT 0
-#elif defined(CONFIG_KGDB_TTYS1)
-#define KGDB_PORT 1
-#elif defined(CONFIG_KGDB_TTYS2)
-#define KGDB_PORT 2
-#elif defined(CONFIG_KGDB_TTYS3)
-#define KGDB_PORT 3
-#else
-#error "invalid kgdb_tty port"
-#endif
-
-void putDebugChar( unsigned char c )
-{
- if ( kgdb_debugport == NULL )
- kgdb_debugport = NS16550_init(KGDB_PORT);
- NS16550_putc( kgdb_debugport, c );
-}
-
-int getDebugChar( void )
-{
- if (kgdb_debugport == NULL)
- kgdb_debugport = NS16550_init(KGDB_PORT);
-
- return(NS16550_getc(kgdb_debugport));
-}
-
-void kgdb_interruptible(int enable)
-{
- return;
-}
-
-void putDebugString(char* str)
-{
- while (*str != '\0') {
- putDebugChar(*str);
- str++;
- }
- putDebugChar('\r');
- return;
-}
-
-void
-kgdb_map_scc(void)
-{
- printk("kgdb init \n");
- kgdb_debugport = NS16550_init(KGDB_PORT);
-}
diff -puN arch/ppc/syslib/ppc4xx_serial.c~ppc-lite arch/ppc/syslib/ppc4xx_serial.c
--- linux-2.6.10-rc1/arch/ppc/syslib/ppc4xx_serial.c~ppc-lite 2004-10-29 11:26:44.722387566 -0700
+++ linux-2.6.10-rc1-trini/arch/ppc/syslib/ppc4xx_serial.c 2004-10-29 11:26:44.734384749 -0700
@@ -20,11 +20,6 @@
#if defined(CONFIG_IBM405GP) || defined(CONFIG_IBM405CR)
-#ifdef CONFIG_KGDB
-#include <asm/kgdb.h>
-#include <linux/init.h>
-#endif
-
#ifdef CONFIG_DEBUG_BRINGUP
#include <linux/console.h>
diff -puN include/asm-ppc/kgdb.h~ppc-lite include/asm-ppc/kgdb.h
--- linux-2.6.10-rc1/include/asm-ppc/kgdb.h~ppc-lite 2004-10-29 11:26:44.723387331 -0700
+++ linux-2.6.10-rc1-trini/include/asm-ppc/kgdb.h 2004-10-29 11:26:44.735384514 -0700
@@ -2,6 +2,8 @@
* kgdb.h: Defines and declarations for serial line source level
* remote debugging of the Linux kernel using gdb.
*
+ * PPC Mods (C) 2004 Tom Rini (trini@mvista.com)
+ * PPC Mods (C) 2003 John Whitney (john.whitney@timesys.com)
* PPC Mods (C) 1998 Michael Tesch (tesch@cs.wisc.edu)
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -12,6 +14,15 @@
#ifndef __ASSEMBLY__
+#define BREAK_INSTR_SIZE 4
+#define MAXREG (PT_FPSCR+1)
+#define NUMREGBYTES (MAXREG * sizeof(int))
+#define BUFMAX ((NUMREGBYTES * 2) + 512)
+#define OUTBUFMAX ((NUMREGBYTES * 2) + 512)
+#define BREAKPOINT() asm(".long 0x7d821008"); /* twge r2, r2 */
+
+#define CHECK_EXCEPTION_STACK() 1
+
/* Things specific to the gen550 backend. */
struct uart_port;
@@ -19,15 +30,6 @@ extern void gen550_progress(char *, unsi
extern void gen550_kgdb_map_scc(void);
extern void gen550_init(int, struct uart_port *);
-/* Things specific to the pmac backend. */
-extern void zs_kgdb_hook(int tty_num);
-
-/* To init the kgdb engine. (called by serial hook)*/
-extern void set_debug_traps(void);
-
-/* To enter the debugger explicitly. */
-extern void breakpoint(void);
-
/* For taking exceptions
* these are defined in traps.c
*/
diff -puN lib/Kconfig.debug~ppc-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~ppc-lite 2004-10-29 11:26:44.725386861 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:57.778768052 -0700
@@ -115,7 +115,7 @@ endif
config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86)
+ depends on DEBUG_KERNEL && (X86 || ((!SMP || BROKEN) && (PPC32)))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [patch 3/8] KGDB support for ppc32
2004-10-29 18:33 ` [patch 3/8] KGDB support for ppc32 Tom Rini
@ 2004-10-30 19:05 ` Tom Rini
0 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-30 19:05 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, mporter, takeharu1219
On Fri, Oct 29, 2004 at 11:33:29AM -0700, Tom Rini wrote:
>
> Cc: Matt Porter <mporter@kernel.crashing.org>
> This adds KGDB support for ppc32 and was done by myself. Note that this
> currently doesn't work on 40x || BOOKE, but that problem is more generic (the
> current ppc stub doesn't work either) and Matt Porter is close to having a
> tested solution now.
As Matt has posted his work which makes KGDB functional again, the
following small patch is needed on top of this to make 40x || BOOKE work
with this stub.
Signed-off-by: Tom Rini <trini@kernel.crashing.org>
linux-2.6.10-rc1/arch/ppc/kernel/kgdb.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
--- linux-2.6.10-rc1/arch/ppc/kernel/kgdb.c
+++ linux-2.6.10-rc1/arch/ppc/kernel/kgdb.c
@@ -54,7 +54,7 @@
{ 0x0d00, 0x04 /* SIGILL */ }, /* reserved */
{ 0x0e00, 0x04 /* SIGILL */ }, /* reserved */
{ 0x0f00, 0x04 /* SIGILL */ }, /* reserved */
- { 0x2000, 0x05 /* SIGTRAP */}, /* debug */
+ { 0x2002, 0x05 /* SIGTRAP */}, /* debug */
#else
{ 0x0200, 0x0b /* SIGSEGV */ }, /* machine check */
{ 0x0300, 0x0b /* SIGSEGV */ }, /* address error (store) */
@@ -240,8 +240,8 @@
if (remcom_in_buffer[0] == 's')
{
#if defined (CONFIG_40x) || defined(CONFIG_BOOKE)
+ mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC);
linux_regs->msr |= MSR_DE;
- current->thread.dbcr0 |= (DBCR0_IDM | DBCR0_IC);
#else
linux_regs->msr |= MSR_SE;
#endif
--
Tom Rini
http://gate.crashing.org/~trini/
^ permalink raw reply [flat|nested] 12+ messages in thread
* [patch 4/8] 8250 uart driver for KGDB
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
2004-10-29 18:33 ` [patch 2/8] KGDB support for i386 Tom Rini
2004-10-29 18:33 ` [patch 3/8] KGDB support for ppc32 Tom Rini
@ 2004-10-29 18:33 ` Tom Rini
2004-10-29 18:33 ` [patch 5/8] KGDB support for MIPS Tom Rini
` (5 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:33 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, trini, rmk
Cc: Russell King <rmk@arm.linux.org.uk>
This adds an 8250 uart driver for KGDB and adds a hook into 8250.c to
unregister a port based on the IRQ, serial8250_release_irq(). We use
serial8250_unregister_port() to do the real work here, however. Note
that we can't use unregister_port in kgdb_8250.c since we don't know
what the 'port' is, in 8250 terms, only the IRQ. The other change is
that we have SERIAL_8250_NR_UARTS always be defined if KGDB_8250.
---
linux-2.6.10-rc1-trini/drivers/serial/8250.c | 39 +
linux-2.6.10-rc1-trini/drivers/serial/Kconfig | 4
linux-2.6.10-rc1-trini/drivers/serial/Makefile | 1
linux-2.6.10-rc1-trini/drivers/serial/kgdb_8250.c | 477 ++++++++++++++++++++
linux-2.6.10-rc1-trini/drivers/serial/serial_core.c | 6
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 101 ++++
6 files changed, 625 insertions(+), 3 deletions(-)
diff -puN drivers/serial/8250.c~8250 drivers/serial/8250.c
--- linux-2.6.10-rc1/drivers/serial/8250.c~8250 2004-10-29 11:26:44.964330757 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/8250.c 2004-10-29 11:26:44.977327706 -0700
@@ -1348,12 +1348,17 @@ static void serial8250_break_ctl(struct
spin_unlock_irqrestore(&up->port.lock, flags);
}
+static int released_irq = -1;
+
static int serial8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
int retval;
+ if (up->port.irq == released_irq)
+ return -EBUSY;
+
up->capabilities = uart_config[up->port.type].flags;
up->mcr = 0;
@@ -1990,6 +1995,9 @@ serial8250_register_ports(struct uart_dr
for (i = 0; i < UART_NR; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
+ if (up->port.irq == released_irq)
+ continue;
+
up->port.line = i;
up->port.ops = &serial8250_pops;
up->port.dev = dev;
@@ -2327,6 +2335,9 @@ int serial8250_register_port(struct uart
struct uart_8250_port *uart;
int ret = -ENOSPC;
+ if (port->line == released_irq)
+ return -EBUSY;
+
down(&serial_sem);
uart = serial8250_find_match_or_unused(port);
@@ -2376,6 +2387,34 @@ void serial8250_unregister_port(int line
}
EXPORT_SYMBOL(serial8250_unregister_port);
+/**
+ * serial8250_release_irq - remove a 16x50 serial port at runtime based
+ * on IRQ
+ * @irq: IRQ number
+ *
+ * Remove one serial port. This may not be called from interrupt
+ * context. This is called when the caller know the IRQ of the
+ * port they want, but not where it is in our table. This allows
+ * for one port, based on IRQ, to be permanently released from us.
+ */
+
+int serial8250_release_irq(int irq)
+{
+ struct uart_8250_port *up;
+ int ttyS;
+
+ if (released_irq != -1)
+ return -EBUSY;
+
+ released_irq = irq;
+ for (ttyS = 0; ttyS < UART_NR; ttyS++){
+ up = &serial8250_ports[ttyS];
+ if (up->port.irq == irq && (irq_lists + irq)->head)
+ serial8250_unregister_port(up->port.line);
+ }
+ return 0;
+}
+
static int __init serial8250_init(void)
{
int ret, i;
diff -puN drivers/serial/Kconfig~8250 drivers/serial/Kconfig
--- linux-2.6.10-rc1/drivers/serial/Kconfig~8250 2004-10-29 11:26:44.966330288 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/Kconfig 2004-10-29 11:26:44.978327471 -0700
@@ -86,8 +86,8 @@ config SERIAL_8250_ACPI
namespace, say Y here. If unsure, say N.
config SERIAL_8250_NR_UARTS
- int "Maximum number of non-legacy 8250/16550 serial ports"
- depends on SERIAL_8250
+ int "Maximum number of non-legacy 8250/16550 serial ports" if SERIAL_8250
+ depends on SERIAL_8250 || KGDB_8250
default "4"
---help---
Set this to the number of non-legacy serial ports you want
diff -puN drivers/serial/Makefile~8250 drivers/serial/Makefile
--- linux-2.6.10-rc1/drivers/serial/Makefile~8250 2004-10-29 11:26:44.967330053 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/Makefile 2004-10-29 11:26:44.978327471 -0700
@@ -42,3 +42,4 @@ obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += s
obj-$(CONFIG_SERIAL_CPM) += cpm_uart/
obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
+obj-$(CONFIG_KGDB_8250) += kgdb_8250.o
diff -puN /dev/null drivers/serial/kgdb_8250.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/kgdb_8250.c 2004-10-29 11:26:44.979327236 -0700
@@ -0,0 +1,477 @@
+/*
+ * 8250 interface for kgdb.
+ *
+ * This is a merging of many different drivers, and all of the people have
+ * had an impact in some form or another:
+ *
+ * Amit Kale <amitkale@emsyssoft.com>, David Grothe <dave@gcom.com>,
+ * Scott Foehner <sfoehner@engr.sgi.com>, George Anzinger <george@mvista.com>,
+ * Robert Walsh <rjwalsh@durables.org>, wangdi <wangdi@clusterfs.com>,
+ * San Mehat, Tom Rini <trini@mvista.com>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/serialP.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/serial.h> /* For BASE_BAUD and SERIAL_PORT_DFNS */
+
+#define GDB_BUF_SIZE 512 /* power of 2, please */
+
+/* Speed of the UART. */
+#if defined(CONFIG_KGDB_9600BAUD)
+static int kgdb8250_baud = 9600;
+#elif defined(CONFIG_KGDB_19200BAUD)
+static int kgdb8250_baud = 19200;
+#elif defined(CONFIG_KGDB_38400BAUD)
+static int kgdb8250_baud = 38400;
+#elif defined(CONFIG_KGDB_57600BAUD)
+static int kgdb8250_baud = 57600;
+#else
+static int kgdb8250_baud = 115200; /* Start with this if not given */
+#endif
+
+/* Index of the UART, matches ttySX naming. */
+#if defined(CONFIG_KGDB_TTYS1)
+static int kgdb8250_ttyS = 1;
+#elif defined(CONFIG_KGDB_TTYS2)
+static int kgdb8250_ttyS = 2;
+#elif defined(CONFIG_KGDB_TTYS3)
+static int kgdb8250_ttyS = 3;
+#else
+static int kgdb8250_ttyS = 0; /* Start with this if not given */
+#endif
+
+static char kgdb8250_buf[GDB_BUF_SIZE];
+static atomic_t kgdb8250_buf_in_cnt;
+static int kgdb8250_buf_out_inx;
+
+/* Old-style serial definitions, if existant, and a counter. */
+static int old_rs_table_copied;
+static struct serial_state old_rs_table[] = {
+#ifdef SERIAL_PORT_DFNS
+ SERIAL_PORT_DFNS
+#endif
+};
+
+/* Our internal table of UARTS. */
+#define UART_NR (ARRAY_SIZE(old_rs_table) + CONFIG_SERIAL_8250_NR_UARTS)
+static struct uart_port kgdb8250_ports[UART_NR] = {
+#ifndef CONFIG_KGDB_SIMPLE_SERIAL
+ {
+#if defined(CONFIG_KGDB_IRQ) && defined(CONFIG_KGDB_PORT)
+ .irq = CONFIG_KGDB_IRQ,
+ .iobase = CONFIG_KGDB_PORT,
+ .iotype = UPIO_PORT,
+#endif
+#ifdef CONFIG_KGDB_IOMEMBASE
+ .membase = (unsigned char *)CONFIG_KGDB_IOMEMBASE,
+ .iotype = UPIO_MEM,
+#endif
+ },
+#endif
+};
+
+/* Macros to easily get what we want from kgdb8250_ports[kgdb8250_ttyS] */
+#define CURRENTPORT kgdb8250_ports[kgdb8250_ttyS]
+#define KGDB8250_IRQ CURRENTPORT.irq
+#define KGDB8250_REG_SHIFT CURRENTPORT.regshift
+
+/* Base of the UART. */
+static unsigned long kgdb8250_addr;
+/* Pointers for I/O. */
+static void (*serial_outb) (unsigned char val, unsigned long addr);
+static unsigned long (*serial_inb) (unsigned long addr);
+
+/* Forward declarations. */
+static int kgdb8250_init(void);
+extern int serial8250_release_irq(int irq);
+
+extern atomic_t debugger_active;
+
+static unsigned long
+direct_inb(unsigned long addr)
+{
+ return readb((void *)addr);
+}
+
+static void
+direct_outb(unsigned char val, unsigned long addr)
+{
+ writeb(val, (void *)addr);
+}
+
+static unsigned long
+io_inb(unsigned long port)
+{
+ return inb(port);
+}
+
+static void
+io_outb(unsigned char val, unsigned long port)
+{
+ outb(val, port);
+}
+
+/*
+ * Wait until the interface can accept a char, then write it.
+ */
+static void
+kgdb_put_debug_char(int chr)
+{
+ while (!(serial_inb(kgdb8250_addr + (UART_LSR << KGDB8250_REG_SHIFT)) &
+ UART_LSR_THRE))
+ ;
+
+ serial_outb(chr, kgdb8250_addr + (UART_TX << KGDB8250_REG_SHIFT));
+}
+
+/*
+ * Get a byte from the hardware data buffer and return it
+ */
+static int
+read_data_bfr(void)
+{
+ char it = serial_inb(kgdb8250_addr + (UART_LSR << KGDB8250_REG_SHIFT));
+
+ if (it & UART_LSR_DR)
+ return serial_inb(kgdb8250_addr +
+ (UART_RX << KGDB8250_REG_SHIFT));
+
+ /*
+ * If we have a framing error assume somebody messed with
+ * our uart. Reprogram it and send '-' both ways...
+ */
+ if (it & 0xc) {
+ kgdb8250_init();
+ kgdb_put_debug_char('-');
+ return '-';
+ }
+
+ return -1;
+}
+
+/*
+ * Get a char if available, return -1 if nothing available.
+ * Empty the receive buffer first, then look at the interface hardware.
+ */
+
+static int
+kgdb_get_debug_char(void)
+{
+ int retchr;
+ unsigned long flags;
+ local_irq_save(flags);
+
+ /* intr routine has q'd chars */
+ if (atomic_read(&kgdb8250_buf_in_cnt) != 0) {
+ retchr = kgdb8250_buf[kgdb8250_buf_out_inx++];
+ kgdb8250_buf_out_inx &= (GDB_BUF_SIZE - 1);
+ atomic_dec(&kgdb8250_buf_in_cnt);
+ goto out;
+ }
+
+ do {
+ retchr = read_data_bfr();
+ } while (retchr < 0);
+
+out:
+ local_irq_restore(flags);
+
+ return retchr;
+}
+
+/*
+ * This is the receiver interrupt routine for the GDB stub.
+ * All that we need to do is verify that the interrupt happened on the
+ * line we're in charge of. If this is true, schedule a breakpoint and
+ * return.
+ */
+static irqreturn_t
+kgdb8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ char iir;
+
+ if (irq != KGDB8250_IRQ)
+ return IRQ_NONE;
+ /*
+ * If there is some other CPU in KGDB then this is a
+ * spurious interrupt. so return without even checking a byte
+ */
+ if (atomic_read(&debugger_active))
+ return IRQ_NONE;
+
+ iir = serial_inb(kgdb8250_addr + (UART_IIR << KGDB8250_REG_SHIFT));
+ if (iir & UART_IIR_RDI)
+ kgdb_schedule_breakpoint();
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Returns:
+ * 0 on success, 1 on failure.
+ */
+static int
+kgdb8250_init(void)
+{
+ unsigned cval;
+ int bits = 8;
+ int parity = 'n';
+ int cflag = CREAD | HUPCL | CLOCAL;
+ char ier = UART_IER_RDI;
+
+ /*
+ * Now construct a cflag setting.
+ */
+ switch (kgdb8250_baud) {
+ case 1200:
+ cflag |= B1200;
+ break;
+ case 2400:
+ cflag |= B2400;
+ break;
+ case 4800:
+ cflag |= B4800;
+ break;
+ case 19200:
+ cflag |= B19200;
+ break;
+ case 38400:
+ cflag |= B38400;
+ break;
+ case 57600:
+ cflag |= B57600;
+ break;
+ case 115200:
+ cflag |= B115200;
+ break;
+ default:
+ kgdb8250_baud = 9600;
+ /* Fall through */
+ case 9600:
+ cflag |= B9600;
+ break;
+ }
+ switch (bits) {
+ case 7:
+ cflag |= CS7;
+ break;
+ default:
+ case 8:
+ cflag |= CS8;
+ break;
+ }
+ switch (parity) {
+ case 'o':
+ case 'O':
+ cflag |= PARODD;
+ break;
+ case 'e':
+ case 'E':
+ cflag |= PARENB;
+ break;
+ }
+
+ /*
+ * Divisor, bytesize and parity
+ *
+ */
+
+ cval = cflag & (CSIZE | CSTOPB);
+ cval >>= 4;
+ if (cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ /* Disable UART interrupts, set DTR and RTS high and set speed. */
+ cval = 0x3;
+ /* set DLAB */
+ serial_outb(cval | UART_LCR_DLAB, kgdb8250_addr +
+ (UART_LCR << KGDB8250_REG_SHIFT));
+ /* LS */
+ serial_outb(BASE_BAUD / kgdb8250_baud & 0xff, kgdb8250_addr +
+ (UART_DLL << KGDB8250_REG_SHIFT));
+ /* MS */
+ serial_outb(BASE_BAUD / kgdb8250_baud >> 8, kgdb8250_addr +
+ (UART_DLM << KGDB8250_REG_SHIFT));
+ /* reset DLAB */
+ serial_outb(cval, kgdb8250_addr + (UART_LCR << KGDB8250_REG_SHIFT));
+
+ /*
+ * XScale-specific bits that need to be set
+ */
+ if (CURRENTPORT.type == PORT_XSCALE)
+ ier |= UART_IER_UUE | UART_IER_RTOIE;
+
+ /* turn on interrupts */
+ serial_outb(ier, kgdb8250_addr + (UART_IER << KGDB8250_REG_SHIFT));
+ serial_outb(UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS,
+ kgdb8250_addr + (UART_MCR << KGDB8250_REG_SHIFT));
+
+ /*
+ * If we read 0xff from the LSR, there is no UART here.
+ */
+ if (serial_inb(kgdb8250_addr + (UART_LSR << KGDB8250_REG_SHIFT)) ==
+ 0xff)
+ return -1;
+ return 0;
+}
+
+/*
+ * Copy the old serial_state table to our uart_port table if we haven't
+ * had values specifically configured in. We need to make sure this only
+ * happens once.
+ */
+static void
+kgdb8250_copy_rs_table(void)
+{
+#ifdef CONFIG_KGDB_SIMPLE_SERIAL
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(old_rs_table); i++) {
+ kgdb8250_ports[i].iobase = old_rs_table[i].port;
+ kgdb8250_ports[i].irq = irq_canonicalize(old_rs_table[i].irq);
+ kgdb8250_ports[i].uartclk = old_rs_table[i].baud_base * 16;
+ kgdb8250_ports[i].membase = old_rs_table[i].iomem_base;
+ kgdb8250_ports[i].iotype = old_rs_table[i].io_type;
+ kgdb8250_ports[i].regshift = old_rs_table[i].iomem_reg_shift;
+ }
+#endif
+
+ old_rs_table_copied = 1;
+}
+
+/*
+ * Perform static initalization tasks which we need to always do,
+ * even if KGDB isn't going to be invoked immediately.
+ */
+static int
+kgdb8250_local_init(void)
+{
+ if (old_rs_table_copied == 0)
+ kgdb8250_copy_rs_table();
+
+ switch (CURRENTPORT.iotype) {
+ case UPIO_MEM:
+ if (CURRENTPORT.mapbase) {
+ if (!request_mem_region(CURRENTPORT.mapbase,
+ 8 << KGDB8250_REG_SHIFT,
+ "kgdb"))
+ return 1; /* Failed. */
+ }
+ if (CURRENTPORT.flags & UPF_IOREMAP) {
+ CURRENTPORT.membase = ioremap(CURRENTPORT.mapbase,
+ 8 << KGDB8250_REG_SHIFT);
+ if (!CURRENTPORT.membase)
+ return 1; /* Failed. */
+ }
+ kgdb8250_addr = (unsigned long)CURRENTPORT.membase;
+ serial_outb = direct_outb;
+ serial_inb = direct_inb;
+ break;
+ case UPIO_PORT:
+ default:
+ kgdb8250_addr = CURRENTPORT.iobase;
+ serial_outb = io_outb;
+ serial_inb = io_inb;
+ }
+
+ return 0;
+}
+
+static int
+kgdb_init_io(void)
+{
+ if (kgdb8250_local_init())
+ return -1;
+
+#ifdef CONFIG_SERIAL_8250
+ if (serial8250_release_irq(KGDB8250_IRQ))
+ return -1;
+#endif
+
+ if (kgdb8250_init() == -1)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Hookup our IRQ line. We will already have been initialized at
+ * this point.
+ */
+static void __init kgdb8250_hookup_irq(void)
+{
+ request_irq(KGDB8250_IRQ, kgdb8250_interrupt, SA_SHIRQ,
+ "GDB-stub", &CURRENTPORT);
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_get_debug_char,
+ .write_char = kgdb_put_debug_char,
+ .init = kgdb_init_io,
+ .late_init = kgdb8250_hookup_irq,
+};
+
+void
+kgdb8250_add_port(int i, struct uart_port *serial_req)
+{
+ /* Copy the old table in if needed. */
+ if (old_rs_table_copied == 0)
+ kgdb8250_copy_rs_table();
+
+ /* Copy the whole thing over. */
+ memcpy(&kgdb8250_ports[i], serial_req, sizeof(struct uart_port));
+}
+
+/*
+ * Syntax for this cmdline option is "kgdb8250=ttyno,baudrate"
+ * with ",irq,iomembase" tacked on the end on IA64.
+ */
+static int __init
+kgdb8250_opt(char *str)
+{
+ if (*str < '0' || *str > '3')
+ goto errout;
+ kgdb8250_ttyS = *str - '0';
+ str++;
+ if (*str != ',')
+ goto errout;
+ str++;
+ kgdb8250_baud = simple_strtoul(str, &str, 10);
+ if (kgdb8250_baud != 9600 && kgdb8250_baud != 19200 &&
+ kgdb8250_baud != 38400 && kgdb8250_baud != 57600 &&
+ kgdb8250_baud != 115200)
+ goto errout;
+
+#ifdef CONFIG_IA64
+ if (*str == ',') {
+ str++;
+ KGDB8250_IRQ = simple_strtoul(str, &str, 10);
+ if (*str == ',') {
+ str++;
+ CURRENTPORT.iotype = SERIAL_IO_MEM;
+ CURRENTPORT.membase =
+ (unsigned char *)simple_strtoul(str, &str, 0);
+ }
+ }
+#endif
+
+ return 0;
+
+errout:
+ printk(KERN_ERR "Invalid syntax for option kgdb8250=\n");
+ return 1;
+}
+early_param("kgdb8250", kgdb8250_opt);
diff -puN drivers/serial/serial_core.c~8250 drivers/serial/serial_core.c
--- linux-2.6.10-rc1/drivers/serial/serial_core.c~8250 2004-10-29 11:26:44.970329349 -0700
+++ linux-2.6.10-rc1-trini/drivers/serial/serial_core.c 2004-10-29 11:26:44.981326767 -0700
@@ -903,7 +903,11 @@ uart_tiocmset(struct tty_struct *tty, st
static void uart_break_ctl(struct tty_struct *tty, int break_state)
{
struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->port;
+ struct uart_port *port;
+
+ if (!state)
+ return;
+ port = state->port;
BUG_ON(!kernel_locked());
diff -puN lib/Kconfig.debug~8250 lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~8250 2004-10-29 11:26:44.972328879 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:57.089929849 -0700
@@ -124,3 +124,104 @@ config KGDB
Documentation of kernel debugger available at
http://kgdb.sourceforge.net
This is only useful for kernel hackers. If unsure, say N.
+
+choice
+ prompt "Method for KGDB communication"
+ depends on KGDB
+ default KGDB_8250
+ help
+ There are a number of different ways in which you can communicate
+ with KGDB. The oldest is using a serial driver. A newer method
+ is to use UDP packets and a special network driver.
+
+config KGDB_8250
+ bool "KGDB: On generic serial port (8250)"
+ help
+ Uses generic serial port (8250) for kgdb. This is independent of the
+ option 9250/16550 and compatible serial port.
+
+endchoice
+
+config KGDB_SIMPLE_SERIAL
+ bool "Simple selection of KGDB serial port"
+ depends on KGDB_8250
+ help
+ If you say Y here, you will only have to pick the baud rate
+ and serial port (ttyS) that you wish to use for KGDB. If you
+ say N, you will have provide the I/O port and IRQ number. Note
+ that if your serial ports are iomapped, then you must say Y here.
+ If in doubt, say Y.
+
+choice
+ depends on KGDB_8250
+ prompt "Debug serial port BAUD"
+ default KGDB_115200BAUD
+ help
+ Gdb and the kernel stub need to agree on the baud rate to be
+ used. Some systems (x86 family at this writing) allow this to
+ be configured.
+
+config KGDB_9600BAUD
+ bool "9600"
+
+config KGDB_19200BAUD
+ bool "19200"
+
+config KGDB_38400BAUD
+ bool "38400"
+
+config KGDB_57600BAUD
+ bool "57600"
+
+config KGDB_115200BAUD
+ bool "115200"
+endchoice
+
+choice
+ prompt "Serial port for KGDB"
+ depends on KGDB_SIMPLE_SERIAL
+ default KGDB_TTYS0
+
+config KGDB_TTYS0
+ bool "ttyS0"
+
+config KGDB_TTYS1
+ bool "ttyS1"
+
+config KGDB_TTYS2
+ bool "ttyS2"
+
+config KGDB_TTYS3
+ bool "ttyS3"
+
+endchoice
+
+config KGDB_IOMEMBASE
+ hex "hex MMIO address of the debug serial port"
+ depends on !KGDB_SIMPLE_SERIAL && IA64
+ help
+ Some systems (IA64) don't access ports through legacy I/O port
+ space. Instead an MMIO address is used to access the UART.
+ There aren't any standard addresses on most system. The value
+ is platform dependent.
+
+config KGDB_PORT
+ hex "hex I/O port address of the debug serial port"
+ depends on !KGDB_SIMPLE_SERIAL && KGDB_8250 && !IA64
+ default 3f8
+ help
+ Some systems (x86 family at this writing) allow the port
+ address to be configured. The number entered is assumed to be
+ hex, don't put 0x in front of it. The standard address are:
+ COM1 3f8 , irq 4 and COM2 2f8 irq 3. Setserial /dev/ttySx
+ will tell you what you have. It is good to test the serial
+ connection with a live system before trying to debug.
+
+config KGDB_IRQ
+ int "IRQ of the debug serial port"
+ depends on !KGDB_SIMPLE_SERIAL && KGDB_8250 && !IA64
+ default 4
+ help
+ This is the irq for the debug port. If everything is working
+ correctly and the kernel has interrupts on a control C to the
+ port should cause a break into the kernel debug stub.
_
^ permalink raw reply [flat|nested] 12+ messages in thread* [patch 5/8] KGDB support for MIPS
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
` (2 preceding siblings ...)
2004-10-29 18:33 ` [patch 4/8] 8250 uart driver for KGDB Tom Rini
@ 2004-10-29 18:33 ` Tom Rini
2004-10-29 18:34 ` [patch 6/8] KGDB support for ia64 Tom Rini
` (4 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:33 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, trini, ralf, mlachwani
Cc: Ralf Baechle <ralf@linux-mips.org>, Manish Lachwani <mlachwani@mvista.com>
This adds KGDB support for MIPS and was done by Manish Lachwani. This has
been tested on MIPS Malta and Sibyte 1250 SWARM (32 & 64).
---
linux-2.6.10-rc1-trini/arch/mips/Kconfig.debug | 19
linux-2.6.10-rc1-trini/arch/mips/kernel/Makefile | 2
linux-2.6.10-rc1-trini/arch/mips/kernel/irq.c | 3
linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb.c | 292 ++
linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb_handler.S | 58
linux-2.6.10-rc1-trini/arch/mips/kernel/traps.c | 29
linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/Makefile | 1
linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/init.c | 64
linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_int.c | 25
linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_setup.c | 8
linux-2.6.10-rc1-trini/arch/mips/sibyte/cfe/setup.c | 14
linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/Makefile | 1
linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/irq.c | 72
linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/kgdb_sibyte.c | 158 +
linux-2.6.10-rc1-trini/arch/mips/sibyte/swarm/Makefile | 2
linux-2.6.10-rc1-trini/include/asm-mips/kdebug.h | 47
linux-2.6.10-rc1-trini/include/asm-mips/kgdb.h | 20
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 9
linux-2.6.10-rc1/arch/mips/kernel/gdb-low.S | 370 ---
linux-2.6.10-rc1/arch/mips/kernel/gdb-stub.c | 1089 ----------
linux-2.6.10-rc1/arch/mips/sibyte/swarm/dbg_io.c | 76
21 files changed, 633 insertions(+), 1726 deletions(-)
diff -puN arch/mips/Kconfig.debug~mips-lite arch/mips/Kconfig.debug
--- linux-2.6.10-rc1/arch/mips/Kconfig.debug~mips-lite 2004-10-29 11:26:45.172281931 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/Kconfig.debug 2004-10-29 11:26:45.204274419 -0700
@@ -27,25 +27,6 @@ config DEBUG_STACK_USAGE
This option will slow down process creation somewhat.
-config KGDB
- bool "Remote GDB kernel debugging"
- depends on DEBUG_KERNEL
- select DEBUG_INFO
- help
- If you say Y here, it will be possible to remotely debug the MIPS
- kernel using gdb. This enlarges your kernel image disk size by
- several megabytes and requires a machine with more than 16 MB,
- better 32 MB RAM to avoid excessive linking time. This is only
- useful for kernel hackers. If unsure, say N.
-
-config GDB_CONSOLE
- bool "Console output to GDB"
- depends on KGDB
- help
- If you are using GDB for remote debugging over a serial port and
- would like kernel messages to be formatted into GDB $O packets so
- that GDB prints them as program output, say 'Y'.
-
config SB1XXX_CORELIS
bool "Corelis Debugger"
depends on SIBYTE_SB1xxx_SOC
diff -puN arch/mips/kernel/Makefile~mips-lite arch/mips/kernel/Makefile
--- linux-2.6.10-rc1/arch/mips/kernel/Makefile~mips-lite 2004-10-29 11:26:45.173281696 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/Makefile 2004-10-29 11:26:45.204274419 -0700
@@ -48,7 +48,7 @@ obj-$(CONFIG_MIPS32_COMPAT) += ioctl32.o
obj-$(CONFIG_MIPS32_N32) += binfmt_elfn32.o scall64-n32.o signal_n32.o
obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o ptrace32.o
-obj-$(CONFIG_KGDB) += gdb-low.o gdb-stub.o
+obj-$(CONFIG_KGDB) += kgdb_handler.o kgdb.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_MIPS64) += cpu-bugs64.o
diff -L arch/mips/kernel/gdb-low.S -puN arch/mips/kernel/gdb-low.S~mips-lite /dev/null
--- linux-2.6.10-rc1/arch/mips/kernel/gdb-low.S
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,370 +0,0 @@
-/*
- * gdb-low.S contains the low-level trap handler for the GDB stub.
- *
- * Copyright (C) 1995 Andreas Busse
- */
-#include <linux/config.h>
-#include <linux/sys.h>
-
-#include <asm/asm.h>
-#include <asm/errno.h>
-#include <asm/mipsregs.h>
-#include <asm/regdef.h>
-#include <asm/stackframe.h>
-#include <asm/gdb-stub.h>
-
-#ifdef CONFIG_MIPS32
-#define DMFC0 mfc0
-#define DMTC0 mtc0
-#define LDC1 lwc1
-#define SDC1 lwc1
-#endif
-#ifdef CONFIG_MIPS64
-#define DMFC0 dmfc0
-#define DMTC0 dmtc0
-#define LDC1 ldc1
-#define SDC1 ldc1
-#endif
-
-/*
- * [jsun] We reserves about 2x GDB_FR_SIZE in stack. The lower (addressed)
- * part is used to store registers and passed to exception handler.
- * The upper part is reserved for "call func" feature where gdb client
- * saves some of the regs, setups call frame and passes args.
- *
- * A trace shows about 200 bytes are used to store about half of all regs.
- * The rest should be big enough for frame setup and passing args.
- */
-
-/*
- * The low level trap handler
- */
- .align 5
- NESTED(trap_low, GDB_FR_SIZE, sp)
- .set noat
- .set noreorder
-
- mfc0 k0, CP0_STATUS
- sll k0, 3 /* extract cu0 bit */
- bltz k0, 1f
- move k1, sp
-
- /*
- * Called from user mode, go somewhere else.
- */
- lui k1, %hi(saved_vectors)
- mfc0 k0, CP0_CAUSE
- andi k0, k0, 0x7c
- add k1, k1, k0
- lw k0, %lo(saved_vectors)(k1)
- jr k0
- nop
-1:
- move k0, sp
- subu sp, k1, GDB_FR_SIZE*2 # see comment above
- LONG_S k0, GDB_FR_REG29(sp)
- LONG_S $2, GDB_FR_REG2(sp)
-
-/*
- * First save the CP0 and special registers
- */
-
- mfc0 v0, CP0_STATUS
- LONG_S v0, GDB_FR_STATUS(sp)
- mfc0 v0, CP0_CAUSE
- LONG_S v0, GDB_FR_CAUSE(sp)
- DMFC0 v0, CP0_EPC
- LONG_S v0, GDB_FR_EPC(sp)
- DMFC0 v0, CP0_BADVADDR
- LONG_S v0, GDB_FR_BADVADDR(sp)
- mfhi v0
- LONG_S v0, GDB_FR_HI(sp)
- mflo v0
- LONG_S v0, GDB_FR_LO(sp)
-
-/*
- * Now the integer registers
- */
-
- LONG_S zero, GDB_FR_REG0(sp) /* I know... */
- LONG_S $1, GDB_FR_REG1(sp)
- /* v0 already saved */
- LONG_S $3, GDB_FR_REG3(sp)
- LONG_S $4, GDB_FR_REG4(sp)
- LONG_S $5, GDB_FR_REG5(sp)
- LONG_S $6, GDB_FR_REG6(sp)
- LONG_S $7, GDB_FR_REG7(sp)
- LONG_S $8, GDB_FR_REG8(sp)
- LONG_S $9, GDB_FR_REG9(sp)
- LONG_S $10, GDB_FR_REG10(sp)
- LONG_S $11, GDB_FR_REG11(sp)
- LONG_S $12, GDB_FR_REG12(sp)
- LONG_S $13, GDB_FR_REG13(sp)
- LONG_S $14, GDB_FR_REG14(sp)
- LONG_S $15, GDB_FR_REG15(sp)
- LONG_S $16, GDB_FR_REG16(sp)
- LONG_S $17, GDB_FR_REG17(sp)
- LONG_S $18, GDB_FR_REG18(sp)
- LONG_S $19, GDB_FR_REG19(sp)
- LONG_S $20, GDB_FR_REG20(sp)
- LONG_S $21, GDB_FR_REG21(sp)
- LONG_S $22, GDB_FR_REG22(sp)
- LONG_S $23, GDB_FR_REG23(sp)
- LONG_S $24, GDB_FR_REG24(sp)
- LONG_S $25, GDB_FR_REG25(sp)
- LONG_S $26, GDB_FR_REG26(sp)
- LONG_S $27, GDB_FR_REG27(sp)
- LONG_S $28, GDB_FR_REG28(sp)
- /* sp already saved */
- LONG_S $30, GDB_FR_REG30(sp)
- LONG_S $31, GDB_FR_REG31(sp)
-
- CLI /* disable interrupts */
-
-/*
- * Followed by the floating point registers
- */
- mfc0 v0, CP0_STATUS /* FPU enabled? */
- srl v0, v0, 16
- andi v0, v0, (ST0_CU1 >> 16)
-
- beqz v0,2f /* disabled, skip */
- nop
-
- SDC1 $0, GDB_FR_FPR0(sp)
- SDC1 $1, GDB_FR_FPR1(sp)
- SDC1 $2, GDB_FR_FPR2(sp)
- SDC1 $3, GDB_FR_FPR3(sp)
- SDC1 $4, GDB_FR_FPR4(sp)
- SDC1 $5, GDB_FR_FPR5(sp)
- SDC1 $6, GDB_FR_FPR6(sp)
- SDC1 $7, GDB_FR_FPR7(sp)
- SDC1 $8, GDB_FR_FPR8(sp)
- SDC1 $9, GDB_FR_FPR9(sp)
- SDC1 $10, GDB_FR_FPR10(sp)
- SDC1 $11, GDB_FR_FPR11(sp)
- SDC1 $12, GDB_FR_FPR12(sp)
- SDC1 $13, GDB_FR_FPR13(sp)
- SDC1 $14, GDB_FR_FPR14(sp)
- SDC1 $15, GDB_FR_FPR15(sp)
- SDC1 $16, GDB_FR_FPR16(sp)
- SDC1 $17, GDB_FR_FPR17(sp)
- SDC1 $18, GDB_FR_FPR18(sp)
- SDC1 $19, GDB_FR_FPR19(sp)
- SDC1 $20, GDB_FR_FPR20(sp)
- SDC1 $21, GDB_FR_FPR21(sp)
- SDC1 $22, GDB_FR_FPR22(sp)
- SDC1 $23, GDB_FR_FPR23(sp)
- SDC1 $24, GDB_FR_FPR24(sp)
- SDC1 $25, GDB_FR_FPR25(sp)
- SDC1 $26, GDB_FR_FPR26(sp)
- SDC1 $27, GDB_FR_FPR27(sp)
- SDC1 $28, GDB_FR_FPR28(sp)
- SDC1 $29, GDB_FR_FPR29(sp)
- SDC1 $30, GDB_FR_FPR30(sp)
- SDC1 $31, GDB_FR_FPR31(sp)
-
-/*
- * FPU control registers
- */
-
- cfc1 v0, CP1_STATUS
- LONG_S v0, GDB_FR_FSR(sp)
- cfc1 v0, CP1_REVISION
- LONG_S v0, GDB_FR_FIR(sp)
-
-/*
- * Current stack frame ptr
- */
-
-2:
- LONG_S sp, GDB_FR_FRP(sp)
-
-/*
- * CP0 registers (R4000/R4400 unused registers skipped)
- */
-
- mfc0 v0, CP0_INDEX
- LONG_S v0, GDB_FR_CP0_INDEX(sp)
- mfc0 v0, CP0_RANDOM
- LONG_S v0, GDB_FR_CP0_RANDOM(sp)
- DMFC0 v0, CP0_ENTRYLO0
- LONG_S v0, GDB_FR_CP0_ENTRYLO0(sp)
- DMFC0 v0, CP0_ENTRYLO1
- LONG_S v0, GDB_FR_CP0_ENTRYLO1(sp)
- DMFC0 v0, CP0_CONTEXT
- LONG_S v0, GDB_FR_CP0_CONTEXT(sp)
- mfc0 v0, CP0_PAGEMASK
- LONG_S v0, GDB_FR_CP0_PAGEMASK(sp)
- mfc0 v0, CP0_WIRED
- LONG_S v0, GDB_FR_CP0_WIRED(sp)
- DMFC0 v0, CP0_ENTRYHI
- LONG_S v0, GDB_FR_CP0_ENTRYHI(sp)
- mfc0 v0, CP0_PRID
- LONG_S v0, GDB_FR_CP0_PRID(sp)
-
- .set at
-
-/*
- * Continue with the higher level handler
- */
-
- move a0,sp
-
- jal handle_exception
- nop
-
-/*
- * Restore all writable registers, in reverse order
- */
-
- .set noat
-
- LONG_L v0, GDB_FR_CP0_ENTRYHI(sp)
- LONG_L v1, GDB_FR_CP0_WIRED(sp)
- DMTC0 v0, CP0_ENTRYHI
- mtc0 v1, CP0_WIRED
- LONG_L v0, GDB_FR_CP0_PAGEMASK(sp)
- LONG_L v1, GDB_FR_CP0_ENTRYLO1(sp)
- mtc0 v0, CP0_PAGEMASK
- DMTC0 v1, CP0_ENTRYLO1
- LONG_L v0, GDB_FR_CP0_ENTRYLO0(sp)
- LONG_L v1, GDB_FR_CP0_INDEX(sp)
- DMTC0 v0, CP0_ENTRYLO0
- LONG_L v0, GDB_FR_CP0_CONTEXT(sp)
- mtc0 v1, CP0_INDEX
- DMTC0 v0, CP0_CONTEXT
-
-
-/*
- * Next, the floating point registers
- */
- mfc0 v0, CP0_STATUS /* check if the FPU is enabled */
- srl v0, v0, 16
- andi v0, v0, (ST0_CU1 >> 16)
-
- beqz v0, 3f /* disabled, skip */
- nop
-
- LDC1 $31, GDB_FR_FPR31(sp)
- LDC1 $30, GDB_FR_FPR30(sp)
- LDC1 $29, GDB_FR_FPR29(sp)
- LDC1 $28, GDB_FR_FPR28(sp)
- LDC1 $27, GDB_FR_FPR27(sp)
- LDC1 $26, GDB_FR_FPR26(sp)
- LDC1 $25, GDB_FR_FPR25(sp)
- LDC1 $24, GDB_FR_FPR24(sp)
- LDC1 $23, GDB_FR_FPR23(sp)
- LDC1 $22, GDB_FR_FPR22(sp)
- LDC1 $21, GDB_FR_FPR21(sp)
- LDC1 $20, GDB_FR_FPR20(sp)
- LDC1 $19, GDB_FR_FPR19(sp)
- LDC1 $18, GDB_FR_FPR18(sp)
- LDC1 $17, GDB_FR_FPR17(sp)
- LDC1 $16, GDB_FR_FPR16(sp)
- LDC1 $15, GDB_FR_FPR15(sp)
- LDC1 $14, GDB_FR_FPR14(sp)
- LDC1 $13, GDB_FR_FPR13(sp)
- LDC1 $12, GDB_FR_FPR12(sp)
- LDC1 $11, GDB_FR_FPR11(sp)
- LDC1 $10, GDB_FR_FPR10(sp)
- LDC1 $9, GDB_FR_FPR9(sp)
- LDC1 $8, GDB_FR_FPR8(sp)
- LDC1 $7, GDB_FR_FPR7(sp)
- LDC1 $6, GDB_FR_FPR6(sp)
- LDC1 $5, GDB_FR_FPR5(sp)
- LDC1 $4, GDB_FR_FPR4(sp)
- LDC1 $3, GDB_FR_FPR3(sp)
- LDC1 $2, GDB_FR_FPR2(sp)
- LDC1 $1, GDB_FR_FPR1(sp)
- LDC1 $0, GDB_FR_FPR0(sp)
-
-/*
- * Now the CP0 and integer registers
- */
-
-3:
- mfc0 t0, CP0_STATUS
- ori t0, 0x1f
- xori t0, 0x1f
- mtc0 t0, CP0_STATUS
-
- LONG_L v0, GDB_FR_STATUS(sp)
- LONG_L v1, GDB_FR_EPC(sp)
- mtc0 v0, CP0_STATUS
- DMTC0 v1, CP0_EPC
- LONG_L v0, GDB_FR_HI(sp)
- LONG_L v1, GDB_FR_LO(sp)
- mthi v0
- mtlo v1
- LONG_L $31, GDB_FR_REG31(sp)
- LONG_L $30, GDB_FR_REG30(sp)
- LONG_L $28, GDB_FR_REG28(sp)
- LONG_L $27, GDB_FR_REG27(sp)
- LONG_L $26, GDB_FR_REG26(sp)
- LONG_L $25, GDB_FR_REG25(sp)
- LONG_L $24, GDB_FR_REG24(sp)
- LONG_L $23, GDB_FR_REG23(sp)
- LONG_L $22, GDB_FR_REG22(sp)
- LONG_L $21, GDB_FR_REG21(sp)
- LONG_L $20, GDB_FR_REG20(sp)
- LONG_L $19, GDB_FR_REG19(sp)
- LONG_L $18, GDB_FR_REG18(sp)
- LONG_L $17, GDB_FR_REG17(sp)
- LONG_L $16, GDB_FR_REG16(sp)
- LONG_L $15, GDB_FR_REG15(sp)
- LONG_L $14, GDB_FR_REG14(sp)
- LONG_L $13, GDB_FR_REG13(sp)
- LONG_L $12, GDB_FR_REG12(sp)
- LONG_L $11, GDB_FR_REG11(sp)
- LONG_L $10, GDB_FR_REG10(sp)
- LONG_L $9, GDB_FR_REG9(sp)
- LONG_L $8, GDB_FR_REG8(sp)
- LONG_L $7, GDB_FR_REG7(sp)
- LONG_L $6, GDB_FR_REG6(sp)
- LONG_L $5, GDB_FR_REG5(sp)
- LONG_L $4, GDB_FR_REG4(sp)
- LONG_L $3, GDB_FR_REG3(sp)
- LONG_L $2, GDB_FR_REG2(sp)
- LONG_L $1, GDB_FR_REG1(sp)
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
- LONG_L k0, GDB_FR_EPC(sp)
- LONG_L $29, GDB_FR_REG29(sp) /* Deallocate stack */
- jr k0
- rfe
-#else
- LONG_L sp, GDB_FR_REG29(sp) /* Deallocate stack */
-
- .set mips3
- eret
- .set mips0
-#endif
- .set at
- .set reorder
- END(trap_low)
-
-LEAF(kgdb_read_byte)
-4: lb t0, (a0)
- sb t0, (a1)
- li v0, 0
- jr ra
- .section __ex_table,"a"
- PTR 4b, kgdbfault
- .previous
- END(kgdb_read_byte)
-
-LEAF(kgdb_write_byte)
-5: sb a0, (a1)
- li v0, 0
- jr ra
- .section __ex_table,"a"
- PTR 5b, kgdbfault
- .previous
- END(kgdb_write_byte)
-
- .type kgdbfault@function
- .ent kgdbfault
-
-kgdbfault: li v0, -EFAULT
- jr ra
- .end kgdbfault
diff -L arch/mips/kernel/gdb-stub.c -puN arch/mips/kernel/gdb-stub.c~mips-lite /dev/null
--- linux-2.6.10-rc1/arch/mips/kernel/gdb-stub.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,1089 +0,0 @@
-/*
- * arch/mips/kernel/gdb-stub.c
- *
- * Originally written by Glenn Engel, Lake Stevens Instrument Division
- *
- * Contributed by HP Systems
- *
- * Modified for SPARC by Stu Grossman, Cygnus Support.
- *
- * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
- * 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
- */
-
-/*
- * 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 BREAK instruction.
- *
- *
- * 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
- *
- *
- * ==============
- * MORE EXAMPLES:
- * ==============
- *
- * For reference -- the following are the steps that one
- * company took (RidgeRun Inc) to get remote gdb debugging
- * going. In this scenario the host machine was a PC and the
- * target platform was a Galileo EVB64120A MIPS evaluation
- * board.
- *
- * Step 1:
- * First download gdb-5.0.tar.gz from the internet.
- * and then build/install the package.
- *
- * Example:
- * $ tar zxf gdb-5.0.tar.gz
- * $ cd gdb-5.0
- * $ ./configure --target=mips-linux-elf
- * $ make
- * $ install
- * $ which mips-linux-elf-gdb
- * /usr/local/bin/mips-linux-elf-gdb
- *
- * Step 2:
- * Configure linux for remote debugging and build it.
- *
- * Example:
- * $ cd ~/linux
- * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging>
- * $ make
- *
- * Step 3:
- * Download the kernel to the remote target and start
- * 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_KGDB which in turn enables your calls
- * to:
- * set_debug_traps();
- * breakpoint();
- *
- * Step 4:
- * Start the gdb session on the host.
- *
- * Example:
- * $ mips-linux-elf-gdb vmlinux
- * (gdb) set remotebaud 115200
- * (gdb) target remote /dev/ttyS1
- * ...at this point you are connected to
- * the remote target and can use gdb
- * in the normal fasion. Setting
- * breakpoints, single stepping,
- * printing variables, etc.
- */
-#include <linux/config.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#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/cacheflush.h>
-#include <asm/mipsregs.h>
-#include <asm/pgtable.h>
-#include <asm/system.h>
-#include <asm/gdb-stub.h>
-#include <asm/inst.h>
-
-/*
- * external low-level support routines
- */
-
-extern int putDebugChar(char c); /* write a single character */
-extern char getDebugChar(void); /* read and return a single char */
-extern void trap_low(void);
-
-/*
- * breakpoint and test functions
- */
-extern void breakpoint(void);
-extern void breakinst(void);
-extern void async_breakpoint(void);
-extern void async_breakinst(void);
-extern void adel(void);
-
-/*
- * local prototypes
- */
-
-static void getpacket(char *buffer);
-static void putpacket(char *buffer);
-static int computeSignal(int tt);
-static int hex(unsigned char ch);
-static int hexToInt(char **ptr, int *intValue);
-static int hexToLong(char **ptr, long *longValue);
-static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault);
-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
- */
-#define BUFMAX 2048
-
-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 char *address, unsigned char *dest);
-int kgdb_write_byte(unsigned char val, unsigned char *dest);
-
-/*
- * 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();
- 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 sequence 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(char *buffer)
-{
- unsigned char checksum;
- int count;
- unsigned char ch;
-
- /*
- * $<packet info>#<checksum>.
- */
-
- do {
- putDebugChar('$');
- checksum = 0;
- count = 0;
-
- while ((ch = buffer[count]) != 0) {
- if (!(putDebugChar(ch)))
- return;
- checksum += ch;
- count += 1;
- }
-
- putDebugChar('#');
- putDebugChar(hexchars[checksum >> 4]);
- putDebugChar(hexchars[checksum & 0xf]);
-
- }
- while ((getDebugChar() & 0x7f) != '+');
-}
-
-
-/*
- * 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.
- * may_fault is non-zero if we are reading from arbitrary memory, but is currently
- * not used.
- */
-static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault)
-{
- unsigned char ch;
-
- while (count-- > 0) {
- if (kgdb_read_byte(mem++, &ch) != 0)
- return 0;
- *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
- * may_fault is non-zero if we are reading from arbitrary memory, but is currently
- * not used.
- */
-static char *hex2mem(char *buf, char *mem, int count, int binary, int may_fault)
-{
- int i;
- unsigned char ch;
-
- for (i=0; i<count; i++)
- {
- if (binary) {
- ch = *buf++;
- if (ch == 0x7d)
- ch = 0x20 ^ *buf++;
- }
- else {
- ch = hex(*buf++) << 4;
- ch |= hex(*buf++);
- }
- if (kgdb_write_byte(ch, mem++) != 0)
- return 0;
- }
-
- 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 MIPS R3xxx and R4xxx */
- unsigned char signo; /* Signal that we map this trap into */
-} hard_trap_info[] = {
- { 6, SIGBUS }, /* instruction bus error */
- { 7, SIGBUS }, /* data bus error */
- { 9, SIGTRAP }, /* break */
- { 10, SIGILL }, /* reserved instruction */
-/* { 11, SIGILL }, */ /* CPU unusable */
- { 12, SIGFPE }, /* overflow */
- { 13, SIGTRAP }, /* trap */
- { 14, SIGSEGV }, /* virtual instruction cache coherency */
- { 15, SIGFPE }, /* floating point exception */
- { 23, SIGSEGV }, /* watch */
- { 31, SIGSEGV }, /* virtual data cache coherency */
- { 0, 0} /* Must be last */
-};
-
-/* Save the normal trap handlers for user-mode traps. */
-void *saved_vectors[32];
-
-/*
- * Set up exception handlers for tracing and breakpoints
- */
-void set_debug_traps(void)
-{
- struct hard_trap_info *ht;
- unsigned long flags;
- unsigned char c;
-
- local_irq_save(flags);
- for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
- saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low);
-
- putDebugChar('+'); /* 'hello world' */
- /*
- * In case GDB is started before us, ack any packets
- * (presumably "$?#xx") sitting there.
- */
- while((c = getDebugChar()) != '$');
- while((c = getDebugChar()) != '#');
- c = getDebugChar(); /* eat first csum byte */
- c = getDebugChar(); /* eat second csum byte */
- putDebugChar('+'); /* ack it */
-
- initialized = 1;
- local_irq_restore(flags);
-}
-
-void restore_debug_traps(void)
-{
- struct hard_trap_info *ht;
- unsigned long flags;
-
- local_irq_save(flags);
- for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
- set_except_vector(ht->tt, saved_vectors[ht->tt]);
- local_irq_restore(flags);
-}
-
-/*
- * Convert the MIPS 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);
-}
-
-static int hexToLong(char **ptr, long *longValue)
-{
- int numChars = 0;
- int hexValue;
-
- *longValue = 0;
-
- while (**ptr) {
- hexValue = hex(**ptr);
- if (hexValue < 0)
- break;
-
- *longValue = (*longValue << 4) | hexValue;
- numChars ++;
-
- (*ptr)++;
- }
-
- return numChars;
-}
-
-
-#if 0
-/*
- * Print registers (on target console)
- * Used only to debug the stub...
- */
-void show_gdbregs(struct gdb_regs * regs)
-{
- /*
- * Saved main processor registers
- */
- printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg0, regs->reg1, regs->reg2, regs->reg3,
- regs->reg4, regs->reg5, regs->reg6, regs->reg7);
- printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg8, regs->reg9, regs->reg10, regs->reg11,
- regs->reg12, regs->reg13, regs->reg14, regs->reg15);
- printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg16, regs->reg17, regs->reg18, regs->reg19,
- regs->reg20, regs->reg21, regs->reg22, regs->reg23);
- printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- regs->reg24, regs->reg25, regs->reg26, regs->reg27,
- regs->reg28, regs->reg29, regs->reg30, regs->reg31);
-
- /*
- * Saved cp0 registers
- */
- printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n",
- regs->cp0_epc, regs->cp0_status, regs->cp0_cause);
-}
-#endif /* dead code */
-
-/*
- * We single-step by setting breakpoints. When an exception
- * is handled, we need to restore the instructions hoisted
- * when the breakpoints were set.
- *
- * This is where we save the original instructions.
- */
-static struct gdb_bp_save {
- unsigned long addr;
- unsigned int val;
-} step_bp[2];
-
-#define BP 0x0000000d /* break opcode */
-
-/*
- * Set breakpoint instructions for single stepping.
- */
-static void single_step(struct gdb_regs *regs)
-{
- union mips_instruction insn;
- unsigned long targ;
- int is_branch, is_cond, i;
-
- targ = regs->cp0_epc;
- insn.word = *(unsigned int *)targ;
- is_branch = is_cond = 0;
-
- switch (insn.i_format.opcode) {
- /*
- * jr and jalr are in r_format format.
- */
- case spec_op:
- switch (insn.r_format.func) {
- case jalr_op:
- case jr_op:
- targ = *(®s->reg0 + insn.r_format.rs);
- is_branch = 1;
- break;
- }
- break;
-
- /*
- * This group contains:
- * bltz_op, bgez_op, bltzl_op, bgezl_op,
- * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
- */
- case bcond_op:
- is_branch = is_cond = 1;
- targ += 4 + (insn.i_format.simmediate << 2);
- break;
-
- /*
- * These are unconditional and in j_format.
- */
- case jal_op:
- case j_op:
- is_branch = 1;
- targ += 4;
- targ >>= 28;
- targ <<= 28;
- targ |= (insn.j_format.target << 2);
- break;
-
- /*
- * These are conditional.
- */
- case beq_op:
- case beql_op:
- case bne_op:
- case bnel_op:
- case blez_op:
- case blezl_op:
- case bgtz_op:
- case bgtzl_op:
- case cop0_op:
- case cop1_op:
- case cop2_op:
- case cop1x_op:
- is_branch = is_cond = 1;
- targ += 4 + (insn.i_format.simmediate << 2);
- break;
- }
-
- if (is_branch) {
- i = 0;
- if (is_cond && targ != (regs->cp0_epc + 8)) {
- step_bp[i].addr = regs->cp0_epc + 8;
- step_bp[i++].val = *(unsigned *)(regs->cp0_epc + 8);
- *(unsigned *)(regs->cp0_epc + 8) = BP;
- }
- step_bp[i].addr = targ;
- step_bp[i].val = *(unsigned *)targ;
- *(unsigned *)targ = BP;
- } else {
- step_bp[0].addr = regs->cp0_epc + 4;
- step_bp[0].val = *(unsigned *)(regs->cp0_epc + 4);
- *(unsigned *)(regs->cp0_epc + 4) = BP;
- }
-}
-
-/*
- * If asynchronously interrupted by gdb, then we need to set a breakpoint
- * at the interrupted instruction so that we wind up stopped with a
- * reasonable stack frame.
- */
-static struct gdb_bp_save async_bp;
-
-/*
- * 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)
-{
- /* 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);
-}
-
-
-/*
- * 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.
- */
-void handle_exception (struct gdb_regs *regs)
-{
- int trap; /* Trap type */
- int sigval;
- long addr;
- int length;
- char *ptr;
- unsigned long *stack;
- int i;
- int bflag = 0;
-
- kgdb_started = 1;
-
- /*
- * acquire the big kgdb spinlock
- */
- 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 = num_online_cpus()-1; i >= 0; i--)
- if (spin_trylock(&kgdb_cpulock[i]) == 0)
- panic("kgdb: couldn't get cpulock %d\n", i);
-
- /*
- * 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;
-
- /*
- * If we were single_stepping, restore the opcodes hoisted
- * for the breakpoint[s].
- */
- if (step_bp[0].addr) {
- *(unsigned *)step_bp[0].addr = step_bp[0].val;
- step_bp[0].addr = 0;
-
- if (step_bp[1].addr) {
- *(unsigned *)step_bp[1].addr = step_bp[1].val;
- step_bp[1].addr = 0;
- }
- }
-
- stack = (long *)regs->reg29; /* stack ptr */
- sigval = computeSignal(trap);
-
- /*
- * reply to host that an exception has occurred
- */
- ptr = output_buffer;
-
- /*
- * Send trap type (converted to signal)
- */
- *ptr++ = 'T';
- *ptr++ = hexchars[sigval >> 4];
- *ptr++ = hexchars[sigval & 0xf];
-
- /*
- * Send Error PC
- */
- *ptr++ = hexchars[REG_EPC >> 4];
- *ptr++ = hexchars[REG_EPC & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®s->cp0_epc, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- /*
- * Send frame pointer
- */
- *ptr++ = hexchars[REG_FP >> 4];
- *ptr++ = hexchars[REG_FP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®s->reg30, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- /*
- * Send stack pointer
- */
- *ptr++ = hexchars[REG_SP >> 4];
- *ptr++ = hexchars[REG_SP & 0xf];
- *ptr++ = ':';
- ptr = mem2hex((char *)®s->reg29, ptr, sizeof(long), 0);
- *ptr++ = ';';
-
- *ptr++ = 0;
- putpacket(output_buffer); /* send it off... */
-
- /*
- * Wait for input from remote GDB
- */
- while (1) {
- output_buffer[0] = 0;
- getpacket(input_buffer);
-
- switch (input_buffer[0])
- {
- case '?':
- output_buffer[0] = 'S';
- output_buffer[1] = hexchars[sigval >> 4];
- output_buffer[2] = hexchars[sigval & 0xf];
- 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;
-
- /*
- * Return the value of the CPU registers
- */
- case 'g':
- ptr = output_buffer;
- ptr = mem2hex((char *)®s->reg0, ptr, 32*sizeof(long), 0); /* r0...r31 */
- ptr = mem2hex((char *)®s->cp0_status, ptr, 6*sizeof(long), 0); /* cp0 */
- ptr = mem2hex((char *)®s->fpr0, ptr, 32*sizeof(long), 0); /* f0...31 */
- ptr = mem2hex((char *)®s->cp1_fsr, ptr, 2*sizeof(long), 0); /* cp1 */
- ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*sizeof(long), 0); /* frp */
- ptr = mem2hex((char *)®s->cp0_index, ptr, 16*sizeof(long), 0); /* cp0 */
- break;
-
- /*
- * set the value of the CPU registers - return OK
- */
- case 'G':
- {
- ptr = &input_buffer[1];
- hex2mem(ptr, (char *)®s->reg0, 32*sizeof(long), 0, 0);
- ptr += 32*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->cp0_status, 6*sizeof(long), 0, 0);
- ptr += 6*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->fpr0, 32*sizeof(long), 0, 0);
- ptr += 32*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->cp1_fsr, 2*sizeof(long), 0, 0);
- ptr += 2*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->frame_ptr, 2*sizeof(long), 0, 0);
- ptr += 2*(2*sizeof(long));
- hex2mem(ptr, (char *)®s->cp0_index, 16*sizeof(long), 0, 0);
- strcpy(output_buffer,"OK");
- }
- break;
-
- /*
- * mAA..AA,LLLL Read LLLL bytes at address AA..AA
- */
- case 'm':
- ptr = &input_buffer[1];
-
- if (hexToLong(&ptr, &addr)
- && *ptr++ == ','
- && hexToInt(&ptr, &length)) {
- if (mem2hex((char *)addr, output_buffer, length, 1))
- break;
- strcpy (output_buffer, "E03");
- } else
- strcpy(output_buffer,"E01");
- break;
-
- /*
- * XAA..AA,LLLL: Write LLLL escaped binary bytes at address AA.AA
- */
- case 'X':
- bflag = 1;
- /* fall through */
-
- /*
- * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK
- */
- case 'M':
- ptr = &input_buffer[1];
-
- if (hexToLong(&ptr, &addr)
- && *ptr++ == ','
- && hexToInt(&ptr, &length)
- && *ptr++ == ':') {
- if (hex2mem(ptr, (char *)addr, length, bflag, 1))
- strcpy(output_buffer, "OK");
- else
- strcpy(output_buffer, "E03");
- }
- else
- strcpy(output_buffer, "E02");
- break;
-
- /*
- * cAA..AA Continue at address AA..AA(optional)
- */
- case 'c':
- /* try to read optional parameter, pc unchanged if no parm */
-
- ptr = &input_buffer[1];
- if (hexToLong(&ptr, &addr))
- regs->cp0_epc = addr;
-
- goto exit_kgdb_exception;
- break;
-
- /*
- * 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
- */
- case 's':
- /*
- * There is no single step insn in the MIPS ISA, so we
- * use breakpoints and continue, instead.
- */
- single_step(regs);
- goto exit_kgdb_exception;
- /* NOTREACHED */
- break;
-
- /*
- * Set baud rate (bBB)
- * FIXME: Needs to be written
- */
- case 'b':
- {
-#if 0
- int baudrate;
- extern void set_timer_3();
-
- ptr = &input_buffer[1];
- if (!hexToInt(&ptr, &baudrate))
- {
- strcpy(output_buffer,"B01");
- break;
- }
-
- /* Convert baud rate to uart clock divider */
-
- switch (baudrate)
- {
- case 38400:
- baudrate = 16;
- break;
- case 19200:
- baudrate = 33;
- break;
- case 9600:
- baudrate = 65;
- break;
- default:
- baudrate = 0;
- strcpy(output_buffer,"B02");
- goto x1;
- }
-
- if (baudrate) {
- putpacket("OK"); /* Ack before changing speed */
- set_timer_3(baudrate); /* Set it */
- }
-#endif
- }
- break;
-
- } /* switch */
-
- /*
- * reply to the request
- */
-
- putpacket(output_buffer);
-
- } /* while */
-
- return;
-
-finish_kgdb:
- restore_debug_traps();
-
-exit_kgdb_exception:
- /* release locks so other CPUs can go */
- for (i = num_online_cpus()-1; i >= 0; i--)
- spin_unlock(&kgdb_cpulock[i]);
- spin_unlock(&kgdb_lock);
-
- __flush_cache_all();
- return;
-}
-
-/*
- * 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;
-
- __asm__ __volatile__(
- ".globl breakinst\n\t"
- ".set\tnoreorder\n\t"
- "nop\n"
- "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"
- "async_breakinst:\tbreak\n\t"
- "nop\n\t"
- ".set\treorder"
- );
-}
-
-void adel(void)
-{
- __asm__ __volatile__(
- ".globl\tadel\n\t"
- "lui\t$8,0x8000\n\t"
- "lw\t$9,1($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
-
-void gdb_putsn(const char *str, int l)
-{
- char outbuf[18];
-
- if (!kgdb_started)
- return;
-
- outbuf[0]='O';
-
- while(l) {
- int i = (l>8)?8:l;
- mem2hex((char *)str, &outbuf[1], i, 0);
- outbuf[(i*2)+1]=0;
- putpacket(outbuf);
- str += i;
- l -= i;
- }
-}
-
-static void gdb_console_write(struct console *con, const char *s, unsigned n)
-{
- gdb_putsn(s, n);
-}
-
-static struct console gdb_console = {
- .name = "gdb",
- .write = gdb_console_write,
- .flags = CON_PRINTBUFFER,
- .index = -1
-};
-
-static int __init register_gdb_console(void)
-{
- register_console(&gdb_console);
-
- return 0;
-}
-
-console_initcall(register_gdb_console);
-
-#endif
diff -puN arch/mips/kernel/irq.c~mips-lite arch/mips/kernel/irq.c
--- linux-2.6.10-rc1/arch/mips/kernel/irq.c~mips-lite 2004-10-29 11:26:45.179280287 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/irq.c 2004-10-29 11:26:45.207273714 -0700
@@ -22,6 +22,7 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>
#include <asm/atomic.h>
#include <asm/system.h>
@@ -420,6 +421,8 @@ out:
irq_exit();
+ kgdb_process_breakpoint();
+
return 1;
}
diff -puN /dev/null arch/mips/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb.c 2004-10-29 11:26:45.208273480 -0700
@@ -0,0 +1,292 @@
+/*
+ * arch/mips/kernel/kgdb.c
+ *
+ * Originally written by Glenn Engel, Lake Stevens Instrument Division
+ *
+ * Contributed by HP Systems
+ *
+ * Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
+ * 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
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/debugger.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <asm/inst.h>
+#include <asm/gdb-stub.h>
+#include <asm/cacheflush.h>
+#include <asm/kdebug.h>
+
+static struct hard_trap_info {
+ unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */
+ unsigned char signo; /* Signal that we map this trap into */
+} hard_trap_info[] = {
+ { 6, SIGBUS }, /* instruction bus error */
+ { 7, SIGBUS }, /* data bus error */
+ { 9, SIGTRAP }, /* break */
+ { 10, SIGILL }, /* reserved instruction */
+/* { 11, SIGILL }, */ /* CPU unusable */
+ { 12, SIGFPE }, /* overflow */
+ { 13, SIGTRAP }, /* trap */
+ { 14, SIGSEGV }, /* virtual instruction cache coherency */
+ { 15, SIGFPE }, /* floating point exception */
+ { 23, SIGSEGV }, /* watch */
+ { 31, SIGSEGV }, /* virtual data cache coherency */
+ { 0, 0} /* Must be last */
+};
+
+extern atomic_t cpu_doing_single_step;
+
+/* Save the normal trap handlers for user-mode traps. */
+void *saved_vectors[32];
+extern void trap_low(void);
+extern void breakinst(void);
+
+static int compute_signal(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 */
+}
+
+/*
+ * Set up exception handlers for tracing and breakpoints
+ */
+void handle_exception(struct pt_regs *regs)
+{
+ int trap = (regs->cp0_cause & 0x7c) >> 2;
+
+ if (atomic_read(&debugger_active))
+ debugger_nmihook(smp_processor_id(), regs);
+
+ if (atomic_read (&kgdb_setting_breakpoint))
+ if ( (trap == 9) && (regs->cp0_epc == (unsigned long)breakinst) )
+ regs->cp0_epc += 4;
+
+ (*linux_debug_hook) (0, compute_signal(trap), 0, regs);
+
+ /* In SMP mode, __flush_cache_all does IPI */
+ __flush_cache_all();
+}
+
+void set_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low);
+
+ local_irq_restore(flags);
+}
+
+#if 0
+/* This should be called before we exit kgdb_handle_exception() I believe.
+ * -- Tom
+ */
+void restore_debug_traps(void)
+{
+ struct hard_trap_info *ht;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ set_except_vector(ht->tt, saved_vectors[ht->tt]);
+ local_irq_restore(flags);
+}
+#endif
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg = 0;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ *(ptr++) = regs->cp0_status;
+ *(ptr++) = regs->lo;
+ *(ptr++) = regs->hi;
+ *(ptr++) = regs->cp0_badvaddr;
+ *(ptr++) = regs->cp0_cause;
+ *(ptr++) = regs->cp0_epc;
+
+ return;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int reg = 0;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 32; reg++)
+ regs->regs[reg] = *(ptr++);
+
+ regs->cp0_status = *(ptr++);
+ regs->lo = *(ptr++);
+ regs->hi = *(ptr++);
+ regs->cp0_badvaddr = *(ptr++);
+ regs->cp0_cause = *(ptr++);
+ regs->cp0_epc = *(ptr++);
+
+ return;
+}
+
+/*
+ * Similar to regs_to_gdb_regs() except that process is sleeping and so
+ * we may not be able to get all the info.
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ int reg = 0;
+ struct thread_info *ti = p->thread_info;
+ unsigned long ksp = (unsigned long)ti + THREAD_SIZE -32;
+ struct pt_regs *regs = (struct pt_regs *)ksp - 1;
+ unsigned long *ptr = gdb_regs;
+
+ for (reg = 0; reg < 16; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ /* S0 - S7 */
+ for (reg = 16; reg < 24; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ for (reg = 24; reg < 28; reg++)
+ *(ptr++) = 0;
+
+ /* GP, SP, FP, RA */
+ for (reg = 28; reg < 32; reg++)
+ *(ptr++) = regs->regs[reg];
+
+ *(ptr++) = regs->cp0_status;
+ *(ptr++) = regs->lo;
+ *(ptr++) = regs->hi;
+ *(ptr++) = regs->cp0_badvaddr;
+ *(ptr++) = regs->cp0_cause;
+ *(ptr++) = regs->cp0_epc;
+
+ return;
+}
+
+/*
+ * Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
+ * then try to fall into the debugger
+ */
+static int kgdb_mips_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *args = (struct die_args *)ptr;
+ struct pt_regs *regs = args->regs;
+ int trap = (regs->cp0_cause & 0x7c) >> 2;
+
+ if (cmd == DIE_PAGE_FAULT)
+ return NOTIFY_DONE;
+
+ if (user_mode(regs)) {
+ if (cmd == DIE_OOPS)
+ CHK_DEBUGGER(trap, compute_signal(trap), 0, regs, 0);
+ return NOTIFY_DONE;
+ }
+
+ CHK_DEBUGGER(trap, compute_signal(trap), 0, regs, 0);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_mips_notify,
+};
+
+/*
+ * Handle the 's' and 'c' commands
+ */
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+ char *InBuffer, char *outBuffer,
+ struct pt_regs *regs)
+{
+ char *ptr;
+ unsigned long address;
+ int cpu = smp_processor_id ();
+
+ switch (InBuffer[0]) {
+ case 's':
+ case 'c':
+ if (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(outBuffer, "E00");
+ break;
+ }
+
+ /* handle the optional parameter */
+ ptr = &InBuffer[1];
+ if (kgdb_hex2long (&ptr, &address))
+ regs->cp0_epc = address;
+
+ atomic_set(&cpu_doing_single_step,-1);
+ if (InBuffer[0] == 's')
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step, cpu);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ .gdb_bpt_instr = {0xd},
+#else
+ .gdb_bpt_instr = {0x00, 0x00, 0x00, 0x0d},
+#endif
+};
+
+/*
+ * Note that on some boards (such as MIPS Malta) we need to do additional
+ * bringup. We do this via kgdb_mips_init(). We provide a weak version
+ * here as others (such as BCM91250A-SWARM) need no extra bringup.
+ */
+void __attribute__ ((weak)) kgdb_mips_init(void)
+{
+}
+
+int kgdb_arch_init(void)
+{
+ /* Board-specifics. */
+ kgdb_mips_init();
+
+ /* Set our traps. */
+ /* This needs to be done more finely grained again, paired in
+ * a before/after in kgdb_handle_exception(...) -- Tom */
+ set_debug_traps();
+ notifier_chain_register(&mips_die_chain, &kgdb_notifier);
+
+ return 0;
+}
diff -puN /dev/null arch/mips/kernel/kgdb_handler.S
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/kgdb_handler.S 2004-10-29 11:26:45.208273480 -0700
@@ -0,0 +1,58 @@
+/*
+ * arch/mips/kernel/kgdb_handler.S
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/*
+ * Trap Handler for the new KGDB framework. The main KGDB handler is
+ * handle_exception that will be called from here
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/sys.h>
+
+#include <asm/asm.h>
+#include <asm/errno.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+ .align 5
+ NESTED(trap_low, PT_SIZE, sp)
+ .set noat
+ .set noreorder
+ SAVE_ALL
+ /*
+ * Check for privileged instructions in user mode. For
+ * this, check the cu0 bit in the CPU status register.
+ */
+ mfc0 k0, CP0_STATUS
+ sll k0, 3
+ bltz k0, 1f
+ move k1, sp
+
+ /*
+ * GDB userland from within KGDB. If a user mode address
+ * then jump to the saved exception handler
+ */
+ lui k1, %hi(saved_vectors)
+ mfc0 k0, CP0_CAUSE
+ andi k0, k0, 0x7c
+ add k1, k1, k0
+ lw k0, %lo(saved_vectors)(k1)
+ jr k0
+ nop
+1:
+ .set at
+ .set reorder
+ move a0, sp
+ jal handle_exception
+ j ret_from_exception
+ END(trap_low)
diff -puN arch/mips/kernel/traps.c~mips-lite arch/mips/kernel/traps.c
--- linux-2.6.10-rc1/arch/mips/kernel/traps.c~mips-lite 2004-10-29 11:26:45.181279818 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/kernel/traps.c 2004-10-29 11:26:45.210273010 -0700
@@ -10,6 +10,8 @@
* Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
* Copyright (C) 2000, 01 MIPS Technologies, Inc.
* Copyright (C) 2002, 2003, 2004 Maciej W. Rozycki
+ *
+ * KGDB specific changes - Manish Lachwani (mlachwani@mvista.com)
*/
#include <linux/config.h>
#include <linux/init.h>
@@ -20,6 +22,7 @@
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
+#include <linux/debugger.h>
#include <asm/bootinfo.h>
#include <asm/branch.h>
@@ -37,6 +40,7 @@
#include <asm/mmu_context.h>
#include <asm/watch.h>
#include <asm/types.h>
+#include <asm/kdebug.h>
extern asmlinkage void handle_mod(void);
extern asmlinkage void handle_tlbl(void);
@@ -72,6 +76,21 @@ int (*board_be_handler)(struct pt_regs *
*/
#define MODULE_RANGE (8*1024*1024)
+struct notifier_block *mips_die_chain;
+static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED;
+
+int register_die_notifier(struct notifier_block *nb)
+{
+ int err = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&die_notifier_lock, flags);
+ err = notifier_chain_register(&mips_die_chain, nb);
+ spin_unlock_irqrestore(&die_notifier_lock, flags);
+
+ return err;
+}
+
/*
* This routine abuses get_user()/put_user() to reference pointers
* with at least a bit of error checking ...
@@ -265,8 +284,10 @@ NORET_TYPE void __die(const char * str,
void __die_if_kernel(const char * str, struct pt_regs * regs,
const char * file, const char * func, unsigned long line)
{
- if (!user_mode(regs))
+ if (!user_mode(regs)) {
+ notify_die(DIE_TRAP, (char *)str, regs, 0);
__die(str, regs, file, func, line);
+ }
}
extern const struct exception_table_entry __start___dbe_table[];
@@ -921,6 +942,9 @@ void __init per_cpu_trap_init(void)
tlb_init();
}
+/* Keep track of if we've done certain initialization already or not. */
+int kgdb_early_setup;
+
void __init trap_init(void)
{
extern char except_vec3_generic, except_vec3_r4000;
@@ -928,6 +952,9 @@ void __init trap_init(void)
extern char except_vec4;
unsigned long i;
+ if (kgdb_early_setup)
+ return; /* Already done */
+
per_cpu_trap_init();
/*
diff -puN arch/mips/mips-boards/generic/Makefile~mips-lite arch/mips/mips-boards/generic/Makefile
--- linux-2.6.10-rc1/arch/mips/mips-boards/generic/Makefile~mips-lite 2004-10-29 11:26:45.183279348 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/Makefile 2004-10-29 11:26:45.210273010 -0700
@@ -21,6 +21,5 @@
obj-y := mipsIRQ.o reset.o display.o init.o memory.o \
printf.o cmdline.o time.o
obj-$(CONFIG_PCI) += pci.o
-obj-$(CONFIG_KGDB) += gdb_hook.o
EXTRA_AFLAGS := $(CFLAGS)
diff -puN arch/mips/mips-boards/generic/init.c~mips-lite arch/mips/mips-boards/generic/init.c
--- linux-2.6.10-rc1/arch/mips/mips-boards/generic/init.c~mips-lite 2004-10-29 11:26:45.185278879 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/generic/init.c 2004-10-29 11:26:45.210273010 -0700
@@ -35,17 +35,6 @@
#include <asm/mips-boards/malta.h>
#endif
-#ifdef CONFIG_KGDB
-extern int rs_kgdb_hook(int, int);
-extern int rs_putDebugChar(char);
-extern char rs_getDebugChar(void);
-extern int saa9730_kgdb_hook(int);
-extern int saa9730_putDebugChar(char);
-extern char saa9730_getDebugChar(void);
-
-int remote_debug = 0;
-#endif
-
int prom_argc;
int *_prom_argv, *_prom_envp;
@@ -172,59 +161,6 @@ static void __init console_config(void)
}
#endif
-#ifdef CONFIG_KGDB
-void __init kgdb_config (void)
-{
- extern int (*generic_putDebugChar)(char);
- extern char (*generic_getDebugChar)(void);
- char *argptr;
- int line, speed;
-
- argptr = prom_getcmdline();
- if ((argptr = strstr(argptr, "kgdb=ttyS")) != NULL) {
- argptr += strlen("kgdb=ttyS");
- if (*argptr != '0' && *argptr != '1')
- printk("KGDB: Unknown serial line /dev/ttyS%c, "
- "falling back to /dev/ttyS1\n", *argptr);
- line = *argptr == '0' ? 0 : 1;
- printk("KGDB: Using serial line /dev/ttyS%d for session\n", line);
-
- speed = 0;
- if (*++argptr == ',')
- {
- int c;
- while ((c = *++argptr) && ('0' <= c && c <= '9'))
- speed = speed * 10 + c - '0';
- }
-#ifdef CONFIG_MIPS_ATLAS
- if (line == 1) {
- speed = saa9730_kgdb_hook(speed);
- generic_putDebugChar = saa9730_putDebugChar;
- generic_getDebugChar = saa9730_getDebugChar;
- }
- else
-#endif
- {
- speed = rs_kgdb_hook(line, speed);
- generic_putDebugChar = rs_putDebugChar;
- generic_getDebugChar = rs_getDebugChar;
- }
-
- prom_printf("KGDB: Using serial line /dev/ttyS%d at %d for session, "
- "please connect your debugger\n", line ? 1 : 0, speed);
-
- {
- char *s;
- for (s = "Please connect GDB to this port\r\n"; *s; )
- generic_putDebugChar (*s++);
- }
-
- remote_debug = 1;
- /* Breakpoint is invoked after interrupts are initialised */
- }
-}
-#endif
-
void __init prom_init(void)
{
prom_argc = fw_arg0;
diff -puN arch/mips/mips-boards/malta/malta_int.c~mips-lite arch/mips/mips-boards/malta/malta_int.c
--- linux-2.6.10-rc1/arch/mips/mips-boards/malta/malta_int.c~mips-lite 2004-10-29 11:26:45.187278409 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_int.c 2004-10-29 11:26:45.211272776 -0700
@@ -40,12 +40,7 @@
#include <asm/mips-boards/msc01_pci.h>
extern asmlinkage void mipsIRQ(void);
-
-#ifdef CONFIG_KGDB
-extern void breakpoint(void);
-extern void set_debug_traps(void);
-extern int remote_debug;
-#endif
+extern int kgdb_early_setup;
static spinlock_t mips_irq_lock = SPIN_LOCK_UNLOCKED;
@@ -189,16 +184,20 @@ void corehi_irqdispatch(struct pt_regs *
void __init init_IRQ(void)
{
+ if (kgdb_early_setup)
+ return; /* Already done. */
set_except_vector(0, mipsIRQ);
init_generic_irq();
init_i8259_irqs();
+}
#ifdef CONFIG_KGDB
- if (remote_debug) {
- set_debug_traps();
- breakpoint();
- }
-#endif
+void kgdb_mips_init(void)
+{
+ trap_init();
+ set_except_vector(0, mipsIRQ);
+ init_generic_irq();
+ init_i8259_irqs();
+ kgdb_early_setup = 1;
}
-
-
+#endif
diff -puN arch/mips/mips-boards/malta/malta_setup.c~mips-lite arch/mips/mips-boards/malta/malta_setup.c
--- linux-2.6.10-rc1/arch/mips/mips-boards/malta/malta_setup.c~mips-lite 2004-10-29 11:26:45.188278175 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/mips-boards/malta/malta_setup.c 2004-10-29 11:26:45.211272776 -0700
@@ -41,10 +41,6 @@ extern void mips_time_init(void);
extern void mips_timer_setup(struct irqaction *irq);
extern unsigned long mips_rtc_get_time(void);
-#ifdef CONFIG_KGDB
-extern void kgdb_config(void);
-#endif
-
struct resource standard_io_resources[] = {
{ "dma1", 0x00, 0x1f, IORESOURCE_BUSY },
{ "timer", 0x40, 0x5f, IORESOURCE_BUSY },
@@ -93,10 +89,6 @@ static int __init malta_setup(void)
*/
enable_dma(4);
-#ifdef CONFIG_KGDB
- kgdb_config ();
-#endif
-
if ((mips_revision_corid == MIPS_REVISION_CORID_BONITO64) ||
(mips_revision_corid == MIPS_REVISION_CORID_CORE_20K) ||
(mips_revision_corid == MIPS_REVISION_CORID_CORE_EMUL_BON)) {
diff -puN arch/mips/sibyte/cfe/setup.c~mips-lite arch/mips/sibyte/cfe/setup.c
--- linux-2.6.10-rc1/arch/mips/sibyte/cfe/setup.c~mips-lite 2004-10-29 11:26:45.190277705 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/cfe/setup.c 2004-10-29 11:26:45.212272541 -0700
@@ -62,10 +62,6 @@ extern void * __rd_start, * __rd_end;
static int reboot_smp = 0;
#endif
-#ifdef CONFIG_KGDB
-extern int kgdb_port;
-#endif
-
static void cfe_linux_exit(void)
{
#ifdef CONFIG_SMP
@@ -243,9 +239,6 @@ void __init prom_init(void)
int argc = fw_arg0;
char **envp = (char **) fw_arg2;
int *prom_vec = (int *) fw_arg3;
-#ifdef CONFIG_KGDB
- char *arg;
-#endif
_machine_restart = (void (*)(char *))cfe_linux_exit;
_machine_halt = cfe_linux_exit;
@@ -309,13 +302,6 @@ void __init prom_init(void)
}
}
-#ifdef CONFIG_KGDB
- if ((arg = strstr(arcs_cmdline,"kgdb=duart")) != NULL)
- kgdb_port = (arg[10] == '0') ? 0 : 1;
- else
- kgdb_port = 1;
-#endif
-
#ifdef CONFIG_BLK_DEV_INITRD
{
char *ptr;
diff -puN arch/mips/sibyte/sb1250/Makefile~mips-lite arch/mips/sibyte/sb1250/Makefile
--- linux-2.6.10-rc1/arch/mips/sibyte/sb1250/Makefile~mips-lite 2004-10-29 11:26:45.192277236 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/Makefile 2004-10-29 11:26:45.212272541 -0700
@@ -4,5 +4,6 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SIBYTE_TBPROF) += bcm1250_tbprof.o
obj-$(CONFIG_SIBYTE_STANDALONE) += prom.o
obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o
+obj-$(CONFIG_KGDB_SIBYTE) += kgdb_sibyte.o
EXTRA_AFLAGS := $(CFLAGS)
diff -puN arch/mips/sibyte/sb1250/irq.c~mips-lite arch/mips/sibyte/sb1250/irq.c
--- linux-2.6.10-rc1/arch/mips/sibyte/sb1250/irq.c~mips-lite 2004-10-29 11:26:45.194276766 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/irq.c 2004-10-29 11:26:45.212272541 -0700
@@ -59,27 +59,7 @@ static void sb1250_set_affinity(unsigned
#ifdef CONFIG_SIBYTE_HAS_LDT
extern unsigned long ldt_eoi_space;
#endif
-
-#ifdef CONFIG_KGDB
-#include <asm/gdb-stub.h>
-extern void breakpoint(void);
-static int kgdb_irq;
-
-/* kgdb is on when configured. Pass "nokgdb" kernel arg to turn it off */
-static int kgdb_flag = 1;
-static int __init nokgdb(char *str)
-{
- kgdb_flag = 0;
- return 1;
-}
-__setup("nokgdb", nokgdb);
-
-/* Default to UART1 */
-int kgdb_port = 1;
-#ifdef CONFIG_SIBYTE_SB1250_DUART
-extern char sb1250_duart_present[];
-#endif
-#endif
+extern int kgdb_early_setup;
static struct hw_interrupt_type sb1250_irq_type = {
"SB1250-IMR",
@@ -340,6 +320,11 @@ void __init init_IRQ(void)
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;
+#ifdef CONFIG_KGDB
+ if (kgdb_early_setup == 1)
+ return;
+#endif
+
/* Default everything to IP2 */
for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */
__raw_writeq(IMR_IP2_VAL,
@@ -389,49 +374,4 @@ void __init init_IRQ(void)
/* Enable necessary IPs, disable the rest */
change_c0_status(ST0_IM, imask);
set_except_vector(0, sb1250_irq_handler);
-
-#ifdef CONFIG_KGDB
- if (kgdb_flag) {
- kgdb_irq = K_INT_UART_0 + kgdb_port;
-
-#ifdef CONFIG_SIBYTE_SB1250_DUART
- sb1250_duart_present[kgdb_port] = 0;
-#endif
- /* Setup uart 1 settings, mapper */
- __raw_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port)));
-
- sb1250_steal_irq(kgdb_irq);
- __raw_writeq(IMR_IP6_VAL,
- IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
- (kgdb_irq<<3)));
- sb1250_unmask_irq(0, kgdb_irq);
-
- prom_printf("Waiting for GDB on UART port %d\n", kgdb_port);
- set_debug_traps();
- breakpoint();
- }
-#endif
-}
-
-#ifdef CONFIG_KGDB
-
-#include <linux/delay.h>
-
-#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-
-void sb1250_kgdb_interrupt(struct pt_regs *regs)
-{
- /*
- * Clear break-change status (allow some time for the remote
- * host to stop the break, since we would see another
- * interrupt on the end-of-break too)
- */
- kstat_this_cpu.irqs[kgdb_irq]++;
- mdelay(500);
- duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
- M_DUART_RX_EN | M_DUART_TX_EN);
- set_async_breakpoint(®s->cp0_epc);
}
-
-#endif /* CONFIG_KGDB */
diff -puN /dev/null arch/mips/sibyte/sb1250/kgdb_sibyte.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/sb1250/kgdb_sibyte.c 2004-10-29 11:26:45.213272306 -0700
@@ -0,0 +1,158 @@
+/*
+ * arch/mips/sibyte/sb1250/kgdb_sibyte.c
+ *
+ * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
+ *
+ * 2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+/*
+ * Support for KGDB on the Broadcom Sibyte. The SWARM board
+ * for example does not have a 8250/16550 compatible serial
+ * port. Hence, we need to have a driver for the serial
+ * ports to handle KGDB. This board needs nothing in addition
+ * to what is normally provided by the gdb portion of the stub.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/kgdb.h>
+
+#include <asm/io.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_uart.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/addrspace.h>
+
+int kgdb_port = 1;
+static int kgdb_irq;
+
+extern char sb1250_duart_present[];
+extern int sb1250_steal_irq(int irq);
+extern int kgdb_early_setup;
+
+/* Forward declarations. */
+static void kgdbsibyte_init_duart(void);
+
+#define IMR_IP6_VAL K_INT_MAP_I4
+#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+
+static void kgdb_swarm_write_char(int c)
+{
+ while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0);
+ duart_out(R_DUART_TX_HOLD, c);
+}
+
+static int kgdb_swarm_read_char(void)
+{
+ unsigned long flags;
+ int ret_char;
+ unsigned int status;
+
+ local_irq_save(flags);
+
+ status = duart_in(R_DUART_STATUS);
+ while ((status & M_DUART_RX_RDY) == 0) {
+ status = duart_in(R_DUART_STATUS);
+ }
+
+ /*
+ * Check for framing error
+ */
+ if (status & M_DUART_FRM_ERR) {
+ kgdbsibyte_init_duart();
+ kgdb_swarm_write_char('-');
+ return '-';
+ }
+
+ ret_char = duart_in(R_DUART_RX_HOLD);
+ local_irq_restore(flags);
+
+ return ret_char;
+}
+
+void sb1250_kgdb_interrupt(struct pt_regs *regs)
+{
+ int kgdb_irq = K_INT_UART_0 + kgdb_port;
+ /*
+ * Clear break-change status (allow some time for the remote
+ * host to stop the break, since we would see another
+ * interrupt on the end-of-break too)
+ */
+ kstat_this_cpu.irqs[kgdb_irq]++;
+ mdelay(500);
+ duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
+ M_DUART_RX_EN | M_DUART_TX_EN);
+ kgdb_schedule_breakpoint();
+}
+
+/*
+ * We use port #1 and we set it for 115200 BAUD, 8n1.
+ */
+static void
+kgdbsibyte_init_duart(void)
+{
+ /* Set 8n1. */
+ duart_out(R_DUART_MODE_REG_1,
+ V_DUART_BITS_PER_CHAR_8 | V_DUART_PARITY_MODE_NONE);
+ duart_out(R_DUART_MODE_REG_2, M_DUART_STOP_BIT_LEN_1);
+ /* Set baud rate of 115200. */
+ duart_out(R_DUART_CLK_SEL, V_DUART_BAUD_RATE(115200));
+ /* Enable rx and tx */
+ duart_out(R_DUART_CMD, M_DUART_RX_EN | M_DUART_TX_EN);
+}
+
+static int
+kgdb_init_io(void)
+{
+#ifdef CONFIG_SIBYTE_SB1250_DUART
+ sb1250_duart_present[kgdb_port] = 0;
+#endif
+
+ kgdbsibyte_init_duart();
+
+ return 0;
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = kgdb_swarm_read_char,
+ .write_char = kgdb_swarm_write_char,
+ .init = kgdb_init_io,
+ .late_init = kgdbsibyte_hookup_irq,
+};
+
+void kgdb_mips_init(void)
+{
+ if (kgdb_early_setup == 0) {
+ trap_init();
+ init_IRQ();
+ kgdb_early_setup = 1;
+ }
+}
+
+/*
+ * Hookup our IRQ line. We will already have been initialized a
+ * this point.
+ */
+static void __init kgdbsibyte_hookup_irq(void)
+{
+ /* Steal the IRQ. */
+ kgdb_irq = K_INT_UART_0 + kgdb_port;
+
+ /* Setup uart 1 settings, mapper */
+ __raw_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port)));
+
+ sb1250_steal_irq(kgdb_irq);
+
+ __raw_writeq(IMR_IP6_VAL,
+ IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+ (kgdb_irq<<3)));
+
+ sb1250_unmask_irq(0, kgdb_irq);
+}
diff -puN arch/mips/sibyte/swarm/Makefile~mips-lite arch/mips/sibyte/swarm/Makefile
--- linux-2.6.10-rc1/arch/mips/sibyte/swarm/Makefile~mips-lite 2004-10-29 11:26:45.196276297 -0700
+++ linux-2.6.10-rc1-trini/arch/mips/sibyte/swarm/Makefile 2004-10-29 11:26:45.213272306 -0700
@@ -1,3 +1 @@
lib-y = setup.o rtc_xicor1241.o rtc_m41t81.o
-
-lib-$(CONFIG_KGDB) += dbg_io.o
diff -L arch/mips/sibyte/swarm/dbg_io.c -puN arch/mips/sibyte/swarm/dbg_io.c~mips-lite /dev/null
--- linux-2.6.10-rc1/arch/mips/sibyte/swarm/dbg_io.c
+++ /dev/null 2004-10-25 00:35:20.587727328 -0700
@@ -1,76 +0,0 @@
-/*
- * kgdb debug routines for SiByte boards.
- *
- * Copyright (C) 2001 MontaVista Software Inc.
- * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-/* -------------------- BEGINNING OF CONFIG --------------------- */
-
-#include <linux/delay.h>
-#include <asm/io.h>
-#include <asm/sibyte/sb1250.h>
-#include <asm/sibyte/sb1250_regs.h>
-#include <asm/sibyte/sb1250_uart.h>
-#include <asm/sibyte/sb1250_int.h>
-#include <asm/addrspace.h>
-
-/*
- * We use the second serial port for kgdb traffic.
- * 115200, 8, N, 1.
- */
-
-#define BAUD_RATE 115200
-#define CLK_DIVISOR V_DUART_BAUD_RATE(BAUD_RATE)
-#define DATA_BITS V_DUART_BITS_PER_CHAR_8 /* or 7 */
-#define PARITY V_DUART_PARITY_MODE_NONE /* or even */
-#define STOP_BITS M_DUART_STOP_BIT_LEN_1 /* or 2 */
-
-static int duart_initialized = 0; /* 0: need to be init'ed by kgdb */
-
-/* -------------------- END OF CONFIG --------------------- */
-extern int kgdb_port;
-
-#define duart_out(reg, val) csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-#define duart_in(reg) csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
-
-void putDebugChar(unsigned char c);
-unsigned char getDebugChar(void);
-static void
-duart_init(int clk_divisor, int data, int parity, int stop)
-{
- duart_out(R_DUART_MODE_REG_1, data | parity);
- duart_out(R_DUART_MODE_REG_2, stop);
- duart_out(R_DUART_CLK_SEL, clk_divisor);
-
- duart_out(R_DUART_CMD, M_DUART_RX_EN | M_DUART_TX_EN); /* enable rx and tx */
-}
-
-void
-putDebugChar(unsigned char c)
-{
- if (!duart_initialized) {
- duart_initialized = 1;
- duart_init(CLK_DIVISOR, DATA_BITS, PARITY, STOP_BITS);
- }
- while ((duart_in(R_DUART_STATUS) & M_DUART_TX_RDY) == 0);
- duart_out(R_DUART_TX_HOLD, c);
-}
-
-unsigned char
-getDebugChar(void)
-{
- if (!duart_initialized) {
- duart_initialized = 1;
- duart_init(CLK_DIVISOR, DATA_BITS, PARITY, STOP_BITS);
- }
- while ((duart_in(R_DUART_STATUS) & M_DUART_RX_RDY) == 0) ;
- return duart_in(R_DUART_RX_HOLD);
-}
-
diff -puN /dev/null include/asm-mips/kdebug.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-mips/kdebug.h 2004-10-29 11:26:45.214272071 -0700
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#ifndef _MIPS_KDEBUG_H
+#define _MIPS_KDEBUG_H
+
+#include <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args {
+ struct pt_regs *regs;
+ const char *str;
+ long err;
+};
+
+int register_die_notifier(struct notifier_block *nb);
+extern struct notifier_block *mips_die_chain;
+
+enum die_val {
+ DIE_OOPS = 1,
+ DIE_PANIC,
+ DIE_DIE,
+ DIE_KERNELDEBUG,
+ DIE_TRAP,
+ DIE_PAGE_FAULT,
+};
+
+/*
+ * trap number can be computed from regs and signr can be computed using
+ * compute_signal()
+ */
+static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err)
+{
+ struct die_args args = { .regs=regs, .str=str, .err=err };
+ return notifier_call_chain(&mips_die_chain, val, &args);
+}
+
+#endif /* _MIPS_KDEBUG_H */
diff -puN /dev/null include/asm-mips/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-mips/kgdb.h 2004-10-29 11:26:45.214272071 -0700
@@ -0,0 +1,20 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+#include <linux/config.h>
+
+#define BUFMAX 2048
+#define NUMREGBYTES (90*sizeof(long))
+#define BREAK_INSTR_SIZE 4
+
+#define BREAKPOINT() __asm__ __volatile__ ( \
+ ".globl breakinst\n\t" \
+ ".set\tnoreorder\n\t" \
+ "nop\n" \
+ "breakinst:\tbreak\n\t" \
+ "nop\n\t" \
+ ".set\treorder")
+
+#define CHECK_EXCEPTION_STACK() 1
+
+#endif /* _ASM_KGDB_H_ */
diff -puN lib/Kconfig.debug~mips-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~mips-lite 2004-10-29 11:26:45.200275358 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:56.134154345 -0700
@@ -115,7 +115,7 @@ endif
config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86 || ((!SMP || BROKEN) && (PPC32)))
+ depends on DEBUG_KERNEL && (X86 || MIPS32 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
@@ -128,6 +128,7 @@ config KGDB
choice
prompt "Method for KGDB communication"
depends on KGDB
+ default KGDB_SIBYTE if SIBYTE_SB1xxx_SOC
default KGDB_8250
help
There are a number of different ways in which you can communicate
@@ -140,6 +141,10 @@ config KGDB_8250
Uses generic serial port (8250) for kgdb. This is independent of the
option 9250/16550 and compatible serial port.
+config KGDB_SIBYTE
+ bool "KGDB: On the Broadcom SWARM serial port"
+ depends on MIPS && SIBYTE_SB1xxx_SOC
+
endchoice
config KGDB_SIMPLE_SERIAL
@@ -153,7 +158,7 @@ config KGDB_SIMPLE_SERIAL
If in doubt, say Y.
choice
- depends on KGDB_8250
+ depends on KGDB_8250 || KGDB_SIBYTE
prompt "Debug serial port BAUD"
default KGDB_115200BAUD
help
_
^ permalink raw reply [flat|nested] 12+ messages in thread* [patch 6/8] KGDB support for ia64
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
` (3 preceding siblings ...)
2004-10-29 18:33 ` [patch 5/8] KGDB support for MIPS Tom Rini
@ 2004-10-29 18:34 ` Tom Rini
2004-10-29 18:34 ` [patch 7/8] KGDB support for x86_64 Tom Rini
` (3 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:34 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, trini, davidm, Robert.Picco, keldon
Cc: David Mosberger-Tang <davidm@hpl.hp.com>, Robert Picco <Robert.Picco@hp.com>, Keldon Jones <keldon@hp.com>
This adds support for KGDB to ia64 and was done by Robert Picco and Keldon
Jones.
---
linux-2.6.10-rc1-trini/arch/ia64/kernel/Makefile | 1
linux-2.6.10-rc1-trini/arch/ia64/kernel/irq.c | 4
linux-2.6.10-rc1-trini/arch/ia64/kernel/ivt.S | 17
linux-2.6.10-rc1-trini/arch/ia64/kernel/kgdb.c | 991 ++++++++++++++++++++++
linux-2.6.10-rc1-trini/arch/ia64/kernel/process.c | 6
linux-2.6.10-rc1-trini/arch/ia64/kernel/smp.c | 17
linux-2.6.10-rc1-trini/arch/ia64/kernel/traps.c | 26
linux-2.6.10-rc1-trini/arch/ia64/kernel/unwind.c | 61 +
linux-2.6.10-rc1-trini/arch/ia64/mm/fault.c | 3
linux-2.6.10-rc1-trini/drivers/firmware/pcdp.c | 19
linux-2.6.10-rc1-trini/include/asm-ia64/kgdb.h | 34
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
12 files changed, 1179 insertions(+), 2 deletions(-)
diff -puN arch/ia64/kernel/Makefile~ia64-lite arch/ia64/kernel/Makefile
--- linux-2.6.10-rc1/arch/ia64/kernel/Makefile~ia64-lite 2004-10-29 11:26:45.490207282 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/Makefile 2004-10-29 11:26:45.514201648 -0700
@@ -19,6 +19,7 @@ obj-$(CONFIG_PERFMON) += perfmon_defaul
obj-$(CONFIG_IA64_CYCLONE) += cyclone.o
obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o
mca_recovery-y += mca_drv.o mca_drv_asm.o
+obj-$(CONFIG_KGDB) += kgdb.o
# The gate DSO image is built using a special linker script.
targets += gate.so gate-syms.o
diff -puN arch/ia64/kernel/irq.c~ia64-lite arch/ia64/kernel/irq.c
--- linux-2.6.10-rc1/arch/ia64/kernel/irq.c~ia64-lite 2004-10-29 11:26:45.492206812 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/irq.c 2004-10-29 11:26:45.515201413 -0700
@@ -42,6 +42,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>
#include <linux/notifier.h>
#include <linux/bitops.h>
@@ -543,6 +544,9 @@ unsigned int do_IRQ(unsigned long irq, s
desc->handler->end(irq);
spin_unlock(&desc->lock);
}
+
+ kgdb_process_breakpoint();
+
return 1;
}
diff -puN arch/ia64/kernel/ivt.S~ia64-lite arch/ia64/kernel/ivt.S
--- linux-2.6.10-rc1/arch/ia64/kernel/ivt.S~ia64-lite 2004-10-29 11:26:45.494206343 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/ivt.S 2004-10-29 11:26:45.516201178 -0700
@@ -68,6 +68,13 @@
# define DBG_FAULT(i)
#endif
+#ifdef CONFIG_KGDB
+#define KGDB_ENABLE_PSR_DB mov r31=psr;; movl r30=IA64_PSR_DB;; or r31=r31,r30;; \
+ mov psr.l=r31;; srlz.i;;
+#else
+#define KGDB_ENABLE_PSR_DB
+#endif
+
#define MINSTATE_VIRT /* needed by minstate.h */
#include "minstate.h"
@@ -473,6 +480,7 @@ ENTRY(page_fault)
movl r14=ia64_leave_kernel
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
mov rp=r14
;;
adds out2=16,r12 // out2 = pointer to pt_regs
@@ -733,6 +741,8 @@ ENTRY(break_fault)
;;
srlz.i // guarantee that interruption collection is on
;;
+ KGDB_ENABLE_PSR_DB
+ ;;
(p15) ssm psr.i // restore psr.i
;;
mov r3=NR_syscalls - 1
@@ -776,6 +786,7 @@ ENTRY(interrupt)
srlz.i // ensure everybody knows psr.ic is back on
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
;;
alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group
mov out0=cr.ivr // pass cr.ivr as first arg
@@ -1003,6 +1014,7 @@ ENTRY(non_syscall)
movl r15=ia64_leave_kernel
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
mov rp=r15
;;
br.call.sptk.many b6=ia64_bad_break // avoid WAW on CFM and ignore return addr
@@ -1036,6 +1048,7 @@ ENTRY(dispatch_unaligned_handler)
adds r3=8,r2 // set up second base pointer
;;
SAVE_REST
+ KGDB_ENABLE_PSR_DB
movl r14=ia64_leave_kernel
;;
mov rp=r14
@@ -1078,6 +1091,10 @@ ENTRY(dispatch_to_fault_handler)
adds r3=8,r2 // set up second base pointer for SAVE_REST
;;
SAVE_REST
+ cmp.eq p6,p0=29,out0
+(p6) br.cond.spnt 1f;; // debug_vector
+ KGDB_ENABLE_PSR_DB
+1:
movl r14=ia64_leave_kernel
;;
mov rp=r14
diff -puN /dev/null arch/ia64/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/kgdb.c 2004-10-29 11:26:45.518200709 -0700
@@ -0,0 +1,991 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ */
+/*
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale<akale@veritas.com>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <dave@gcom.com>
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <asm/unwind.h>
+#include <asm/rse.h>
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+
+#define NUM_REGS 590
+#define REGISTER_BYTES (NUM_REGS*8+128*8)
+#define REGISTER_BYTE(N) (((N) * 8) \
+ + ((N) <= IA64_FR0_REGNUM ? 0 : 8 * (((N) > IA64_FR127_REGNUM) ? 128 : (N) - IA64_FR0_REGNUM)))
+#define REGISTER_SIZE(N) (((N) >= IA64_FR0_REGNUM && (N) <= IA64_FR127_REGNUM) ? 16 : 8)
+#define IA64_GR0_REGNUM 0
+#define IA64_FR0_REGNUM 128
+#define IA64_FR127_REGNUM (IA64_FR0_REGNUM+127)
+#define IA64_PR0_REGNUM 256
+#define IA64_BR0_REGNUM 320
+#define IA64_VFP_REGNUM 328
+#define IA64_PR_REGNUM 330
+#define IA64_IP_REGNUM 331
+#define IA64_PSR_REGNUM 332
+#define IA64_CFM_REGNUM 333
+#define IA64_AR0_REGNUM 334
+#define IA64_NAT0_REGNUM 462
+#define IA64_NAT31_REGNUM (IA64_NAT0_REGNUM+31)
+#define IA64_NAT32_REGNUM (IA64_NAT0_REGNUM+32)
+#define IA64_RSC_REGNUM (IA64_AR0_REGNUM+16)
+#define IA64_BSP_REGNUM (IA64_AR0_REGNUM+17)
+#define IA64_BSPSTORE_REGNUM (IA64_AR0_REGNUM+18)
+#define IA64_RNAT_REGNUM (IA64_AR0_REGNUM+19)
+#define IA64_FCR_REGNUM (IA64_AR0_REGNUM+21)
+#define IA64_EFLAG_REGNUM (IA64_AR0_REGNUM+24)
+#define IA64_CSD_REGNUM (IA64_AR0_REGNUM+25)
+#define IA64_SSD_REGNUM (IA64_AR0_REGNUM+26)
+#define IA64_CFLG_REGNUM (IA64_AR0_REGNUM+27)
+#define IA64_FSR_REGNUM (IA64_AR0_REGNUM+28)
+#define IA64_FIR_REGNUM (IA64_AR0_REGNUM+29)
+#define IA64_FDR_REGNUM (IA64_AR0_REGNUM+30)
+#define IA64_CCV_REGNUM (IA64_AR0_REGNUM+32)
+#define IA64_UNAT_REGNUM (IA64_AR0_REGNUM+36)
+#define IA64_FPSR_REGNUM (IA64_AR0_REGNUM+40)
+#define IA64_ITC_REGNUM (IA64_AR0_REGNUM+44)
+#define IA64_PFS_REGNUM (IA64_AR0_REGNUM+64)
+#define IA64_LC_REGNUM (IA64_AR0_REGNUM+65)
+#define IA64_EC_REGNUM (IA64_AR0_REGNUM+66)
+
+#define REGISTER_INDEX(N) (REGISTER_BYTE(N) / sizeof (unsigned long))
+
+#define ptoff(V) ((unsigned int) &((struct pt_regs *)0x0)->V)
+struct reg_to_ptreg_index {
+ unsigned int reg;
+ unsigned int ptregoff;
+};
+struct reg_to_ptreg_index gr_reg_to_ptreg_index[] = {
+ {IA64_GR0_REGNUM+8, ptoff(r8)},
+ {IA64_GR0_REGNUM+9, ptoff(r9)},
+ {IA64_GR0_REGNUM+10, ptoff(r10)},
+ {IA64_GR0_REGNUM+11, ptoff(r11)},
+ {IA64_GR0_REGNUM+1, ptoff(r1)},
+ {IA64_GR0_REGNUM+12, ptoff(r12)},
+ {IA64_GR0_REGNUM+13, ptoff(r13)},
+ {IA64_GR0_REGNUM+14, ptoff(r14)},
+ {IA64_GR0_REGNUM+15, ptoff(r15)},
+};
+
+struct reg_to_ptreg_index br_reg_to_ptreg_index[] = {
+ {IA64_BR0_REGNUM, ptoff(b0)},
+ {IA64_BR0_REGNUM+6, ptoff(b6)},
+ {IA64_BR0_REGNUM+7, ptoff(b7)},
+};
+
+extern atomic_t cpu_doing_single_step;
+
+void kgdb_get_reg(char *outbuffer, int regnum, struct unw_frame_info *info, struct pt_regs *ptregs)
+{
+ unsigned long reg, size = 0, *mem = ®
+ char nat;
+ struct ia64_fpreg freg;
+ int i;
+
+ if ((regnum >= IA64_GR0_REGNUM && regnum <= (IA64_GR0_REGNUM+1)) ||
+ (regnum >= (IA64_GR0_REGNUM+4) && regnum <= (IA64_GR0_REGNUM+7)) ||
+ (regnum >= (IA64_GR0_REGNUM+16) && regnum <= (IA64_GR0_REGNUM+31))) {
+ unw_access_gr(info, regnum - IA64_GR0_REGNUM, ®, &nat, 0);
+ size = sizeof(reg);
+ }
+ else if ((regnum >= (IA64_GR0_REGNUM+2) && regnum <= (IA64_GR0_REGNUM+3)) ||
+ (regnum >= (IA64_GR0_REGNUM+8) && regnum <= (IA64_GR0_REGNUM+15))) {
+ if (ptregs) {
+ for (i = 0; i < (sizeof (gr_reg_to_ptreg_index) /
+ sizeof(gr_reg_to_ptreg_index[0])); i++)
+ if (gr_reg_to_ptreg_index[0].reg == regnum) {
+ reg = * ((unsigned long *)
+ (((void *) ptregs) + gr_reg_to_ptreg_index[i].ptregoff));
+ break;
+ }
+ } else
+ unw_access_gr(info, regnum - IA64_GR0_REGNUM, ®, &nat, 0);
+ size = sizeof(reg);
+ }
+ else if (regnum >= IA64_BR0_REGNUM && regnum <= (IA64_BR0_REGNUM+7)) switch(regnum) {
+ case IA64_BR0_REGNUM:
+ case IA64_BR0_REGNUM+6:
+ case IA64_BR0_REGNUM+7:
+ if (ptregs) {
+ for (i = 0; i < (sizeof(br_reg_to_ptreg_index) /
+ sizeof(br_reg_to_ptreg_index[0])); i++)
+ if (br_reg_to_ptreg_index[i].reg == regnum) {
+ reg = * ((unsigned long *)
+ (((void *) ptregs) + br_reg_to_ptreg_index[i].ptregoff));
+ break;
+ }
+ } else
+ unw_access_br(info, regnum - IA64_BR0_REGNUM, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_BR0_REGNUM+1:
+ case IA64_BR0_REGNUM+2:
+ case IA64_BR0_REGNUM+3:
+ case IA64_BR0_REGNUM+4:
+ case IA64_BR0_REGNUM+5:
+ unw_access_br(info, regnum - IA64_BR0_REGNUM, ®, 0);
+ size = sizeof(reg);
+ break;
+ }
+ else if (regnum >= IA64_FR0_REGNUM && regnum <= (IA64_FR0_REGNUM + 127)) switch (regnum) {
+ case IA64_FR0_REGNUM+6:
+ case IA64_FR0_REGNUM+7:
+ case IA64_FR0_REGNUM+8:
+ case IA64_FR0_REGNUM+9:
+ case IA64_FR0_REGNUM+10:
+ case IA64_FR0_REGNUM+11:
+ case IA64_FR0_REGNUM+12:
+ if (!ptregs)
+ unw_access_fr(info, regnum - IA64_FR0_REGNUM, &freg, 0);
+ else {
+ freg = *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM+6)));
+ }
+ size = sizeof(freg);
+ mem = (unsigned long *) &freg;
+ break;
+ default:
+ unw_access_fr(info, regnum - IA64_FR0_REGNUM, &freg, 0);
+ break;
+ }
+ else if (regnum == IA64_IP_REGNUM) {
+ if (!ptregs)
+ unw_get_ip(info, ®);
+ else
+ reg = ptregs->cr_iip;
+ size = sizeof(reg);
+ }
+ else if (regnum == IA64_CFM_REGNUM) {
+ if (!ptregs)
+ unw_get_cfm(info, ®);
+ else
+ reg = ptregs->cr_ifs;
+ size = sizeof(reg);
+ }
+ else if (regnum == IA64_PSR_REGNUM) {
+ if (!ptregs && kgdb_usethread)
+ ptregs = (struct pt_regs *)
+ ((unsigned long) kgdb_usethread + IA64_STK_OFFSET) - 1;
+ if (ptregs)
+ reg = ptregs->cr_ipsr;
+ size = sizeof(reg);
+ }
+ else if (regnum == IA64_PR_REGNUM) {
+ if (ptregs)
+ reg = ptregs->pr;
+ else
+ unw_access_pr(info, ®, 0);
+ size = sizeof(reg);
+ }
+ else if (regnum == IA64_BSP_REGNUM) {
+ unw_get_bsp(info, ®);
+ size = sizeof(reg);
+ }
+ else if (regnum >= IA64_AR0_REGNUM && regnum <= IA64_EC_REGNUM) switch (regnum) {
+ case IA64_CSD_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_csd;
+ else
+ unw_access_ar(info, UNW_AR_CSD, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_SSD_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_ssd;
+ else
+ unw_access_ar(info, UNW_AR_SSD, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_UNAT_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_unat;
+ else
+ unw_access_ar(info, UNW_AR_UNAT, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_RNAT_REGNUM:
+ unw_access_ar(info, UNW_AR_RNAT, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_BSPSTORE_REGNUM:
+ unw_access_ar(info, UNW_AR_BSPSTORE, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_PFS_REGNUM:
+ unw_access_ar(info, UNW_AR_PFS, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_LC_REGNUM:
+ unw_access_ar(info, UNW_AR_LC, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_EC_REGNUM:
+ unw_access_ar(info, UNW_AR_EC, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_FPSR_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_fpsr;
+ else
+ unw_access_ar(info, UNW_AR_FPSR, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_RSC_REGNUM:
+ if (ptregs)
+ reg = ptregs->ar_rsc;
+ else
+ unw_access_ar(info, UNW_AR_RNAT, ®, 0);
+ size = sizeof(reg);
+ break;
+ case IA64_CCV_REGNUM:
+ unw_access_ar(info, UNW_AR_CCV, ®, 0);
+ size = sizeof(reg);
+ break;
+ }
+
+ if (size) {
+ kgdb_mem2hex((char *) mem, outbuffer, size);
+ outbuffer[size*2] = 0;
+ }
+ else
+ strcpy(outbuffer, "E0");
+
+ return;
+}
+
+void kgdb_put_reg(char *inbuffer, char *outbuffer, int regnum,
+ struct unw_frame_info *info, struct pt_regs *ptregs)
+{
+ unsigned long reg;
+ char nat = 0;
+ struct ia64_fpreg freg;
+ int i;
+ char *ptr;
+
+ ptr = inbuffer;
+ kgdb_hex2long(&ptr, ®);
+ strcpy(outbuffer, "OK");
+
+ if ((regnum >= IA64_GR0_REGNUM && regnum <= (IA64_GR0_REGNUM+1)) ||
+ (regnum >= (IA64_GR0_REGNUM+4) && regnum <= (IA64_GR0_REGNUM+7)) ||
+ (regnum >= (IA64_GR0_REGNUM+16) && regnum <= (IA64_GR0_REGNUM+31))) {
+ unw_access_gr(info, regnum - IA64_GR0_REGNUM, ®, &nat, 1);
+ }
+ else if ((regnum >= (IA64_GR0_REGNUM+2) && regnum <= (IA64_GR0_REGNUM+3)) ||
+ (regnum >= (IA64_GR0_REGNUM+8) && regnum <= (IA64_GR0_REGNUM+15))) {
+ for (i = 0; i < (sizeof (gr_reg_to_ptreg_index) /
+ sizeof(gr_reg_to_ptreg_index[0])); i++)
+ if (gr_reg_to_ptreg_index[0].reg == regnum) {
+ * ((unsigned long *)
+ (((void *) ptregs) + gr_reg_to_ptreg_index[i].ptregoff)) = reg;
+ break;
+ }
+ }
+ else if (regnum >= IA64_BR0_REGNUM && regnum <= (IA64_BR0_REGNUM+7)) switch(regnum) {
+ case IA64_BR0_REGNUM:
+ case IA64_BR0_REGNUM+6:
+ case IA64_BR0_REGNUM+7:
+ for (i = 0; i < (sizeof(br_reg_to_ptreg_index) /
+ sizeof(br_reg_to_ptreg_index[0])); i++)
+ if (br_reg_to_ptreg_index[i].reg == regnum) {
+ * ((unsigned long *)
+ (((void *) ptregs) + br_reg_to_ptreg_index[i].ptregoff)) = reg;
+ break;
+ }
+ break;
+ case IA64_BR0_REGNUM+1:
+ case IA64_BR0_REGNUM+2:
+ case IA64_BR0_REGNUM+3:
+ case IA64_BR0_REGNUM+4:
+ case IA64_BR0_REGNUM+5:
+ unw_access_br(info, regnum - IA64_BR0_REGNUM, ®, 1);
+ break;
+ }
+ else if (regnum >= IA64_FR0_REGNUM && regnum <= (IA64_FR0_REGNUM + 127)) switch (regnum) {
+ case IA64_FR0_REGNUM+6:
+ case IA64_FR0_REGNUM+7:
+ case IA64_FR0_REGNUM+8:
+ case IA64_FR0_REGNUM+9:
+ case IA64_FR0_REGNUM+10:
+ case IA64_FR0_REGNUM+11:
+ case IA64_FR0_REGNUM+12:
+ freg.u.bits[0] = reg;
+ kgdb_hex2long(&ptr, &freg.u.bits[1]);
+ *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM+6))) = freg;
+ break;
+ default:
+ break;
+ }
+ else if (regnum == IA64_IP_REGNUM)
+ ptregs->cr_iip = reg;
+ else if (regnum == IA64_CFM_REGNUM)
+ ptregs->cr_ifs = reg;
+ else if (regnum == IA64_PSR_REGNUM)
+ ptregs->cr_ipsr = reg;
+ else if (regnum == IA64_PR_REGNUM)
+ ptregs->pr = reg;
+ else if (regnum >= IA64_AR0_REGNUM && regnum <= IA64_EC_REGNUM) switch (regnum) {
+ case IA64_CSD_REGNUM:
+ ptregs->ar_csd = reg;
+ break;
+ case IA64_SSD_REGNUM:
+ ptregs->ar_ssd = reg;
+ case IA64_UNAT_REGNUM:
+ ptregs->ar_unat = reg;
+ case IA64_RNAT_REGNUM:
+ unw_access_ar(info, UNW_AR_RNAT, ®, 1);
+ break;
+ case IA64_BSPSTORE_REGNUM:
+ unw_access_ar(info, UNW_AR_BSPSTORE, ®, 1);
+ break;
+ case IA64_PFS_REGNUM:
+ unw_access_ar(info, UNW_AR_PFS, ®, 1);
+ break;
+ case IA64_LC_REGNUM:
+ unw_access_ar(info, UNW_AR_LC, ®, 1);
+ break;
+ case IA64_EC_REGNUM:
+ unw_access_ar(info, UNW_AR_EC, ®, 1);
+ break;
+ case IA64_FPSR_REGNUM:
+ ptregs->ar_fpsr = reg;
+ break;
+ case IA64_RSC_REGNUM:
+ ptregs->ar_rsc = reg;
+ break;
+ case IA64_CCV_REGNUM:
+ unw_access_ar(info, UNW_AR_CCV, ®, 1);
+ break;
+ }
+ else
+ strcpy(outbuffer, "E01");
+
+ return;
+}
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+
+}
+
+#define MAX_HW_BREAKPOINT (20)
+long hw_break_total_dbr, hw_break_total_ibr;
+#define HW_BREAKPOINT (hw_break_total_dbr + hw_break_total_ibr)
+#define WATCH_INSTRUCTION 0x0
+#define WATCH_WRITE 0x1
+#define WATCH_READ 0x2
+#define WATCH_ACCESS 0x3
+
+#define HWCAP_DBR ((1 << WATCH_WRITE) | (1 << WATCH_READ))
+#define HWCAP_IBR (1 << WATCH_INSTRUCTION)
+struct hw_breakpoint {
+ unsigned enabled;
+ unsigned long capable;
+ unsigned long type;
+ unsigned long mask;
+ unsigned long addr;
+} *breakinfo;
+
+static struct hw_breakpoint hwbreaks[MAX_HW_BREAKPOINT];
+
+enum instruction_type {A, I, M, F, B, L, X, u};
+
+static enum instruction_type bundle_encoding[32][3] = {
+ { M, I, I }, /* 00 */
+ { M, I, I }, /* 01 */
+ { M, I, I }, /* 02 */
+ { M, I, I }, /* 03 */
+ { M, L, X }, /* 04 */
+ { M, L, X }, /* 05 */
+ { u, u, u }, /* 06 */
+ { u, u, u }, /* 07 */
+ { M, M, I }, /* 08 */
+ { M, M, I }, /* 09 */
+ { M, M, I }, /* 0A */
+ { M, M, I }, /* 0B */
+ { M, F, I }, /* 0C */
+ { M, F, I }, /* 0D */
+ { M, M, F }, /* 0E */
+ { M, M, F }, /* 0F */
+ { M, I, B }, /* 10 */
+ { M, I, B }, /* 11 */
+ { M, B, B }, /* 12 */
+ { M, B, B }, /* 13 */
+ { u, u, u }, /* 14 */
+ { u, u, u }, /* 15 */
+ { B, B, B }, /* 16 */
+ { B, B, B }, /* 17 */
+ { M, M, B }, /* 18 */
+ { M, M, B }, /* 19 */
+ { u, u, u }, /* 1A */
+ { u, u, u }, /* 1B */
+ { M, F, B }, /* 1C */
+ { M, F, B }, /* 1D */
+ { u, u, u }, /* 1E */
+ { u, u, u }, /* 1F */
+};
+
+static int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
+{
+ extern unsigned long _start[];
+ unsigned long slot = addr & 0xf, bundle_addr;
+ unsigned long template;
+ struct bundle {
+ struct {
+ unsigned long long template : 5;
+ unsigned long long slot0 : 41;
+ unsigned long long slot1_p0 : 64-46;
+ } quad0;
+ struct {
+ unsigned long long slot1_p1 : 41 - (64-46);
+ unsigned long long slot2 : 41;
+ } quad1;
+ } bundle;
+ int ret;
+
+ bundle_addr = addr & ~0xFULL;
+
+ if (bundle_addr == (unsigned long) _start)
+ return 0;
+
+ ret = kgdb_get_mem((char *) bundle_addr, (char *) &bundle, BREAK_INSTR_SIZE);
+ if (ret < 0)
+ return ret;
+
+ if (slot > 2)
+ slot = 0;
+
+ memcpy(saved_instr, &bundle, BREAK_INSTR_SIZE);
+ template = bundle.quad0.template;
+
+ if (slot == 1 && bundle_encoding[template][1] == L)
+ slot = 2;
+
+ switch (slot) {
+ case 0:
+ bundle.quad0.slot0 = BREAKNUM;
+ break;
+ case 1:
+ bundle.quad0.slot1_p0 = BREAKNUM;
+ bundle.quad1.slot1_p1 = (BREAKNUM >> (64-46));
+ break;
+ case 2:
+ bundle.quad1.slot2 = BREAKNUM;
+ break;
+ }
+
+ return kgdb_set_mem((char *) bundle_addr, (char *) &bundle, BREAK_INSTR_SIZE);
+}
+
+static int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
+{
+ extern unsigned long _start[];
+
+ addr = addr & ~0xFULL;
+ if (addr == (unsigned long) _start)
+ return 0;
+ return kgdb_set_mem((char *) addr, (char *) bundle, BREAK_INSTR_SIZE);
+}
+
+static int hw_breakpoint_init;
+
+void do_init_hw_break(void)
+{
+ s64 status;
+ int i;
+
+ hw_breakpoint_init = 1;
+
+#ifdef CONFIG_IA64_HP_SIM
+ hw_break_total_ibr = 8;
+ hw_break_total_dbr = 8;
+ status = 0;
+#else
+ status = ia64_pal_debug_info(&hw_break_total_ibr, &hw_break_total_dbr);
+#endif
+
+ if (status) {
+ printk(KERN_INFO "do_init_hw_break: pal call failed %d\n", (int) status);
+ return;
+ }
+
+ if (HW_BREAKPOINT > MAX_HW_BREAKPOINT) {
+ printk(KERN_INFO "do_init_hw_break: %d exceeds max %d\n", (int) HW_BREAKPOINT,
+ (int) MAX_HW_BREAKPOINT);
+
+ while ((HW_BREAKPOINT > MAX_HW_BREAKPOINT) && hw_break_total_ibr != 1)
+ hw_break_total_ibr--;
+ while (HW_BREAKPOINT > MAX_HW_BREAKPOINT)
+ hw_break_total_dbr--;
+ }
+
+ breakinfo = hwbreaks;
+
+ memset(breakinfo, 0, HW_BREAKPOINT * sizeof(struct hw_breakpoint));
+
+ for (i = 0; i < hw_break_total_dbr; i++)
+ breakinfo[i].capable = HWCAP_DBR;
+
+ for (; i < HW_BREAKPOINT; i++)
+ breakinfo[i].capable = HWCAP_IBR;
+
+ return;
+}
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+
+ if (!breakinfo)
+ return;
+
+ for (breakno = 0; breakno < HW_BREAKPOINT; breakno++) {
+ if (breakinfo[breakno].enabled) {
+ if (breakinfo[breakno].capable & HWCAP_IBR) {
+ int ibreakno = breakno - hw_break_total_dbr;
+ ia64_set_ibr(ibreakno << 1, breakinfo[breakno].addr);
+ ia64_set_ibr((ibreakno << 1) + 1,
+ (~breakinfo[breakno].mask & ((1UL << 56UL) - 1)) |
+ (1UL << 56UL) | (1UL << 63UL));
+ }
+ else {
+ ia64_set_dbr(breakno << 1, breakinfo[breakno].addr);
+ ia64_set_dbr((breakno << 1) + 1,
+ (~breakinfo[breakno].mask & ((1UL << 56UL) - 1)) |
+ (1UL << 56UL) | (breakinfo[breakno].type << 62UL));
+ }
+ }
+ else {
+ if (breakinfo[breakno].capable & HWCAP_IBR)
+ ia64_set_ibr(((breakno - hw_break_total_dbr) << 1) + 1, 0);
+ else
+ ia64_set_dbr((breakno << 1) + 1, 0);
+ }
+ }
+
+ return;
+}
+
+int hardware_breakpoint(unsigned long addr, int length, int type, int action)
+{
+ int breakno, found, watch;
+ unsigned long mask;
+ extern unsigned long _start[];
+
+ if (!hw_breakpoint_init)
+ do_init_hw_break();
+
+ if (!breakinfo)
+ return 0;
+ else if (addr == (unsigned long) _start)
+ return 1;
+
+ if (type == WATCH_ACCESS)
+ mask = HWCAP_DBR;
+ else
+ mask = 1UL << type;
+
+ for (watch = 0, found = 0, breakno = 0; breakno < HW_BREAKPOINT; breakno++) {
+ if (action) {
+ if (breakinfo[breakno].enabled || !(breakinfo[breakno].capable & mask))
+ continue;
+ breakinfo[breakno].enabled = 1;
+ breakinfo[breakno].type = type;
+ breakinfo[breakno].mask = length - 1;
+ breakinfo[breakno].addr = addr;
+ watch = breakno;
+ } else if (breakinfo[breakno].enabled &&
+ ((length < 0 && breakinfo[breakno].addr == addr) ||
+ ((breakinfo[breakno].capable & mask) &&
+ (breakinfo[breakno].mask == (length - 1)) &&
+ (breakinfo[breakno].addr == addr)))) {
+ breakinfo[breakno].enabled = 0;
+ breakinfo[breakno].type = 0UL;
+ }
+ else
+ continue;
+ found++;
+ if (type != WATCH_ACCESS)
+ break;
+ else if (found == 2)
+ break;
+ else
+ mask = HWCAP_IBR;
+ }
+
+ if (type == WATCH_ACCESS && found == 1) {
+ breakinfo[watch].enabled = 0;
+ found = 0;
+ }
+
+ return found;
+}
+
+int kgdb_arch_set_hw_breakpoint(unsigned long addr, int len, enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 1);
+}
+
+int kgdb_arch_remove_hw_breakpoint(unsigned long addr, int len, enum kgdb_bptype type)
+{
+ return hardware_breakpoint(addr, len, type - '1', 0);
+}
+
+int kgdb_remove_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 0);
+
+}
+
+void kgdb_remove_all_hw_break(void)
+{
+ int i;
+
+ for (i = 0; i < HW_BREAKPOINT; i++)
+ memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+}
+
+int kgdb_set_hw_break(unsigned long addr)
+{
+ return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 1);
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status;
+
+ hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status ^ IA64_PSR_DB);
+}
+
+volatile static struct smp_unw {
+ struct unw_frame_info *unw;
+ struct task_struct *task;
+} smp_unw[NR_CPUS];
+
+static int inline kgdb_get_blocked_state(struct task_struct *p, struct unw_frame_info *unw)
+{
+ unsigned long ip;
+ int count = 0;
+
+ unw_init_from_blocked_task(unw, p);
+ ip = 0UL;
+ do {
+ if (unw_unwind(unw) < 0)
+ return -1;
+ unw_get_ip(unw, &ip);
+ if (!in_sched_functions(ip))
+ break;
+ } while (count++ < 16);
+
+ if (!ip)
+ return -1;
+ else
+ return 0;
+}
+
+static void inline kgdb_wait(struct pt_regs *regs)
+{
+ unsigned long hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status ^ IA64_PSR_DB);
+ kgdb_nmihook(smp_processor_id(), regs);
+ if (hw_breakpoint_status & IA64_PSR_DB)
+ ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status);
+ return;
+}
+
+static void inline normalize(struct unw_frame_info *running, struct pt_regs *regs)
+{
+ unsigned long sp;
+
+ do {
+ unw_get_sp(running, &sp);
+ if ((sp + 0x10) >= (unsigned long) regs)
+ break;
+ } while (unw_unwind(running) >= 0);
+
+ return;
+}
+
+static void kgdb_init_running(struct unw_frame_info *unw, void *data)
+{
+ struct pt_regs *regs;
+
+ regs = data;
+ normalize(unw, regs);
+ smp_unw[smp_processor_id()].unw = unw;
+ kgdb_wait(regs);
+}
+
+void kgdb_wait_ipi(struct pt_regs *regs)
+{
+ struct unw_frame_info unw;
+
+ smp_unw[smp_processor_id()].task = current;
+
+ if (user_mode(regs)) {
+ smp_unw[smp_processor_id()].unw = (struct unw_frame_info *) 1;
+ kgdb_wait(regs);
+ }
+ else {
+ if (current->state == TASK_RUNNING)
+ unw_init_running(kgdb_init_running, regs);
+ else {
+ if (kgdb_get_blocked_state(current, &unw))
+ smp_unw[smp_processor_id()].unw = (struct unw_frame_info *) 1;
+ else
+ smp_unw[smp_processor_id()].unw = &unw;
+ kgdb_wait(regs);
+ }
+ }
+
+ smp_unw[smp_processor_id()].unw = NULL;
+ return;
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+{
+ if (num_online_cpus() > 1)
+ smp_send_nmi_allbutself();
+}
+
+static void do_kgdb_handle_exception(struct unw_frame_info *, void *data);
+
+struct kgdb_state {
+ int exceptionVector;
+ int signo;
+ unsigned long err_code;
+ struct pt_regs *regs;
+ struct unw_frame_info *unw;
+ char *inbuf;
+ char *outbuf;
+ int unwind;
+ int ret;
+};
+
+static void inline kgdb_pc(struct pt_regs *regs, unsigned long pc)
+{
+ regs->cr_iip = pc & ~0xf;
+ ia64_psr(regs)->ri = pc & 0x3;
+ return;
+}
+
+volatile int kgdb_hwbreak_sstep[NR_CPUS];
+
+int kgdb_arch_handle_exception(int exceptionVector, int signo,
+ int err_code, char *remcom_in_buffer,
+ char *remcom_out_buffer, struct pt_regs *linux_regs)
+{
+ struct kgdb_state info;
+
+ info.exceptionVector = exceptionVector;
+ info.signo = signo;
+ info.err_code = err_code;
+ info.unw = (void *) 0;
+ info.inbuf = remcom_in_buffer;
+ info.outbuf = remcom_out_buffer;
+ info.unwind = 0;
+ info.ret = -1;
+
+ if (remcom_in_buffer[0] == 'c' || remcom_in_buffer[0] == 's') {
+ info.regs = linux_regs;
+ do_kgdb_handle_exception(NULL, &info);
+ }
+ else if (kgdb_usethread == current) {
+ info.unwind = 1;
+ info.regs = linux_regs;
+ unw_init_running(do_kgdb_handle_exception, &info);
+ } else if (kgdb_usethread->state != TASK_RUNNING) {
+ struct unw_frame_info unw_info;
+
+ if (kgdb_get_blocked_state(kgdb_usethread, &unw_info)) {
+ info.ret = 1;
+ goto bad;
+ }
+ info.regs = NULL;
+ do_kgdb_handle_exception(&unw_info, &info);
+ }
+ else {
+ int i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (smp_unw[i].task == kgdb_usethread && smp_unw[i].unw &&
+ smp_unw[i].unw != (struct unw_frame_info *) 1) {
+ info.regs = NULL;
+ do_kgdb_handle_exception(smp_unw[i].unw, &info);
+ break;
+ }
+ else {
+ info.ret = 1;
+ goto bad;
+ }
+ }
+
+bad:
+ if (info.ret != -1 && remcom_in_buffer[0] == 'p') {
+ unsigned long bad = 0xbad4badbadbadbadUL;
+
+ printk("kgdb_arch_handle_exception: p packet bad\n");
+ kgdb_mem2hex((char *) &bad, remcom_out_buffer, sizeof (bad));
+ remcom_out_buffer[sizeof (bad) * 2] = 0;
+ info.ret = -1;
+ }
+ return info.ret;
+}
+
+
+
+static void do_kgdb_handle_exception(struct unw_frame_info *unw_info, void *data)
+{
+ long addr;
+ char *ptr;
+ unsigned long newPC;
+ int exceptionVector, signo;
+ unsigned long err_code;
+ struct pt_regs *linux_regs;
+ struct kgdb_state *info;
+ char *remcom_in_buffer, *remcom_out_buffer;
+
+ info = data;
+ info->unw = unw_info;
+ exceptionVector = info->exceptionVector;
+ signo = info->signo;
+ err_code = info->err_code;
+ remcom_in_buffer = info->inbuf;
+ remcom_out_buffer = info->outbuf;
+ linux_regs = info->regs;
+
+ if (info->unwind)
+ normalize(unw_info, linux_regs);
+
+ switch (remcom_in_buffer[0]) {
+ case 'p':
+ {
+ int regnum;
+
+ kgdb_hex2mem(&remcom_in_buffer[1], (char *) ®num, sizeof(regnum));
+ if (regnum >= NUM_REGS) {
+ remcom_out_buffer[0] = 'E';
+ remcom_out_buffer[1] = 0;
+ }
+ else
+ kgdb_get_reg(remcom_out_buffer, regnum, unw_info, linux_regs);
+ break;
+ }
+ case 'P':
+ {
+ int regno;
+ long v;
+ char *ptr;
+
+ ptr = &remcom_in_buffer[1];
+ if ((!kgdb_usethread || kgdb_usethread == current) &&
+ kgdb_hex2long(&ptr, &v) &&
+ *ptr++ == '=' && (v >= 0)) {
+ regno = (int) v;
+ regno = (regno >= NUM_REGS ? 0 : regno);
+ kgdb_put_reg(ptr, remcom_out_buffer, regno,
+ unw_info, linux_regs);
+ } else
+ strcpy(remcom_out_buffer, "E01");
+ break;
+ }
+ case 'c':
+ case 's':
+ if (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(remcom_out_buffer, "E00");
+ break;
+ }
+
+ if (exceptionVector == 11 && err_code == KGDBBREAKNUM) {
+ if (ia64_psr(linux_regs)->ri < 2)
+ kgdb_pc(linux_regs, linux_regs->cr_iip +
+ ia64_psr(linux_regs)->ri + 1);
+ else
+ kgdb_pc(linux_regs, linux_regs->cr_iip + 16);
+ }
+
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr)) {
+ linux_regs->cr_iip = addr;
+ }
+ newPC = linux_regs->cr_iip;
+
+ /* clear the trace bit */
+ linux_regs->cr_ipsr &= ~IA64_PSR_SS;
+
+ atomic_set(&cpu_doing_single_step,-1);
+
+ /* set the trace bit if we're stepping or took a hardware break*/
+ if (remcom_in_buffer[0] == 's' || exceptionVector == 29) {
+ linux_regs->cr_ipsr |= IA64_PSR_SS;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,smp_processor_id());
+ }
+
+ kgdb_correct_hw_break();
+
+ /* if not hardware breakpoint, then reenable them */
+ if (exceptionVector != 29)
+ linux_regs->cr_ipsr |= IA64_PSR_DB;
+ else {
+ kgdb_hwbreak_sstep[smp_processor_id()] = 1;
+ linux_regs->cr_ipsr &= ~IA64_PSR_DB;
+ }
+
+ info->ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .set_breakpoint = kgdb_arch_set_breakpoint,
+ .remove_breakpoint = kgdb_arch_remove_breakpoint,
+ .set_hw_breakpoint = kgdb_arch_set_hw_breakpoint,
+ .remove_hw_breakpoint = kgdb_arch_remove_hw_breakpoint,
+ .gdb_bpt_instr = {0xcc},
+ .flags = KGDB_HW_BREAKPOINT,
+};
diff -puN arch/ia64/kernel/process.c~ia64-lite arch/ia64/kernel/process.c
--- linux-2.6.10-rc1/arch/ia64/kernel/process.c~ia64-lite 2004-10-29 11:26:45.496205873 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/process.c 2004-10-29 11:26:45.518200709 -0700
@@ -415,6 +415,9 @@ copy_thread (int nr, unsigned long clone
*/
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
+#ifdef CONFIG_KGDB
+ child_ptregs->cr_ipsr |= IA64_PSR_DB;
+#endif
/*
* NOTE: The calling convention considers all floating point
@@ -643,6 +646,9 @@ kernel_thread (int (*fn)(void *), void *
regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
/* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
+#ifdef CONFIG_KGDB
+ regs.pt.cr_ipsr |= IA64_PSR_DB;
+#endif
regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
diff -puN arch/ia64/kernel/smp.c~ia64-lite arch/ia64/kernel/smp.c
--- linux-2.6.10-rc1/arch/ia64/kernel/smp.c~ia64-lite 2004-10-29 11:26:45.498205404 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/smp.c 2004-10-29 11:26:45.519200474 -0700
@@ -47,6 +47,7 @@
#include <asm/tlbflush.h>
#include <asm/unistd.h>
#include <asm/mca.h>
+#include <linux/kgdb.h>
/*
* Structure and data for smp_call_function(). This is designed to minimise static memory
@@ -66,6 +67,9 @@ static volatile struct call_data_struct
#define IPI_CALL_FUNC 0
#define IPI_CPU_STOP 1
+#ifdef CONFIG_KGDB
+#define IPI_KGDB_INTERRUPT 2
+#endif
/* This needs to be cacheline aligned because it is written to by *other* CPUs. */
static DEFINE_PER_CPU(u64, ipi_operation) ____cacheline_aligned;
@@ -155,6 +159,11 @@ handle_IPI (int irq, void *dev_id, struc
case IPI_CPU_STOP:
stop_this_cpu();
break;
+#ifdef CONFIG_KGDB
+ case IPI_KGDB_INTERRUPT:
+ kgdb_wait_ipi(regs);
+ break;
+#endif
default:
printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which);
@@ -303,6 +312,14 @@ smp_call_function_single (int cpuid, voi
}
EXPORT_SYMBOL(smp_call_function_single);
+#ifdef CONFIG_KGDB
+void
+smp_send_nmi_allbutself(void)
+{
+ send_IPI_allbutself(IPI_KGDB_INTERRUPT);
+}
+#endif
+
/*
* this function sends a 'generic call function' IPI to all other CPUs
* in the system.
diff -puN arch/ia64/kernel/traps.c~ia64-lite arch/ia64/kernel/traps.c
--- linux-2.6.10-rc1/arch/ia64/kernel/traps.c~ia64-lite 2004-10-29 11:26:45.500204934 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/traps.c 2004-10-29 11:26:45.520200239 -0700
@@ -15,6 +15,7 @@
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <linux/hardirq.h>
+#include <linux/kgdb.h>
#include <asm/fpswa.h>
#include <asm/ia32.h>
@@ -92,6 +93,8 @@ die (const char *str, struct pt_regs *re
} else
printk(KERN_ERR "Recursive die() failure, output suppressed\n");
+ CHK_DEBUGGER(1, SIGTRAP, err, regs,);
+
bust_spinlocks(0);
die.lock_owner = -1;
spin_unlock_irq(&die.lock);
@@ -119,7 +122,9 @@ ia64_bad_break (unsigned long break_num,
switch (break_num) {
case 0: /* unknown error (used by GCC for __builtin_abort()) */
+#ifndef CONFIG_KGDB
die_if_kernel("bugcheck!", regs, break_num);
+#endif
sig = SIGILL; code = ILL_ILLOPC;
break;
@@ -172,8 +177,10 @@ ia64_bad_break (unsigned long break_num,
break;
default:
+#ifndef CONFIG_KGDB
if (break_num < 0x40000 || break_num > 0x100000)
die_if_kernel("Bad break", regs, break_num);
+#endif
if (break_num < 0x80000) {
sig = SIGILL; code = __ILL_BREAK;
@@ -181,6 +188,13 @@ ia64_bad_break (unsigned long break_num,
sig = SIGTRAP; code = TRAP_BRKPT;
}
}
+#ifdef CONFIG_KGDB
+ /*
+ * We don't want to trap simulator system calls.
+ */
+ if (break_num != 0x80001)
+ CHK_DEBUGGER(11, sig, break_num, regs, return);
+#endif
siginfo.si_signo = sig;
siginfo.si_errno = 0;
siginfo.si_code = code;
@@ -487,9 +501,18 @@ ia64_fault (unsigned long vector, unsign
sprintf(buf, "Unsupported data reference");
break;
+ case 36: /* Single Step Trap */
+#ifdef CONFIG_KGDB
+ if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs) &&
+ kgdb_hwbreak_sstep[smp_processor_id()]) {
+ kgdb_hwbreak_sstep[smp_processor_id()] = 0;
+ regs->cr_ipsr &= ~IA64_PSR_SS;
+ return;
+ }
+#endif
case 29: /* Debug */
+ CHK_DEBUGGER(vector, SIGTRAP, isr, regs, return);
case 35: /* Taken Branch Trap */
- case 36: /* Single Step Trap */
if (fsys_mode(current, regs)) {
extern char __kernel_syscall_via_break[];
/*
@@ -604,6 +627,7 @@ ia64_fault (unsigned long vector, unsign
sprintf(buf, "Fault %lu", vector);
break;
}
+ CHK_DEBUGGER(vector, SIGTRAP, isr, regs,);
die_if_kernel(buf, regs, error);
force_sig(SIGILL, current);
}
diff -puN arch/ia64/kernel/unwind.c~ia64-lite arch/ia64/kernel/unwind.c
--- linux-2.6.10-rc1/arch/ia64/kernel/unwind.c~ia64-lite 2004-10-29 11:26:45.502204465 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/kernel/unwind.c 2004-10-29 11:26:45.522199770 -0700
@@ -74,10 +74,68 @@
# define STAT(x...)
#endif
+#ifdef CONFIG_KGDB
+#define KGDB_EARLY_SIZE 100
+static struct unw_reg_state __initdata kgdb_reg_state[KGDB_EARLY_SIZE];
+static struct unw_labeled_state __initdata kgdb_labeled_state[KGDB_EARLY_SIZE];
+void __initdata *kgdb_reg_state_free, __initdata *kgdb_labeled_state_free;
+
+static void __init
+kgdb_malloc_init(void)
+{
+ int i;
+
+ kgdb_reg_state_free = kgdb_reg_state;
+ for (i = 1; i < KGDB_EARLY_SIZE; i++) {
+ *((unsigned long *) &kgdb_reg_state[i]) = (unsigned long) kgdb_reg_state_free;
+ kgdb_reg_state_free = &kgdb_reg_state[i];
+ }
+
+ kgdb_labeled_state_free = kgdb_labeled_state;
+ for (i = 1; i < KGDB_EARLY_SIZE; i++) {
+ *((unsigned long *) &kgdb_labeled_state[i]) =
+ (unsigned long) kgdb_labeled_state_free;
+ kgdb_labeled_state_free = &kgdb_labeled_state[i];
+ }
+
+}
+
+static void * __init
+kgdb_malloc(void **mem)
+{
+ void *p;
+
+ p = *mem;
+ *mem = *((void **) p);
+ return p;
+}
+
+static void __init
+kgdb_free(void **mem, void *p)
+{
+ *((void **)p) = *mem;
+ *mem = p;
+}
+
+#define alloc_reg_state() (!malloc_sizes[0].cs_cachep ? \
+ kgdb_malloc(&kgdb_reg_state_free) : \
+ kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC))
+#define free_reg_state(usr) (!malloc_sizes[0].cs_cachep ? \
+ kgdb_free(&kgdb_reg_state_free, usr) : \
+ kfree(usr))
+#define alloc_labeled_state() (!malloc_sizes[0].cs_cachep ? \
+ kgdb_malloc(&kgdb_labeled_state_free) : \
+ kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC))
+#define free_labeled_state(usr) (!malloc_sizes[0].cs_cachep ? \
+ kgdb_free(&kgdb_labeled_state_free, usr) : \
+ kfree(usr))
+
+#else
#define alloc_reg_state() kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC)
#define free_reg_state(usr) kfree(usr)
#define alloc_labeled_state() kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC)
#define free_labeled_state(usr) kfree(usr)
+#endif
typedef unsigned long unw_word;
typedef unsigned char unw_hash_index_t;
@@ -2261,6 +2319,9 @@ unw_init (void)
init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) __gp,
__start_unwind, __end_unwind);
+#ifdef CONFIG_KGDB
+ kgdb_malloc_init();
+#endif
}
/*
diff -puN arch/ia64/mm/fault.c~ia64-lite arch/ia64/mm/fault.c
--- linux-2.6.10-rc1/arch/ia64/mm/fault.c~ia64-lite 2004-10-29 11:26:45.504203995 -0700
+++ linux-2.6.10-rc1-trini/arch/ia64/mm/fault.c 2004-10-29 11:26:45.522199770 -0700
@@ -9,6 +9,7 @@
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
+#include <linux/kgdb.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -232,6 +233,8 @@ ia64_do_page_fault (unsigned long addres
*/
bust_spinlocks(1);
+ CHK_DEBUGGER(14, SIGSEGV, isr, regs,);
+
if (address < PAGE_SIZE)
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address);
else
diff -puN drivers/firmware/pcdp.c~ia64-lite drivers/firmware/pcdp.c
--- linux-2.6.10-rc1/drivers/firmware/pcdp.c~ia64-lite 2004-10-29 11:26:45.506203526 -0700
+++ linux-2.6.10-rc1-trini/drivers/firmware/pcdp.c 2004-10-29 11:26:45.523199535 -0700
@@ -56,7 +56,11 @@ uart_edge_level(int rev, struct pcdp_uar
}
static void __init
+#ifndef CONFIG_KGDB
setup_serial_console(int rev, struct pcdp_uart *uart)
+#else
+setup_serial_console(int rev, struct pcdp_uart *uart, int line)
+#endif
{
#ifdef CONFIG_SERIAL_8250_CONSOLE
struct uart_port port;
@@ -64,6 +68,9 @@ setup_serial_console(int rev, struct pcd
int mapsize = 64;
memset(&port, 0, sizeof(port));
+#ifdef CONFIG_KGDB
+ port.line = line;
+#endif
port.uartclk = uart->clock_rate;
if (!port.uartclk) /* some FW doesn't supply this */
port.uartclk = BASE_BAUD * 16;
@@ -110,6 +117,9 @@ setup_serial_console(int rev, struct pcd
snprintf(options, sizeof(options), "%lun%d", uart->baud,
uart->bits ? uart->bits : 8);
+#ifdef CONFIG_KGDB
+ if (!line)
+#endif
add_preferred_console("ttyS", port.line, options);
printk(KERN_INFO "PCDP: serial console at %s 0x%lx (ttyS%d, options %s)\n",
@@ -156,10 +166,19 @@ efi_setup_pcdp_console(char *cmdline)
for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) {
if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) {
if (uart->type == PCDP_CONSOLE_UART) {
+#ifndef CONFIG_KGDB
setup_serial_console(pcdp->rev, uart);
return;
+#else
+ setup_serial_console(pcdp->rev, uart, 0);
+ serial = 0;
+#endif
}
}
+#ifdef CONFIG_KGDB
+ else if (uart->type == PCDP_DEBUG_UART)
+ setup_serial_console(pcdp->rev, uart, 1);
+#endif
}
end = (struct pcdp_device *) ((u8 *) pcdp + pcdp->length);
diff -puN /dev/null include/asm-ia64/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-ia64/kgdb.h 2004-10-29 11:26:45.523199535 -0700
@@ -0,0 +1,34 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/ptrace.h>
+
+/************************************************************************/
+/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
+/* at least NUMREGBYTES*2 are needed for register packets */
+/* Longer buffer is needed to list all threads */
+#define BUFMAX 1024
+
+/* Number of bytes of registers. */
+#define NUMREGBYTES 0
+
+#define BREAKNUM 0x00003333300LL
+#define KGDBBREAKNUM 0x6665UL
+
+extern volatile int kgdb_hwbreak_sstep[NR_CPUS];
+
+#define BREAKPOINT() asm volatile ("break.m 0x6665")
+#define BREAK_INSTR_SIZE 16
+
+#define CHECK_EXCEPTION_STACK() 1
+
+#define SERIAL_PORT_DFNS {0,}, {0,}, {0,}, {0,}
+
+extern void smp_send_nmi_allbutself(void);
+extern void kgdb_wait_ipi(struct pt_regs *);
+
+#endif /* _ASM_KGDB_H_ */
diff -puN lib/Kconfig.debug~ia64-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~ia64-lite 2004-10-29 11:26:45.510202587 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:55.162382599 -0700
@@ -115,7 +115,7 @@ endif
config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86 || MIPS32 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS32 || IA64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_
^ permalink raw reply [flat|nested] 12+ messages in thread* [patch 7/8] KGDB support for x86_64
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
` (4 preceding siblings ...)
2004-10-29 18:34 ` [patch 6/8] KGDB support for ia64 Tom Rini
@ 2004-10-29 18:34 ` Tom Rini
2004-10-29 23:13 ` Andi Kleen
2004-10-29 18:34 ` [patch 8/8] KGDB over ethernet driver Tom Rini
` (2 subsequent siblings)
8 siblings, 1 reply; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:34 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, trini, ak
Cc: Andi Kleen <ak@suse.de>
This adds KGDB support for x86_64.
---
linux-2.6.10-rc1-trini/arch/x86_64/Kconfig.debug | 3
linux-2.6.10-rc1-trini/arch/x86_64/kernel/Makefile | 3
linux-2.6.10-rc1-trini/arch/x86_64/kernel/irq.c | 8
linux-2.6.10-rc1-trini/arch/x86_64/kernel/kgdb.c | 448 +++++++++++++++++++++
linux-2.6.10-rc1-trini/arch/x86_64/kernel/traps.c | 3
linux-2.6.10-rc1-trini/include/asm-x86_64/kgdb.h | 56 ++
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 2
7 files changed, 518 insertions(+), 5 deletions(-)
diff -puN arch/x86_64/Kconfig.debug~x86_64-lite arch/x86_64/Kconfig.debug
--- linux-2.6.10-rc1/arch/x86_64/Kconfig.debug~x86_64-lite 2004-10-29 11:26:45.744147657 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/Kconfig.debug 2004-10-29 11:26:45.757144605 -0700
@@ -41,7 +41,4 @@ config IOMMU_LEAK
Add a simple leak tracer to the IOMMU code. This is useful when you
are debugging a buggy device driver that leaks IOMMU mappings.
-#config X86_REMOTE_DEBUG
-# bool "kgdb debugging stub"
-
endmenu
diff -puN arch/x86_64/kernel/Makefile~x86_64-lite arch/x86_64/kernel/Makefile
--- linux-2.6.10-rc1/arch/x86_64/kernel/Makefile~x86_64-lite 2004-10-29 11:26:45.746147187 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/Makefile 2004-10-29 11:26:45.757144605 -0700
@@ -10,6 +10,8 @@ obj-y := process.o semaphore.o signal.o
setup64.o bootflag.o e820.o reboot.o warmreboot.o
obj-y += mce.o
+CFLAGS_vsyscall.o := -g0
+
obj-$(CONFIG_MTRR) += ../../i386/kernel/cpu/mtrr/
obj-$(CONFIG_ACPI_BOOT) += acpi/
obj-$(CONFIG_X86_MSR) += msr.o
@@ -27,6 +29,7 @@ obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o
obj-$(CONFIG_SWIOTLB) += swiotlb.o
obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_KGDB) += kgdb.o
obj-y += topology.o
obj-y += intel_cacheinfo.o
diff -puN arch/x86_64/kernel/irq.c~x86_64-lite arch/x86_64/kernel/irq.c
--- linux-2.6.10-rc1/arch/x86_64/kernel/irq.c~x86_64-lite 2004-10-29 11:26:45.748146718 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/irq.c 2004-10-29 11:26:45.757144605 -0700
@@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/seq_file.h>
#include <linux/module.h>
+#include <linux/kgdb.h>
#include <asm/uaccess.h>
#include <asm/io_apic.h>
@@ -103,6 +104,13 @@ asmlinkage unsigned int do_IRQ(struct pt
__do_IRQ(irq, regs);
irq_exit();
+ /*
+ * Do not call breakpoint as on the x86_64 architecture if the
+ * exception tables are not set.
+ */
+ if(CHECK_EXCEPTION_STACK())
+ kgdb_process_breakpoint();
+
return 1;
}
diff -puN /dev/null arch/x86_64/kernel/kgdb.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/kgdb.c 2004-10-29 11:26:45.758144370 -0700
@@ -0,0 +1,448 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Amit S. Kale
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002 Andi Kleen, SuSE Labs
+ */
+/****************************************************************************
+ * Contributor: Lake Stevens Instrument Division$
+ * Written by: Glenn Engel $
+ * Updated by: Amit Kale<akale@veritas.com>
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe <dave@gcom.com>
+ * Integrated into 2.2.5 kernel by Tigran Aivazian <tigran@sco.com>
+ * X86_64 changes from Andi Kleen's patch merged by Jim Houston
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h> /* for linux pt_regs struct */
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/debugger.h>
+#include <asm/kdebug.h>
+
+/* Put the error code here just in case the user cares. */
+int gdb_x86_64errcode;
+/* Likewise, the vector number here (since GDB only gets the signal
+ number through the usual means, and that's not very specific). */
+int gdb_x86_64vector = -1;
+
+extern atomic_t cpu_doing_single_step;
+
+void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ gdb_regs[_RAX] = regs->rax;
+ gdb_regs[_RBX] = regs->rbx;
+ gdb_regs[_RCX] = regs->rcx;
+ gdb_regs[_RDX] = regs->rdx;
+ gdb_regs[_RSI] = regs->rsi;
+ gdb_regs[_RDI] = regs->rdi;
+ gdb_regs[_RBP] = regs->rbp;
+ gdb_regs[_PS] = regs->eflags;
+ gdb_regs[_PC] = regs->rip;
+ gdb_regs[_R8] = regs->r8;
+ gdb_regs[_R9] = regs->r9;
+ gdb_regs[_R10] = regs->r10;
+ gdb_regs[_R11] = regs->r11;
+ gdb_regs[_R12] = regs->r12;
+ gdb_regs[_R13] = regs->r13;
+ gdb_regs[_R14] = regs->r14;
+ gdb_regs[_R15] = regs->r15;
+ gdb_regs[_RSP] = regs->rsp;
+}
+
+struct task_struct *__switch_to(struct task_struct *prev_p,
+ struct task_struct *next_p);
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ gdb_regs[_RAX] = 0;
+ gdb_regs[_RBX] = 0;
+ gdb_regs[_RCX] = 0;
+ gdb_regs[_RDX] = 0;
+ gdb_regs[_RSI] = 0;
+ gdb_regs[_RDI] = 0;
+ gdb_regs[_RBP] = 0;
+ gdb_regs[_PS] = 0;
+ gdb_regs[_PC] = (unsigned long)__switch_to;
+ gdb_regs[_R8] = 0;
+ gdb_regs[_R9] = 0;
+ gdb_regs[_R10] = 0;
+ gdb_regs[_R11] = 0;
+ gdb_regs[_R12] = 0;
+ gdb_regs[_R13] = 0;
+ gdb_regs[_R14] = 0;
+ gdb_regs[_R15] = 0;
+ gdb_regs[_RSP] = p->thread.rsp;
+}
+
+void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ regs->rax = gdb_regs[_RAX];
+ regs->rbx = gdb_regs[_RBX];
+ regs->rcx = gdb_regs[_RCX];
+ regs->rdx = gdb_regs[_RDX];
+ regs->rsi = gdb_regs[_RSI];
+ regs->rdi = gdb_regs[_RDI];
+ regs->rbp = gdb_regs[_RBP];
+ regs->eflags = gdb_regs[_PS];
+ regs->rip = gdb_regs[_PC];
+ regs->r8 = gdb_regs[_R8];
+ regs->r9 = gdb_regs[_R9];
+ regs->r10 = gdb_regs[_R10];
+ regs->r11 = gdb_regs[_R11];
+ regs->r12 = gdb_regs[_R12];
+ regs->r13 = gdb_regs[_R13];
+ regs->r14 = gdb_regs[_R14];
+ regs->r15 = gdb_regs[_R15];
+#if 0 /* can't change these */
+ regs->rsp = gdb_regs[_RSP];
+ regs->ss = gdb_regs[_SS];
+ regs->fs = gdb_regs[_FS];
+ regs->gs = gdb_regs[_GS];
+#endif
+
+} /* gdb_regs_to_regs */
+
+struct hw_breakpoint {
+ unsigned enabled;
+ unsigned type;
+ unsigned len;
+ unsigned long addr;
+} breakinfo[4] = { {
+enabled:0}, {
+enabled:0}, {
+enabled:0}, {
+enabled:0}};
+
+void kgdb_correct_hw_break(void)
+{
+ int breakno;
+ int correctit;
+ int breakbit;
+ unsigned long dr7;
+
+ asm volatile ("movq %%db7, %0\n":"=r" (dr7):);
+ do {
+ unsigned long addr0, addr1, addr2, addr3;
+ asm volatile ("movq %%db0, %0\n"
+ "movq %%db1, %1\n"
+ "movq %%db2, %2\n"
+ "movq %%db3, %3\n":"=r" (addr0), "=r"(addr1),
+ "=r"(addr2), "=r"(addr3):);
+ } while (0);
+ correctit = 0;
+ for (breakno = 0; breakno < 3; breakno++) {
+ breakbit = 2 << (breakno << 1);
+ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 |= breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ dr7 |= (((breakinfo[breakno].len << 2) |
+ breakinfo[breakno].type) << 16) <<
+ (breakno << 2);
+ switch (breakno) {
+ case 0:
+ asm volatile ("movq %0, %%dr0\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 1:
+ asm volatile ("movq %0, %%dr1\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 2:
+ asm volatile ("movq %0, %%dr2\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+
+ case 3:
+ asm volatile ("movq %0, %%dr3\n"::"r"
+ (breakinfo[breakno].addr));
+ break;
+ }
+ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+ correctit = 1;
+ dr7 &= ~breakbit;
+ dr7 &= ~(0xf0000 << (breakno << 2));
+ }
+ }
+ if (correctit) {
+ asm volatile ("movq %0, %%db7\n"::"r" (dr7));
+ }
+}
+
+int kgdb_remove_hw_break(unsigned long addr)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (breakinfo[i].addr == addr && breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+
+ breakinfo[idx].enabled = 0;
+ return 0;
+}
+
+int kgdb_set_hw_break(unsigned long addr)
+{
+ int i, idx = -1;
+ for (i = 0; i < 4; i++) {
+ if (!breakinfo[i].enabled) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ return -1;
+
+ breakinfo[idx].enabled = 1;
+ breakinfo[idx].type = 1;
+ breakinfo[idx].len = 1;
+ breakinfo[idx].addr = addr;
+ return 0;
+}
+
+int remove_hw_break(unsigned breakno)
+{
+ if (!breakinfo[breakno].enabled) {
+ return -1;
+ }
+ breakinfo[breakno].enabled = 0;
+ return 0;
+}
+
+int set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr)
+{
+ if (breakinfo[breakno].enabled) {
+ return -1;
+ }
+ breakinfo[breakno].enabled = 1;
+ breakinfo[breakno].type = type;
+ breakinfo[breakno].len = len;
+ breakinfo[breakno].addr = addr;
+ return 0;
+}
+
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+ /* Disable hardware debugging while we are in kgdb */
+ asm volatile ("movq %0,%%db7": /* no output */ :"r" (0UL));
+}
+
+void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_x86_64vector = eVector;
+ gdb_x86_64errcode = err_code;
+}
+
+int kgdb_arch_handle_exception(int exceptionVector, int signo, int err_code,
+ char *remcomInBuffer, char *remcomOutBuffer,
+ struct pt_regs *linux_regs)
+{
+ unsigned long addr, length;
+ unsigned long breakno, breaktype;
+ char *ptr;
+ int newPC;
+ unsigned long dr6;
+
+ switch (remcomInBuffer[0]) {
+ case 'c':
+ case 's':
+ if (kgdb_contthread && kgdb_contthread != current) {
+ strcpy(remcomOutBuffer, "E00");
+ break;
+ }
+
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcomInBuffer[1];
+ if (kgdb_hex2long(&ptr, &addr)) {
+ linux_regs->rip = addr;
+ }
+ newPC = linux_regs->rip;
+
+ /* clear the trace bit */
+ linux_regs->eflags &= ~TF_MASK;
+
+ atomic_set(&cpu_doing_single_step,-1);
+ /* set the trace bit if we're stepping */
+ if (remcomInBuffer[0] == 's') {
+ linux_regs->eflags |= TF_MASK;
+ debugger_step = 1;
+ if (kgdb_contthread)
+ atomic_set(&cpu_doing_single_step,smp_processor_id());
+
+ }
+
+ asm volatile ("movq %%db6, %0\n":"=r" (dr6));
+ if (!(dr6 & 0x4000)) {
+ for (breakno = 0; breakno < 4; ++breakno) {
+ if (dr6 & (1 << breakno)) {
+ if (breakinfo[breakno].type == 0) {
+ /* Set restore flag */
+ linux_regs->eflags |= X86_EFLAGS_RF;
+ break;
+ }
+ }
+ }
+ }
+ kgdb_correct_hw_break();
+ asm volatile ("movq %0, %%db6\n"::"r" (0UL));
+
+ return (0);
+
+ case 'Y':
+ ptr = &remcomInBuffer[1];
+ kgdb_hex2long(&ptr, &breakno);
+ ptr++;
+ kgdb_hex2long(&ptr, &breaktype);
+ ptr++;
+ kgdb_hex2long(&ptr, &length);
+ ptr++;
+ kgdb_hex2long(&ptr, &addr);
+ if (set_hw_break(breakno & 0x3, breaktype & 0x3,
+ length & 0x3, addr) == 0) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "ERROR");
+ }
+ break;
+
+ /* Remove hardware breakpoint */
+ case 'y':
+ ptr = &remcomInBuffer[1];
+ kgdb_hex2long(&ptr, &breakno);
+ if (remove_hw_break(breakno & 0x3) == 0) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "ERROR");
+ }
+ break;
+
+ } /* switch */
+ return -1; /* this means that we do not want to exit from the handler */
+}
+
+static struct pt_regs *in_interrupt_stack(unsigned long rsp, int cpu)
+{
+ struct pt_regs *regs;
+ unsigned long end = (unsigned long)cpu_pda[cpu].irqstackptr;
+ if (rsp <= end && rsp >= end - IRQSTACKSIZE + 8) {
+ regs = *(((struct pt_regs **)end) - 1);
+ return regs;
+ }
+ return NULL;
+}
+
+static struct pt_regs *in_exception_stack(unsigned long rsp, int cpu)
+{
+ int i;
+ struct tss_struct *init_tss = &per_cpu(init_tss,get_cpu());
+ for (i = 0; i < N_EXCEPTION_STACKS; i++)
+ if (rsp >= init_tss[cpu].ist[i] &&
+ rsp <= init_tss[cpu].ist[i] + EXCEPTION_STKSZ) {
+ struct pt_regs *r =
+ (void *)init_tss[cpu].ist[i] + EXCEPTION_STKSZ;
+ return r - 1;
+ }
+ return NULL;
+}
+
+void kgdb_shadowinfo(struct pt_regs *regs, char *buffer, unsigned threadid)
+{
+ static char intr_desc[] = "Stack at interrupt entrypoint";
+ static char exc_desc[] = "Stack at exception entrypoint";
+ struct pt_regs *stregs;
+ int cpu = hard_smp_processor_id();
+
+ if ((stregs = in_interrupt_stack(regs->rsp, cpu))) {
+ kgdb_mem2hex(intr_desc, buffer, strlen(intr_desc));
+ } else if ((stregs = in_exception_stack(regs->rsp, cpu))) {
+ kgdb_mem2hex(exc_desc, buffer, strlen(exc_desc));
+ }
+}
+
+struct task_struct *kgdb_get_shadow_thread(struct pt_regs *regs, int threadid)
+{
+ struct pt_regs *stregs;
+ int cpu = hard_smp_processor_id();
+
+ if ((stregs = in_interrupt_stack(regs->rsp, cpu))) {
+ return current;
+ } else if ((stregs = in_exception_stack(regs->rsp, cpu))) {
+ return current;
+ }
+ return NULL;
+}
+
+struct pt_regs *kgdb_shadow_regs(struct pt_regs *regs, int threadid)
+{
+ struct pt_regs *stregs;
+ int cpu = hard_smp_processor_id();
+
+ if ((stregs = in_interrupt_stack(regs->rsp, cpu))) {
+ return stregs;
+ } else if ((stregs = in_exception_stack(regs->rsp, cpu))) {
+ return stregs;
+ }
+ return NULL;
+}
+
+/* Register KGDB with the die_chain so that we hook into all of the right
+ * spots. */
+static int kgdb_notify(struct notifier_block *self, unsigned long cmd,
+ void *ptr)
+{
+ struct die_args *d = ptr;
+
+ if (cmd == DIE_DEBUG && user_mode(d->regs))
+ return NOTIFY_STOP;
+ else if (cmd == DIE_NMI_IPI) {
+ if (atomic_read(&debugger_active))
+ debugger_nmihook(smp_processor_id(), ptr);
+ } else
+ CHK_DEBUGGER(d->trapnr, d->signr, d->err, d->regs,);
+
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block kgdb_notifier = {
+ .notifier_call = kgdb_notify,
+ .priority = 0x7fffffff, /* we need to notified first */
+};
+
+int kgdb_arch_init(void)
+{
+ notifier_chain_register(&die_chain, &kgdb_notifier);
+ return 0;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = {0xcc},
+ .flags = KGDB_HW_BREAKPOINT,
+ .shadowth = 1,
+};
diff -puN arch/x86_64/kernel/traps.c~x86_64-lite arch/x86_64/kernel/traps.c
--- linux-2.6.10-rc1/arch/x86_64/kernel/traps.c~x86_64-lite 2004-10-29 11:26:45.750146248 -0700
+++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/traps.c 2004-10-29 11:26:45.759144136 -0700
@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/debugger.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -642,7 +643,7 @@ asmlinkage void *do_debug(struct pt_regs
tsk->thread.debugreg6 = condition;
/* Mask out spurious TF errors due to lazy TF clearing */
- if (condition & DR_STEP) {
+ if (condition & DR_STEP && !debugger_step) {
/*
* The TF error should be masked out only if the current
* process is not traced and if the TRAP flag has been set
diff -puN /dev/null include/asm-x86_64/kgdb.h
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/include/asm-x86_64/kgdb.h 2004-10-29 11:26:45.759144136 -0700
@@ -0,0 +1,56 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ */
+
+#include <linux/ptrace.h>
+
+/************************************************************************/
+/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
+/* at least NUMREGBYTES*2 are needed for register packets */
+/* Longer buffer is needed to list all threads */
+#define BUFMAX 1024
+
+/*
+ * Note that this register image is in a different order than
+ * the register image that Linux produces at interrupt time.
+ *
+ * Linux's register image is defined by struct pt_regs in ptrace.h.
+ * Just why GDB uses a different order is a historical mystery.
+ */
+
+enum regnames { _RAX,
+ _RDX,
+ _RCX,
+ _RBX,
+ _RSI,
+ _RDI,
+ _RBP,
+ _RSP,
+ _R8,
+ _R9,
+ _R10,
+ _R11,
+ _R12,
+ _R13,
+ _R14,
+ _R15,
+ _PC,
+ _PS,
+ _LASTREG = _PS
+};
+
+/* Number of bytes of registers. */
+#define NUMREGBYTES (_LASTREG*8)
+
+#define BREAKPOINT() asm(" int $3");
+#define BREAK_INSTR_SIZE 1
+
+/*
+ * Call kgdb_process_breakpoint() only if the interrupt and exception
+ * stacks of x86_64 are setup.
+ */
+#define CHECK_EXCEPTION_STACK() ((&per_cpu(init_tss, get_cpu ()))[0].ist[0])
+#endif /* _ASM_KGDB_H_ */
diff -puN lib/Kconfig.debug~x86_64-lite lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~x86_64-lite 2004-10-29 11:26:45.753145544 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:32:54.317581029 -0700
@@ -115,7 +115,7 @@ endif
config KGDB
bool "KGDB: kernel debugging with remote gdb"
- depends on DEBUG_KERNEL && (X86 || MIPS32 || IA64 || ((!SMP || BROKEN) && PPC32))
+ depends on DEBUG_KERNEL && (X86 || MIPS32 || IA64 || X86_64 || ((!SMP || BROKEN) && PPC32))
help
If you say Y here, it will be possible to remotely debug the
kernel using gdb. This enlarges your kernel image disk size by
_
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [patch 7/8] KGDB support for x86_64
2004-10-29 18:34 ` [patch 7/8] KGDB support for x86_64 Tom Rini
@ 2004-10-29 23:13 ` Andi Kleen
0 siblings, 0 replies; 12+ messages in thread
From: Andi Kleen @ 2004-10-29 23:13 UTC (permalink / raw)
To: Tom Rini; +Cc: akpm, linux-kernel, ak
On Fri, Oct 29, 2004 at 11:34:21AM -0700, Tom Rini wrote:
>
> Cc: Andi Kleen <ak@suse.de>
> This adds KGDB support for x86_64.
Can you please explain what the additional hooks are good for?
> +++ linux-2.6.10-rc1-trini/arch/x86_64/kernel/Makefile 2004-10-29 11:26:45.757144605 -0700
> @@ -10,6 +10,8 @@ obj-y := process.o semaphore.o signal.o
> setup64.o bootflag.o e820.o reboot.o warmreboot.o
> obj-y += mce.o
>
> +CFLAGS_vsyscall.o := -g0
> +
What's this?
> +#include <linux/kgdb.h>
> #include <asm/uaccess.h>
> #include <asm/io_apic.h>
>
> @@ -103,6 +104,13 @@ asmlinkage unsigned int do_IRQ(struct pt
> __do_IRQ(irq, regs);
> irq_exit();
>
> + /*
> + * Do not call breakpoint as on the x86_64 architecture if the
> + * exception tables are not set.
> + */
> + if(CHECK_EXCEPTION_STACK())
> + kgdb_process_breakpoint();
> +
And that? Why do you need to check breakpoints in interrupts?
> #include <linux/moduleparam.h>
> +#include <linux/debugger.h>
>
> #include <asm/system.h>
> #include <asm/uaccess.h>
> @@ -642,7 +643,7 @@ asmlinkage void *do_debug(struct pt_regs
> tsk->thread.debugreg6 = condition;
>
> /* Mask out spurious TF errors due to lazy TF clearing */
> - if (condition & DR_STEP) {
> + if (condition & DR_STEP && !debugger_step) {
This also looks bogus. What is this exactly for and why is it not
handled through the debug chain?
-Andi
^ permalink raw reply [flat|nested] 12+ messages in thread
* [patch 8/8] KGDB over ethernet driver
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
` (5 preceding siblings ...)
2004-10-29 18:34 ` [patch 7/8] KGDB support for x86_64 Tom Rini
@ 2004-10-29 18:34 ` Tom Rini
2004-10-29 18:35 ` [patch 1/8] A different KGDB stub for -mm Tom Rini
2004-10-29 19:53 ` Tom Rini
8 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:34 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, trini, jgarzik, mpm
Cc: Jeff Garzik <jgarzik@pobox.com>, Matt Mackall <mpm@selenic.com>
This adds a KGDB over ethernet driver that makes use of the netpoll API to
function.
---
linux-2.6.10-rc1-trini/drivers/net/Makefile | 1
linux-2.6.10-rc1-trini/drivers/net/kgdb_eth.c | 132 ++++++++++++++++++++++++++
linux-2.6.10-rc1-trini/kernel/kgdb.c | 9 +
linux-2.6.10-rc1-trini/lib/Kconfig.debug | 8 +
4 files changed, 150 insertions(+)
diff -puN drivers/net/Makefile~eth drivers/net/Makefile
--- linux-2.6.10-rc1/drivers/net/Makefile~eth 2004-10-29 11:26:45.949099534 -0700
+++ linux-2.6.10-rc1-trini/drivers/net/Makefile 2004-10-29 11:26:45.958097421 -0700
@@ -196,3 +196,4 @@ obj-$(CONFIG_HAMRADIO) += hamradio/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_NETCONSOLE) += netconsole.o
+obj-$(CONFIG_KGDB_ETH) += kgdb_eth.o
diff -puN /dev/null drivers/net/kgdb_eth.c
--- /dev/null 2004-10-25 00:35:20.587727328 -0700
+++ linux-2.6.10-rc1-trini/drivers/net/kgdb_eth.c 2004-10-29 11:26:45.958097421 -0700
@@ -0,0 +1,132 @@
+/*
+ * drivers/net/kgdb_eth.c
+ *
+ * A network interface for GDB.
+ * Based upon 'gdbserial' by David Grothe <dave@gcom.com>
+ * and Scott Foehner <sfoehner@engr.sgi.com>
+ *
+ * Maintainers: Amit S. Kale <amitkale@emsyssoft.com> and
+ * Tom Rini <trini@kernel.crashing.org>
+ *
+ * 2004 (c) Amit S. Kale <amitkale@emsyssoft.com>
+ * 2004 (c) MontaVista Software, Inc.
+ *
+ * Other folks:
+ * San Mehat <nettwerk@biodome.org>
+ * Robert Walsh <rjwalsh@durables.org>
+ * wangdi <wangdi@clusterfs.com>.
+ * Matt Mackall <mpm@selenic.com>
+ * Pavel Machek <pavel@suse.cz>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/kgdb.h>
+#include <linux/netpoll.h>
+#include <linux/init.h>
+
+#include <asm/atomic.h>
+
+#define IN_BUF_SIZE 512 /* power of 2, please */
+#define OUT_BUF_SIZE 30 /* We don't want to send too big of a packet. */
+
+static char in_buf[IN_BUF_SIZE], out_buf[OUT_BUF_SIZE];
+static int in_head, in_tail, out_count;
+static atomic_t in_count;
+static int configured;
+
+static void rx_hook(struct netpoll *np, int port, char *msg, int len);
+
+static struct netpoll np = {
+ .name = "kgdboe",
+ .dev_name = "eth0",
+ .rx_hook = rx_hook,
+ .local_port = 6443,
+ .remote_port = 6442,
+ .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+};
+
+static int eth_getDebugChar(void)
+{
+ int chr;
+
+ while (atomic_read(&in_count) == 0)
+ netpoll_poll(&np);
+
+ chr = in_buf[in_tail++];
+ in_tail &= (IN_BUF_SIZE - 1);
+ atomic_dec(&in_count);
+ return chr;
+}
+
+static void eth_flushDebugChar(void)
+{
+ if (out_count && np.dev) {
+ netpoll_send_udp(&np, out_buf, out_count);
+ memset(out_buf, 0, sizeof(out_buf));
+ out_count = 0;
+ }
+}
+
+static void eth_putDebugChar(int chr)
+{
+ out_buf[out_count++] = chr;
+ if (out_count == OUT_BUF_SIZE)
+ eth_flushDebugChar();
+}
+
+static void rx_hook(struct netpoll *np, int port, char *msg, int len)
+{
+ int i;
+
+ np->remote_port = port;
+
+ /* Is this gdb trying to attach? */
+ if (!kgdb_connected)
+ kgdb_schedule_breakpoint();
+
+ for (i = 0; i < len; i++) {
+ if (msg[i] == 3)
+ kgdb_schedule_breakpoint();
+
+ if (atomic_read(&in_count) >= IN_BUF_SIZE) {
+ /* buffer overflow, clear it */
+ in_head = in_tail = 0;
+ atomic_set(&in_count, 0);
+ break;
+ }
+ in_buf[in_head++] = msg[i];
+ in_head &= (IN_BUF_SIZE - 1);
+ atomic_inc(&in_count);
+ }
+}
+
+/* We must be passed configuration options. */
+static int option_setup(char *opt)
+{
+ configured = !netpoll_parse_options(&np, opt);
+ return 0;
+}
+__setup("kgdboe=", option_setup);
+
+int init_kgdboe(void)
+{
+ if (!configured || netpoll_setup(&np))
+ return 1;
+
+ printk(KERN_INFO "kgdb: debugging over ethernet enabled\n");
+
+ return 0;
+}
+
+struct kgdb_io kgdb_io_ops = {
+ .read_char = eth_getDebugChar,
+ .write_char = eth_putDebugChar,
+ .init = init_kgdboe,
+ .flush = eth_flushDebugChar,
+};
diff -puN kernel/kgdb.c~eth kernel/kgdb.c
--- linux-2.6.10-rc1/kernel/kgdb.c~eth 2004-10-29 11:26:45.952098830 -0700
+++ linux-2.6.10-rc1-trini/kernel/kgdb.c 2004-10-29 11:26:45.959097187 -0700
@@ -52,6 +52,7 @@
#include <asm/cacheflush.h>
#include <linux/init.h>
#include <linux/sysrq.h>
+#include <linux/netpoll.h>
extern int pid_max;
extern int pidhash_init_done;
@@ -730,6 +731,10 @@ int kgdb_handle_exception(int exVector,
acquirelock:
+#ifdef CONFIG_KGDB_ETH
+ netpoll_set_trap(1);
+#endif
+
/*
* Interrupts will be restored by the 'trap return' code, except when
* single stepping.
@@ -1144,6 +1149,10 @@ acquirelock:
}
kgdb_exit:
+#ifdef CONFIG_KGDB_ETH
+ netpoll_set_trap(0);
+#endif
+
kgdb_info[processor].debuggerinfo = NULL;
kgdb_info[processor].task = NULL;
procindebug[smp_processor_id()] = 0;
diff -puN lib/Kconfig.debug~eth lib/Kconfig.debug
--- linux-2.6.10-rc1/lib/Kconfig.debug~eth 2004-10-29 11:26:45.954098360 -0700
+++ linux-2.6.10-rc1-trini/lib/Kconfig.debug 2004-10-29 11:26:45.960096952 -0700
@@ -145,6 +145,14 @@ config KGDB_SIBYTE
bool "KGDB: On the Broadcom SWARM serial port"
depends on MIPS && SIBYTE_SB1xxx_SOC
+config KGDB_ETH
+ bool "KGDB: On ethernet"
+ select NETPOLL
+ select NETPOLL_TRAP
+ select NETPOLL_RX
+ help
+ Uses ethernet interface for kgdb.
+
endchoice
config KGDB_SIMPLE_SERIAL
_
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [patch 1/8] A different KGDB stub for -mm
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
` (6 preceding siblings ...)
2004-10-29 18:34 ` [patch 8/8] KGDB over ethernet driver Tom Rini
@ 2004-10-29 18:35 ` Tom Rini
2004-10-29 19:53 ` Tom Rini
8 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-29 18:35 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, kgdb-bugreport, amitkale, mithlesh, ak, ralf, davidm
On Fri, Oct 29, 2004 at 11:32:20AM -0700, Tom Rini wrote:
> Cc: <trini1@cox.net>, <tom.rini@gmail.com>
> The following series of patches adds (or in some cases, replaces) KGDB
> support for i386, ppc32, ia64, x86_64 and mips (both 32 and 64bit
> tested). This version of KGDB can be used either via serial or ethernet.
... Not off to a good start today, forgot to refresh the descs one last
time before sending. Only this and I think 2/8 will go out twice, sorry
for the noise.
--
Tom Rini
http://gate.crashing.org/~trini/
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [patch 1/8] A different KGDB stub for -mm
2004-10-29 18:32 [patch 1/8] A different KGDB stub for -mm Tom Rini
` (7 preceding siblings ...)
2004-10-29 18:35 ` [patch 1/8] A different KGDB stub for -mm Tom Rini
@ 2004-10-29 19:53 ` Tom Rini
8 siblings, 0 replies; 12+ messages in thread
From: Tom Rini @ 2004-10-29 19:53 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, amitkale
> diff -puN kernel/sched.c~core-lite kernel/sched.c
> --- linux-2.6.10-rc1/kernel/sched.c~core-lite 2004-10-29 11:26:42.888818087 -0700
> +++ linux-2.6.10-rc1-trini/kernel/sched.c 2004-10-29 11:26:42.904814331 -0700
[snip]
> @@ -4567,6 +4568,12 @@ void __might_sleep(char *file, int line)
> #if defined(in_atomic)
> static unsigned long prev_jiffy; /* ratelimiting */
>
> + if (atomic_read(&debugger_active))
> + return;
> +
> + if (atomic_read(&debugger_active))
> + return;
> +
> if ((in_atomic() || irqs_disabled()) &&
> system_state == SYSTEM_RUNNING) {
> if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
Olaf Hering pointed this out to me privately. Obvious patch follows.
(And, er, I fogot this on all of the other patches too, as I said, not a
good start to the day...)
Signed-off-by: Tom Rini <trini@kernel.crashing.org>
diff -u linux-2.6.10-rc1/kernel/sched.c linux-2.6.10-rc1/kernel/sched.c
--- linux-2.6.10-rc1/kernel/sched.c
+++ linux-2.6.10-rc1/kernel/sched.c
@@ -4571,9 +4571,6 @@
if (atomic_read(&debugger_active))
return;
- if (atomic_read(&debugger_active))
- return;
-
if ((in_atomic() || irqs_disabled()) &&
system_state == SYSTEM_RUNNING) {
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
--
Tom Rini
http://gate.crashing.org/~trini/
^ permalink raw reply [flat|nested] 12+ messages in thread