From: Robert Picco KGDB support for ia64. It requires that gdb itself be patched. The patch for this is in ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/gdb/ There are a few issues which are minor: 1) netpoll causes unaligned accesses 2) took an exception in kgdb once but can't reproduce and 3) HP's simeth driver for ski simulator really needs to reside in drivers/net. DESC ia64 kgdb repair and cleanup EDESC From: Robert Picco This patch fixes some bugs and removes some obsolete gdb packet support. DESC ia64 kgdb fix EDESC From: Robert Picco This fixes the broken kgdb patch. Signed-off-by: Bob Picco Signed-off-by: Andrew Morton --- 25-akpm/Documentation/ia64/kgdb.txt | 9 25-akpm/arch/ia64/Kconfig | 178 ++ 25-akpm/arch/ia64/kernel/Makefile | 1 25-akpm/arch/ia64/kernel/irq.c | 5 25-akpm/arch/ia64/kernel/ivt.S | 14 25-akpm/arch/ia64/kernel/kgdb_stub.c | 3010 ++++++++++++++++++++++++++++++++++ 25-akpm/arch/ia64/kernel/process.c | 6 25-akpm/arch/ia64/kernel/setup.c | 23 25-akpm/arch/ia64/kernel/smp.c | 18 25-akpm/arch/ia64/kernel/traps.c | 32 25-akpm/arch/ia64/kernel/unwind.c | 63 25-akpm/arch/ia64/lib/Makefile | 1 25-akpm/arch/ia64/lib/kgdb_serial.c | 606 ++++++ 25-akpm/arch/ia64/mm/fault.c | 6 25-akpm/drivers/firmware/pcdp.c | 19 25-akpm/include/asm-ia64/kgdb.h | 69 25-akpm/include/asm-ia64/kgdb_local.h | 114 + 25-akpm/net/Kconfig | 2 18 files changed, 4174 insertions(+), 2 deletions(-) diff -puN arch/ia64/Kconfig~kgdb-ia64-support arch/ia64/Kconfig --- 25/arch/ia64/Kconfig~kgdb-ia64-support 2004-10-21 14:54:16.945347408 -0700 +++ 25-akpm/arch/ia64/Kconfig 2004-10-21 14:54:16.967344064 -0700 @@ -188,6 +188,184 @@ config DISCONTIGMEM or have huge holes in the physical address space for other reasons. See for more. +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL + help + If you say Y here, the system will be compiled with the debug + option (-g) and a debugging stub will be included in the + kernel. This stub communicates with gdb on another (host) + computer via a serial port. The host computer should have + access to the kernel binary file (vmlinux) and a serial port + that is connected to the target machine. Gdb can be made to + configure the serial port or you can use stty and setserial to + do this. See the 'target' command in gdb. This option also + configures in the ability to request a breakpoint early in the + boot process. To request the breakpoint just include 'kgdb' + as a boot option when booting the target machine. The system + will then break as soon as it looks at the boot options. This + option also installs a breakpoint in panic and sends any + kernel faults to the debugger. For more information see the + Documentation/i386/kgdb/kgdb.txt file. + +config KGDB_EARLY + bool + depends on KGDB + default n + prompt "KGDB Early" + help + Kgdb debugging in kernel can start shortly before/after setup_arch routine exits. + +choice + depends on KGDB + 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 + +config KGDB_IOMEM + hex "hex I/O port IOMEM address" + depends on KGDB + default 0xc0000000ff5e0000 + help + Some systems use IOMEM address for the port. This value is from + the rx2600 chassis console port. + +config KGDB_IOMEM_REG_SHIFT + hex "hex I/O port IOMEM reg shift" + depends on KGDB + default 0x0 + help + This is the memory shift for IOMEM. + +config KGDB_IRQ + int "IRQ of the debug serial port" + depends on KGDB + default 59 + 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. This value + is the rx2600 chassis's console port. + +config DEBUG_INFO + bool + depends on KGDB + default y + +config KGDB_MORE + bool "Add any additional compile options" + depends on KGDB + default n + help + Saying yes here turns on the ability to enter additional + compile options. + + +config KGDB_OPTIONS + depends on KGDB_MORE + string "Additional compile arguments" + default "-O1" + help + This option allows you enter additional compile options for + the whole kernel compile. Each platform will have a default + that seems right for it. For example on PPC "-ggdb -O1", and + for i386 "-O1". Note that by configuring KGDB "-g" is already + turned on. In addition, on i386 platforms + "-fomit-frame-pointer" is deleted from the standard compile + options. + +config NO_KGDB_CPUS + int "Number of CPUs" + depends on KGDB && SMP + default NR_CPUS + help + + This option sets the number of cpus for kgdb ONLY. It is used + to prune some internal structures so they look "nice" when + displayed with gdb. This is to overcome possibly larger + numbers that may have been entered above. Enter the real + number to get nice clean kgdb_info displays. + +config KGDB_TS + bool "Enable kgdb time stamp macros?" + depends on KGDB + default n + help + Kgdb event macros allow you to instrument your code with calls + to the kgdb event recording function. The event log may be + examined with gdb at a break point. Turning on this + capability also allows you to choose how many events to + keep. Kgdb always keeps the lastest events. + +choice + depends on KGDB_TS + prompt "Max number of time stamps to save?" + default KGDB_TS_128 + +config KGDB_TS_64 + bool "64" + +config KGDB_TS_128 + bool "128" + +config KGDB_TS_256 + bool "256" + +config KGDB_TS_512 + bool "512" + +config KGDB_TS_1024 + bool "1024" + +endchoice + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB + default n + help + This option enables the command line "console=kgdb" option. + When the system is booted with this option in the command line + all kernel printk output is sent to gdb (as well as to other + consoles). For this to work gdb must be connected. For this + reason, this command line option will generate a breakpoint if + gdb has not yet connected. After the gdb continue command is + given all pent up console output will be printed by gdb on the + host machine. Neither this option, nor KGDB require the + serial driver to be configured. + +config KGDB_SYSRQ + bool "Turn on SysRq 'G' command to do a break?" + depends on KGDB + default y + help + This option includes an option in the SysRq code that allows + you to enter SysRq G which generates a breakpoint to the KGDB + stub. This will work if the keyboard is alive and can + interrupt the system. Because of constraints on when the + serial port interrupt can be enabled, this code may allow you + to interrupt the system before the serial port control C is + available. Just say yes here. + config IA64_CYCLONE bool "Cyclone (EXA) Time Source support" help diff -puN arch/ia64/kernel/irq.c~kgdb-ia64-support arch/ia64/kernel/irq.c --- 25/arch/ia64/kernel/irq.c~kgdb-ia64-support 2004-10-21 14:54:16.946347256 -0700 +++ 25-akpm/arch/ia64/kernel/irq.c 2004-10-21 14:54:16.968343912 -0700 @@ -543,6 +543,11 @@ unsigned int do_IRQ(unsigned long irq, s desc->handler->end(irq); spin_unlock(&desc->lock); } + +#ifdef CONFIG_KGDB + kgdb_process_breakpoint(); +#endif + return 1; } diff -puN arch/ia64/kernel/ivt.S~kgdb-ia64-support arch/ia64/kernel/ivt.S --- 25/arch/ia64/kernel/ivt.S~kgdb-ia64-support 2004-10-21 14:54:16.948346952 -0700 +++ 25-akpm/arch/ia64/kernel/ivt.S 2004-10-21 14:54:16.969343760 -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 @@ -1004,6 +1015,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 @@ -1037,6 +1049,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 @@ -1079,6 +1092,7 @@ ENTRY(dispatch_to_fault_handler) adds r3=8,r2 // set up second base pointer for SAVE_REST ;; SAVE_REST + KGDB_ENABLE_PSR_DB movl r14=ia64_leave_kernel ;; mov rp=r14 diff -puN /dev/null arch/ia64/kernel/kgdb_stub.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/arch/ia64/kernel/kgdb_stub.c 2004-10-21 14:54:16.982341784 -0700 @@ -0,0 +1,3010 @@ +/* + * + * 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 VERITAS Software Corporation. + * + */ +/**************************************************************************** + * 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 $ + * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Compatibility with 2.1.xx kernel by David Grothe + * + * Changes to allow auto initilization. All that is needed is that it + * be linked with the kernel and a break point (int 3) be executed. + * The header file defines BREAKPOINT to allow one to do + * this. It should also be possible, once the interrupt system is up, to + * call putDebugChar("+"). Once this is done, the remote debugger should + * get our attention by sending a ^C in a packet. George Anzinger + * + * Integrated into 2.2.5 kernel by Tigran Aivazian + * Added thread support, support for multiple processors, + * support for ia-32(x86) hardware debugging. + * Amit S. Kale ( akale@veritas.com ) + * + * Modified to support debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + * + * 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 an int 3. + * + ************* + * + * 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) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * 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 + * + ****************************************************************************/ +#define KGDB_VERSION "<20030915.1651.33>" +#include +#include +#include /* for strcpy */ +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * IA64 ToDo: + * 1) more testing needs to be done when modifying registers. + * 2) probably could use cleanup in register get/put from unw_info but gdb work + * is on going (WIP) to reduce g-packet on wire for serial + * 3) until gdb work is complete and accepted by gdb folks, the serial interface + * isn't complete. the current g-packet is over 10,000 bytes which is too + * large for a serial line. without a g-packet a delay is used with large putpacket + requests. however, this won't solve a gdb sent packet which is a g-packet. + * 4) NMI for hung machine. Well we don't have real NMI! + */ + +/************************************************************************ + * + * external low-level support routines + */ +typedef void (*Function) (void); /* pointer to a function */ + +/* Thread reference */ +typedef unsigned char threadref[8]; + +extern int tty_putDebugChar(int); /* write a single character */ +extern int tty_getDebugChar(void); /* read and return a single char */ +extern void tty_flushDebugChar(void); /* flush pending characters */ +extern int eth_putDebugChar(int); /* write a single character */ +extern int eth_getDebugChar(void); /* read and return a single char */ +extern void eth_flushDebugChar(void); /* flush pending characters */ + +/************************************************************************/ +/* 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 */ +/* purloined from gdb ia64 config support */ +#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 BUFMAX (REGISTER_BYTES*2+10) +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; + + +char *kgdb_version = KGDB_VERSION; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int debug_regs = 0; /* set to non-zero to print registers */ + +/* filled in by an external module */ +char *gdb_module_offsets; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES REGISTER_BYTES +#define BREAKNUM 0x00003333300LL +#define KGDBBREAKNUM 0x6665UL + +static void inline +kgdb_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->cr_iip = pc & ~0xf; + ia64_psr(regs)->ri = pc & 0x3; + return; +} + +void +breakpoint(void) +{ + asm volatile ("break.m 0x6665"); +} + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* + * Put the error code here just in case the user cares. + * Likewise, the vector number here (since GDB only gets the signal + * number through the usual means, and that's not very specific). + * The called_from is the return address so he can tell how we entered kgdb. + * This will allow him to seperate out the various possible entries. + */ +#define REMOTE_DEBUG 0 /* set != to turn on printing (also available in info) */ + +#define PID_MAX PID_MAX_DEFAULT + +#ifdef CONFIG_SMP +void smp_send_nmi_allbutself(void); +#define IF_SMP(x) x +#undef MAX_NO_CPUS +#ifndef CONFIG_NO_KGDB_CPUS +#define CONFIG_NO_KGDB_CPUS 2 +#endif +#if CONFIG_NO_KGDB_CPUS > NR_CPUS +#define MAX_NO_CPUS NR_CPUS +#else +#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS +#endif +#define hold_init hold_on_sstep: 1, +#define MAX_CPU_MASK (unsigned long)((1LL << MAX_NO_CPUS) - 1LL) +#define NUM_CPUS num_online_cpus() +#else +#define IF_SMP(x) +#define hold_init +#undef MAX_NO_CPUS +#define MAX_NO_CPUS 1 +#define NUM_CPUS 1 +#endif +#define NOCPU (struct task_struct *)0xbad1fbad +struct kgdb_state { + int exceptionVector; + int signo; + unsigned long err_code; + struct pt_regs *regs; + struct unw_frame_info + *unw; + int ret; +}; +/* *INDENT-OFF* */ +struct kgdb_info { + int used_malloc; + void *called_from; + long long entry_itc; + int errcode; + unsigned long vector; + int print_debug_info; + unsigned long ia64_regs[REGISTER_BYTES / sizeof(long)]; +#ifdef CONFIG_SMP + int hold_on_sstep; + struct { + volatile struct task_struct *task; + int pid; + int hold; + struct pt_regs *regs; + unsigned long ia64_regs[REGISTER_BYTES / sizeof(long)]; + } cpus_waiting[MAX_NO_CPUS]; +#endif +} kgdb_info = {hold_init print_debug_info:REMOTE_DEBUG, vector:-1UL}; + +/* *INDENT-ON* */ + +#define used_m kgdb_info.used_malloc +/* + * This is little area we set aside to contain the stack we + * need to build to allow gdb to call functions. We use one + * per cpu to avoid locking issues. We will do all this work + * with interrupts off so that should take care of the protection + * issues. + */ +#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; + +#define LOOKASIDE_SIZE (200 + (sizeof(struct hw_breakpoint) * MAX_HW_BREAKPOINT)) +#define MALLOC_MAX LOOKASIDE_SIZE /* Max malloc size */ +struct { + unsigned int esp; + int array[LOOKASIDE_SIZE]; +} fn_call_lookaside[MAX_NO_CPUS]; + +#define IF_BIT 0x200 +#define TF_BIT 0x100 + +#define MALLOC_ROUND 8-1 + +static char malloc_array[MALLOC_MAX]; +IF_SMP(static void to_gdb(const char *mess)); +void * +malloc(int size) +{ + + if (size <= (MALLOC_MAX - used_m)) { + int old_used = used_m; + used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND)); + return &malloc_array[old_used]; + } else { + return NULL; + } +} + +/* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + char ch; + while (1) { + ch = tty_getDebugChar() & 0x7f; + if (ch != 0x7f) + return ch; + } + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + tty_flushDebugChar(); + } else { + eth_flushDebugChar(); + } +} + +/* + * Gdb calls functions by pushing agruments, including a return address + * on the stack and the adjusting EIP to point to the function. The + * whole assumption in GDB is that we are on a different stack than the + * one the "user" i.e. code that hit the break point, is on. This, of + * course is not true in the kernel. Thus various dodges are needed to + * do the call without directly messing with EIP (which we can not change + * as it is just a location and not a register. To adjust it would then + * require that we move every thing below EIP up or down as needed. This + * will not work as we may well have stack relative pointer on the stack + * (such as the pointer to regs, for example). + + * So here is what we do: + * We detect gdb attempting to store into the stack area and instead, store + * into the fn_call_lookaside.array at the same relative location as if it + * were the area ESP pointed at. We also trap ESP modifications + * and uses these to adjust fn_call_lookaside.esp. On entry + * fn_call_lookaside.esp will be set to point at the last entry in + * fn_call_lookaside.array. This allows us to check if it has changed, and + * if so, on exit, we add the registers we will use to do the move and a + * trap/ interrupt return exit sequence. We then adjust the eflags in the + * regs array (remember we now have a copy in the fn_call_lookaside.array) to + * kill the interrupt bit, AND we change EIP to point at our set up stub. + * As part of the register set up we preset the registers to point at the + * begining and end of the fn_call_lookaside.array, so all the stub needs to + * do is move words from the array to the stack until ESP= the desired value + * then do the rti. This will then transfer to the desired function with + * all the correct registers. Nifty huh? + */ +extern asmlinkage void fn_call_stub(void); +extern asmlinkage void fn_rtn_stub(void); +/* *INDENT-OFF* */ +/*__asm__("fn_rtn_stub:\n\t" + "movl %eax,%esp\n\t" + "fn_call_stub:\n\t" + "1:\n\t" + "addl $-4,%ebx\n\t" + "movl (%ebx), %eax\n\t" + "pushl %eax\n\t" + "cmpl %esp,%ecx\n\t" + "jne 1b\n\t" + "popl %eax\n\t" + "popl %ebx\n\t" + "popl %ecx\n\t" + "iret \n\t"); +*/ +/* *INDENT-ON* */ +#define gdb_ia64vector kgdb_info.vector +#define gdb_ia64errcode kgdb_info.errcode +#define waiting_cpus kgdb_info.cpus_waiting +#define remote_debug kgdb_info.print_debug_info +#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold +/* gdb locks */ + +#ifdef CONFIG_SMP +static int in_kgdb_called; +static spinlock_t waitlocks[MAX_NO_CPUS] = + {[0 ... MAX_NO_CPUS - 1] = SPIN_LOCK_UNLOCKED }; +/* + * The following array has the thread pointer of each of the "other" + * cpus. We make it global so it can be seen by gdb. + */ +volatile int in_kgdb_entry_log[MAX_NO_CPUS]; +volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS]; +/* +static spinlock_t continuelocks[MAX_NO_CPUS]; +*/ +spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED; +/* waiters on our spinlock plus us */ +static atomic_t spinlock_waiters = ATOMIC_INIT(1); +static int spinlock_count = 0; +static int spinlock_cpu = 0; +/* + * Note we use nested spin locks to account for the case where a break + * point is encountered when calling a function by user direction from + * kgdb. Also there is the memory exception recursion to account for. + * Well, yes, but this lets other cpus thru too. Lets add a + * cpu id to the lock. + */ +#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \ + spinlock_cpu != smp_processor_id()){\ + atomic_inc(&spinlock_waiters); \ + while (! spin_trylock(x)) {\ + in_kgdb(linux_regs, unw_info);\ + }\ + atomic_dec(&spinlock_waiters); \ + spinlock_count = 1; \ + spinlock_cpu = smp_processor_id(); \ + }else{ \ + spinlock_count++; \ + } +#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x) +#else +unsigned kgdb_spinlock = 0; +#define KGDB_SPIN_LOCK(x) --*x +#define KGDB_SPIN_UNLOCK(x) ++*x +#endif + +int +hex(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 $# */ +void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + 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; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum += hex(getDebugChar() & 0x7f); + if ((remote_debug) && (checksum != xmitcsum)) { + printk + ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + + 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); + + if (remote_debug) + printk("R:%s\n", buffer); + flushDebugChar(); +} + +/* send the packet in buffer. */ + +void +putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + + if (!kgdboe) { + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ + +#define MAX_SEND_COUNT 30 + + int send_count = 0, i = 0; + char send_buf[MAX_SEND_COUNT]; + + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + send_count = 0; + while ((ch = buffer[count])) { + if (send_count >= MAX_SEND_COUNT) { + for(i = 0; i < MAX_SEND_COUNT; i++) { + putDebugChar(send_buf[i]); + } + flushDebugChar(); + send_count = 0; + } else { + send_buf[send_count] = ch; + checksum += ch; + count ++; + send_count++; + } + } + for(i = 0; i < send_count; i++) + putDebugChar(send_buf[i]); + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + } while ((getDebugChar() & 0x7f) != '+'); + } +} + + +void +debug_error(char *format, char *parm) +{ + if (remote_debug) + printk(format, parm); +} + +static void +print_regs(struct pt_regs *regs) +{ + printk("B0=0x%lx\n", regs->b0); + printk("IPSR=0x%lx\n", regs->cr_ipsr); + printk("IIP=0x%lx\n", regs->cr_iip); + printk("\n"); + +} /* print_regs */ + + + +static void +unw_ar_regs(struct unw_frame_info *unw, unsigned long *regs, int put) +{ + if (put) + unw_access_ar(unw, UNW_AR_BSP, ®s[REGISTER_INDEX(IA64_BSP_REGNUM)], put); + else + unw_get_bsp(unw, ®s[REGISTER_INDEX(IA64_BSP_REGNUM)]); + unw_access_ar(unw, UNW_AR_BSPSTORE, ®s[REGISTER_INDEX(IA64_BSPSTORE_REGNUM)], put); + unw_access_ar(unw, UNW_AR_PFS, ®s[REGISTER_INDEX(IA64_PFS_REGNUM)], put); + unw_access_ar(unw, UNW_AR_RNAT, ®s[REGISTER_INDEX(IA64_RNAT_REGNUM)], put); + unw_access_ar(unw, UNW_AR_UNAT, ®s[REGISTER_INDEX(IA64_UNAT_REGNUM)], put); + unw_access_ar(unw, UNW_AR_LC, ®s[REGISTER_INDEX(IA64_LC_REGNUM)], put); + unw_access_ar(unw, UNW_AR_EC, ®s[REGISTER_INDEX(IA64_EC_REGNUM)], put); + unw_access_ar(unw, UNW_AR_FPSR, ®s[REGISTER_INDEX(IA64_FPSR_REGNUM)], put); + unw_access_ar(unw, UNW_AR_RSC, ®s[REGISTER_INDEX(IA64_RSC_REGNUM)], put); + unw_access_ar(unw, UNW_AR_CCV, ®s[REGISTER_INDEX(IA64_CCV_REGNUM)], put); + unw_access_ar(unw, UNW_AR_CSD, ®s[REGISTER_INDEX(IA64_CSD_REGNUM)], put); + unw_access_ar(unw, UNW_AR_SSD, ®s[REGISTER_INDEX(IA64_SSD_REGNUM)], put); + return; +} + +static void +unw_get_regs(struct unw_frame_info *unw, unsigned long *regs, struct pt_regs *ptregs) +{ + int i, j; + char nat; + unsigned long *preg; + struct ia64_fpreg *fr, freg; + + memset(regs, 0, sizeof (kgdb_info.ia64_regs)); + + for (i = 1; i < 32; i++) { + if (ptregs && ((i >= 8 && i <= 11) || (i >= 12 && i <= 15))) + continue; + unw_access_gr(unw, i, ®s[REGISTER_INDEX(IA64_GR0_REGNUM + i)], &nat, 0); + regs[REGISTER_INDEX(IA64_NAT0_REGNUM + i)] = nat; + } + + if (ptregs) { + for (preg = &ptregs->r8, i = 8; i < 12; i++, preg++) + regs[REGISTER_INDEX(IA64_GR0_REGNUM + i)] = *preg; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 12)] = ptregs->r12; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 13)] = ptregs->r13; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 14)] = ptregs->r14; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 15)] = ptregs->r15; + } + + for (i = 1; i < 8; i++) { + if (ptregs && (i == 6 || i == 7)) + continue; + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + i)], 0); + } + + if (ptregs) { + regs[REGISTER_INDEX(IA64_BR0_REGNUM)] = ptregs->b0; + regs[REGISTER_INDEX(IA64_BR0_REGNUM + 6)] = ptregs->b6; + regs[REGISTER_INDEX(IA64_BR0_REGNUM + 7)] = ptregs->b7; + } else { + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + 6)], 0); + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + 7)], 0); + unw_access_br(unw, 0, ®s[REGISTER_INDEX(IA64_BR0_REGNUM)], 0); + } + + if (ptregs) + fr = &ptregs->f6; + else + fr = &freg; + + for (i = 6; i < 12; i++) { + if (!ptregs) + unw_access_fr(unw, i, fr, 0); + regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i)] = fr->u.bits[0]; + regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i + 1)] = fr->u.bits[1]; + if (ptregs) + fr++; + } + + unw_ar_regs(unw, regs, 0); + + unw_access_pr(unw, ®s[REGISTER_INDEX(IA64_PR_REGNUM)], 0); + for (j = 1, i = 0; i < 64; i++, j <<= 1, i++) + regs[REGISTER_INDEX(IA64_PR0_REGNUM + i)] = + (regs[REGISTER_INDEX(IA64_PR_REGNUM)] & j) ? 1 : 0; + + + if (!ptregs) { + unw_get_ip(unw, ®s[REGISTER_INDEX(IA64_IP_REGNUM)]); + unw_get_cfm(unw, ®s[REGISTER_INDEX(IA64_CFM_REGNUM)]); + } else { + regs[REGISTER_INDEX(IA64_IP_REGNUM)] = ptregs->cr_iip; + regs[REGISTER_INDEX(IA64_PSR_REGNUM)] = ptregs->cr_ipsr; + regs[REGISTER_INDEX(IA64_CFM_REGNUM)] = ptregs->cr_ifs; + } + + return; +} + +static void +kgdb_get_block_task(struct task_struct *p, unsigned long *ia64regs) +{ + struct unw_frame_info info; + unsigned long ip; + int count = 0; + + unw_init_from_blocked_task(&info, p); + ip = 0UL; + do { + if (unw_unwind(&info) < 0) + return; + unw_get_ip(&info, &ip); + if (!in_sched_functions(ip)) + break; + } while (count++ < 16); + + if (!ip) + return; + + unw_get_regs(&info, ia64regs, (struct pt_regs *) 0); + + return; +} + +static void +regs_to_gdb_regs(struct kgdb_state *state) +{ + unw_get_regs(state->unw, kgdb_info.ia64_regs, state->regs); + return; +} /* regs_to_gdb_regs */ + +static void +gdb_regs_to_regs(struct kgdb_state *state) +{ + int i; + char nat; + unsigned long *regs, *preg; + struct ia64_fpreg *fr; + struct unw_frame_info *unw; + + unw = state->unw; + regs = kgdb_info.ia64_regs; + + for (i = 1; i < 32; i++) { + if ((i >= 8 && i <= 11) || (i >= 12 && i <= 15)) + continue; + nat = (char) regs[REGISTER_INDEX(IA64_NAT0_REGNUM + i)]; + unw_access_gr(unw, i, ®s[REGISTER_INDEX(IA64_GR0_REGNUM + i)], &nat, 1); + } + + for (preg = &state->regs->r8, i = 8; i < 12; i++, preg++) + regs[REGISTER_INDEX(IA64_GR0_REGNUM + i)] = *preg; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 12)]; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 13)]; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 14)]; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 15)]; + + for (i = 1; i < 8; i++) { + if ((i == 6 || i == 7)) + continue; + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + i)], 1); + } + state->regs->b0 = regs[REGISTER_INDEX(IA64_BR0_REGNUM)]; + state->regs->b6 = regs[REGISTER_INDEX(IA64_BR0_REGNUM + 6)]; + state->regs->b7 = regs[REGISTER_INDEX(IA64_BR0_REGNUM + 7)]; + + for (fr = &state->regs->f6, i = 6; i < 12; i++, fr++) { + fr->u.bits[0] = regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i)]; + fr->u.bits[1] = regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i + 1)]; + } + + unw_ar_regs(unw, regs, 1); + + unw_access_pr(unw, ®s[IA64_PR_REGNUM], 1); + + state->regs->cr_iip = regs[REGISTER_INDEX(IA64_IP_REGNUM)]; + state->regs->cr_ipsr = regs[REGISTER_INDEX(IA64_PSR_REGNUM)]; + state->regs->cr_ifs = regs[REGISTER_INDEX(IA64_CFM_REGNUM)]; + + return; + +} /* gdb_regs_to_regs */ + +int thread_list = 0; + +void +get_gdb_regs(struct task_struct *p, struct kgdb_state *state) +{ + IF_SMP(int i); + if (!p || p == current) { + regs_to_gdb_regs(state); + return; + } +#ifdef CONFIG_SMP + for (i = 0; i < MAX_NO_CPUS; i++) { + if (p == kgdb_info.cpus_waiting[i].task && + !user_mode(kgdb_info.cpus_waiting[i].regs)){ + memcpy(kgdb_info.ia64_regs, kgdb_info.cpus_waiting[i].ia64_regs, + sizeof(kgdb_info.ia64_regs)); + return; + } + } +#endif +/* + * This code is to give a more informative notion of where a process + * is waiting. It is used only when the user asks for a thread info + * list. If he then switches to the thread, s/he will find the task + * is in schedule, but a back trace should show the same info we come + * up with. Some of this code was purloined from get_wchan; + */ + + if (!thread_list) + return; + else if (p->state == TASK_RUNNING) + return; + + kgdb_get_block_task(p, kgdb_info.ia64_regs); + + return; + +} + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; +static volatile int mem_err_expected = 0; +static volatile int mem_err_cnt = 0; + +int +get_char(char *addr, unsigned char *data) +{ + mm_segment_t fs; + int ret; + + if ((unsigned long) addr < DEFAULT_TASK_SIZE) + return -EFAULT; + + wmb(); + fs = get_fs(); + set_fs(KERNEL_DS); + + if (get_user(*data, addr) != 0) + ret = -EFAULT; + else + ret = 0; + + set_fs(fs); + return ret; +} + +int +set_char(char *addr, int val) +{ + mm_segment_t fs; + int ret; + + if ((unsigned long) addr < DEFAULT_TASK_SIZE) + return -EFAULT; + + wmb(); + fs = get_fs(); + set_fs(KERNEL_DS); + + if (put_user(val, addr) != 0) + ret = -EFAULT; + else + ret = 0; + + set_fs(fs); + return ret; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + + for (i = 0; i < count; i++) { + /* printk("%lx = ", mem) ; */ + + if (get_char(mem++, &ch)) { + if (remote_debug) + printk("Mem fault fetching from addr %lx\n", (long) (mem - 1)); + *buf = 0; /* truncate buffer */ + return buf; + } + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_err_expected = 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 */ +/* NOTE: We use the may fault flag to also indicate if the write is to + * the registers (0) or "other" memory (!=0) + */ +char * +hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + if (set_char(mem++, ch)) { + if (remote_debug) + printk("Mem fault storing to addr %lx\n", + (long) (mem - 1)); + return (mem); + } + } + if (may_fault) + mem_err_expected = 0; + return (mem); +} + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToLong(char **ptr, unsigned long *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0UL; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +#define stubhex(h) hex(h) +#ifdef old_thread_list + +static int +stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} +#endif +static char * +pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char * +pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +#ifdef old_thread_list +static char * +unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char * +unpack_threadid(char *inbuf, threadref * id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} +#endif +void +int_to_threadref(threadref * id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} +int +int_to_hex_v(unsigned char * id, int value) +{ + unsigned char *start = id; + int shift; + int ch; + + for (shift = 28; shift >= 0; shift -= 4) { + if ((ch = (value >> shift) & 0xf) || (id != start)) { + *id = hexchars[ch]; + id++; + } + } + if (id == start) + *id++ = '0'; + return id - start; +} +#ifdef old_thread_list + +static int +threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} +#endif +static int +cmp_str(char *s1, char *s2, int count) +{ + while (count--) { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +#if 1 /* this is a hold over from 2.4 where O(1) was "sometimes" */ +extern struct task_struct *kgdb_get_idle(int cpu); +#define idle_task(cpu) kgdb_get_idle(cpu) +#else +#define idle_task(cpu) init_tasks[cpu] +#endif + +extern int kgdb_pid_init_done; + +#ifdef CONFIG_KGDB_EARLY +struct task_struct kgdb_task = {.comm = "kgdb-dummy"}; +#endif + +struct task_struct * +getthread(int pid) +{ + struct task_struct *thread; + if (pid >= PID_MAX && pid <= (PID_MAX + MAX_NO_CPUS)) { + + return idle_task(pid - PID_MAX); + } else { + /* + * find_task_by_pid is relatively safe all the time + * Other pid functions require lock downs which imply + * that we may be interrupting them (as we get here + * in the middle of most any lock down). + * Still we don't want to call until the table exists! + */ + if (kgdb_pid_init_done){ + thread = find_task_by_pid(pid); + if (thread) { + return thread; + } + } + } +#ifdef CONFIG_KGDB_EARLY + if (!kgdb_pid_init_done) + return &kgdb_task; +#endif + return NULL; +} +/* *INDENT-OFF* */ + +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 */ +}; + +#define MAX_BREAK_POINTS (20) + +struct z0_break_point { + unsigned long addr; + unsigned long bundle[2]; + unsigned int enabled; +} z0_break_point[MAX_BREAK_POINTS]; + +int kgdb_arch_set_breakpoint(unsigned long addr) +{ + 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 i; + struct z0_break_point *z0 = NULL; + unsigned long valid; + + asm volatile("probe.w %0 = %1, 0" : "=r" (valid) : "r" (addr) : "memory"); + if (!valid) + return 0; + asm volatile("probe.w %0 = %1, 0" : "=r" (valid) : "r" (addr+8) : "memory"); + if (!valid) + return 0; + + + for (i = 0; i < MAX_BREAK_POINTS; i++) + if (!z0_break_point[i].enabled) { + z0 = &z0_break_point[i]; + break; + } + + if (!z0) + return 0; + + if (slot > 2) + slot = 0; + + bundle_addr = addr & ~0xFULL; + memcpy(&bundle, (unsigned long *)bundle_addr, sizeof(bundle)); + memcpy(z0->bundle, &bundle, sizeof(bundle)); + + 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; + } + + memcpy((char *) bundle_addr, (char *) &bundle, sizeof(bundle)); + flush_icache_range(bundle_addr, bundle_addr + sizeof(bundle)); + z0->addr = addr; + z0->enabled = 1; + + return 1; +} + +int kgdb_arch_remove_breakpoint(unsigned long addr) +{ + struct z0_break_point *z0 = NULL; + int i; + + for (i = 0; i < MAX_BREAK_POINTS; i++) + if (z0_break_point[i].enabled && z0_break_point[i].addr == addr) { + z0 = &z0_break_point[i]; + break; + } + + if (!z0) + return 0; + + addr = addr & ~0xFULL; + (void) memcpy((char *) addr, (char *) z0->bundle, sizeof (z0->bundle)); + flush_icache_range(addr, addr + sizeof(z0->bundle)); + z0->enabled = 0; + return 1; +} + + +unsigned long hw_breakpoint_status; + +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 = malloc(HW_BREAKPOINT * sizeof(struct hw_breakpoint)); + + if (!breakinfo) { + printk(KERN_INFO "Failed to allocate hardware break array\n"); + return; + } + + 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 +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; +} + +#ifdef oldbreak_protocol +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].mask = len - 1; + breakinfo[breakno].addr = addr; + return 0; +} +#endif + +static void inline +normalize(struct unw_frame_info *running, struct pt_regs *regs) +{ + unsigned long sp; + + /* + * unwind to last frame before exception which will be an error + * and then fetch the bsp at exception time. + */ + + do { + unw_get_sp(running, &sp); + if ((sp + 0x10) >= (unsigned long) regs) + break; + } while (unw_unwind(running) >= 0); + + return; +} + +#ifdef CONFIG_SMP +static int in_kgdb_console = 0; + + +static void +snap_regs(struct unw_frame_info *unw_info, void *data) +{ + struct pt_regs *regs; + int cpu; + + regs = data; + normalize(unw_info, regs); + cpu = smp_processor_id(); + unw_get_regs(unw_info, kgdb_info.cpus_waiting[cpu].ia64_regs, regs); + + return; +} + +int +in_kgdb(struct pt_regs *regs, struct unw_frame_info *unw_info) +{ + unsigned flags; + int cpu = smp_processor_id(); + in_kgdb_called = 1; + + preempt_disable(); + + if (!spin_is_locked(&kgdb_spinlock)) { + if (in_kgdb_here_log[cpu] || /* we are holding this cpu */ + in_kgdb_console) { /* or we are doing slow i/o */ + preempt_enable_no_resched(); + return 1; + } + preempt_enable_no_resched(); + return 0; + } + + /* As I see it the only reason not to let all cpus spin on + * the same spin_lock is to allow selected ones to proceed. + * This would be a good thing, so we leave it this way. + * Maybe someday.... Done ! + + * in_kgdb() is called from an NMI so we don't pretend + * to have any resources, like printk() for example. + */ + + kgdb_local_irq_save(flags); /* only local here, to avoid hanging */ + /* + * log arival of this cpu + * The NMI keeps on ticking. Protect against recurring more + * than once, and ignor the cpu that has the kgdb lock + */ + in_kgdb_entry_log[cpu]++; + in_kgdb_here_log[cpu] = regs; + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) + goto exit_in_kgdb; + + /* + * For protection of the initilization of the spin locks by kgdb + * it locks the kgdb spinlock before it gets the wait locks set + * up. We wait here for the wait lock to be taken. If the + * kgdb lock goes away first?? Well, it could be a slow exit + * sequence where the wait lock is removed prior to the kgdb lock + * so if kgdb gets unlocked, we just exit. + */ + + while (spin_is_locked(&kgdb_spinlock) && + !spin_is_locked(waitlocks + cpu)) ; + if (!spin_is_locked(&kgdb_spinlock)) + goto exit_in_kgdb; + + if (unw_info) + unw_get_regs(unw_info, kgdb_info.cpus_waiting[cpu].ia64_regs, regs); + else if (!user_mode(regs)) { + if (current->state == TASK_RUNNING) + unw_init_running(snap_regs, regs); + else + kgdb_get_block_task(current, kgdb_info.cpus_waiting[cpu].ia64_regs); + } + waiting_cpus[cpu].task = current; + waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); + waiting_cpus[cpu].regs = regs; + + spin_unlock_wait(waitlocks + cpu); + + + /* + * log departure of this cpu + */ + waiting_cpus[cpu].task = 0; + waiting_cpus[cpu].pid = 0; + waiting_cpus[cpu].regs = 0; + correct_hw_break(); + exit_in_kgdb: + in_kgdb_here_log[cpu] = 0; + kgdb_local_irq_restore(flags); + preempt_enable_no_resched(); + return 1; + /* + spin_unlock(continuelocks + smp_processor_id()); + */ +} + +void +smp__in_kgdb(struct pt_regs regs) +{ + in_kgdb(®s, NULL); +} +#else +int +in_kgdb(struct pt_regs *regs, struct unw_frame_info *unw) +{ + return (kgdb_spinlock); +} +#endif + +void +printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + sprintf(buffer, "Unknown trap"); + return; +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * NOTE: The INT nn instruction leaves the state of the interrupt + * enable flag UNCHANGED. That means that when this routine + * is entered via a breakpoint (INT 3) instruction from code + * that has interrupts enabled, then interrupts will STILL BE + * enabled when this routine is entered. The first thing that + * we do here is disable interrupts so as to prevent recursive + * entries and bothersome serial interrupts while we are + * trying to run the serial port in polled mode. + * + * For kernel version 2.1.xx the kgdb_cli() actually gets a spin lock so + * it is always necessary to do a restore_flags before returning + * so as to let go of that lock. + */ + +static void do_kgdb_handle_exception(struct unw_frame_info *, void *data); + +int +kgdb_handle_exception(int exceptionVector, int signo, unsigned long err_code, struct pt_regs *linux_regs) +{ + struct kgdb_state info; + + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if (user_mode(linux_regs)) { + printk("ignoring non-kernel exception\n"); + print_regs(linux_regs); + return (0); + } + + preempt_disable(); + + kgdb_info.called_from = __builtin_return_address(0); + + info.exceptionVector = exceptionVector; + info.signo = signo; + info.err_code = err_code; + info.regs = linux_regs; + info.unw = (void *) 0; + unw_init_running(do_kgdb_handle_exception, &info); + + preempt_enable_no_resched(); + + return info.ret; +} + +static int kgdb_dbregs_enabled; + +static void +do_kgdb_handle_exception(struct unw_frame_info *unw_info, void *data) +{ + int exceptionVector, signo, have_regs = 0; + unsigned long err_code; + struct pt_regs *linux_regs; + struct kgdb_state *info; + struct task_struct *usethread = NULL; + struct task_struct *thread_list_start = 0, *thread = NULL; + int length; + unsigned long addr; + char *ptr; + int newPC; + threadref thref; + int threadid; + int thread_min = PID_MAX + MAX_NO_CPUS; +#ifdef old_thread_list + int maxthreads; +#endif + int nothreads; + unsigned long flags; +#define gdb_regs kgdb_info.ia64_regs + IF_SMP(int entry_state = 0); /* 0, ok, 1, no nmi, 2 sync failed */ +#define NO_NMI 1 +#define NO_SYNC 2 +#define ptregs (*linux_regs) +#define NUMREGS NUM_REGS + + info = data; + info->unw = unw_info; + exceptionVector = info->exceptionVector; + signo = info->signo; + err_code = info->err_code; + linux_regs = info->regs; + + normalize(unw_info, linux_regs); + + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + if (kgdboe) + netpoll_set_trap(1); + + kgdb_local_irq_save(flags); + + /* Get kgdb spinlock */ + + KGDB_SPIN_LOCK(&kgdb_spinlock); + kgdb_info.entry_itc = ia64_get_itc(); + /* + * We depend on this spinlock and the NMI watch dog to control the + * other cpus. They will arrive at "in_kgdb()" as a result of the + * NMI and will wait there for the following spin locks to be + * released. + */ +#ifdef CONFIG_SMP + +#if 0 + if (cpu_callout_map & ~MAX_CPU_MASK) { + printk("kgdb : too many cpus, possibly not mapped" + " in contiguous space, change MAX_NO_CPUS" + " in kgdb_stub and make new kernel.\n" + " cpu_callout_map is %lx\n", cpu_callout_map); + goto exit_just_unlock; + } +#endif + if (spinlock_count == 1) { + long time = 0, end_time; + int i; + int cpu_logged_in[MAX_NO_CPUS] = {[0 ... MAX_NO_CPUS - 1] = (0) + }; + if (remote_debug) { + printk("kgdb : cpu %d entry, syncing others\n", + smp_processor_id()); + } + for (i = 0; i < MAX_NO_CPUS; i++) { + /* + * Use trylock as we may already hold the lock if + * we are holding the cpu. Net result is all + * locked. + */ + spin_trylock(&waitlocks[i]); + } + for (i = 0; i < MAX_NO_CPUS; i++) + cpu_logged_in[i] = 0; + /* + * Wait for their arrival. We know the watch dog is active if + * in_kgdb() has ever been called, as it is always called on a + * watchdog tick. + */ + time = ia64_get_itc(); + end_time = time + 2; /* Note: we use the High order bits! */ + i = 1; + if (num_online_cpus() > 1) { + int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; + smp_send_nmi_allbutself(); + + while (i < num_online_cpus() && time != end_time) { + int j; + for (j = 0; j < MAX_NO_CPUS; j++) { + if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && + !cpu_logged_in[j]) { + i++; + cpu_logged_in[j] = 1; + if (remote_debug) { + printk + ("kgdb : cpu %d arrived at kgdb\n", + j); + } + break; + } else if (!waiting_cpus[j].task && + !cpu_online(j)) { + waiting_cpus[j].task = NOCPU; + cpu_logged_in[j] = 1; + waiting_cpus[j].hold = 1; + break; + } + if (!waiting_cpus[j].task && in_kgdb_here_log[j]) { + int wait = 100000; + while (wait-- && !waiting_cpus[j].task ); + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + printk + ("kgdb : cpu %d stall" + " in in_kgdb\n", + j); + i++; + cpu_logged_in[j] = 1; + waiting_cpus[j].task = + (struct task_struct *) 1; + } + } + } + + if (in_kgdb_entry_log[smp_processor_id()] > + (me_in_kgdb + 10)) { + break; + } + + time = ia64_get_itc(); + } + if (i < num_online_cpus()) { + printk + ("kgdb : time out, proceeding without sync\n"); +#if 0 + printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n", + waiting_cpus[0].task != 0, + waiting_cpus[1].task != 0); + printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n", + cpu_logged_in[0], cpu_logged_in[1]); + printk + ("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n", + in_kgdb_here_log[0] != 0, + in_kgdb_here_log[1] != 0); +#endif + entry_state = NO_SYNC; + } else { +#if 0 + int ent = + in_kgdb_entry_log[smp_processor_id()] - + me_in_kgdb; + printk("kgdb : sync after %d entries\n", ent); +#endif + } + } else { + if (remote_debug) { + printk + ("kgdb : %ld cpus, but watchdog not active\n" + "proceeding without locking down other cpus\n", + num_online_cpus()); + entry_state = NO_NMI; + } + } + } +#endif + + if (remote_debug) { + unsigned long *lp = (unsigned long *) &linux_regs; + + printk("handle_exception(exceptionVector=%d, " + "signo=%d, err_code=%ld, linux_regs=%p)\n", + exceptionVector, signo, err_code, linux_regs); + if (debug_regs) { + print_regs(&ptregs); + printk("Stk: %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[0], lp[1], lp[2], lp[3], + lp[4], lp[5], lp[6], lp[7]); + printk(" %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[8], lp[9], lp[10], lp[11], + lp[12], lp[13], lp[14], lp[15]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[16], lp[17], lp[18], lp[19], + lp[20], lp[21], lp[22], lp[23]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[24], lp[25], lp[26], lp[27], + lp[28], lp[29], lp[30], lp[31]); + } + } + + /* Disable hardware debugging while we are in kgdb */ + /* Get the debug register status register */ + 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); + + + switch (exceptionVector) { + case -1: /* general death */ + case 11: /* break fix signo */ + switch (err_code) { /* break_num */ + case BREAKNUM: + signo = SIGTRAP; + break; + case KGDBBREAKNUM: + signo = SIGTRAP; + 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); + break; + } + break; + case 29: /* hardware breakpoint ibr/dbr fault */ + case 36: /* single step trap */ + signo = SIGTRAP; + break; + case 6: /* ikey_miss */ + case 7: /* dkey_miss */ + case 24: /* general exception */ + case 31: /* unsupported data reference */ + signo = SIGSEGV; + break; + case 13: /* reserved */ + case 14: /* " "" */ + case 15: /* " "" */ + case 16: /* " "" */ + case 17: /* " "" */ + case 18: /* " "" */ + case 19: /* " "" */ + case 28: /* " "" */ + case 25: /* disable fp */ + case 32: /* floating point */ + case 33: /* floating point trap */ + case 34: /* lower privilege fault */ + case 35: /* taken branch trap */ + case 37: /* reserved */ + case 38: /* reserved */ + case 39: /* reserved */ + case 40: /* reserved */ + case 41: /* reserved */ + case 42: /* reserved */ + case 43: /* reserved */ + case 44: /* reserved */ + case 45: /* ia32_exception */ + case 47: /* ia32 interrupt */ + default: /* reserved and undefined */ + signo = SIGILL; + break; + case 5: /* kernel page fault */ + if (mem_err_expected) { + /* + * This fault occured because of the + * get_char or set_char routines. These + * two routines use either eax of edx to + * indirectly reference the location in + * memory that they are working with. + * For a page fault, when we return the + * instruction will be retried, so we + * have to make sure that these + * registers point to valid memory. + */ + mem_err = 1; /* set mem error flag */ + mem_err_expected = 0; + mem_err_cnt++; /* helps in debugging */ + 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); + if (remote_debug) + printk("Return after memory error: " + "mem_err_cnt=%d\n", mem_err_cnt); + if (debug_regs) + print_regs(&ptregs); + goto exit_kgdb; + } + break; + } + if (remote_debug) + printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id()); + + gdb_ia64vector = exceptionVector; + gdb_ia64errcode = err_code; +#ifdef CONFIG_SMP + /* + * OK, we can now communicate, lets tell gdb about the sync. + * but only if we had a problem. + */ + switch (entry_state) { + case NO_NMI: + to_gdb("NMI not active, other cpus not stopped\n"); + break; + case NO_SYNC: + to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n"); + default:; + } + +#endif +/* + * Set up the gdb function call area. + */ + + IF_SMP(once_again:) + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + putpacket(remcomOutBuffer); + + while (1 == 1) { + error = 0; + remcomOutBuffer[0] = 0; + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + printk("Remote debug %s\n", + remote_debug ? "on" : "off"); + break; + case 'p': /* fetch register */ + { + int regnum; + + if (!have_regs) { + get_gdb_regs(usethread, info); + have_regs = 1; + } + + hex2mem(&remcomInBuffer[1], (char *) ®num, sizeof(regnum), 0); + if (regnum >= NUMREGS) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = 0; + break; + } + mem2hex((char *) &gdb_regs[REGISTER_INDEX(regnum)], remcomOutBuffer, + REGISTER_SIZE(regnum), 0); + remcomOutBuffer[REGISTER_SIZE(regnum) * 2] = 0; + break; + } + case 'g': /* return the value of the CPU registers */ + get_gdb_regs(usethread, info); + mem2hex((char *) gdb_regs, remcomOutBuffer, NUMREGBYTES, 0); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], (char *) gdb_regs, NUMREGBYTES, 0); + if (!usethread || usethread == current) { + gdb_regs_to_regs(info); + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E00"); + } + break; + + case 'P':{ /* set the value of a single CPU register - + return OK */ + /* + * For some reason, gdb wants to talk about psudo + * registers (greater than 15). These may have + * meaning for ptrace, but for us it is safe to + * ignor them. We do this by dumping them into + * _GS which we also ignor, but do have memory for. + */ + int regno; + + ptr = &remcomInBuffer[1]; + regs_to_gdb_regs(info); + if ((!usethread || usethread == current) && + hexToInt(&ptr, ®no) && + *ptr++ == '=' && (regno >= 0)) { + regno = + (regno >= NUMREGS ? 0 : regno); + hex2mem(ptr, (char *) &gdb_regs[REGISTER_INDEX(regno)], + 4, 0); + gdb_regs_to_regs(info); + strcpy(remcomOutBuffer, "OK"); + break; + } + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && (hexToInt(&ptr, &length))) { + ptr = 0; + /* + * hex doubles the byte count + */ + if (length > (BUFMAX / 2)) + length = BUFMAX / 2; + mem2hex((char *) addr, + remcomOutBuffer, length, 1); + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } + } + + if (ptr) { + strcpy(remcomOutBuffer, "E01"); + debug_error + ("malformed read memory command: %s\n", + remcomInBuffer); + } + break; + + /* MAA..AA,LLLL: + Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && + (hexToInt(&ptr, &length)) && (*(ptr++) == ':')) { + extern unsigned long _start[]; + + if (addr == (unsigned long) _start) + strcpy(remcomOutBuffer, "OK"); + + else { + hex2mem(ptr, (char *) addr, length, 1); + + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } else { + if (kernel_text_address(addr)) + flush_icache_range(addr, addr + length); + strcpy(remcomOutBuffer, "OK"); + } + } + + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + debug_error + ("malformed write memory command: %s\n", + remcomInBuffer); + } + break; + case 'S': + remcomInBuffer[0] = 's'; + case 'C': + /* Csig;AA..AA where ;AA..AA is optional + * continue with signal + * Since signals are meaning less to us, delete that + * part and then fall into the 'c' code. + */ + ptr = &remcomInBuffer[1]; + length = 2; + while (*ptr && *ptr != ';') { + length++; + ptr++; + } + if (*ptr) { + do { + ptr++; + *(ptr - length++) = *ptr; + } while (*ptr); + } else { + remcomInBuffer[1] = 0; + } + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* D detach, reply OK and then continue */ + case 'c': + case 's': + case 'D': + + /* try to read optional parameter, + pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr)) { + if (remote_debug) + printk("Changing EIP to 0x%lx\n", addr); + + ptregs.cr_iip = addr; + } + + newPC = ptregs.cr_iip; + + /* clear the trace bit */ + ptregs.cr_ipsr &= ~IA64_PSR_SS; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + ptregs.cr_ipsr |= IA64_PSR_SS; + + /* detach is a friendly version of continue. Note that + debugging is still enabled (e.g hit control C) + */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + } + + if (remote_debug) { + printk("Resuming execution\n"); + print_regs(&ptregs); + } + + if (kgdboe) + netpoll_set_trap(0); + + correct_hw_break(); + ptregs.cr_ipsr |= IA64_PSR_DB; + goto exit_kgdb; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* query */ + case 'q': + nothreads = 0; + switch (remcomInBuffer[1]) { + case 'f': + threadid = 1; + thread_list = 2; + thread_list_start = (usethread ? : current); + case 's': + if (!cmp_str(&remcomInBuffer[2], + "ThreadInfo", 10)) + break; + + remcomOutBuffer[nothreads++] = 'm'; + for (; threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + nothreads += int_to_hex_v( + &remcomOutBuffer[ + nothreads], + threadid); + if (thread_min > threadid) + thread_min = threadid; + remcomOutBuffer[ + nothreads] = ','; + nothreads++; + if (nothreads > BUFMAX - 10) + break; + } + } + if (remcomOutBuffer[nothreads - 1] == 'm') { + remcomOutBuffer[nothreads - 1] = 'l'; + } else { + nothreads--; + } + remcomOutBuffer[nothreads] = 0; + break; + +#ifdef old_thread_list /* Old thread info request */ + case 'L': + /* List threads */ + thread_list = 2; + thread_list_start = (usethread ? : current); + unpack_byte(remcomInBuffer + 3, &maxthreads); + unpack_threadid(remcomInBuffer + 5, &thref); + do { + int buf_thread_limit = + (BUFMAX - 22) / BUF_THREAD_ID_SIZE; + if (maxthreads > buf_thread_limit) { + maxthreads = buf_thread_limit; + } + } while (0); + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads && + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid(remcomOutBuffer + + 21 + + nothreads * 16, + &thref); + nothreads++; + if (thread_min > threadid) + thread_min = threadid; + } + } + + if (threadid == PID_MAX + MAX_NO_CPUS) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; +#endif + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + if (!threadid) { + /* + * idle thread + */ + for (threadid = PID_MAX; + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + if (current == + idle_task(threadid - + PID_MAX)) + break; + } + } + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + printexceptioninfo(exceptionVector, + err_code, remcomOutBuffer); + break; + case 'T':{ + char * nptr; + /* Thread extra info */ + if (!cmp_str(&remcomInBuffer[2], + "hreadExtraInfo,", 15)) { + break; + } + ptr = &remcomInBuffer[17]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + nptr = &thread->comm[0]; + length = 0; + ptr = &remcomOutBuffer[0]; + do { + length++; + ptr = pack_hex_byte(ptr, *nptr++); + } while (*nptr && length < 16); + /* + * would like that 16 to be the size of + * task_struct.comm but don't know the + * syntax.. + */ + *ptr = 0; + } + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (!thread) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + /* + * Just in case I forget what this is all about, + * the "thread info" command to gdb causes it + * to ask for a thread list. It then switches + * to each thread and asks for the registers. + * For this (and only this) usage, we want to + * fudge the registers of tasks not on the run + * list (i.e. waiting) to show the routine that + * called schedule. Also, gdb, is a minimalist + * in that if the current thread is the last + * it will not re-read the info when done. + * This means that in this case we must show + * the real registers. So here is how we do it: + * Each entry we keep track of the min + * thread in the list (the last that gdb will) + * get info for. We also keep track of the + * starting thread. + * "thread_list" is cleared when switching back + * to the min thread if it is was current, or + * if it was not current, thread_list is set + * to 1. When the switch to current comes, + * if thread_list is 1, clear it, else do + * nothing. + */ + usethread = thread; + have_regs = 0; + if ((thread_list == 1) && + (thread == thread_list_start)) { + thread_list = 0; + } + if (thread_list && (threadid == thread_min)) { + if (thread == thread_list_start) { + thread_list = 0; + } else { + thread_list = 1; + } + } + /* follow through */ + case 'c': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + if (thread_min > threadid) + thread_min = threadid; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + +#ifdef oldbreak_protocol + case 'Y': /* set up a hardware breakpoint */ + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + ptr++; + hexToInt(&ptr, &breaktype); + ptr++; + hexToInt(&ptr, &length); + ptr++; + hexToLong(&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]; + hexToInt(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; +#endif + /* + * Set/Remove watchpoint + * + */ + case 'Z': + case 'z': + if (!kgdb_dbregs_enabled) + break; + + switch (remcomInBuffer[1]) { + case '0': /* insert hwd break */ + case '1': /* hardware breakpoint */ + case '2': /* write watchpoint */ + case '3': /* read watchpoint */ + case '4': /* access watchpoint */ + { + int ret; + if (remcomInBuffer[2] != ',') { + strcpy(remcomOutBuffer, "ERROR"); + break; + } + ptr = &remcomInBuffer[3]; + if (hexToLong(&ptr, &addr)) { + ptr++; + if (!hexToInt(&ptr, &length)) { + strcpy(remcomOutBuffer, "ERROR"); + break; + } + } + else { + strcpy(remcomOutBuffer, "ERROR"); + break; + } + + if (remcomInBuffer[1] == '0') { + if (remcomInBuffer[0] == 'z') + ret = kgdb_arch_remove_breakpoint(addr); + else + ret = kgdb_arch_set_breakpoint(addr); + } + else ret = hardware_breakpoint(addr, length, + remcomInBuffer[1] - '1', + remcomInBuffer[0] == 'Z'); + if (ret) + strcpy(remcomOutBuffer, "OK"); + else + strcpy(remcomOutBuffer, "ERROR"); + break; + } + default: + strcpy(remcomOutBuffer, "ERROR"); + break; + } + break; + case 'R': /* reboot */ + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + /*to_gdb("Rebooting\n"); */ + machine_restart(NULL); + + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1==1) */ + /* + * reached by goto only. + */ + exit_kgdb: +#ifdef CONFIG_SMP + /* + * Release gdb wait locks + * Sanity check time. Must have at least one cpu to run. Also single + * step must not be done if the current cpu is on hold. + */ + if (spinlock_count == 1) { + int ss_hold = (ptregs.cr_ipsr & IA64_PSR_SS) && kgdb_info.hold_on_sstep; + int cpu_avail = 0; + int i; + + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!cpu_online(i)) + break; + if (!hold_cpu(i)) { + cpu_avail = 1; + } + } + /* + * Early in the bring up there will be NO cpus on line... + */ + if (!cpu_avail && !cpus_empty(cpu_online_map)) { + to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n"); + goto once_again; + } + if (hold_cpu(smp_processor_id()) && (ptregs.cr_ipsr & IA64_PSR_SS)) { + to_gdb + ("Current cpu must be unblocked to single step\n"); + goto once_again; + } + + if (!ss_hold) { + int i; + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!hold_cpu(i)) { + spin_unlock(&waitlocks[i]); + } + } + } else { + spin_unlock(&waitlocks[smp_processor_id()]); + } + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + /* + * If this cpu is on hold, this is where we + * do it. Note, the NMI will pull us out of here, + * but will return as the above lock is not held. + * We will stay here till another cpu releases the lock for us. + */ + spin_unlock_wait(waitlocks + smp_processor_id()); + kgdb_local_irq_restore(flags); + info->ret = 0; + return; + } +#if 0 +exit_just_unlock: +#endif +#endif + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + kgdb_local_irq_restore(flags); + info->ret = 0; + return; +} + +/* this function is used to set up exception handlers for tracing and + * breakpoints. + * This function is not needed as the above line does all that is needed. + * We leave it for backward compatitability... + */ +void +set_debug_traps(void) +{ + /* + * linux_debug_hook is defined in traps.c. We store a pointer + * to our own exception handler into it. + + * But really folks, every hear of labeled common, an old Fortran + * concept. Lots of folks can reference it and it is define if + * anyone does. Only one can initialize it at link time. We do + * this with the hook. See the statement above. No need for any + * executable code and it is ready as soon as the kernel is + * loaded. Very desirable in kernel debugging. + + linux_debug_hook = handle_exception ; + */ + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. + putDebugChar ('+'); + + initialized = 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. */ +/* But really, just use the BREAKPOINT macro. We will handle the int stuff + */ + +#ifdef later +/* + * possibly we should not go thru the traps.c code at all? Someday. + */ +void +do_kgdb_int3(struct pt_regs *regs, long error_code) +{ + kgdb_handle_exception(3, 5, error_code, regs); + return; +} +#endif +#undef regs +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS +asmlinkage void +bad_sys_call_exit(int stuff) +{ + struct pt_regs *regs = (struct pt_regs *) &stuff; + printk("Sys call %d return with %x preempt_count\n", + (int) regs->orig_eax, preempt_count()); +} +#endif +#ifdef CONFIG_STACK_OVERFLOW_TEST +#include +asmlinkage void +stack_overflow(void) +{ +#ifdef BREAKPOINT + BREAKPOINT; +#else + printk("Kernel stack overflow, looping forever\n"); +#endif + while (1) { + } +} +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_KGDB_CONSOLE) +char gdbconbuf[BUFMAX]; + +static void +kgdb_gdb_message(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + /* + * This takes care of NMI while spining out chars to gdb + */ + IF_SMP(in_kgdb_console = 1); + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + IF_SMP(in_kgdb_console = 0); +} +#endif +#ifdef CONFIG_SMP +static void +to_gdb(const char *s) +{ + int count = 0; + while (s[count] && (count++ < BUFMAX)) ; + kgdb_gdb_message(s, count); +} +#endif +#ifdef CONFIG_KGDB_CONSOLE +#include +#include +#include +#include +#include + +void +kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + + if (gdb_ia64vector == -1) { + /* + * We have not yet talked to gdb. What to do... + * lets break, on continue we can do the write. + * But first tell him whats up. Uh, well no can do, + * as this IS the console. Oh well... + * We do need to wait or the messages will be lost. + * Other option would be to tell the above code to + * ignore this breakpoint and do an auto return, + * but that might confuse gdb. Also this happens + * early enough in boot up that we don't have the traps + * set up yet, so... + */ + breakpoint(); + } + kgdb_gdb_message(s, count); +} + +/* + * ------------------------------------------------------------ + * Serial KGDB driver + * ------------------------------------------------------------ + */ + +static struct console kgdbcons = { + name:"kgdb", + write:kgdb_console_write, +#ifdef CONFIG_KGDB_USER_CONSOLE + device:kgdb_console_device, +#endif + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +/* + * The trick here is that this file gets linked before printk.o + * That means we get to peer at the console info in the command + * line before it does. If we are up, we register, otherwise, + * do nothing. By returning 0, we allow printk to look also. + */ +static int kgdb_console_enabled; + +int __init +kgdb_console_init(char *str) +{ + if ((strncmp(str, "kgdb", 4) == 0) || (strncmp(str, "gdb", 3) == 0)) { + register_console(&kgdbcons); + kgdb_console_enabled = 1; + } + return 0; /* let others look at the string */ +} + +__setup("console=", kgdb_console_init); + +int __init +kgdb_enable_dbregs(char *str) +{ + kgdb_dbregs_enabled = 1; + return 1; +} + +__setup("kgdb_debug_regs=", kgdb_enable_dbregs); + +#ifdef CONFIG_KGDB_USER_CONSOLE +static kdev_t kgdb_console_device(struct console *c); +/* This stuff sort of works, but it knocks out telnet devices + * we are leaving it here in case we (or you) find time to figure it out + * better.. + */ + +/* + * We need a real char device as well for when the console is opened for user + * space activities. + */ + +static int +kgdb_consdev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +kgdb_consdev_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int size, ret = 0; + static char kbuf[128]; + static DECLARE_MUTEX(sem); + + /* We are not reentrant... */ + if (down_interruptible(&sem)) + return -ERESTARTSYS; + + while (count > 0) { + /* need to copy the data from user space */ + size = count; + if (size > sizeof (kbuf)) + size = sizeof (kbuf); + if (copy_from_user(kbuf, buf, size)) { + ret = -EFAULT; + break;; + } + kgdb_console_write(&kgdbcons, kbuf, size); + count -= size; + ret += size; + buf += size; + } + + up(&sem); + + return ret; +} + +struct file_operations kgdb_consdev_fops = { + open:kgdb_consdev_open, + write:kgdb_consdev_write +}; +static kdev_t +kgdb_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 1); +} + +/* + * This routine gets called from the serial stub in the i386/lib + * This is so it is done late in bring up (just before the console open). + */ +void +kgdb_console_finit(void) +{ + if (kgdb_console_enabled) { + char *cptr = cdevname(MKDEV(TTYAUX_MAJOR, 1)); + char *cp = cptr; + while (*cptr && *cptr != '(') + cptr++; + *cptr = 0; + unregister_chrdev(TTYAUX_MAJOR, cp); + register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops); + } +} +#endif +#endif +#ifdef CONFIG_KGDB_TS +#include /* time stamp code */ +#include /* in_interrupt */ +#ifdef CONFIG_KGDB_TS_64 +#define DATA_POINTS 64 +#endif +#ifdef CONFIG_KGDB_TS_128 +#define DATA_POINTS 128 +#endif +#ifdef CONFIG_KGDB_TS_256 +#define DATA_POINTS 256 +#endif +#ifdef CONFIG_KGDB_TS_512 +#define DATA_POINTS 512 +#endif +#ifdef CONFIG_KGDB_TS_1024 +#define DATA_POINTS 1024 +#endif +#ifndef DATA_POINTS +#define DATA_POINTS 128 /* must be a power of two */ +#endif +#define INDEX_MASK (DATA_POINTS - 1) +#if (INDEX_MASK & DATA_POINTS) +#error "CONFIG_KGDB_TS_COUNT must be a power of 2" +#endif +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + int data0; + int data1; +}; +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + struct task_struct *t1; + struct task_struct *t2; +}; +struct kgdb_and_then_struct kgdb_data[DATA_POINTS]; + +struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0]; +int kgdb_and_then_count; + +void +kgdb_tstamp(int line, char *source, int data0, int data1) +{ + static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED; + int flags; + kgdb_local_irq_save(flags); + spin_lock(&ts_spin); + kgdb_and_then->at_time = ia64_get_itc(); +#ifdef CONFIG_SMP + kgdb_and_then->on_cpu = smp_processor_id(); +#endif + kgdb_and_then->task = current; + kgdb_and_then->from_ln = line; + kgdb_and_then->in_src = source; + kgdb_and_then->from = __builtin_return_address(0); + kgdb_and_then->with_shpf = (int *) (((flags & IF_BIT) >> 9) | + (preempt_count() << 8)); + kgdb_and_then->data0 = data0; + kgdb_and_then->data1 = data1; + kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK]; + spin_unlock(&ts_spin); + kgdb_local_irq_restore(flags); +#ifdef CONFIG_PREEMPT + +#endif + return; +} +#endif +typedef int gdb_debug_hook(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs); +gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; /* histerical reasons... */ + +static int kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + BREAKPOINT; + } +} + diff -puN arch/ia64/kernel/Makefile~kgdb-ia64-support arch/ia64/kernel/Makefile --- 25/arch/ia64/kernel/Makefile~kgdb-ia64-support 2004-10-21 14:54:16.949346800 -0700 +++ 25-akpm/arch/ia64/kernel/Makefile 2004-10-21 14:54:16.983341632 -0700 @@ -18,6 +18,7 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o do obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o obj-$(CONFIG_IA64_CYCLONE) += cyclone.o obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o +obj-$(CONFIG_KGDB) += kgdb_stub.o mca_recovery-y += mca_drv.o mca_drv_asm.o # The gate DSO image is built using a special linker script. diff -puN arch/ia64/kernel/process.c~kgdb-ia64-support arch/ia64/kernel/process.c --- 25/arch/ia64/kernel/process.c~kgdb-ia64-support 2004-10-21 14:54:16.951346496 -0700 +++ 25-akpm/arch/ia64/kernel/process.c 2004-10-21 14:54:16.984341480 -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/setup.c~kgdb-ia64-support arch/ia64/kernel/setup.c --- 25/arch/ia64/kernel/setup.c~kgdb-ia64-support 2004-10-21 14:54:16.952346344 -0700 +++ 25-akpm/arch/ia64/kernel/setup.c 2004-10-21 14:54:16.984341480 -0700 @@ -51,6 +51,7 @@ #include #include #include +#include #if defined(CONFIG_SMP) && (IA64_CPU_SIZE > PAGE_SIZE) # error "struct cpuinfo_ia64 too big!" @@ -375,6 +376,28 @@ setup_arch (char **cmdline_p) } #endif +#ifndef CONFIG_IA64_HP_SIM +#ifdef CONFIG_KGDB + { + unsigned long total_ibr, total_dbr; + long status; + int dbr; + + status = ia64_pal_debug_info(&total_ibr, &total_dbr); + + if (!status) { + printk(KERN_INFO "kgdb has DBR = %d IBR = %d\n", + (int) total_dbr, (int) total_ibr); + + for (dbr = 0; dbr < total_dbr; dbr++) + ia64_set_dbr((dbr << 1) + 1, 0); + for (dbr = 0; dbr < total_ibr; dbr++) + ia64_set_ibr((dbr << 1) + 1, 0); + } + } +#endif +#endif + /* enable IA-64 Machine Check Abort Handling unless disabled */ if (!strstr(saved_command_line, "nomca")) ia64_mca_init(); diff -puN arch/ia64/kernel/smp.c~kgdb-ia64-support arch/ia64/kernel/smp.c --- 25/arch/ia64/kernel/smp.c~kgdb-ia64-support 2004-10-21 14:54:16.953346192 -0700 +++ 25-akpm/arch/ia64/kernel/smp.c 2004-10-21 14:54:16.985341328 -0700 @@ -47,6 +47,7 @@ #include #include #include +#include /* * 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; @@ -156,6 +160,12 @@ handle_IPI (int irq, void *dev_id, struc stop_this_cpu(); break; +#ifdef CONFIG_KGDB + case IPI_KGDB_INTERRUPT: + (void) in_kgdb(regs, NULL); + break; +#endif + default: printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); break; @@ -361,6 +371,14 @@ smp_call_function (void (*func) (void *i } EXPORT_SYMBOL(smp_call_function); +#ifdef CONFIG_KGDB +void +smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(IPI_KGDB_INTERRUPT); +} +#endif + /* * this function calls the 'stop' function on all other CPUs in the system. */ diff -puN arch/ia64/kernel/traps.c~kgdb-ia64-support arch/ia64/kernel/traps.c --- 25/arch/ia64/kernel/traps.c~kgdb-ia64-support 2004-10-21 14:54:16.954346040 -0700 +++ 25-akpm/arch/ia64/kernel/traps.c 2004-10-21 14:54:16.986341176 -0700 @@ -35,6 +35,19 @@ trap_init (void) fpswa_interface = __va(ia64_boot_param->fpswa); } +#ifdef CONFIG_KGDB +extern int kgdb_handle_exception(int, int, int, struct pt_regs *); +#define CHK_REMOTE_DEBUG(trapnr, signr, error_code, regs, after) \ + { \ + if (!user_mode(regs)) { \ + kgdb_handle_exception(trapnr, signr, error_code, regs); \ + after; \ + } \ + } +#else +#define CHK_REMOTE_DEBUG(trapnr, signr, error_code, regs, after) +#endif + /* * Unlock any spinlocks which will prevent us from getting the message out (timerlist_lock * is acquired through the console unblank code) @@ -85,6 +98,8 @@ die (const char *str, struct pt_regs *re bust_spinlocks(1); } + CHK_REMOTE_DEBUG(-1, SIGTRAP, err, regs,) + if (++die.lock_owner_depth < 3) { printk("%s[%d]: %s %ld [%d]\n", current->comm, current->pid, str, err, ++die_counter); @@ -117,9 +132,13 @@ ia64_bad_break (unsigned long break_num, siginfo.si_flags = 0; /* clear __ISR_VALID */ siginfo.si_isr = 0; + + 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 +191,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 +202,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_REMOTE_DEBUG(11, sig, break_num, regs, return) +#endif siginfo.si_signo = sig; siginfo.si_errno = 0; siginfo.si_code = code; @@ -488,8 +516,9 @@ ia64_fault (unsigned long vector, unsign break; case 29: /* Debug */ - case 35: /* Taken Branch Trap */ case 36: /* Single Step Trap */ + CHK_REMOTE_DEBUG(vector, SIGTRAP, isr, regs, return) + case 35: /* Taken Branch Trap */ if (fsys_mode(current, regs)) { extern char __kernel_syscall_via_break[]; /* @@ -604,6 +633,7 @@ ia64_fault (unsigned long vector, unsign sprintf(buf, "Fault %lu", vector); break; } + CHK_REMOTE_DEBUG(vector, SIGTRAP, isr, regs,) die_if_kernel(buf, regs, error); force_sig(SIGILL, current); } diff -puN arch/ia64/kernel/unwind.c~kgdb-ia64-support arch/ia64/kernel/unwind.c --- 25/arch/ia64/kernel/unwind.c~kgdb-ia64-support 2004-10-21 14:54:16.956345736 -0700 +++ 25-akpm/arch/ia64/kernel/unwind.c 2004-10-21 14:54:16.988340872 -0700 @@ -74,10 +74,69 @@ # define STAT(x...) #endif + +#ifdef CONFIG_KGDB_EARLY +#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 +2320,10 @@ unw_init (void) init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) __gp, __start_unwind, __end_unwind); + +#ifdef CONFIG_KGDB_EARLY + kgdb_malloc_init(); +#endif } /* diff -puN /dev/null arch/ia64/lib/kgdb_serial.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/arch/ia64/lib/kgdb_serial.c 2004-10-21 14:54:16.991340416 -0700 @@ -0,0 +1,606 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * Modified to allow invokation early in boot see also + * kgdb.h for instructions by George Anzinger(george@mvista.com) + * Modified to handle debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KGDB_USER_CONSOLE +extern void kgdb_console_finit(void); +#endif +#define PRNT_off +#define TEST_EXISTANCE +#ifdef PRNT +#define dbprintk(s) printk s +#else +#define dbprintk(s) +#endif +#define TEST_INTERRUPT_off +#ifdef TEST_INTERRUPT +#define intprintk(s) printk s +#else +#define intprintk(s) +#endif + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE]; +static int gdb_buf_in_inx; +static atomic_t gdb_buf_in_cnt; +static int gdb_buf_out_inx; + +struct async_struct *gdb_async_info; +static int gdb_async_irq; + +#define outb_px(a,b,c) kgdb_serial_out(a, b, c) +#define outb_py(a, b, c) kgdb_serial_out(b, c, a) +#define inb_py(ASYNC, OFF) kgdb_serial_in(ASYNC, OFF) + +static void inline +kgdb_serial_out(struct async_struct *serial, unsigned long offset, char ch) +{ + offset <<= serial->iomem_reg_shift; + + switch(serial->io_type) { + case SERIAL_IO_MEM: + writeb(ch, serial->iomem_base + offset); + break; + default: + outb(serial->port + offset, ch); + break; + } + return; +} + +static inline unsigned int +kgdb_serial_in(struct async_struct *serial, unsigned long offset) +{ + unsigned int ch; + + offset <<= serial->iomem_reg_shift; + + switch(serial->io_type) { + case SERIAL_IO_MEM: + ch = readb(serial->iomem_base + offset); + break; + default: + ch = inb(serial->port + offset); + break; + } + + return ch; +} + +static void program_uart(struct async_struct *info); +static void write_char(struct async_struct *info, int chr); +/* + * Get a byte from the hardware data buffer and return it + */ +static int +read_data_bfr(struct async_struct *info) +{ + char it = inb_py(info, UART_LSR); + + if (it & UART_LSR_DR) + return (inb_py(info, UART_RX)); + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + program_uart(info); + write_char(info, '-'); + return ('-'); + } + return (-1); + +} /* read_data_bfr */ + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + + * Locking here is a bit of a problem. We MUST not lock out communication + * if we are trying to talk to gdb about a kgdb entry. ON the other hand + * we can loose chars in the console pass thru if we don't lock. It is also + * possible that we could hold the lock or be waiting for it when kgdb + * NEEDS to talk. Since kgdb locks down the world, it does not need locks. + * We do, of course have possible issues with interrupting a uart operation, + * but we will just depend on the uart status to help keep that straight. + + */ +static spinlock_t uart_interrupt_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SMP +extern spinlock_t kgdb_spinlock; +#endif + +static int +read_char(struct async_struct *info) +{ + int chr; + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_lock(&uart_interrupt_lock); + } +#endif + if (atomic_read(&gdb_buf_in_cnt) != 0) { /* intr routine has q'd chars */ + chr = gdb_buf[gdb_buf_out_inx++]; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&gdb_buf_in_cnt); + } else { + chr = read_data_bfr(info); + } +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_unlock(&uart_interrupt_lock); + } +#endif + local_irq_restore(flags); + return (chr); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void +write_char(struct async_struct *info, int chr) +{ + while (!(inb_py(info, UART_LSR) & UART_LSR_THRE)) ; + + outb_py(chr, info, UART_TX); + +} /* write_char */ + +/* + * Mostly we don't need a spinlock, but since the console goes + * thru here with interrutps on, well, we need to catch those + * chars. + */ +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine tty_getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via tty_putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static irqreturn_t +gdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct async_struct *info; + unsigned long flags; + + info = gdb_async_info; + if (!info || !info->tty || irq != gdb_async_irq) + return IRQ_NONE; + + local_irq_save(flags); + spin_lock(&uart_interrupt_lock); + do { + int chr = read_data_bfr(info); + intprintk(("Debug char on int: %x hex\n", chr)); + if (chr < 0) + continue; + + if (chr == 3) { /* Ctrl-C means remote interrupt */ + BREAKPOINT; + continue; + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) { + /* buffer overflow tosses early char */ + read_char(info); + } + gdb_buf[gdb_buf_in_inx++] = chr; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1); + } while (inb_py(info, UART_IIR) & UART_IIR_RDI); + spin_unlock(&uart_interrupt_lock); + local_irq_restore(flags); + return IRQ_HANDLED; +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void +gdb_null(void) +{ +} /* gdb_null */ + +/* These structure are filled in with values defined in asm/kgdb_local.h + */ +static struct serial_state state = SB_STATE; +static struct async_struct local_info = SB_INFO; +static int ok_to_enable_ints = 0; +static void kgdb_enable_ints_now(void); + +extern char *kgdb_version; +/* + * Hook an IRQ for KGDB. + * + * This routine is called from tty_putDebugChar, below. + */ +static int ints_disabled = 1; +int +gdb_hook_interrupt(struct async_struct *info, int verb) +{ + struct serial_state *state = info->state; + unsigned long flags; + int port; +#ifdef TEST_EXISTANCE + int scratch, scratch2; +#endif + + /* The above fails if memory managment is not set up yet. + * Rather than fail the set up, just keep track of the fact + * and pick up the interrupt thing later. + */ + gdb_async_info = info; + port = gdb_async_info->port; + gdb_async_irq = state->irq; + if (verb) { + printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n", + kgdb_version, + port, + gdb_async_irq, gdb_async_info->state->custom_divisor); + } + local_irq_save(flags); +#ifdef TEST_EXISTANCE + /* Existance test */ + /* Should not need all this, but just in case.... */ + + scratch = inb_py(info, UART_IER); + outb_px(info, UART_IER, 0); + scratch2 = inb_py(info, UART_IER); + outb_px(info, UART_IER, scratch); + if (scratch2) { + printk + ("gdb_hook_interrupt: Could not clear IER, not a UART!\n"); + local_irq_restore(flags); + return 1; /* We failed; there's nothing here */ + } + scratch2 = inb_py(info, UART_LCR); + outb_px(info, UART_LCR, 0xBF); /* set up for StarTech test */ + outb_px(info, UART_EFR, 0); /* EFR is the same as FCR */ + outb_px(info, UART_LCR, 0); + outb_px(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = inb_py(info, UART_IIR) >> 6; + if (scratch == 1) { + printk("gdb_hook_interrupt: Undefined UART type!" + " Not a UART! \n"); + local_irq_restore(flags); + return 1; + } else { + dbprintk(("gdb_hook_interrupt: UART type " + "is %d where 0=16450, 2=16550 3=16550A\n", scratch)); + } + scratch = inb_py(info, UART_MCR); + outb_px(info, UART_MCR, UART_MCR_LOOP | scratch); + outb_px(info, UART_MCR, UART_MCR_LOOP | 0x0A); + scratch2 = inb_py(info, UART_MSR) & 0xF0; + outb_px(info, UART_MCR, scratch); +#ifndef CONFIG_SERIAL_8250_HCDP /* HCDP seems to skip this test */ + if (scratch2 != 0x90) { + printk("gdb_hook_interrupt: " + "Loop back test failed! Not a UART!\n"); + local_irq_restore(flags); + return scratch2 + 1000; /* force 0 to fail */ + } +#endif +#endif /* test existance */ + program_uart(info); + local_irq_restore(flags); + + return (0); + +} /* gdb_hook_interrupt */ + +static void +program_uart(struct async_struct *info) +{ + (void) inb_py(info, UART_RX); + outb_px(info, UART_IER, 0); + + (void) inb_py(info, UART_RX); /* serial driver comments say */ + (void) inb_py(info, UART_IIR); /* this clears the interrupt regs */ + (void) inb_py(info, UART_MSR); + outb_px(info, UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + outb_px(info, UART_DLL, info->state->custom_divisor & 0xff); /* LS */ + outb_px(info, UART_DLM, info->state->custom_divisor >> 8); /* MS */ + outb_px(info, UART_MCR, info->MCR); + printk("UART_LCR = 0x%x\n", inb_py(info, UART_LCR)); + + outb_px(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 | UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); /* set fcr */ + outb_px(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + outb_px(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1); /* set fcr */ + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info, UART_IER, gdb_async_info->IER); + } + return; +} + +/* + * tty_getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. In the + */ +int kgdb_in_isr = 0; +int kgdb_in_lsr = 0; +extern spinlock_t kgdb_spinlock; + +/* Caller takes needed protections */ + +int +tty_getDebugChar(void) +{ + volatile int chr; + volatile long time, end_time; + + dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + /* + * This trick says if we wait a very long time and get + * no char, return the -1 and let the upper level deal + * with it. + */ + time = ia64_get_itc(); + end_time = time + local_cpu_data->itm_delta * (HZ / 10); + if (!local_cpu_data->itm_delta) + end_time = time + 500; + while (((chr = read_char(gdb_async_info)) == -1) && + (end_time - time) > 0) { + time = ia64_get_itc(); + }; + /* + * This covers our butts if some other code messes with + * our uart, hay, it happens :o) + */ + if (chr == -1) + udelay(10000); +// program_uart(gdb_async_info); + + dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')); + return (chr); + +} /* tty_getDebugChar */ + +static int count = 3; +static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED; + +static int __init +kgdb_enable_ints(void) +{ + if (kgdboe) { + return 0; + } + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 1); + } + ok_to_enable_ints = 1; + kgdb_enable_ints_now(); +#ifdef CONFIG_KGDB_USER_CONSOLE + kgdb_console_finit(); +#endif + return 0; +} + +static int mem_init_done = 1; + +#ifdef CONFIG_SERIAL_8250 +void shutdown_for_kgdb(struct async_struct *gdb_async_info); +#endif + + +static inline int kgdb_mem_init_done(void) +{ + return mem_init_done; +} + +#ifdef CONFIG_KGDB_EARLY +static struct irqaction kgdb_action; +#endif + +static void +kgdb_enable_ints_now(void) +{ + if (!spin_trylock(&one_at_atime)) + return; + if (!ints_disabled) + goto exit; + if (kgdb_mem_init_done() && + ints_disabled) { /* don't try till mem init */ +#ifdef CONFIG_SERIAL_8250 + /* + * The ifdef here allows the system to be configured + * without the serial driver. + * Don't make it a module, however, it will steal the port + */ + shutdown_for_kgdb(gdb_async_info); +#endif +#ifndef CONFIG_KGDB_EARLY + ints_disabled = request_irq(gdb_async_info->state->irq, + gdb_interrupt, + IRQ_T(gdb_async_info), + "KGDB-stub", NULL); +#else +{ + irq_desc_t *desc; + kgdb_action.handler = gdb_interrupt; + kgdb_action.flags = IRQ_T(gdb_async_info); + kgdb_action.mask = CPU_MASK_NONE; + kgdb_action.name = "KGDB-stub"; + kgdb_action.next = NULL; + kgdb_action.dev_id = NULL; + desc = irq_descp(gdb_async_info->state->irq); + if (desc->handler != &no_irq_type) { + desc->action = &kgdb_action; + ints_disabled = 0; + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | + IRQ_WAITING | IRQ_INPROGRESS); + desc->handler->startup(gdb_async_info->state->irq); + } + if (ints_disabled) + printk("kgdb_enable_ints_now: setup_irq failed\n"); + else + printk("kgdb early access enabled\n"); +} +#endif + intprintk(("KGDB: request_irq returned %d\n", ints_disabled)); + } + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info, UART_IER, gdb_async_info->IER); + } + exit: + spin_unlock(&one_at_atime); +} + +/* + * tty_putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. Caller takes needed protections. + */ +void +tty_putDebugChar(int chr) +{ + dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n", + gdb_async_info->port, + chr, + chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + + write_char(gdb_async_info, chr); /* this routine will wait */ + count = (chr == '#') ? 0 : count + 1; + if ((count == 2)) { /* try to enable after */ + if (ints_disabled & ok_to_enable_ints) + kgdb_enable_ints_now(); /* try to enable after */ + + /* We do this a lot because, well we really want to get these + * interrupts. The serial driver will clear these bits when it + * initializes the chip. Every thing else it does is ok, + * but this. + */ + if (!ints_disabled) { + outb_px(gdb_async_info, UART_IER, + gdb_async_info->IER); + } + } + +} /* tty_putDebugChar */ + +/* + * This does nothing for the serial port, since it doesn't buffer. + */ + +void tty_flushDebugChar(void) +{ +} + +void __init +kgdb_serial_init(void) +{ + extern char saved_command_line[]; + char *cp, *str; + int kgdbbreak = 0; + + for (cp = saved_command_line; *cp; cp++) { + if (memcmp(cp, "kgdb=1", 6) == 0) { + kgdbbreak = 1; + } + else if (memcmp(cp,"kgdbio=", 7) == 0) { + int baud; + + cp += 7; + baud = simple_strtoul(cp, &str, 10); + state.custom_divisor = SB_BASE/baud; + if (*str == ',') { + str++; + state.irq = simple_strtoul(str, &str, 10); + if (*str == ',') { + str++; + local_info.iomem_base = state.iomem_base = (char *) + simple_strtoul(str, &str, 0); + local_info.io_type = state.io_type = SERIAL_IO_MEM; + } + } + printk("kgdb_serial_init: irq = %d iomem_base = 0x%lx baud = %d\n", + state.irq, state.iomem_base, baud); + } + } + + kgdb_enable_ints(); + if (!ints_disabled && kgdbbreak) + BREAKPOINT; +} + +#ifndef CONFIG_IA64_HP_SIM +late_initcall(kgdb_enable_ints); +#endif diff -puN arch/ia64/lib/Makefile~kgdb-ia64-support arch/ia64/lib/Makefile --- 25/arch/ia64/lib/Makefile~kgdb-ia64-support 2004-10-21 14:54:16.958345432 -0700 +++ 25-akpm/arch/ia64/lib/Makefile 2004-10-21 14:54:16.991340416 -0700 @@ -16,6 +16,7 @@ lib-$(CONFIG_MCKINLEY) += copy_page_mck. lib-$(CONFIG_PERFMON) += carta_random.o lib-$(CONFIG_MD_RAID5) += xor.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o +lib-$(CONFIG_KGDB) += kgdb_serial.o AFLAGS___divdi3.o = AFLAGS___udivdi3.o = -DUNSIGNED diff -puN arch/ia64/mm/fault.c~kgdb-ia64-support arch/ia64/mm/fault.c --- 25/arch/ia64/mm/fault.c~kgdb-ia64-support 2004-10-21 14:54:16.959345280 -0700 +++ 25-akpm/arch/ia64/mm/fault.c 2004-10-21 14:54:16.992340264 -0700 @@ -14,6 +14,7 @@ #include #include #include +#include extern void die (char *, struct pt_regs *, long); @@ -232,6 +233,11 @@ ia64_do_page_fault (unsigned long addres */ bust_spinlocks(1); +#ifdef CONFIG_KGDB + kgdb_handle_exception(5, SIGBUS, isr, regs); + return; +#endif + if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address); else diff -puN /dev/null Documentation/ia64/kgdb.txt --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/Documentation/ia64/kgdb.txt 2004-10-21 14:54:16.992340264 -0700 @@ -0,0 +1,9 @@ +IA64 kgdb requires a patch to gdb/remote.c. The proposed patch will eventually be in gdb but +until then, within gdb do "show remote" and look for "Support for remote protocol `p' (fetch-register) packet is auto-detected, currently unknown." Should this line not appear, then you must +obtain the patch, apply it and build gdb. + +CONFIG_KGDB_EARLY enables one with serial kgdb support to debug the kernel starting at the +end of the routine setup_arch. A boot option of "kgdb=1" will result in a kernel breakpoint +and requires gdb to continue from the breakpoint. + +For further information consult the i386 kgdb documentation. diff -puN drivers/firmware/pcdp.c~kgdb-ia64-support drivers/firmware/pcdp.c --- 25/drivers/firmware/pcdp.c~kgdb-ia64-support 2004-10-21 14:54:16.960345128 -0700 +++ 25-akpm/drivers/firmware/pcdp.c 2004-10-21 14:54:16.993340112 -0700 @@ -56,7 +56,11 @@ uart_edge_level(int rev, struct pcdp_uar } static void __init +#ifndef CONFIG_KGDB_EARLY 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_EARLY + 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_EARLY + 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_EARLY setup_serial_console(pcdp->rev, uart); return; +#else + setup_serial_console(pcdp->rev, uart, 0); + serial = 0; +#endif } } +#ifdef CONFIG_KGDB_EARLY + 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 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/include/asm-ia64/kgdb.h 2004-10-21 14:54:16.993340112 -0700 @@ -0,0 +1,69 @@ +#ifndef __KGDB +#define __KGDB + +/* + * This file should not include ANY others. This makes it usable + * most anywhere without the fear of include order or inclusion. + * Make it so! + * + * This file may be included all the time. It is only active if + * CONFIG_KGDB is defined, otherwise it stubs out all the macros + * and entry points. + */ +#if defined(CONFIG_KGDB) && !defined(__ASSEMBLY__) + +extern void breakpoint(void); +#define INIT_KGDB_INTS kgdb_enable_ints() + +#ifndef BREAKPOINT +#define BREAKPOINT asm volatile ("break.m 0x6665") +#endif + +extern void kgdb_schedule_breakpoint(void); +extern void kgdb_process_breakpoint(void); + +extern int kgdb_tty_hook(void); +extern int kgdb_eth_hook(void); +extern int kgdboe; + +struct kgdb_serial { + unsigned long iobase; + unsigned long shift; + int line; + int iotype; + int claimed; +}; + +/* + * GDB debug stub (or any debug stub) can point the 'linux_debug_hook' + * pointer to its routine and it will be entered as the first thing + * when a trap occurs. + * + * Return values are, at present, undefined. + * + * The debug hook routine does not necessarily return to its caller. + * It has the register image and thus may choose to resume execution + * anywhere it pleases. + */ +struct pt_regs; + +extern int kgdb_handle_exception(int trapno, + int signo, unsigned long err_code, struct pt_regs *regs); +struct unw_frame_info; +extern int in_kgdb(struct pt_regs *regs, struct unw_frame_info *); +#ifdef CONFIG_KGDB_EARLY +extern void __init kgdb_serial_init(void); +#endif + +#else /* CONFIG_KGDB && ! __ASSEMBLY__ ,stubs follow... */ +#ifndef BREAKPOINT +#define BREAKPOINT +#endif +#define in_kgdb +#define kgdb_handle_exception +#define breakpoint +#define INIT_KGDB_INTS +#define kgdb_process_breakpoint() do {} while(0) + +#endif +#endif /* __KGDB */ diff -puN /dev/null include/asm-ia64/kgdb_local.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/include/asm-ia64/kgdb_local.h 2004-10-21 14:54:16.994339960 -0700 @@ -0,0 +1,114 @@ +#ifndef __KGDB_LOCAL +#define ___KGDB_LOCAL +#include +#include +#include +#include +#include +#include +#include + +#define PORT 0x0 +#ifdef CONFIG_KGDB_PORT +#undef PORT +#define PORT CONFIG_KGDB_PORT +#define IOTYPE SERIAL_IO_PORT +#endif + +#define IOMEM 0x0 +#ifdef CONFIG_KGDB_IOMEM +#undef IOMEM +#define IOMEM CONFIG_KGDB_IOMEM +#define IOTYPE SERIAL_IO_MEM +#endif + +#define IRQ 4 +#ifdef CONFIG_KGDB_IRQ +#undef IRQ +#define IRQ CONFIG_KGDB_IRQ +#endif + +#define IOMEM_REG_SHIFT 0 +#ifdef CONFIG_IOMEM_REG_SHIFT +#undef IOMEM_REG_SHIFT +#define IOMEM_REG_SHIFT CONFIG_IOMEM_REG_SHIFT +#endif + + +#define SB_CLOCK 1843200 +#define SB_BASE (SB_CLOCK/16) +#define SB_BAUD9600 SB_BASE/9600 +#define SB_BAUD192 SB_BASE/19200 +#define SB_BAUD384 SB_BASE/38400 +#define SB_BAUD576 SB_BASE/57600 +#define SB_BAUD1152 SB_BASE/115200 +#ifdef CONFIG_KGDB_9600BAUD +#define SB_BAUD SB_BAUD9600 +#endif +#ifdef CONFIG_KGDB_19200BAUD +#define SB_BAUD SB_BAUD192 +#endif +#ifdef CONFIG_KGDB_38400BAUD +#define SB_BAUD SB_BAUD384 +#endif +#ifdef CONFIG_KGDB_57600BAUD +#define SB_BAUD SB_BAUD576 +#endif +#ifdef CONFIG_KGDB_115200BAUD +#define SB_BAUD SB_BAUD1152 +#endif +#ifndef SB_BAUD +#define SB_BAUD SB_BAUD1152 /* Start with this if not given */ +#endif + + +#ifdef _raw_read_unlock /* must use a name that is "define"ed, not an inline */ +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#define spin_lock _raw_spin_lock +#define spin_trylock _raw_spin_trylock +#define spin_unlock _raw_spin_unlock +#else +#endif +#undef spin_unlock_wait +#define spin_unlock_wait(x) do { cpu_relax(); barrier();} \ + while(spin_is_locked(x)) + +#define SB_IER 1 +#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS + +#define FLAGS 0 +#define SB_STATE { \ + magic: SSTATE_MAGIC, \ + baud_base: SB_BASE, \ + port: PORT, \ + iomem_base:(u8 *) IOMEM, \ + iomem_reg_shift: IOMEM_REG_SHIFT,\ + io_type: IOTYPE, \ + irq: IRQ, \ + flags: FLAGS, \ + custom_divisor:SB_BAUD} +#define SB_INFO { \ + magic: SERIAL_MAGIC, \ + port: PORT,0,FLAGS, \ + io_type: IOTYPE, \ + iomem_base: (u8 *) IOMEM, \ + iomem_reg_shift: IOMEM_REG_SHIFT, \ + state: &state, \ + tty: (struct tty_struct *)&state, \ + IER: SB_IER, \ + MCR: SB_MCR} +extern void putDebugChar(int); +/* RTAI support needs us to really stop/start interrupts */ + +#define kgdb_local_save_flags(x) local_save_flags(x) +#define kgdb_local_irq_restore(x) local_irq_restore(x) +#define kgdb_local_irq_save(x) local_irq_save(x) + +#ifdef CONFIG_SERIAL +extern void shutdown_for_kgdb(struct async_struct *info); +extern void kgdb_serial_setup(struct uart_port *); +#endif +#define INIT_KDEBUG putDebugChar("+"); +#endif /* __KGDB_LOCAL */ diff -puN net/Kconfig~kgdb-ia64-support net/Kconfig --- 25/net/Kconfig~kgdb-ia64-support 2004-10-21 14:54:16.962344824 -0700 +++ 25-akpm/net/Kconfig 2004-10-21 14:54:16.995339808 -0700 @@ -646,7 +646,7 @@ endmenu endmenu config KGDBOE - def_bool X86 && KGDB + def_bool (X86 || IA64) && KGDB config NETPOLL def_bool NETCONSOLE || KGDBOE _