Yes It's known issue. On Mon, 05 May 2008 13:06:11 +0300 Avi Nehori <[EMAIL PROTECTED]> wrote:
> Hi, > I'm trying to set a watch point with bpha as follows : > bpha address dataw 4 > but the watchpoint is never called when the memory address is changed. > is it a know bug ? > is there a patch ? Here is my previous mail (http://oss.sgi.com/archives/kdb/2007-11/msg00037.html) with patch that fixes bpha problem: Global HW BP don't work: 1) Install global HW BP [EMAIL PROTECTED]:~# [EMAIL PROTECTED]:~# D Entering kdb (current=0xc04433a0, pid 0) on processor 0 due to Keyboard Entry [0]kdb> bpha do_sync Forced Instruction(Register) BP #0 at 0xc0181539 (do_sync) is enabled in dr0 globally [0]kdb> go -bash: D: command not found [EMAIL PROTECTED]:~# 1) Try CPU 0 [EMAIL PROTECTED]:~# [EMAIL PROTECTED]:~# taskset -c 0 sync Instruction(Register) breakpoint #0 at 0xc0181539 0xc0181539 do_sync: push %ebx Entering kdb (0xc1b6d030, pid 1319) on processor 0 due to Debug @ 0xc0181539 [0]kdb> go [EMAIL PROTECTED]:~# - OK 1) Try CPU 1 [EMAIL PROTECTED]:~# [EMAIL PROTECTED]:~# taskset -c 1 sync [EMAIL PROTECTED]:~# - Doesn't work. Signed-off-by: Konstantin Baydarov <[EMAIL PROTECTED]> Description: This patch adds support for global hardware breakpoints to KDB on x86 targets. Hardware breakpoints are installed by setting per CPU db registers. So to make a hardware breakpoint global it should be installed in db registers on every CPU in system. So global hw bp can't be handle by kdb_bp_install_global and kdb_bp_remove_global because these functions are called only on "monarch" CPU, kdb_bp_install_local and kdb_bp_remove_local should be used instead because these are called for all CPUs. Main changes: - kdb_hardbreaks[KDB_MAXHARDBPT] - The processor architecture hardware breakpoint registers descriptors is defined for every CPU: static kdbhard_bp_t kdb_hardbreaks[NR_CPUS][KDB_MAXHARDBPT]; - "kdb_bp_t" (main breakpint structure) contains hardware breakpoint registers for every CPU: kdbhard_bp_t* bp_hard[NR_CPUS]; - global hardware breakpoint installation and removal is handled by kdb_bp_install_local and kdb_bp_remove_local which are executed on every CPU - kdba_allocbp andkdba_freebp are static, now kdba_alloc_hwbp and kdba_free_hwbp are used for allocating/freeing hardware breakpoint registers. If the hardware breakpoint is global then kdba_alloc_hwbp tries to allocate hardware breakpoint registers on every CPU. If there is no free hardware breakpoint on a CPU the allocation fails. - bph_installed was added to the hardware breakpoint descriptor to track per CPU hardware breakpoint installation. Patch against kernel 2.6.24-rc2 Index: linux-2.6.24-rc2/arch/x86/kdb/kdba_bp_32.c =================================================================== --- linux-2.6.24-rc2.orig/arch/x86/kdb/kdba_bp_32.c +++ linux-2.6.24-rc2/arch/x86/kdb/kdba_bp_32.c @@ -22,10 +22,10 @@ static char *kdba_rwtypes[] = { "Instruc /* * Table describing processor architecture hardware - * breakpoint registers. + * breakpoint registers for every CPU. */ -static kdbhard_bp_t kdb_hardbreaks[KDB_MAXHARDBPT]; +static kdbhard_bp_t kdb_hardbreaks[NR_CPUS][KDB_MAXHARDBPT]; /* * kdba_db_trap @@ -75,6 +75,7 @@ kdba_db_trap(struct pt_regs *regs, int e int i; kdb_dbtrap_t rv = KDB_DB_BPT; kdb_bp_t *bp; + int cpu = smp_processor_id(); if (KDB_NULL_REGS(regs)) return KDB_DB_NOBPT; @@ -103,8 +104,12 @@ kdba_db_trap(struct pt_regs *regs, int e kdb_printf("bp for this cpu\n"); if (bp->bp_delayed) { bp->bp_delayed = 0; - if (KDB_DEBUG(BP)) + if (KDB_DEBUG(BP)){ + /* Can't be hw breakpoint */ + if (bp->bp_hardtype) + kdb_printf("kdb: Error - hw bp delayed\n"); kdb_printf("kdba_installbp\n"); + } kdba_installbp(regs, bp); if (!KDB_STATE(DOING_SS)) { regs->eflags &= ~EF_TF; @@ -211,8 +216,8 @@ handle: for(i=0, bp=kdb_breakpoints; i<KDB_MAXBPT; i++, bp++) { if (!(bp->bp_free) && (bp->bp_global || bp->bp_cpu == smp_processor_id()) - && (bp->bp_hard) - && (bp->bp_hard->bph_reg == reg)) { + && (bp->bp_hard[cpu]) + && (bp->bp_hard[cpu]->bph_reg == reg)) { /* * Hit this breakpoint. */ @@ -438,12 +443,18 @@ kdba_printbpreg(kdbhard_bp_t *bph) void kdba_printbp(kdb_bp_t *bp) { + int cpu; + kdb_printf("\n is enabled"); if (bp->bp_hardtype) { - kdba_printbpreg(bp->bp_hard); - if (bp->bp_hard->bph_mode != 0) { + if (bp->bp_global) + cpu = smp_processor_id(); + else + cpu = bp->bp_cpu; + kdba_printbpreg(bp->bp_hard[cpu]); + if (bp->bp_hard[cpu]->bph_mode != 0) { kdb_printf(" for %d bytes", - bp->bp_hard->bph_length+1); + bp->bp_hard[cpu]->bph_length+1); } } } @@ -556,7 +567,7 @@ kdba_parsebp(int argc, const char **argv /* * kdba_allocbp * - * Associate a hardware register with a breakpoint. + * Allocate hw register for bp on specific CPU * * Parameters: * None. @@ -570,13 +581,14 @@ kdba_parsebp(int argc, const char **argv * Remarks: */ -kdbhard_bp_t * -kdba_allocbp(kdbhard_bp_t *bph, int *diagp) +static kdbhard_bp_t * +kdba_allocbp(kdbhard_bp_t *bph, int *diagp, unsigned int cpu) { int i; kdbhard_bp_t *newbph; - for(i=0,newbph=kdb_hardbreaks; i < KDB_MAXHARDBPT; i++, newbph++) { + for(i=0; i < KDB_MAXHARDBPT; i++) { + newbph=&(kdb_hardbreaks[cpu][i]); if (newbph->bph_free) { break; } @@ -608,9 +620,49 @@ kdba_allocbp(kdbhard_bp_t *bph, int *dia } /* + * kdba_alloc_hwbp + * + * Associate a hardware registers with a breakpoint. + * If hw bp is global hw registers descriptor will be allocated + * on every CPU. + * + * Parameters: + * bp - hardware bp + * diagp - pointer to variable that will store error when + * function complete + * Outputs: + * None. + * Returns: + * None + * Locking: + * None. + * Remarks: + * Should be called with correct bp->bp_template + */ + +void +kdba_alloc_hwbp(kdb_bp_t *bp, int *diagp) +{ + int i; + + if (bp->bp_global){ + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_online(i)) + continue; + bp->bp_hard[i] = kdba_allocbp(&bp->bp_template, diagp, i); + if (*diagp) + break; + } + } else { + bp->bp_hard[bp->bp_cpu] = kdba_allocbp(&bp->bp_template, diagp, bp->bp_cpu); + } + bp->bp_hardtype = 1; +} + +/* * kdba_freebp * - * Deallocate a hardware breakpoint + * Deallocate hw registers descriptor for bp on specific CPU * * Parameters: * None. @@ -623,13 +675,57 @@ kdba_allocbp(kdbhard_bp_t *bph, int *dia * Remarks: */ -void +static void kdba_freebp(kdbhard_bp_t *bph) { bph->bph_free = 1; } /* + * kdba_free_hwbp + * + * Frees allocated hw registers descriptors for bp. + * If hw bp is global, hw registers descriptors will be freed + * on every CPU. + * + * Parameters: + * bp - hardware bp + * Outputs: + * None. + * Returns: + * None + * Locking: + * None. + * Remarks: + * Should be called with correct bp->bp_template + */ + +void +kdba_free_hwbp(kdb_bp_t *bp) +{ + int i; + + /* When kernel enters KDB, first, all local bps + * are removed, so here we don't need to clear + * debug registers. + */ + + if (bp->bp_global){ + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_online(i)) + continue; + if (bp->bp_hard[i]) + kdba_freebp(bp->bp_hard[i]); + bp->bp_hard[i] = 0; + } + } else { + kdba_freebp(bp->bp_hard[bp->bp_cpu]); + bp->bp_hard[bp->bp_cpu] = NULL; + } + bp->bp_hardtype = 0; +} + +/* * kdba_initbp * * Initialize the breakpoint table for the hardware breakpoint @@ -653,7 +749,7 @@ kdba_freebp(kdbhard_bp_t *bph) void kdba_initbp(void) { - int i; + int i,j; kdbhard_bp_t *bph; /* @@ -662,9 +758,15 @@ kdba_initbp(void) memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); - for(i=0,bph=kdb_hardbreaks; i<KDB_MAXHARDBPT; i++, bph++) { - bph->bph_reg = i; - bph->bph_free = 1; + for (i = 0; i < NR_CPUS; ++i) { + /* Called early so we don't know actual + * ammount of CPUs + */ + for(j=0; j < KDB_MAXHARDBPT; j++) { + bph=&(kdb_hardbreaks[i][j]); + bph->bph_reg = j; + bph->bph_free = 1; + } } } @@ -698,6 +800,8 @@ kdba_initbp(void) int kdba_installbp(struct pt_regs *regs, kdb_bp_t *bp) { + int cpu = smp_processor_id(); + /* * Install the breakpoint, if it is not already installed. */ @@ -707,15 +811,27 @@ kdba_installbp(struct pt_regs *regs, kdb } if (!KDB_STATE(SSBPT)) bp->bp_delay = 0; - if (!bp->bp_installed) { - if (bp->bp_hardtype) { + + if (bp->bp_hardtype) { + if (KDB_DEBUG(BP) && !bp->bp_global && cpu != bp->bp_cpu){ + kdb_printf("kdba_removebp: cpu != bp->bp_cpu for local hw bp\n"); + } + + if (KDB_DEBUG(BP) && !bp->bp_hard[cpu]){ + kdb_printf("kdba_removebp: Error - bp_hard[smp_processor_id()] is emply\n"); + return 1; + } + + if (!bp->bp_hard[cpu]->bph_installed){ kdba_installdbreg(bp); - bp->bp_installed = 1; + bp->bp_hard[cpu]->bph_installed = 1; if (KDB_DEBUG(BP)) { kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", - bp->bp_hard->bph_reg, bp->bp_addr); + bp->bp_hard[cpu]->bph_reg, bp->bp_addr); } - } else if (bp->bp_delay) { + } + } else if (!bp->bp_installed) { + if (bp->bp_delay) { if (KDB_DEBUG(BP)) kdb_printf("kdba_installbp delayed bp\n"); kdba_handle_bp(regs, bp); @@ -753,6 +869,8 @@ kdba_installbp(struct pt_regs *regs, kdb int kdba_removebp(kdb_bp_t *bp) { + int cpu = smp_processor_id(); + /* * For hardware breakpoints, remove it from the active register, * for software breakpoints, restore the instruction stream. @@ -760,20 +878,36 @@ kdba_removebp(kdb_bp_t *bp) if (KDB_DEBUG(BP)) { kdb_printf("kdba_removebp bp_installed %d\n", bp->bp_installed); } - if (bp->bp_installed) { - if (bp->bp_hardtype) { + + if (bp->bp_hardtype) { + if (KDB_DEBUG(BP) && !bp->bp_global && cpu != bp->bp_cpu){ + kdb_printf("kdba_removebp: cpu != bp->bp_cpu for local hw bp\n"); + } + + if (KDB_DEBUG(BP) && !bp->bp_hard[cpu]){ + kdb_printf("kdba_removebp: Error - bp_hard[smp_processor_id()] is emply\n"); + return 1; + } + + if (KDB_DEBUG(BP)) { + kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard[cpu]->bph_reg, bp->bp_addr); + } + + if (bp->bp_hard[cpu]->bph_installed){ if (KDB_DEBUG(BP)) { - kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", - bp->bp_hard->bph_reg, bp->bp_addr); + kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard[cpu]->bph_reg, bp->bp_addr); } kdba_removedbreg(bp); - } else { - if (KDB_DEBUG(BP)) - kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", - bp->bp_inst, bp->bp_addr); - if (kdb_putword(bp->bp_addr, bp->bp_inst, 1)) - return(1); + bp->bp_hard[cpu]->bph_installed = 0; } + } else if (bp->bp_installed) { + if (KDB_DEBUG(BP)) + kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", + bp->bp_inst, bp->bp_addr); + if (kdb_putword(bp->bp_addr, bp->bp_inst, 1)) + return(1); bp->bp_installed = 0; } return(0); Index: linux-2.6.24-rc2/arch/x86/kdb/kdbasupport_32.c =================================================================== --- linux-2.6.24-rc2.orig/arch/x86/kdb/kdbasupport_32.c +++ linux-2.6.24-rc2/arch/x86/kdb/kdbasupport_32.c @@ -82,7 +82,7 @@ kdba_putdr(int regnum, kdb_machreg_t con } } -static kdb_machreg_t +kdb_machreg_t kdba_getdr(int regnum) { kdb_machreg_t contents = 0; @@ -142,40 +142,42 @@ kdba_putdr7(kdb_machreg_t contents) void kdba_installdbreg(kdb_bp_t *bp) { + int cpu = smp_processor_id(); + kdb_machreg_t dr7; dr7 = kdba_getdr7(); - kdba_putdr(bp->bp_hard->bph_reg, bp->bp_addr); + kdba_putdr(bp->bp_hard[cpu]->bph_reg, bp->bp_addr); dr7 |= DR7_GE; if (cpu_has_de) set_in_cr4(X86_CR4_DE); - switch (bp->bp_hard->bph_reg){ + switch (bp->bp_hard[cpu]->bph_reg){ case 0: - DR7_RW0SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN0SET(dr7,bp->bp_hard->bph_length); + DR7_RW0SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN0SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G0SET(dr7); break; case 1: - DR7_RW1SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN1SET(dr7,bp->bp_hard->bph_length); + DR7_RW1SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN1SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G1SET(dr7); break; case 2: - DR7_RW2SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN2SET(dr7,bp->bp_hard->bph_length); + DR7_RW2SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN2SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G2SET(dr7); break; case 3: - DR7_RW3SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN3SET(dr7,bp->bp_hard->bph_length); + DR7_RW3SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN3SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G3SET(dr7); break; default: kdb_printf("kdb: Bad debug register!! %ld\n", - bp->bp_hard->bph_reg); + bp->bp_hard[cpu]->bph_reg); break; } @@ -188,11 +190,12 @@ kdba_removedbreg(kdb_bp_t *bp) { int regnum; kdb_machreg_t dr7; + int cpu = smp_processor_id(); - if (!bp->bp_hard) + if (!bp->bp_hard[cpu]) return; - regnum = bp->bp_hard->bph_reg; + regnum = bp->bp_hard[cpu]->bph_reg; dr7 = kdba_getdr7(); Index: linux-2.6.24-rc2/include/linux/kdbprivate.h =================================================================== --- linux-2.6.24-rc2.orig/include/linux/kdbprivate.h +++ linux-2.6.24-rc2/include/linux/kdbprivate.h @@ -204,7 +204,7 @@ typedef struct _kdb_bp { int bp_cpu; /* Cpu # (if bp_global == 0) */ kdbhard_bp_t bp_template; /* Hardware breakpoint template */ - kdbhard_bp_t *bp_hard; /* Hardware breakpoint structure */ + kdbhard_bp_t* bp_hard[NR_CPUS]; /* Hardware breakpoint structure */ int bp_adjust; /* Adjustment to PC for real instruction */ } kdb_bp_t; @@ -219,8 +219,8 @@ extern kdb_bp_t kdb_breakpoints[/* KDB_M */ extern void kdba_initbp(void); extern void kdba_printbp(kdb_bp_t *); -extern kdbhard_bp_t *kdba_allocbp(kdbhard_bp_t *, int *); -extern void kdba_freebp(kdbhard_bp_t *); +extern void kdba_alloc_hwbp(kdb_bp_t *bp, int *diagp); +extern void kdba_free_hwbp(kdb_bp_t *bp); extern int kdba_parsebp(int, const char**, int *, kdb_bp_t*); extern char *kdba_bptype(kdbhard_bp_t *); extern void kdba_setsinglestep(struct pt_regs *); Index: linux-2.6.24-rc2/kdb/kdb_bp.c =================================================================== --- linux-2.6.24-rc2.orig/kdb/kdb_bp.c +++ linux-2.6.24-rc2/kdb/kdb_bp.c @@ -54,8 +54,9 @@ kdb_bp_install_global(struct pt_regs *re kdb_printf("kdb_bp_install_global bp %d bp_enabled %d bp_global %d\n", i, kdb_breakpoints[i].bp_enabled, kdb_breakpoints[i].bp_global); } + /* Hw breakpoints local or global are installed in kdb_bp_install_local() */ if (kdb_breakpoints[i].bp_enabled - && kdb_breakpoints[i].bp_global) { + && (kdb_breakpoints[i].bp_global && !kdb_breakpoints[i].bp_hardtype)) { kdba_installbp(regs, &kdb_breakpoints[i]); } } @@ -86,18 +87,32 @@ void kdb_bp_install_local(struct pt_regs *regs) { int i; + int do_install; for(i=0; i<KDB_MAXBPT; i++) { + do_install = 0; + if (KDB_DEBUG(BP)) { kdb_printf("kdb_bp_install_local bp %d bp_enabled %d bp_global %d cpu %d bp_cpu %d\n", i, kdb_breakpoints[i].bp_enabled, kdb_breakpoints[i].bp_global, smp_processor_id(), kdb_breakpoints[i].bp_cpu); } - if (kdb_breakpoints[i].bp_enabled - && kdb_breakpoints[i].bp_cpu == smp_processor_id() - && !kdb_breakpoints[i].bp_global){ - kdba_installbp(regs, &kdb_breakpoints[i]); + + if(!kdb_breakpoints[i].bp_enabled) + continue; + + if (kdb_breakpoints[i].bp_hardtype){ + if(kdb_breakpoints[i].bp_cpu == smp_processor_id() || + kdb_breakpoints[i].bp_global) + do_install = 1; + } else { + if(kdb_breakpoints[i].bp_cpu == smp_processor_id() && + !kdb_breakpoints[i].bp_global) + do_install = 1; } + + if(do_install) + kdba_installbp(regs, &kdb_breakpoints[i]); } } @@ -127,8 +142,9 @@ kdb_bp_remove_global(void) kdb_printf("kdb_bp_remove_global bp %d bp_enabled %d bp_global %d\n", i, kdb_breakpoints[i].bp_enabled, kdb_breakpoints[i].bp_global); } + /* Hw breakpoints local or global are remove in kdb_bp_remove_local() */ if (kdb_breakpoints[i].bp_enabled - && kdb_breakpoints[i].bp_global) { + && (kdb_breakpoints[i].bp_global && !kdb_breakpoints[i].bp_hardtype)) { kdba_removebp(&kdb_breakpoints[i]); } } @@ -155,18 +171,32 @@ void kdb_bp_remove_local(void) { int i; + int do_remove; for(i=KDB_MAXBPT-1; i>=0; i--) { + do_remove = 0; + if (KDB_DEBUG(BP)) { kdb_printf("kdb_bp_remove_local bp %d bp_enabled %d bp_global %d cpu %d bp_cpu %d\n", i, kdb_breakpoints[i].bp_enabled, kdb_breakpoints[i].bp_global, smp_processor_id(), kdb_breakpoints[i].bp_cpu); } - if (kdb_breakpoints[i].bp_enabled - && kdb_breakpoints[i].bp_cpu == smp_processor_id() - && !kdb_breakpoints[i].bp_global){ - kdba_removebp(&kdb_breakpoints[i]); + + if (!kdb_breakpoints[i].bp_enabled) + continue; + + if (kdb_breakpoints[i].bp_hardtype){ + if((kdb_breakpoints[i].bp_cpu == smp_processor_id()) || + kdb_breakpoints[i].bp_global) + do_remove = 1; + } else { + if(kdb_breakpoints[i].bp_cpu == smp_processor_id() && + !kdb_breakpoints[i].bp_global) + do_remove = 1; } + + if (do_remove) + kdba_removebp(&kdb_breakpoints[i]); } } @@ -332,12 +362,13 @@ kdb_bp(int argc, const char **argv) * attempt to allocate a hardware register for it. */ if (!bp->bp_template.bph_free) { - bp->bp_hard = kdba_allocbp(&bp->bp_template, &diag); - if (diag) { + kdba_alloc_hwbp(bp, &diag); + if (diag){ bp->bp_enabled = 0; + bp->bp_hardtype = 0; + kdba_free_hwbp(bp); return diag; } - bp->bp_hardtype = 1; } kdb_printbp(bp, bpno); @@ -432,11 +463,8 @@ kdb_bc(int argc, const char **argv) switch (cmd) { case KDBCMD_BC: - if (bp->bp_hardtype) { - kdba_freebp(bp->bp_hard); - bp->bp_hard = NULL; - bp->bp_hardtype = 0; - } + if (bp->bp_hardtype) + kdba_free_hwbp(bp); bp->bp_enabled = 0; bp->bp_global = 0; @@ -455,12 +483,14 @@ kdb_bc(int argc, const char **argv) */ if (!bp->bp_template.bph_free && !bp->bp_hardtype) { - bp->bp_hard = kdba_allocbp(&bp->bp_template, &diag); - if (diag) { + kdba_alloc_hwbp(bp, &diag); + if (diag){ bp->bp_enabled = 0; + bp->bp_hardtype = 0; + kdba_free_hwbp(bp); return diag; } - bp->bp_hardtype = 1; + } bp->bp_enabled = 1; @@ -479,11 +509,9 @@ kdb_bc(int argc, const char **argv) * give up the hardware register which is allocated * to it. */ - if (bp->bp_hardtype) { - kdba_freebp(bp->bp_hard); - bp->bp_hard = NULL; - bp->bp_hardtype = 0; - } + + if (bp->bp_hardtype) + kdba_free_hwbp(bp); bp->bp_enabled = 0; Index: linux-2.6.24-rc2/include/asm-x86/kdbprivate_32.h =================================================================== --- linux-2.6.24-rc2.orig/include/asm-x86/kdbprivate_32.h +++ linux-2.6.24-rc2/include/asm-x86/kdbprivate_32.h @@ -45,6 +45,7 @@ typedef struct _kdbhard_bp { unsigned int bph_write:1; /* Write Data breakpoint */ unsigned int bph_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ unsigned int bph_length:2; /* 0=1, 1=2, 2=BAD, 3=4 (bytes) */ + unsigned int bph_installed; /* flag: hw bp is installed */ } kdbhard_bp_t; #define IA32_BREAKPOINT_INSTRUCTION 0xcc Index: linux-2.6.24-rc2/arch/x86/kdb/kdba_bp_64.c =================================================================== --- linux-2.6.24-rc2.orig/arch/x86/kdb/kdba_bp_64.c +++ linux-2.6.24-rc2/arch/x86/kdb/kdba_bp_64.c @@ -22,10 +22,10 @@ static char *kdba_rwtypes[] = { "Instruc /* * Table describing processor architecture hardware - * breakpoint registers. + * breakpoint registers for every CPU. */ -kdbhard_bp_t kdb_hardbreaks[KDB_MAXHARDBPT]; +static kdbhard_bp_t kdb_hardbreaks[NR_CPUS][KDB_MAXHARDBPT]; /* * kdba_db_trap @@ -75,6 +75,7 @@ kdba_db_trap(struct pt_regs *regs, int e int i; kdb_dbtrap_t rv = KDB_DB_BPT; kdb_bp_t *bp; + int cpu = smp_processor_id(); if (KDB_NULL_REGS(regs)) return KDB_DB_NOBPT; @@ -103,8 +104,12 @@ kdba_db_trap(struct pt_regs *regs, int e kdb_printf("bp for this cpu\n"); if (bp->bp_delayed) { bp->bp_delayed = 0; - if (KDB_DEBUG(BP)) + if (KDB_DEBUG(BP)){ + /* Can't be hw breakpoint */ + if (bp->bp_hardtype) + kdb_printf("kdb: Error - hw bp delayed\n"); kdb_printf("kdba_installbp\n"); + } kdba_installbp(regs, bp); if (!KDB_STATE(DOING_SS)) { regs->eflags &= ~EF_TF; @@ -211,8 +216,8 @@ handle: for(i=0, bp=kdb_breakpoints; i<KDB_MAXBPT; i++, bp++) { if (!(bp->bp_free) && (bp->bp_global || bp->bp_cpu == smp_processor_id()) - && (bp->bp_hard) - && (bp->bp_hard->bph_reg == reg)) { + && (bp->bp_hard[cpu]) + && (bp->bp_hard[cpu]->bph_reg == reg)) { /* * Hit this breakpoint. */ @@ -437,12 +442,18 @@ kdba_printbpreg(kdbhard_bp_t *bph) void kdba_printbp(kdb_bp_t *bp) { + int cpu; + kdb_printf("\n is enabled"); if (bp->bp_hardtype) { - kdba_printbpreg(bp->bp_hard); - if (bp->bp_hard->bph_mode != 0) { + if (bp->bp_global) + cpu = smp_processor_id(); + else + cpu = bp->bp_cpu; + kdba_printbpreg(bp->bp_hard[cpu]); + if (bp->bp_hard[cpu]->bph_mode != 0) { kdb_printf(" for %d bytes", - bp->bp_hard->bph_length+1); + bp->bp_hard[cpu]->bph_length+1); } } } @@ -555,7 +566,7 @@ kdba_parsebp(int argc, const char **argv /* * kdba_allocbp * - * Associate a hardware register with a breakpoint. + * Allocate hw register for bp on specific CPU * * Parameters: * None. @@ -569,13 +580,14 @@ kdba_parsebp(int argc, const char **argv * Remarks: */ -kdbhard_bp_t * -kdba_allocbp(kdbhard_bp_t *bph, int *diagp) +static kdbhard_bp_t * +kdba_allocbp(kdbhard_bp_t *bph, int *diagp, unsigned int cpu) { int i; kdbhard_bp_t *newbph; - for(i=0,newbph=kdb_hardbreaks; i < KDB_MAXHARDBPT; i++, newbph++) { + for(i=0; i < KDB_MAXHARDBPT; i++) { + newbph=&(kdb_hardbreaks[cpu][i]); if (newbph->bph_free) { break; } @@ -607,9 +619,49 @@ kdba_allocbp(kdbhard_bp_t *bph, int *dia } /* + * kdba_alloc_hwbp + * + * Associate a hardware registers with a breakpoint. + * If hw bp is global hw registers descriptor will be allocated + * on every CPU. + * + * Parameters: + * bp - hardware bp + * diagp - pointer to variable that will store error when + * function complete + * Outputs: + * None. + * Returns: + * None + * Locking: + * None. + * Remarks: + * Should be called with correct bp->bp_template + */ + +void +kdba_alloc_hwbp(kdb_bp_t *bp, int *diagp) +{ + int i; + + if (bp->bp_global){ + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_online(i)) + continue; + bp->bp_hard[i] = kdba_allocbp(&bp->bp_template, diagp, i); + if (*diagp) + break; + } + } else { + bp->bp_hard[bp->bp_cpu] = kdba_allocbp(&bp->bp_template, diagp, bp->bp_cpu); + } + bp->bp_hardtype = 1; +} + +/* * kdba_freebp * - * Deallocate a hardware breakpoint + * Deallocate hw registers descriptor for bp on specific CPU * * Parameters: * None. @@ -622,13 +674,57 @@ kdba_allocbp(kdbhard_bp_t *bph, int *dia * Remarks: */ -void +static void kdba_freebp(kdbhard_bp_t *bph) { bph->bph_free = 1; } /* + * kdba_free_hwbp + * + * Frees allocated hw registers descriptors for bp. + * If hw bp is global, hw registers descriptors will be freed + * on every CPU. + * + * Parameters: + * bp - hardware bp + * Outputs: + * None. + * Returns: + * None + * Locking: + * None. + * Remarks: + * Should be called with correct bp->bp_template + */ + +void +kdba_free_hwbp(kdb_bp_t *bp) +{ + int i; + + /* When kernel enters KDB, first, all local bps + * are removed, so here we don't need to clear + * debug registers. + */ + + if (bp->bp_global){ + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_online(i)) + continue; + if (bp->bp_hard[i]) + kdba_freebp(bp->bp_hard[i]); + bp->bp_hard[i] = 0; + } + } else { + kdba_freebp(bp->bp_hard[bp->bp_cpu]); + bp->bp_hard[bp->bp_cpu] = NULL; + } + bp->bp_hardtype = 0; +} + +/* * kdba_initbp * * Initialize the breakpoint table for the hardware breakpoint @@ -652,7 +748,7 @@ kdba_freebp(kdbhard_bp_t *bph) void kdba_initbp(void) { - int i; + int i,j; kdbhard_bp_t *bph; /* @@ -661,9 +757,15 @@ kdba_initbp(void) memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); - for(i=0,bph=kdb_hardbreaks; i<KDB_MAXHARDBPT; i++, bph++) { - bph->bph_reg = i; - bph->bph_free = 1; + for (i = 0; i < NR_CPUS; ++i) { + /* Called early so we don't know actual + * ammount of CPUs + */ + for(j=0; j < KDB_MAXHARDBPT; j++) { + bph=&(kdb_hardbreaks[i][j]); + bph->bph_reg = j; + bph->bph_free = 1; + } } } @@ -695,6 +797,8 @@ kdba_initbp(void) int kdba_installbp(struct pt_regs *regs, kdb_bp_t *bp) { + int cpu = smp_processor_id(); + /* * Install the breakpoint, if it is not already installed. */ @@ -704,15 +808,27 @@ kdba_installbp(struct pt_regs *regs, kdb } if (!KDB_STATE(SSBPT)) bp->bp_delay = 0; - if (!bp->bp_installed) { - if (bp->bp_hardtype) { + + if (bp->bp_hardtype) { + if (KDB_DEBUG(BP) && !bp->bp_global && cpu != bp->bp_cpu){ + kdb_printf("kdba_removebp: cpu != bp->bp_cpu for local hw bp\n"); + } + + if (KDB_DEBUG(BP) && !bp->bp_hard[cpu]){ + kdb_printf("kdba_removebp: Error - bp_hard[smp_processor_id()] is emply\n"); + return 1; + } + + if (!bp->bp_hard[cpu]->bph_installed){ kdba_installdbreg(bp); - bp->bp_installed = 1; + bp->bp_hard[cpu]->bph_installed = 1; if (KDB_DEBUG(BP)) { kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", - bp->bp_hard->bph_reg, bp->bp_addr); + bp->bp_hard[cpu]->bph_reg, bp->bp_addr); } - } else if (bp->bp_delay) { + } + } else if (!bp->bp_installed) { + if (bp->bp_delay) { if (KDB_DEBUG(BP)) kdb_printf("kdba_installbp delayed bp\n"); kdba_handle_bp(regs, bp); @@ -750,6 +866,8 @@ kdba_installbp(struct pt_regs *regs, kdb int kdba_removebp(kdb_bp_t *bp) { + int cpu = smp_processor_id(); + /* * For hardware breakpoints, remove it from the active register, * for software breakpoints, restore the instruction stream. @@ -757,20 +875,36 @@ kdba_removebp(kdb_bp_t *bp) if (KDB_DEBUG(BP)) { kdb_printf("kdba_removebp bp_installed %d\n", bp->bp_installed); } - if (bp->bp_installed) { - if (bp->bp_hardtype) { + + if (bp->bp_hardtype) { + if (KDB_DEBUG(BP) && !bp->bp_global && cpu != bp->bp_cpu){ + kdb_printf("kdba_removebp: cpu != bp->bp_cpu for local hw bp\n"); + } + + if (KDB_DEBUG(BP) && !bp->bp_hard[cpu]){ + kdb_printf("kdba_removebp: Error - bp_hard[smp_processor_id()] is emply\n"); + return 1; + } + + if (KDB_DEBUG(BP)) { + kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard[cpu]->bph_reg, bp->bp_addr); + } + + if (bp->bp_hard[cpu]->bph_installed){ if (KDB_DEBUG(BP)) { - kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", - bp->bp_hard->bph_reg, bp->bp_addr); + kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard[cpu]->bph_reg, bp->bp_addr); } kdba_removedbreg(bp); - } else { - if (KDB_DEBUG(BP)) - kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", - bp->bp_inst, bp->bp_addr); - if (kdb_putword(bp->bp_addr, bp->bp_inst, 1)) - return(1); + bp->bp_hard[cpu]->bph_installed = 0; } + } else if (bp->bp_installed) { + if (KDB_DEBUG(BP)) + kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", + bp->bp_inst, bp->bp_addr); + if (kdb_putword(bp->bp_addr, bp->bp_inst, 1)) + return(1); bp->bp_installed = 0; } return(0); Index: linux-2.6.24-rc2/arch/x86/kdb/kdbasupport_64.c =================================================================== --- linux-2.6.24-rc2.orig/arch/x86/kdb/kdbasupport_64.c +++ linux-2.6.24-rc2/arch/x86/kdb/kdbasupport_64.c @@ -55,40 +55,42 @@ kdba_putdr7(kdb_machreg_t contents) void kdba_installdbreg(kdb_bp_t *bp) { + int cpu = smp_processor_id(); + kdb_machreg_t dr7; dr7 = kdba_getdr7(); - kdba_putdr(bp->bp_hard->bph_reg, bp->bp_addr); + kdba_putdr(bp->bp_hard[cpu]->bph_reg, bp->bp_addr); dr7 |= DR7_GE; if (cpu_has_de) set_in_cr4(X86_CR4_DE); - switch (bp->bp_hard->bph_reg){ + switch (bp->bp_hard[cpu]->bph_reg){ case 0: - DR7_RW0SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN0SET(dr7,bp->bp_hard->bph_length); + DR7_RW0SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN0SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G0SET(dr7); break; case 1: - DR7_RW1SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN1SET(dr7,bp->bp_hard->bph_length); + DR7_RW1SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN1SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G1SET(dr7); break; case 2: - DR7_RW2SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN2SET(dr7,bp->bp_hard->bph_length); + DR7_RW2SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN2SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G2SET(dr7); break; case 3: - DR7_RW3SET(dr7,bp->bp_hard->bph_mode); - DR7_LEN3SET(dr7,bp->bp_hard->bph_length); + DR7_RW3SET(dr7,bp->bp_hard[cpu]->bph_mode); + DR7_LEN3SET(dr7,bp->bp_hard[cpu]->bph_length); DR7_G3SET(dr7); break; default: kdb_printf("kdb: Bad debug register!! %ld\n", - bp->bp_hard->bph_reg); + bp->bp_hard[cpu]->bph_reg); break; } @@ -101,11 +103,12 @@ kdba_removedbreg(kdb_bp_t *bp) { int regnum; kdb_machreg_t dr7; + int cpu = smp_processor_id(); - if (!bp->bp_hard) + if (!bp->bp_hard[cpu]) return; - regnum = bp->bp_hard->bph_reg; + regnum = bp->bp_hard[cpu]->bph_reg; dr7 = kdba_getdr7(); Index: linux-2.6.24-rc2/include/asm-x86/kdbprivate_64.h =================================================================== --- linux-2.6.24-rc2.orig/include/asm-x86/kdbprivate_64.h +++ linux-2.6.24-rc2/include/asm-x86/kdbprivate_64.h @@ -45,10 +45,9 @@ typedef struct _kdbhard_bp { unsigned int bph_write:1; /* Write Data breakpoint */ unsigned int bph_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ unsigned int bph_length:2; /* 0=1, 1=2, 2=BAD, 3=4 (bytes) */ + unsigned int bph_installed; /* flag: hw bp is installed */ } kdbhard_bp_t; -extern kdbhard_bp_t kdb_hardbreaks[/* KDB_MAXHARDBPT */]; - #define IA32_BREAKPOINT_INSTRUCTION 0xcc #define DR6_BT 0x00008000 --------------------------- Use http://oss.sgi.com/ecartis to modify your settings or to unsubscribe.
