/********************************************************************* * * Copyright (C) 2002-2010, Karlsruhe University * * File path: kdb/glue/v4-x86/prepost.cc * Description: IA-32 specific handlers for KDB entry and exit * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: prepost.cc,v 1.23 2006/12/05 15:23:15 skoglund Exp $ * ********************************************************************/ #include #include #include #include #include #include #include #include INC_API(cpu.h) #include INC_API(tcb.h) #include INC_ARCH(apic.h) #include INC_ARCH(traps.h) #include INC_ARCH(trapgate.h) #include INC_GLUE(schedule.h) #include INC_PLAT(nmi.h) extern space_t *current_disas_space; #if defined(CONFIG_KDB_DISAS) extern "C" int disas(addr_t pc); INLINE void disas_addr (addr_t ip, const char * str = "") { printf ("%p ", ip); current_disas_space = kdb.kdb_current->get_space(); disas (ip); printf ("\n"); } #else INLINE void disas_addr (addr_t ip, const char * str = "") { printf ("ip=%p -- %s\n", ip, str); } #endif extern word_t x86_kdb_last_ip; extern bool x86_kdb_singlestep; extern bool x86_kdb_branchstep; #if defined(CONFIG_TRACEPOINTS) extern bool x86_breakpoint_cpumask; extern bool x86_breakpoint_cpumask_kdb; DECLARE_TRACEPOINT(X86_BREAKPOINT); EXTERN_TRACEPOINT(X86_APIC_IPI); #endif #if defined(CONFIG_SMP) atomic_t kdb_current_cpu; KDEBUG_INIT(kdb_prepost_init); void kdb_prepost_init() { kdb_current_cpu = CONFIG_SMP_MAX_CPUS; } #endif bool kdb_t::pre() { bool enter_kernel_debugger = true; debug_param_t * param = (debug_param_t*) kdb_param; x86_exceptionframe_t* f = param->frame; kdb_current = param->tcb; #if defined(CONFIG_SMP) while (!kdb_current_cpu.cmpxchg(CONFIG_SMP_MAX_CPUS, current_cpu)) { /* Execute a dummy iret to receive NMIs again, then sleep */ x86_iret_self(); x86_sleep_uninterruptible(); /* * if we wanted to enter KDB, we have come here via any other * exception than NMI. Otherwise we're just any of the unrelated cpus * having been stopped via broadcast NMI. */ if (param->exception == X86_EXC_NMI) { cpuid_t cpu = get_current_cpu(); if (kdb_current_cpu == cpu) { printf("--- Switched to CPU %d ---\n", cpu); return true; } else if (kdb_current_cpu == CONFIG_SMP_MAX_CPUS) return false; } } if (param->exception != X86_EXC_NMI) { local_apic_t local_apic; local_apic.broadcast_nmi(); } #endif switch (param->exception) { case X86_EXC_DEBUG: /* single step, hw breakpoints */ { if (f->regs[x86_exceptionframe_t::freg] & (1 << 8)) { x86_kdb_singlestep = false; #if defined(CONFIG_CPU_X86_I686) || defined(CONFIG_CPU_X86_P4) if (x86_kdb_branchstep) { addr_t last_branch_ip; x86_wrmsr (X86_MSR_DEBUGCTL, 0); x86_kdb_branchstep = false; #if defined(CONFIG_CPU_X86_I686) last_branch_ip = (addr_t) (word_t) x86_rdmsr (X86_MSR_LASTBRANCHFROMIP); #else word_t last_branch_tos = x86_rdmsr (X86_MSR_LASTBRANCH_TOS); ASSERT(last_branch_tos < 16); word_t lbipmsr; u32_t model, dummy; x86_cpuid(1, &model, &dummy, &dummy, &dummy); if (((model >> 4) & 0xF) > 2) lbipmsr = X86_MSR_LASTBRANCH_FROM; else lbipmsr = X86_MSR_LASTBRANCH_FROM_OLD; lbipmsr+= last_branch_tos; last_branch_ip = (addr_t) (word_t) x86_rdmsr(lbipmsr); #endif disas_addr (last_branch_ip, "branch to"); x86_kdb_last_ip = f->regs[x86_exceptionframe_t::ipreg]; } #endif if (x86_kdb_last_ip != ~0U) disas_addr ((addr_t) x86_kdb_last_ip); f->regs[x86_exceptionframe_t::freg] &= ~((1 << 8) + (1 << 16)); /* !RF + !TF */ x86_kdb_last_ip = ~0U; } else { #if defined(CONFIG_TRACEPOINTS) // TCB, address, content word_t db7, db6, dbnum = 0, db = 0; X86_GET_DR(7, db7); X86_GET_DR(6, db6); dbnum = ((db6 & 8) ? 3 : ((db6 & 4) ? 2 : ((db6 & 2) ? 1 : 0))); db = x86_dr_read(dbnum); space_t *space = kdb.kdb_current->get_space(); if (!space) space = get_kernel_space(); word_t content; ENABLE_TRACEPOINT(X86_BREAKPOINT, x86_breakpoint_cpumask, 0); if (! readmem(space, (addr_t) db, &content) ) TRACEPOINT(X86_BREAKPOINT, "breakpoint dr%d ip: %x addr %x content ########", dbnum, f->regs[x86_exceptionframe_t::ipreg], db); else TRACEPOINT(X86_BREAKPOINT, "breakpoint dr%d ip: %x addr %x content %x", dbnum, f->regs[x86_exceptionframe_t::ipreg], db, content); enter_kernel_debugger = ((x86_breakpoint_cpumask_kdb & (1 << get_current_cpu())) != 0); #else printf("--- Debug Exception ---\n"); #endif } } break; case X86_EXC_NMI: /* well, the name says enough */ #if !defined(CONFIG_SMP) { printf("--- NMI ---\n"); } #else /* * if we wanted to enter KDB, we have come here via any other * exception than NMI. Otherwise we're just any of the unrelated cpus * having been stopped via broadcast NMI. This case here occurs if we have * received a broadcast after a different CPU has exited KDB. */ enter_kernel_debugger = false; #endif break; case X86_EXC_BREAKPOINT: /* int3 */ { space_t * space = kdb.kdb_current->get_space(); if (!space) space = get_kernel_space(); addr_t addr = (addr_t)(f->regs[x86_exceptionframe_t::ipreg]); unsigned char c; if (! readmem(space, addr, &c) ) break; if (c == 0x90) { addr = addr_offset(addr, 1); enter_kernel_debugger = false; } if (! readmem(space, addr, &c) ) break; /* * KDB_Enter() is implemented as: * * int $3 * jmp 1f * mov $2f, %eax * 1: * * .rodata * 2: .ascii "output text" * .byte 0 * */ if (c == 0xeb) /* jmp rel */ { if (! readmem(space, addr_offset(addr, 2), &c) ) break; addr_t user_addr = NULL; bool mapped = false; if (c == 0xb8) { #if defined(CONFIG_X86_COMPATIBILITY_MODE) if (space->is_compatibility_mode() && space->is_user_area(addr)) { u32_t user_word32 = 0; /* movl addr32, %eax */ mapped = readmem (space, addr_offset(addr, 3), &user_word32); user_addr = (addr_t) user_word32; } else #endif { word_t user_word = 0; /* mov addr, AREG */ mapped = readmem (space, addr_offset(addr, 3), &user_word); user_addr = (addr_t) user_word; } } else if (c == 0x48) { if (! readmem(space, addr_offset(addr, 3), &c) ) break; if (c == 0xc7) { /* movq addr32, %rax */ s32_t suser_addr = 0; mapped = readmem (space, addr_offset(addr, 5), (s32_t *) &suser_addr); user_addr = (addr_t) suser_addr; } } if (user_addr) { printf("--- \""); if (!mapped) printf("[string address not mapped]"); else { while (readmem(space, user_addr, &c) && (c != 0)) { putc(c); user_addr = addr_offset(user_addr, 1); } if (c != 0) printf("[string not completely mapped]"); printf("\" ---\n" "--------------------------------- (eip=%p, esp=%p) ---\n", f->regs[x86_exceptionframe_t::ipreg] - 1, f->regs[x86_exceptionframe_t::spreg]); } } } /* * Other kdebug operations are implemented as follows: * * int $3 * cmpb , %al * */ else if (c == 0x3c) /* cmpb */ { enter_kernel_debugger = false; if (!readmem (space, addr_offset(addr, 1), &c)) break; switch (c) { case 0x0: // // KDB_PrintChar() // putc(f->regs[x86_exceptionframe_t::areg]); break; case 0x1: { // // KDB_PrintString() // addr_t user_addr = (addr_t) f->regs[x86_exceptionframe_t::areg]; while (readmem (space, user_addr, &c) && (c != 0)) { putc(c); user_addr = addr_offset (user_addr, 1); } if (c != 0) printf("[string not completely mapped]"); break; } case 0x3: // // KDB_ToggleBreakin() // #if defined(CONFIG_KDB_BREAKIN) extern bool kdebug_check_breakin_enabled; if (kdebug_check_breakin_enabled) kdebug_check_breakin_enabled = false; else kdebug_check_breakin_enabled = true; #endif break; case 0xd: // // KDB_ReadChar_Blocked() // f->regs[x86_exceptionframe_t::areg] = getc (true); break; case 0x8: // // KDB_ReadChar() // f->regs[x86_exceptionframe_t::areg] = getc (false); break; default: enter_kernel_debugger = true; printf("kdb: unknown opcode: int3, cmpb %d\n", space->get_from_user(addr_offset(addr, 1))); break; } } else { printf("kdb: unknown kdb op: %x ip %x\n", c, f->regs[x86_exceptionframe_t::ipreg]); } } break; default: { printf("--- KD# unknown reason %d ip %x ---\n", f->reason, f->regs[x86_exceptionframe_t::ipreg]); break; } /* switch */ } return enter_kernel_debugger; }; void kdb_t::post() { debug_param_t * param = (debug_param_t*)kdb_param; if (param->exception == X86_EXC_DEBUG) { /* Set RF in EFLAGS. This will disable breakpoints for one instruction. The processor will reset it afterwards. */ param->frame->regs[x86_exceptionframe_t::freg] |= (1 << 16); } /* switch */ #if defined(CONFIG_SMP) if (kdb_current_cpu == get_current_cpu()) { kdb_current_cpu = CONFIG_SMP_MAX_CPUS; local_apic_t local_apic; local_apic.broadcast_nmi(); } #endif };