This is support of the IA64 arch for KGDB, and is primarily the work of Bob Picco. Signed-off-by: Milind Dumbare Signed-off-by: Bob Picco arch/ia64/kernel/Makefile | 1 arch/ia64/kernel/kgdb-jmp.S | 238 +++++++++ arch/ia64/kernel/kgdb.c | 1131 ++++++++++++++++++++++++++++++++++++++++++++ arch/ia64/kernel/smp.c | 17 arch/ia64/kernel/traps.c | 6 arch/ia64/mm/extable.c | 6 arch/ia64/mm/fault.c | 4 include/asm-ia64/kdebug.h | 1 include/asm-ia64/kgdb.h | 36 + lib/Kconfig.debug | 2 10 files changed, 1440 insertions(+), 2 deletions(-) Index: linux-2.6.18-rc7/arch/ia64/mm/extable.c =================================================================== --- linux-2.6.18-rc7.orig/arch/ia64/mm/extable.c 2006-10-13 16:56:45.000000000 +0100 +++ linux-2.6.18-rc7/arch/ia64/mm/extable.c 2006-10-13 17:07:06.000000000 +0100 @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -73,6 +74,11 @@ search_extable (const struct exception_t else last = mid - 1; } +#ifdef CONFIG_KGDB + if (atomic_read(&debugger_active) && kgdb_may_fault) + kgdb_fault_longjmp(kgdb_fault_jmp_regs); + /* Not reached. */ +#endif return NULL; } Index: linux-2.6.18-rc7/arch/ia64/mm/fault.c =================================================================== --- linux-2.6.18-rc7.orig/arch/ia64/mm/fault.c 2006-10-13 16:56:45.000000000 +0100 +++ linux-2.6.18-rc7/arch/ia64/mm/fault.c 2006-10-13 17:07:06.000000000 +0100 @@ -266,6 +266,10 @@ ia64_do_page_fault (unsigned long addres */ bust_spinlocks(1); + if (notify_die(DIE_PAGE_FAULT_NO_CONTEXT, "no context", regs, + isr, 14, SIGSEGV) == NOTIFY_STOP) + return; + if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address); else Index: linux-2.6.18-rc7/arch/ia64/kernel/kgdb-jmp.S =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/arch/ia64/kernel/kgdb-jmp.S 2006-10-13 17:07:06.000000000 +0100 @@ -0,0 +1,238 @@ +/* setjmp() and longjmp() assembler support for kdb on ia64. + + This code was copied from glibc CVS as of 2001-06-27 and modified where + necessary to fit the kernel. + Keith Owens 2001-06-27 + */ + +/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +GLOBAL_ENTRY(kgdb_fault_setjmp) + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) + alloc loc1=ar.pfs,2,2,2,0 + mov r16=ar.unat + ;; + mov r17=ar.fpsr + mov r2=in0 + add r3=8,in0 + ;; +.mem.offset 0,0; + st8.spill.nta [r2]=sp,16 // r12 (sp) +.mem.offset 8,0; + st8.spill.nta [r3]=gp,16 // r1 (gp) + ;; + st8.nta [r2]=r16,16 // save caller's unat + st8.nta [r3]=r17,16 // save fpsr + add r8=0xa0,in0 + ;; +.mem.offset 160,0; + st8.spill.nta [r2]=r4,16 // r4 +.mem.offset 168,0; + st8.spill.nta [r3]=r5,16 // r5 + add r9=0xb0,in0 + ;; + stf.spill.nta [r8]=f2,32 + stf.spill.nta [r9]=f3,32 + mov loc0=rp + .body + ;; + stf.spill.nta [r8]=f4,32 + stf.spill.nta [r9]=f5,32 + mov r17=b1 + ;; + stf.spill.nta [r8]=f16,32 + stf.spill.nta [r9]=f17,32 + mov r18=b2 + ;; + stf.spill.nta [r8]=f18,32 + stf.spill.nta [r9]=f19,32 + mov r19=b3 + ;; + stf.spill.nta [r8]=f20,32 + stf.spill.nta [r9]=f21,32 + mov r20=b4 + ;; + stf.spill.nta [r8]=f22,32 + stf.spill.nta [r9]=f23,32 + mov r21=b5 + ;; + stf.spill.nta [r8]=f24,32 + stf.spill.nta [r9]=f25,32 + mov r22=ar.lc + ;; + stf.spill.nta [r8]=f26,32 + stf.spill.nta [r9]=f27,32 + mov r24=pr + ;; + stf.spill.nta [r8]=f28,32 + stf.spill.nta [r9]=f29,32 + ;; + stf.spill.nta [r8]=f30 + stf.spill.nta [r9]=f31 + +.mem.offset 0,0; + st8.spill.nta [r2]=r6,16 // r6 +.mem.offset 8,0; + st8.spill.nta [r3]=r7,16 // r7 + ;; + mov r23=ar.bsp + mov r25=ar.unat + st8.nta [r2]=loc0,16 // b0 + st8.nta [r3]=r17,16 // b1 + ;; + st8.nta [r2]=r18,16 // b2 + st8.nta [r3]=r19,16 // b3 + ;; + st8.nta [r2]=r20,16 // b4 + st8.nta [r3]=r21,16 // b5 + ;; + st8.nta [r2]=loc1,16 // ar.pfs + st8.nta [r3]=r22,16 // ar.lc + ;; + st8.nta [r2]=r24,16 // pr + st8.nta [r3]=r23,16 // ar.bsp + ;; + st8.nta [r2]=r25 // ar.unat + st8.nta [r3]=in0 // &__jmp_buf + mov r8=0 + mov rp=loc0 + mov ar.pfs=loc1 + br.ret.sptk.few rp +END(kdba_setjmp) +#define pPos p6 /* is rotate count positive? */ +#define pNeg p7 /* is rotate count negative? */ +GLOBAL_ENTRY(kgdb_fault_longjmp) + alloc r8=ar.pfs,2,1,0,0 + mov r27=ar.rsc + add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr + ;; + ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr + mov r10=ar.bsp + and r11=~0x3,r27 // clear ar.rsc.mode + ;; + flushrs // flush dirty regs to backing store (must be first in insn grp) + ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp + sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf + ;; + ld8 r25=[r2] // r25 <- jmpbuf.ar_unat + extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f + ;; + cmp.lt pNeg,pPos=r8,r0 + mov r2=in0 + ;; +(pPos) mov r16=r8 +(pNeg) add r16=64,r8 +(pPos) sub r17=64,r8 +(pNeg) sub r17=r0,r8 + ;; + mov ar.rsc=r11 // put RSE in enforced lazy mode + shr.u r8=r25,r16 + add r3=8,in0 // r3 <- &jmpbuf.r1 + shl r9=r25,r17 + ;; + or r25=r8,r9 + ;; + mov r26=ar.rnat + mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12) + ;; + ld8.fill.nta sp=[r2],16 // r12 (sp) + ld8.fill.nta gp=[r3],16 // r1 (gp) + dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ;; + ld8.nta r16=[r2],16 // caller's unat + ld8.nta r17=[r3],16 // fpsr + ;; + ld8.fill.nta r4=[r2],16 // r4 + ld8.fill.nta r5=[r3],16 // r5 (gp) + cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp) + ;; + ld8.fill.nta r6=[r2],16 // r6 + ld8.fill.nta r7=[r3],16 // r7 + ;; + mov ar.unat=r16 // restore caller's unat + mov ar.fpsr=r17 // restore fpsr + ;; + ld8.nta r16=[r2],16 // b0 + ld8.nta r17=[r3],16 // b1 + ;; +(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp) + mov ar.bspstore=r23 // restore ar.bspstore + ;; + ld8.nta r18=[r2],16 // b2 + ld8.nta r19=[r3],16 // b3 + ;; + ld8.nta r20=[r2],16 // b4 + ld8.nta r21=[r3],16 // b5 + ;; + ld8.nta r11=[r2],16 // ar.pfs + ld8.nta r22=[r3],56 // ar.lc + ;; + ld8.nta r24=[r2],32 // pr + mov b0=r16 + ;; + ldf.fill.nta f2=[r2],32 + ldf.fill.nta f3=[r3],32 + mov b1=r17 + ;; + ldf.fill.nta f4=[r2],32 + ldf.fill.nta f5=[r3],32 + mov b2=r18 + ;; + ldf.fill.nta f16=[r2],32 + ldf.fill.nta f17=[r3],32 + mov b3=r19 + ;; + ldf.fill.nta f18=[r2],32 + ldf.fill.nta f19=[r3],32 + mov b4=r20 + ;; + ldf.fill.nta f20=[r2],32 + ldf.fill.nta f21=[r3],32 + mov b5=r21 + ;; + ldf.fill.nta f22=[r2],32 + ldf.fill.nta f23=[r3],32 + mov ar.lc=r22 + ;; + ldf.fill.nta f24=[r2],32 + ldf.fill.nta f25=[r3],32 + cmp.eq p8,p9=0,in1 + ;; + ldf.fill.nta f26=[r2],32 + ldf.fill.nta f27=[r3],32 + mov ar.pfs=r11 + ;; + ldf.fill.nta f28=[r2],32 + ldf.fill.nta f29=[r3],32 + ;; + ldf.fill.nta f30=[r2] + ldf.fill.nta f31=[r3] +(p8) mov r8=1 + + mov ar.rnat=r26 // restore ar.rnat + ;; + mov ar.rsc=r27 // restore ar.rsc +(p9) mov r8=in1 + + invala // virt. -> phys. regnum mapping may change + mov pr=r24,-1 + br.ret.sptk.few rp +END(kgdb_fault_longjmp) Index: linux-2.6.18-rc7/arch/ia64/kernel/kgdb.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/arch/ia64/kernel/kgdb.c 2006-10-13 17:07:06.000000000 +0100 @@ -0,0 +1,1131 @@ +/* + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +/* + * Copyright (C) 2000-2001 VERITAS Software Corporation. + * (c) Copyright 2005 Hewlett-Packard Development Company, L.P. + * Bob Picco + */ +/* + * Contributor: Lake Stevens Instrument Division$ + * Written by: Glenn Engel $ + * Updated by: Amit Kale + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include + +#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 BREAK_INSTR_ALIGN (~0xfULL) + +#define ptoff(V) ((unsigned int) &((struct pt_regs *)0x0)->V) +struct reg_to_ptreg_index { + unsigned int reg; + unsigned int ptregoff; +}; + +static struct reg_to_ptreg_index gr_reg_to_ptreg_index[] = { + {IA64_GR0_REGNUM + 1, ptoff(r1)}, + {IA64_GR0_REGNUM + 2, ptoff(r2)}, + {IA64_GR0_REGNUM + 3, ptoff(r3)}, + {IA64_GR0_REGNUM + 8, ptoff(r8)}, + {IA64_GR0_REGNUM + 9, ptoff(r9)}, + {IA64_GR0_REGNUM + 10, ptoff(r10)}, + {IA64_GR0_REGNUM + 11, ptoff(r11)}, + {IA64_GR0_REGNUM + 12, ptoff(r12)}, + {IA64_GR0_REGNUM + 13, ptoff(r13)}, + {IA64_GR0_REGNUM + 14, ptoff(r14)}, + {IA64_GR0_REGNUM + 15, ptoff(r15)}, + {IA64_GR0_REGNUM + 16, ptoff(r16)}, + {IA64_GR0_REGNUM + 17, ptoff(r17)}, + {IA64_GR0_REGNUM + 18, ptoff(r18)}, + {IA64_GR0_REGNUM + 19, ptoff(r19)}, + {IA64_GR0_REGNUM + 20, ptoff(r20)}, + {IA64_GR0_REGNUM + 21, ptoff(r21)}, + {IA64_GR0_REGNUM + 22, ptoff(r22)}, + {IA64_GR0_REGNUM + 23, ptoff(r23)}, + {IA64_GR0_REGNUM + 24, ptoff(r24)}, + {IA64_GR0_REGNUM + 25, ptoff(r25)}, + {IA64_GR0_REGNUM + 26, ptoff(r26)}, + {IA64_GR0_REGNUM + 27, ptoff(r27)}, + {IA64_GR0_REGNUM + 28, ptoff(r28)}, + {IA64_GR0_REGNUM + 29, ptoff(r29)}, + {IA64_GR0_REGNUM + 30, ptoff(r30)}, + {IA64_GR0_REGNUM + 31, ptoff(r31)}, +}; + +static struct reg_to_ptreg_index br_reg_to_ptreg_index[] = { + {IA64_BR0_REGNUM, ptoff(b0)}, + {IA64_BR0_REGNUM + 6, ptoff(b6)}, + {IA64_BR0_REGNUM + 7, ptoff(b7)}, +}; + +static struct reg_to_ptreg_index ar_reg_to_ptreg_index[] = { + {IA64_PFS_REGNUM, ptoff(ar_pfs)}, + {IA64_UNAT_REGNUM, ptoff(ar_unat)}, + {IA64_RNAT_REGNUM, ptoff(ar_rnat)}, + {IA64_BSPSTORE_REGNUM, ptoff(ar_bspstore)}, + {IA64_RSC_REGNUM, ptoff(ar_rsc)}, + {IA64_CSD_REGNUM, ptoff(ar_csd)}, + {IA64_SSD_REGNUM, ptoff(ar_ssd)}, + {IA64_FPSR_REGNUM, ptoff(ar_fpsr)}, + {IA64_CCV_REGNUM, ptoff(ar_ccv)}, +}; + +extern atomic_t cpu_doing_single_step; + +static int kgdb_gr_reg(int regnum, struct unw_frame_info *info, + unsigned long *reg, int rw) +{ + char nat; + + if ((regnum >= IA64_GR0_REGNUM && regnum <= (IA64_GR0_REGNUM + 1)) || + (regnum >= (IA64_GR0_REGNUM + 4) && + regnum <= (IA64_GR0_REGNUM + 7))) + return !unw_access_gr(info, regnum - IA64_GR0_REGNUM, + reg, &nat, rw); + else + return 0; +} +static int kgdb_gr_ptreg(int regnum, struct pt_regs * ptregs, + struct unw_frame_info *info, unsigned long *reg, int rw) +{ + int i, result = 1; + char nat; + + if (!((regnum >= (IA64_GR0_REGNUM + 2) && + regnum <= (IA64_GR0_REGNUM + 3)) || + (regnum >= (IA64_GR0_REGNUM + 8) && + regnum <= (IA64_GR0_REGNUM + 15)) || + (regnum >= (IA64_GR0_REGNUM + 16) && + regnum <= (IA64_GR0_REGNUM + 31)))) + return 0; + else if (rw && ptregs) { + for (i = 0; i < ARRAY_SIZE(gr_reg_to_ptreg_index); i++) + if (gr_reg_to_ptreg_index[i].reg == regnum) { + *((unsigned long *)(((void *)ptregs) + + gr_reg_to_ptreg_index[i].ptregoff)) = *reg; + break; + } + } else if (!rw && ptregs) { + for (i = 0; i < ARRAY_SIZE(gr_reg_to_ptreg_index); i++) + if (gr_reg_to_ptreg_index[i].reg == regnum) { + *reg = *((unsigned long *) + (((void *)ptregs) + + gr_reg_to_ptreg_index[i].ptregoff)); + break; + } + } else + result = !unw_access_gr(info, regnum - IA64_GR0_REGNUM, + reg, &nat, rw); + return result; +} + +static int kgdb_br_reg(int regnum, struct pt_regs * ptregs, + struct unw_frame_info *info, unsigned long *reg, int rw) +{ + int i, result = 1; + + if (!(regnum >= IA64_BR0_REGNUM && regnum <= (IA64_BR0_REGNUM + 7))) + return 0; + + switch (regnum) { + case IA64_BR0_REGNUM: + case IA64_BR0_REGNUM + 6: + case IA64_BR0_REGNUM + 7: + if (rw) { + for (i = 0; i < ARRAY_SIZE(br_reg_to_ptreg_index); i++) + if (br_reg_to_ptreg_index[i].reg == regnum) { + *((unsigned long *) + (((void *)ptregs) + + br_reg_to_ptreg_index[i].ptregoff)) = + *reg; + break; + } + } else + for (i = 0; i < ARRAY_SIZE(br_reg_to_ptreg_index); i++) + if (br_reg_to_ptreg_index[i].reg == regnum) { + *reg = *((unsigned long *) + (((void *)ptregs) + + br_reg_to_ptreg_index[i]. + ptregoff)); + break; + } + break; + case IA64_BR0_REGNUM + 1: + case IA64_BR0_REGNUM + 2: + case IA64_BR0_REGNUM + 3: + case IA64_BR0_REGNUM + 4: + case IA64_BR0_REGNUM + 5: + result = !unw_access_br(info, regnum - IA64_BR0_REGNUM, + reg, rw); + break; + } + + return result; +} + +static int kgdb_fr_reg(int regnum, char *inbuffer, struct pt_regs * ptregs, + struct unw_frame_info *info, unsigned long *reg, + struct ia64_fpreg *freg, int rw) +{ + int result = 1; + + if (!(regnum >= IA64_FR0_REGNUM && regnum <= (IA64_FR0_REGNUM + 127))) + return 0; + + switch (regnum) { + case IA64_FR0_REGNUM + 6: + case IA64_FR0_REGNUM + 7: + case IA64_FR0_REGNUM + 8: + case IA64_FR0_REGNUM + 9: + case IA64_FR0_REGNUM + 10: + case IA64_FR0_REGNUM + 11: + case IA64_FR0_REGNUM + 12: + if (rw) { + char *ptr = inbuffer; + + freg->u.bits[0] = *reg; + kgdb_hex2long(&ptr, &freg->u.bits[1]); + *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM + 6))) = + *freg; + break; + } else if (!ptregs) + result = !unw_access_fr(info, regnum - IA64_FR0_REGNUM, + freg, rw); + else + *freg = + *(&ptregs->f6 + (regnum - (IA64_FR0_REGNUM + 6))); + break; + default: + if (!rw) + result = !unw_access_fr(info, regnum - IA64_FR0_REGNUM, + freg, rw); + else + result = 0; + break; + } + + return result; +} + +static int kgdb_ar_reg(int regnum, struct pt_regs * ptregs, + struct unw_frame_info *info, unsigned long *reg, int rw) +{ + int result = 0, i; + + if (!(regnum >= IA64_AR0_REGNUM && regnum <= IA64_EC_REGNUM)) + return 0; + + if (rw && ptregs) { + for (i = 0; i < ARRAY_SIZE(ar_reg_to_ptreg_index); i++) + if (ar_reg_to_ptreg_index[i].reg == regnum) { + *((unsigned long *) (((void *)ptregs) + + ar_reg_to_ptreg_index[i].ptregoff)) = + *reg; + result = 1; + break; + } + } else if (ptregs) { + for (i = 0; i < ARRAY_SIZE(ar_reg_to_ptreg_index); i++) + if (ar_reg_to_ptreg_index[i].reg == regnum) { + *reg = *((unsigned long *) (((void *)ptregs) + + ar_reg_to_ptreg_index[i].ptregoff)); + result = 1; + break; + } + } + + if (result) + return result; + + result = 1; + + switch (regnum) { + case IA64_CSD_REGNUM: + result = !unw_access_ar(info, UNW_AR_CSD, reg, rw); + break; + case IA64_SSD_REGNUM: + result = !unw_access_ar(info, UNW_AR_SSD, reg, rw); + break; + case IA64_UNAT_REGNUM: + result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw); + break; + case IA64_RNAT_REGNUM: + result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw); + break; + case IA64_BSPSTORE_REGNUM: + result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw); + break; + case IA64_PFS_REGNUM: + result = !unw_access_ar(info, UNW_AR_RNAT, reg, rw); + break; + case IA64_LC_REGNUM: + result = !unw_access_ar(info, UNW_AR_LC, reg, rw); + break; + case IA64_EC_REGNUM: + result = !unw_access_ar(info, UNW_AR_EC, reg, rw); + break; + case IA64_FPSR_REGNUM: + result = !unw_access_ar(info, UNW_AR_FPSR, reg, rw); + break; + case IA64_RSC_REGNUM: + result = !unw_access_ar(info, UNW_AR_RSC, reg, rw); + break; + case IA64_CCV_REGNUM: + result = !unw_access_ar(info, UNW_AR_CCV, reg, rw); + break; + default: + result = 0; + } + + return result; +} + +void kgdb_get_reg(char *outbuffer, int regnum, struct unw_frame_info *info, + struct pt_regs *ptregs) +{ + unsigned long reg, size = 0, *mem = ® + struct ia64_fpreg freg; + + if (kgdb_gr_reg(regnum, info, ®, 0) || + kgdb_gr_ptreg(regnum, ptregs, info, ®, 0) || + kgdb_br_reg(regnum, ptregs, info, ®, 0) || + kgdb_ar_reg(regnum, ptregs, info, ®, 0)) + size = sizeof(reg); + else if (kgdb_fr_reg(regnum, NULL, ptregs, info, ®, &freg, 0)) { + size = sizeof(freg); + mem = (unsigned long *)&freg; + } else if (regnum == IA64_IP_REGNUM) { + if (!ptregs) { + unw_get_ip(info, ®); + size = sizeof(reg); + } else { + reg = ptregs->cr_iip; + size = sizeof(reg); + } + } else if (regnum == IA64_CFM_REGNUM) { + if (!ptregs) + unw_get_cfm(info, ®); + else + reg = ptregs->cr_ifs; + size = sizeof(reg); + } else if (regnum == IA64_PSR_REGNUM) { + if (!ptregs && kgdb_usethread) + ptregs = (struct pt_regs *) + ((unsigned long)kgdb_usethread + + IA64_STK_OFFSET) - 1; + if (ptregs) + reg = ptregs->cr_ipsr; + size = sizeof(reg); + } else if (regnum == IA64_PR_REGNUM) { + if (ptregs) + reg = ptregs->pr; + else + unw_access_pr(info, ®, 0); + size = sizeof(reg); + } else if (regnum == IA64_BSP_REGNUM) { + unw_get_bsp(info, ®); + size = sizeof(reg); + } + + if (size) { + kgdb_mem2hex((char *) mem, outbuffer, size); + outbuffer[size*2] = 0; + } + else + strcpy(outbuffer, "E0"); + + return; +} + +void kgdb_put_reg(char *inbuffer, char *outbuffer, int regnum, + struct unw_frame_info *info, struct pt_regs *ptregs) +{ + unsigned long reg; + struct ia64_fpreg freg; + char *ptr = inbuffer; + + kgdb_hex2long(&ptr, ®); + strcpy(outbuffer, "OK"); + + if (kgdb_gr_reg(regnum, info, ®, 1) || + kgdb_gr_ptreg(regnum, ptregs, info, ®, 1) || + kgdb_br_reg(regnum, ptregs, info, ®, 1) || + kgdb_fr_reg(regnum, inbuffer, ptregs, info, ®, &freg, 1) || + kgdb_ar_reg(regnum, ptregs, info, ®, 1)) ; + else if (regnum == IA64_IP_REGNUM) + ptregs->cr_iip = reg; + else if (regnum == IA64_CFM_REGNUM) + ptregs->cr_ifs = reg; + else if (regnum == IA64_PSR_REGNUM) + ptregs->cr_ipsr = reg; + else if (regnum == IA64_PR_REGNUM) + ptregs->pr = reg; + else + strcpy(outbuffer, "E01"); + return; +} + +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ +} + +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + +} + +#define MAX_HW_BREAKPOINT (20) +long hw_break_total_dbr, hw_break_total_ibr; +#define HW_BREAKPOINT (hw_break_total_dbr + hw_break_total_ibr) +#define WATCH_INSTRUCTION 0x0 +#define WATCH_WRITE 0x1 +#define WATCH_READ 0x2 +#define WATCH_ACCESS 0x3 + +#define HWCAP_DBR ((1 << WATCH_WRITE) | (1 << WATCH_READ)) +#define HWCAP_IBR (1 << WATCH_INSTRUCTION) +struct hw_breakpoint { + unsigned enabled; + unsigned long capable; + unsigned long type; + unsigned long mask; + unsigned long addr; +} *breakinfo; + +static struct hw_breakpoint hwbreaks[MAX_HW_BREAKPOINT]; + +enum instruction_type { A, I, M, F, B, L, X, u }; + +static enum instruction_type bundle_encoding[32][3] = { + {M, I, I}, /* 00 */ + {M, I, I}, /* 01 */ + {M, I, I}, /* 02 */ + {M, I, I}, /* 03 */ + {M, L, X}, /* 04 */ + {M, L, X}, /* 05 */ + {u, u, u}, /* 06 */ + {u, u, u}, /* 07 */ + {M, M, I}, /* 08 */ + {M, M, I}, /* 09 */ + {M, M, I}, /* 0A */ + {M, M, I}, /* 0B */ + {M, F, I}, /* 0C */ + {M, F, I}, /* 0D */ + {M, M, F}, /* 0E */ + {M, M, F}, /* 0F */ + {M, I, B}, /* 10 */ + {M, I, B}, /* 11 */ + {M, B, B}, /* 12 */ + {M, B, B}, /* 13 */ + {u, u, u}, /* 14 */ + {u, u, u}, /* 15 */ + {B, B, B}, /* 16 */ + {B, B, B}, /* 17 */ + {M, M, B}, /* 18 */ + {M, M, B}, /* 19 */ + {u, u, u}, /* 1A */ + {u, u, u}, /* 1B */ + {M, F, B}, /* 1C */ + {M, F, B}, /* 1D */ + {u, u, u}, /* 1E */ + {u, u, u}, /* 1F */ +}; + +int kgdb_validate_break_address(unsigned long addr) +{ + int error; + char tmp_variable[BREAK_INSTR_SIZE]; + error = kgdb_get_mem((char *)(addr & BREAK_INSTR_ALIGN), tmp_variable, + BREAK_INSTR_SIZE); + return error; +} + +int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) +{ + extern unsigned long _start[]; + unsigned long slot = addr & BREAK_INSTR_ALIGN, bundle_addr; + unsigned long template; + struct bundle { + struct { + unsigned long long template:5; + unsigned long long slot0:41; + unsigned long long slot1_p0:64 - 46; + } quad0; + struct { + unsigned long long slot1_p1:41 - (64 - 46); + unsigned long long slot2:41; + } quad1; + } bundle; + int ret; + + bundle_addr = addr & ~0xFULL; + + if (bundle_addr == (unsigned long)_start) + return 0; + + ret = kgdb_get_mem((char *)bundle_addr, (char *)&bundle, + BREAK_INSTR_SIZE); + if (ret < 0) + return ret; + + if (slot > 2) + slot = 0; + + memcpy(saved_instr, &bundle, BREAK_INSTR_SIZE); + template = bundle.quad0.template; + + if (slot == 1 && bundle_encoding[template][1] == L) + slot = 2; + + switch (slot) { + case 0: + bundle.quad0.slot0 = BREAKNUM; + break; + case 1: + bundle.quad0.slot1_p0 = BREAKNUM; + bundle.quad1.slot1_p1 = (BREAKNUM >> (64 - 46)); + break; + case 2: + bundle.quad1.slot2 = BREAKNUM; + break; + } + + return kgdb_set_mem((char *)bundle_addr, (char *)&bundle, + BREAK_INSTR_SIZE); +} + +int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) +{ + extern unsigned long _start[]; + + addr = addr & BREAK_INSTR_ALIGN; + if (addr == (unsigned long)_start) + return 0; + return kgdb_set_mem((char *)addr, (char *)bundle, BREAK_INSTR_SIZE); +} + +static int hw_breakpoint_init; + +void do_init_hw_break(void) +{ + s64 status; + int i; + + hw_breakpoint_init = 1; + +#ifdef CONFIG_IA64_HP_SIM + hw_break_total_ibr = 8; + hw_break_total_dbr = 8; + status = 0; +#else + status = ia64_pal_debug_info(&hw_break_total_ibr, &hw_break_total_dbr); +#endif + + if (status) { + printk(KERN_INFO "do_init_hw_break: pal call failed %d\n", + (int)status); + return; + } + + if (HW_BREAKPOINT > MAX_HW_BREAKPOINT) { + printk(KERN_INFO "do_init_hw_break: %d exceeds max %d\n", + (int)HW_BREAKPOINT, (int)MAX_HW_BREAKPOINT); + + while ((HW_BREAKPOINT > MAX_HW_BREAKPOINT) + && hw_break_total_ibr != 1) + hw_break_total_ibr--; + while (HW_BREAKPOINT > MAX_HW_BREAKPOINT) + hw_break_total_dbr--; + } + + breakinfo = hwbreaks; + + memset(breakinfo, 0, HW_BREAKPOINT * sizeof(struct hw_breakpoint)); + + for (i = 0; i < hw_break_total_dbr; i++) + breakinfo[i].capable = HWCAP_DBR; + + for (; i < HW_BREAKPOINT; i++) + breakinfo[i].capable = HWCAP_IBR; + + return; +} + +void kgdb_correct_hw_break(void) +{ + int breakno; + + if (!breakinfo) + return; + + for (breakno = 0; breakno < HW_BREAKPOINT; breakno++) { + if (breakinfo[breakno].enabled) { + if (breakinfo[breakno].capable & HWCAP_IBR) { + int ibreakno = breakno - hw_break_total_dbr; + ia64_set_ibr(ibreakno << 1, + breakinfo[breakno].addr); + ia64_set_ibr((ibreakno << 1) + 1, + (~breakinfo[breakno].mask & + ((1UL << 56UL) - 1)) | + (1UL << 56UL) | (1UL << 63UL)); + } else { + ia64_set_dbr(breakno << 1, + breakinfo[breakno].addr); + ia64_set_dbr((breakno << 1) + 1, + (~breakinfo[breakno]. + mask & ((1UL << 56UL) - 1)) | + (1UL << 56UL) | + (breakinfo[breakno].type << 62UL)); + } + } else { + if (breakinfo[breakno].capable & HWCAP_IBR) + ia64_set_ibr(((breakno - + hw_break_total_dbr) << 1) + 1, + 0); + else + ia64_set_dbr((breakno << 1) + 1, 0); + } + } + + return; +} + +int hardware_breakpoint(unsigned long addr, int length, int type, int action) +{ + int breakno, found, watch; + unsigned long mask; + extern unsigned long _start[]; + + if (!hw_breakpoint_init) + do_init_hw_break(); + + if (!breakinfo) + return 0; + else if (addr == (unsigned long)_start) + return 1; + + if (type == WATCH_ACCESS) + mask = HWCAP_DBR; + else + mask = 1UL << type; + + for (watch = 0, found = 0, breakno = 0; breakno < HW_BREAKPOINT; + breakno++) { + if (action) { + if (breakinfo[breakno].enabled + || !(breakinfo[breakno].capable & mask)) + continue; + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].mask = length - 1; + breakinfo[breakno].addr = addr; + watch = breakno; + } else if (breakinfo[breakno].enabled && + ((length < 0 && breakinfo[breakno].addr == addr) || + ((breakinfo[breakno].capable & mask) && + (breakinfo[breakno].mask == (length - 1)) && + (breakinfo[breakno].addr == addr)))) { + breakinfo[breakno].enabled = 0; + breakinfo[breakno].type = 0UL; + } else + continue; + found++; + if (type != WATCH_ACCESS) + break; + else if (found == 2) + break; + else + mask = HWCAP_IBR; + } + + if (type == WATCH_ACCESS && found == 1) { + breakinfo[watch].enabled = 0; + found = 0; + } + + mb(); + return found; +} + +int kgdb_arch_set_hw_breakpoint(unsigned long addr, int len, + enum kgdb_bptype type) +{ + return hardware_breakpoint(addr, len, type - '1', 1); +} + +int kgdb_arch_remove_hw_breakpoint(unsigned long addr, int len, + enum kgdb_bptype type) +{ + return hardware_breakpoint(addr, len, type - '1', 0); +} + +int kgdb_remove_hw_break(unsigned long addr) +{ + return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 0); + +} + +void kgdb_remove_all_hw_break(void) +{ + int i; + + for (i = 0; i < HW_BREAKPOINT; i++) + memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); +} + +int kgdb_set_hw_break(unsigned long addr) +{ + return hardware_breakpoint(addr, 8, WATCH_INSTRUCTION, 1); +} + +void kgdb_disable_hw_debug(struct pt_regs *regs) +{ + unsigned long hw_breakpoint_status; + + hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR); + if (hw_breakpoint_status & IA64_PSR_DB) + ia64_setreg(_IA64_REG_PSR_L, + hw_breakpoint_status ^ IA64_PSR_DB); +} + +volatile static struct smp_unw { + struct unw_frame_info *unw; + struct task_struct *task; +} smp_unw[NR_CPUS]; + +static int inline kgdb_get_blocked_state(struct task_struct *p, + struct unw_frame_info *unw) +{ + unsigned long ip; + int count = 0; + + unw_init_from_blocked_task(unw, p); + ip = 0UL; + do { + if (unw_unwind(unw) < 0) + return -1; + unw_get_ip(unw, &ip); + if (!in_sched_functions(ip)) + break; + } while (count++ < 16); + + if (!ip) + return -1; + else + return 0; +} + +static void inline kgdb_wait(struct pt_regs *regs) +{ + unsigned long hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR); + if (hw_breakpoint_status & IA64_PSR_DB) + ia64_setreg(_IA64_REG_PSR_L, + hw_breakpoint_status ^ IA64_PSR_DB); + kgdb_nmihook(smp_processor_id(), regs); + if (hw_breakpoint_status & IA64_PSR_DB) + ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status); + + return; +} + +static void inline normalize(struct unw_frame_info *running, + struct pt_regs *regs) +{ + unsigned long sp; + + do { + unw_get_sp(running, &sp); + if ((sp + 0x10) >= (unsigned long)regs) + break; + } while (unw_unwind(running) >= 0); + + return; +} + +static void kgdb_init_running(struct unw_frame_info *unw, void *data) +{ + struct pt_regs *regs; + + regs = data; + normalize(unw, regs); + smp_unw[smp_processor_id()].unw = unw; + kgdb_wait(regs); +} + +void kgdb_wait_ipi(struct pt_regs *regs) +{ + struct unw_frame_info unw; + + smp_unw[smp_processor_id()].task = current; + + if (user_mode(regs)) { + smp_unw[smp_processor_id()].unw = (struct unw_frame_info *)1; + kgdb_wait(regs); + } else { + if (current->state == TASK_RUNNING) + unw_init_running(kgdb_init_running, regs); + else { + if (kgdb_get_blocked_state(current, &unw)) + smp_unw[smp_processor_id()].unw = + (struct unw_frame_info *)1; + else + smp_unw[smp_processor_id()].unw = &unw; + kgdb_wait(regs); + } + } + + smp_unw[smp_processor_id()].unw = NULL; + return; +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + if (num_online_cpus() > 1) + smp_send_nmi_allbutself(); +} + +static volatile int kgdb_hwbreak_sstep[NR_CPUS]; + +static int kgdb_notify(struct notifier_block *self, unsigned long cmd, + void *ptr) +{ + struct die_args *args = ptr; + struct pt_regs *regs = args->regs; + unsigned long err = args->err; + + switch (cmd) { + default: + return NOTIFY_DONE; + case DIE_PAGE_FAULT_NO_CONTEXT: + if (atomic_read(&debugger_active) && kgdb_may_fault) { + kgdb_fault_longjmp(kgdb_fault_jmp_regs); + return NOTIFY_STOP; + } + break; + case DIE_BREAK: + if (user_mode(regs) || err == 0x80001) + return NOTIFY_DONE; + break; + case DIE_FAULT: + if (user_mode(regs)) + return NOTIFY_DONE; + else if (err == 36 && kgdb_hwbreak_sstep[smp_processor_id()]) { + kgdb_hwbreak_sstep[smp_processor_id()] = 0; + regs->cr_ipsr &= ~IA64_PSR_SS; + return NOTIFY_STOP; + } + case DIE_MCA_MONARCH_PROCESS: + case DIE_INIT_MONARCH_PROCESS: + break; + } + + kgdb_handle_exception(args->trapnr, args->signr, args->err, regs); + return NOTIFY_STOP; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_notify, +}; + +int kgdb_arch_init(void) +{ + atomic_notifier_chain_register(&ia64die_chain, &kgdb_notifier); + return 0; +} + +static void do_kgdb_handle_exception(struct unw_frame_info *, void *data); + +struct kgdb_state { + int e_vector; + int signo; + unsigned long err_code; + struct pt_regs *regs; + struct unw_frame_info *unw; + char *inbuf; + char *outbuf; + int unwind; + int ret; +}; + +static void inline kgdb_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->cr_iip = pc & ~0xf; + ia64_psr(regs)->ri = pc & 0x3; + return; +} + +int kgdb_arch_handle_exception(int e_vector, int signo, + int err_code, char *remcom_in_buffer, + char *remcom_out_buffer, + struct pt_regs *linux_regs) +{ + struct kgdb_state info; + + info.e_vector = e_vector; + info.signo = signo; + info.err_code = err_code; + info.unw = (void *)0; + info.inbuf = remcom_in_buffer; + info.outbuf = remcom_out_buffer; + info.unwind = 0; + info.ret = -1; + + if (remcom_in_buffer[0] == 'c' || remcom_in_buffer[0] == 's') { + info.regs = linux_regs; + do_kgdb_handle_exception(NULL, &info); + } else if (kgdb_usethread == current) { + info.regs = linux_regs; + info.unwind = 1; + unw_init_running(do_kgdb_handle_exception, &info); + } else if (kgdb_usethread->state != TASK_RUNNING) { + struct unw_frame_info unw_info; + + if (kgdb_get_blocked_state(kgdb_usethread, &unw_info)) { + info.ret = 1; + goto bad; + } + info.regs = NULL; + do_kgdb_handle_exception(&unw_info, &info); + } else { + int i; + + for (i = 0; i < NR_CPUS; i++) + if (smp_unw[i].task == kgdb_usethread && smp_unw[i].unw + && smp_unw[i].unw != (struct unw_frame_info *)1) { + info.regs = NULL; + do_kgdb_handle_exception(smp_unw[i].unw, &info); + break; + } else { + info.ret = 1; + goto bad; + } + } + + bad: + if (info.ret != -1 && remcom_in_buffer[0] == 'p') { + unsigned long bad = 0xbad4badbadbadbadUL; + + printk("kgdb_arch_handle_exception: p packet bad (%s)\n", + remcom_in_buffer); + kgdb_mem2hex((char *)&bad, remcom_out_buffer, sizeof(bad)); + remcom_out_buffer[sizeof(bad) * 2] = 0; + info.ret = -1; + } + return info.ret; +} + +/* + * This is done because I evidently made an incorrect 'p' encoding + * when my patch for gdb was committed. It was later corrected. This + * check supports both my wrong encoding of the register number and + * the correct encoding. Eventually this should be eliminated and + * kgdb_hex2long should be demarshalling the regnum. + */ +static inline int check_packet(unsigned int regnum, char *packet) +{ + static int check_done, swap; + unsigned long reglong; + + if (likely(check_done)) { + if (swap) { + kgdb_hex2long(&packet, ®long); + regnum = (int) reglong; + } + + } else { + if (regnum > NUM_REGS) { + kgdb_hex2long(&packet, ®long); + regnum = (int) reglong; + swap = 1; + } + check_done = 1; + } + return regnum; +} + +static void do_kgdb_handle_exception(struct unw_frame_info *unw_info, + void *data) +{ + long addr; + char *ptr; + unsigned long newPC; + int e_vector, signo; + unsigned long err_code; + struct pt_regs *linux_regs; + struct kgdb_state *info; + char *remcom_in_buffer, *remcom_out_buffer; + + info = data; + info->unw = unw_info; + e_vector = info->e_vector; + signo = info->signo; + err_code = info->err_code; + remcom_in_buffer = info->inbuf; + remcom_out_buffer = info->outbuf; + linux_regs = info->regs; + + if (info->unwind) + normalize(unw_info, linux_regs); + + switch (remcom_in_buffer[0]) { + case 'p': + { + unsigned int regnum; + + kgdb_hex2mem(&remcom_in_buffer[1], (char *)®num, + sizeof(regnum)); + regnum = check_packet(regnum, &remcom_in_buffer[1]); + if (regnum >= NUM_REGS) { + remcom_out_buffer[0] = 'E'; + remcom_out_buffer[1] = 0; + } else + kgdb_get_reg(remcom_out_buffer, regnum, + unw_info, linux_regs); + break; + } + case 'P': + { + unsigned int regno; + long v; + char *ptr; + + ptr = &remcom_in_buffer[1]; + if ((!kgdb_usethread || kgdb_usethread == current) && + kgdb_hex2long(&ptr, &v) && + *ptr++ == '=' && (v >= 0)) { + regno = (unsigned int)v; + regno = (regno >= NUM_REGS ? 0 : regno); + kgdb_put_reg(ptr, remcom_out_buffer, regno, + unw_info, linux_regs); + } else + strcpy(remcom_out_buffer, "E01"); + break; + } + case 'c': + case 's': + if (e_vector == TRAP_BRKPT && err_code == KGDBBREAKNUM) { + if (ia64_psr(linux_regs)->ri < 2) + kgdb_pc(linux_regs, linux_regs->cr_iip + + ia64_psr(linux_regs)->ri + 1); + else + kgdb_pc(linux_regs, linux_regs->cr_iip + 16); + } + + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) { + linux_regs->cr_iip = addr; + } + newPC = linux_regs->cr_iip; + + /* clear the trace bit */ + linux_regs->cr_ipsr &= ~IA64_PSR_SS; + + atomic_set(&cpu_doing_single_step, -1); + + /* set the trace bit if we're stepping or took a hardware break */ + if (remcom_in_buffer[0] == 's' || e_vector == TRAP_HWBKPT) { + linux_regs->cr_ipsr |= IA64_PSR_SS; + debugger_step = 1; + if (kgdb_contthread) + atomic_set(&cpu_doing_single_step, + smp_processor_id()); + } + + kgdb_correct_hw_break(); + + /* if not hardware breakpoint, then reenable them */ + if (e_vector != TRAP_HWBKPT) + linux_regs->cr_ipsr |= IA64_PSR_DB; + else { + kgdb_hwbreak_sstep[smp_processor_id()] = 1; + linux_regs->cr_ipsr &= ~IA64_PSR_DB; + } + + info->ret = 0; + break; + default: + break; + } + + return; +} + +struct kgdb_arch arch_kgdb_ops = { + .set_hw_breakpoint = kgdb_arch_set_hw_breakpoint, + .remove_hw_breakpoint = kgdb_arch_remove_hw_breakpoint, + .gdb_bpt_instr = {0xcc}, + .flags = KGDB_HW_BREAKPOINT, +}; Index: linux-2.6.18-rc7/arch/ia64/kernel/smp.c =================================================================== --- linux-2.6.18-rc7.orig/arch/ia64/kernel/smp.c 2006-10-13 16:56:45.000000000 +0100 +++ linux-2.6.18-rc7/arch/ia64/kernel/smp.c 2006-10-13 17:07:06.000000000 +0100 @@ -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; @@ -155,6 +159,11 @@ handle_IPI (int irq, void *dev_id, struc case IPI_CPU_STOP: stop_this_cpu(); break; +#ifdef CONFIG_KGDB + case IPI_KGDB_INTERRUPT: + kgdb_wait_ipi(regs); + break; +#endif default: printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); @@ -305,6 +314,14 @@ smp_call_function_single (int cpuid, voi } EXPORT_SYMBOL(smp_call_function_single); +#ifdef CONFIG_KGDB +void +smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(IPI_KGDB_INTERRUPT); +} +#endif + /* * this function sends a 'generic call function' IPI to all other CPUs * in the system. Index: linux-2.6.18-rc7/arch/ia64/kernel/traps.c =================================================================== --- linux-2.6.18-rc7.orig/arch/ia64/kernel/traps.c 2006-10-13 16:56:45.000000000 +0100 +++ linux-2.6.18-rc7/arch/ia64/kernel/traps.c 2006-10-13 17:07:06.000000000 +0100 @@ -200,8 +200,12 @@ __kprobes ia64_bad_break (unsigned long break; default: - if (break_num < 0x40000 || break_num > 0x100000) + if (break_num < 0x40000 || break_num > 0x100000) { + if (notify_die(DIE_BREAK, "bad break", regs, + break_num, TRAP_BRKPT, SIGTRAP) == NOTIFY_STOP) + return; die_if_kernel("Bad break", regs, break_num); + } if (break_num < 0x80000) { sig = SIGILL; code = __ILL_BREAK; Index: linux-2.6.18-rc7/arch/ia64/kernel/Makefile =================================================================== --- linux-2.6.18-rc7.orig/arch/ia64/kernel/Makefile 2006-10-13 16:56:45.000000000 +0100 +++ linux-2.6.18-rc7/arch/ia64/kernel/Makefile 2006-10-13 17:07:06.000000000 +0100 @@ -31,6 +31,7 @@ obj-$(CONFIG_KPROBES) += kprobes.o jpro obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o obj-$(CONFIG_AUDIT) += audit.o mca_recovery-y += mca_drv.o mca_drv_asm.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o # The gate DSO image is built using a special linker script. targets += gate.so gate-syms.o Index: linux-2.6.18-rc7/lib/Kconfig.debug =================================================================== --- linux-2.6.18-rc7.orig/lib/Kconfig.debug 2006-10-13 17:07:03.000000000 +0100 +++ linux-2.6.18-rc7/lib/Kconfig.debug 2006-10-13 17:07:06.000000000 +0100 @@ -378,7 +378,7 @@ config WANT_EXTRA_DEBUG_INFORMATION config KGDB bool "KGDB: kernel debugging with remote gdb" select WANT_EXTRA_DEBUG_INFORMATION - depends on DEBUG_KERNEL && (X86 || MIPS || PPC) + depends on DEBUG_KERNEL && (X86 || MIPS || IA64 || PPC) help If you say Y here, it will be possible to remotely debug the kernel using gdb. It is strongly suggested that you enable Index: linux-2.6.18-rc7/include/asm-ia64/kgdb.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/include/asm-ia64/kgdb.h 2006-10-13 17:07:06.000000000 +0100 @@ -0,0 +1,36 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +/* + * Copyright (C) 2001-2004 Amit S. Kale + */ + +#include + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 1024 + +/* Number of bytes of registers. We set this to 0 so that certain GDB + * packets will fail, forcing the use of others, which are more friendly + * on ia64. */ +#define NUMREGBYTES 0 + +#define NUMCRITREGBYTES (70*8) +#define JMP_REGS_ALIGNMENT __attribute__ ((aligned (16))) + +#define BREAKNUM 0x00003333300LL +#define KGDBBREAKNUM 0x6665UL +#define BREAKPOINT() asm volatile ("break.m 0x6665") +#define BREAK_INSTR_SIZE 16 +#define CACHE_FLUSH_IS_SAFE 1 + +struct pt_regs; +extern volatile int kgdb_hwbreak_sstep[NR_CPUS]; +extern void smp_send_nmi_allbutself(void); +extern void kgdb_wait_ipi(struct pt_regs *); +#endif /* _ASM_KGDB_H_ */ +#endif /* __KERNEL__ */ Index: linux-2.6.18-rc7/include/asm-ia64/kdebug.h =================================================================== --- linux-2.6.18-rc7.orig/include/asm-ia64/kdebug.h 2006-10-13 16:56:45.000000000 +0100 +++ linux-2.6.18-rc7/include/asm-ia64/kdebug.h 2006-10-13 17:07:06.000000000 +0100 @@ -72,6 +72,7 @@ enum die_val { DIE_KDEBUG_LEAVE, DIE_KDUMP_ENTER, DIE_KDUMP_LEAVE, + DIE_PAGE_FAULT_NO_CONTEXT, }; static inline int notify_die(enum die_val val, char *str, struct pt_regs *regs,