File: [Development] / linux-2.4-xfs / arch / i386 / kdb / kdbasupport.c (download)
Revision 1.3, Wed Dec 21 14:32:30 2005 UTC (11 years, 9 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
CVS Tags: HEAD Changes since 1.2: +0 -0
lines
Merge up to 2.4.32.
Merge of 2.4.x-xfs-melb:linux:24898a by kenmcd.
|
/*
* Kernel Debugger Architecture Independent Support Functions
*
* Copyright (C) 1999-2003 Silicon Graphics, Inc. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan
*/
#include <linux/config.h>
#include <linux/string.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/kdb.h>
#include <linux/kdbprivate.h>
#include <asm/processor.h>
#include <asm/msr.h>
#include <asm/uaccess.h>
/*
* kdba_find_return_1
*
* Given a starting point on the stack and symtab data for the
* current function, scan up the stack looking for a return
* address for this function.
* Inputs:
* sp Starting stack pointer for scan
* ss Start of stack for current process
* symtab kallsyms symbol data for the function
* assume When false, do not apply tests that have to assume a branch is valid
* Outputs:
* None.
* Returns:
* Position on stack of return address, 0 if not found.
* Locking:
* None.
* Remarks:
* This is sensitive to the calling sequence generated by gcc.
*/
static kdb_machreg_t
kdba_find_return_1(kdb_machreg_t sp, kdb_machreg_t ss, const kdb_symtab_t *symtab, int assume)
{
kdb_machreg_t ret;
kdb_symtab_t caller_symtab;
unsigned long disp8;
unsigned long disp32;
unsigned char code[7];
#define retaddr(off) code[sizeof(code)+(off)]
for (;ret = 0, sp & (THREAD_SIZE-1);sp += 4) {
if (KDB_DEBUG(ARA)) {
kdb_printf(" sp=0x%lx", sp);
}
if (kdb_getword(&ret, sp, 4))
break;
kdbnearsym(ret, &caller_symtab);
if (KDB_DEBUG(ARA)) {
kdb_printf(" ret=");
kdb_symbol_print(ret, &caller_symtab, KDB_SP_DEFAULT|KDB_SP_SYMSIZE);
}
if (!caller_symtab.sym_name) {
if (KDB_DEBUG(ARA)) {
kdb_printf("\n");
}
continue; /* not a valid kernel address */
}
KDB_STATE_SET(SUPPRESS);
if (kdb_getarea(code, ret-sizeof(code)) ||
kdb_getword(&disp32, ret-4, 4) ||
kdb_getword(&disp8, ret-1, 1))
continue; /* not a valid return address */
if (retaddr(-5) == 0xe8) {
/* call disp32 */
if (KDB_DEBUG(ARA)) {
kdb_printf(" call disp32");
}
if (ret + (s32) disp32 == symtab->sym_start) {
if (KDB_DEBUG(ARA)) {
kdb_printf(" matched\n");
}
break; /* call to this function */
}
if (KDB_DEBUG(ARA)) {
kdb_printf(" failed");
}
} else if (retaddr(-5) == 0xe9) {
/* jmp disp32. I have been told that gcc may
* do function tail optimization and replace
* call with jmp.
*/
if (KDB_DEBUG(ARA)) {
kdb_printf(" jmp disp32\n");
}
if (ret + (s32) disp32 == symtab->sym_start) {
if (KDB_DEBUG(ARA)) {
kdb_printf(" matched\n");
}
break; /* jmp to this function */
}
if (KDB_DEBUG(ARA)) {
kdb_printf(" failed");
}
} else if (retaddr(-2) == 0xeb) {
/* jmp disp8 */
if (KDB_DEBUG(ARA)) {
kdb_printf(" jmp disp8\n");
}
if (ret + (s8) disp8 == symtab->sym_start) {
if (KDB_DEBUG(ARA)) {
kdb_printf(" matched\n");
}
break; /* jmp to this function */
}
if (KDB_DEBUG(ARA)) {
kdb_printf(" failed");
}
} else if (strcmp(caller_symtab.sym_name, "ret_from_intr") == 0
&& ret == caller_symtab.sym_start) {
/* ret_from_intr is pushed on stack for interrupts */
if (KDB_DEBUG(ARA)) {
kdb_printf(" ret_from_intr matched\n");
}
break; /* special case, hand crafted frame */
} else if (!assume) {
/* All following tests cannot validate the target address so they
* must assume that the return address is valid.
*/
if (KDB_DEBUG(ARA)) {
kdb_printf("\n");
}
continue;
} else if (retaddr(-7) == 0xff && retaddr(-6) == 0x14 && retaddr(-5) == 0x85) {
/* call *disp32(,%eax,4), used by syscall.
* Cannot calculate address, assume it is valid
* if the current function name starts with
* 'sys_' or 'old_'.
*/
if (KDB_DEBUG(ARA)) {
kdb_printf(" call *0xnnnn(,%%eax,4)");
}
if (strncmp(symtab->sym_name, "sys_", 4) == 0 ||
strncmp(symtab->sym_name, "old_", 4) == 0) {
if (KDB_DEBUG(ARA)) {
kdb_printf(" assume valid\n");
}
break; /* probably call to this function */
}
if (KDB_DEBUG(ARA)) {
kdb_printf(" failed");
}
} else if (retaddr(-2) == 0xff &&
((retaddr(-1) & 0xf8) == 0xd0 || (retaddr(-1) & 0xf8) == 0x10)) {
/* call *%reg. Cannot validate, have to assume
* it is valid.
*/
if (KDB_DEBUG(ARA)) {
kdb_printf(" call *%%reg, assume valid\n");
}
break; /* hope it is a call to this function */
} else if (retaddr(-3) == 0xff && (retaddr(-2) & 0xf8) == 0x50) {
/* call *disp8(%reg). Cannot validate, have to assume
* it is valid.
*/
if (KDB_DEBUG(ARA)) {
kdb_printf(" call *disp8(%%reg), assume valid\n");
}
break; /* hope it is a call to this function */
} else if (retaddr(-6) == 0xff && (retaddr(-5) & 0xf8) == 0x90) {
/* call *disp32(%reg). Cannot validate, have to assume
* it is valid.
*/
if (KDB_DEBUG(ARA)) {
kdb_printf(" call *disp32(%%reg), assume valid\n");
}
break; /* hope it is a call to this function */
}
if (KDB_DEBUG(ARA)) {
kdb_printf("\n");
}
}
if (KDB_DEBUG(ARA)) {
kdb_printf(" end ret=0x%lx sp=0x%lx\n", ret, sp);
}
if (ret)
return sp;
return 0;
}
/*
* kdba_find_return
*
* Given a starting point on the stack and symtab data for the
* current function, scan up the stack looking for a return
* address for this function.
* Inputs:
* sp Starting stack pointer for scan
* ss Start of stack for current process
* symtab kallsyms symbol data for the function
* Outputs:
* None.
* Returns:
* Position on stack of return address, 0 if not found.
* Locking:
* None.
* Remarks:
* This is sensitive to the calling sequence generated by gcc.
*/
static kdb_machreg_t
kdba_find_return(kdb_machreg_t sp, kdb_machreg_t ss, const kdb_symtab_t *symtab)
{
kdb_machreg_t ret;
if (KDB_DEBUG(ARA)) {
kdb_printf(" kdba_find_return: start\n");
}
if ((sp & -THREAD_SIZE) != ss) {
kdb_printf(" sp is in wrong stack 0x%lx 0x%lx 0x%lx\n", sp, ss, sp & -THREAD_SIZE);
return 0;
}
if ((sp & (THREAD_SIZE - 1)) < sizeof(struct task_struct)) {
kdb_printf(" sp is inside task_struct\n");
return 0;
}
if (KDB_DEBUG(ARA)) {
kdb_printf(" kdba_find_return_1(assume==0)\n");
}
if ((ret = kdba_find_return_1(sp, ss, symtab, 0)))
return ret;
if (KDB_DEBUG(ARA)) {
kdb_printf(" kdba_find_return_1(assume==1)\n");
}
ret = kdba_find_return_1(sp, ss, symtab, 1);
return ret;
}
/*
* kdba_prologue
*
* This function analyzes a gcc-generated function prototype
* with or without frame pointers to determine the amount of
* automatic storage and register save storage is used on the
* stack of the target function. It only counts instructions
* that have been executed up to but excluding the current eip.
* Inputs:
* code Start address of function code to analyze
* pc Current program counter within function
* sp Current stack pointer for function
* fp Current frame pointer for function, may not be valid
* ss Start of stack for current process.
* caller 1 if looking for data on the caller frame, 0 for callee.
* Outputs:
* ar Activation record, all fields may be set. fp and oldfp
* are 0 if they cannot be extracted. return is 0 if the
* code cannot find a valid return address. args and arg0
* are 0 if the number of arguments cannot be safely
* calculated.
* Returns:
* 1 if prologue is valid, 0 otherwise. If pc is 0 treat it as a
* valid prologue to allow bt on wild branches.
* Locking:
* None.
* Remarks:
*
* A prologue for ia32 generally looks like:
*
* pushl %ebp [All functions, but only if
* movl %esp, %ebp compiled with frame pointers]
* subl $auto, %esp [some functions]
* pushl %reg [some functions]
* pushl %reg [some functions]
*
* FIXME: Mike Galbraith says that gcc 2.95 can generate a slightly
* different prologue. No support for gcc 2.95 yet.
*/
int
kdba_prologue(const kdb_symtab_t *symtab, kdb_machreg_t pc, kdb_machreg_t sp,
kdb_machreg_t fp, kdb_machreg_t ss, int caller, kdb_ar_t *ar)
{
kdb_machreg_t ret_p, code = symtab->sym_start;
int oldfp_present = 0, unwound = 0;
unsigned char instruction[6];
if (KDB_DEBUG(ARA)) {
kdb_printf("kdba_prologue: code=0x%lx %s pc=0x%lx sp=0x%lx fp=0x%lx\n",
code, symtab->sym_name, pc, sp, fp);
}
/* Special case for wild branches. Assumes top of stack is return address */
if (pc == 0) {
memset(ar, 0, sizeof(*ar));
ar->setup = 4;
ar->end = sp;
ar->start = ar->end + 4;
kdb_getword(&(ar->ret), sp, 4);
if (KDB_DEBUG(ARA)) {
kdb_printf(" pc==0: ret=0x%lx\n", ar->ret);
}
return(1);
}
if (code == 0 || sp & 3 || ss != (sp & -THREAD_SIZE))
return(0);
ar->end = sp; /* End of activation record +1 */
/* Special cases galore when the caller pc is within entry.S.
* The return address for these routines is outside the kernel,
* so the normal algorithm to find the frame does not work.
* Hand craft the frame to no setup, regs, locals etc, assume 6
* parameters.
* This list was extracted from entry.S by looking for all call
* instructions that were eventually followed by RESTORE_ALL,
* take the label before each such instruction.
*/
if (caller &&
(strcmp(symtab->sym_name, "lcall7") == 0 ||
strcmp(symtab->sym_name, "lcall27") == 0 ||
strcmp(symtab->sym_name, "kdb_call") == 0 ||
strcmp(symtab->sym_name, "system_call") == 0 ||
strcmp(symtab->sym_name, "tracesys") == 0 ||
strcmp(symtab->sym_name, "signal_return") == 0 ||
strcmp(symtab->sym_name, "v86_signal_return") == 0 ||
strcmp(symtab->sym_name, "tracesys") == 0 ||
strcmp(symtab->sym_name, "tracesys_exit") == 0 ||
strcmp(symtab->sym_name, "handle_softirq") == 0 ||
strcmp(symtab->sym_name, "reschedule") == 0 ||
strcmp(symtab->sym_name, "error_code") == 0 ||
strcmp(symtab->sym_name, "device_not_available") == 0 ||
strcmp(symtab->sym_name, "nmi") == 0)) {
ar->start = ar->end + 6*4; /* 6 parameters */
if ((ar->start & -THREAD_SIZE) != ss)
ar->start = 0;
return(1);
}
ar->setup = 4; /* Return address is always on stack */
/* Kludge. If we are sitting on 'ret' then the stack has been unwound,
* ignore all the startup code.
*/
if (kdb_getarea(instruction[0], pc))
return(0);
if (instruction[0] == 0xc3) {
/* ret */
unwound = 1;
}
if (kdb_getarea(instruction, code))
return(0);
if (!unwound && code < pc && instruction[0] == 0x55) {
/* pushl %ebp */
ar->setup += 4; /* Frame pointer is on stack */
oldfp_present = 1;
++code;
if (KDB_DEBUG(ARA)) {
kdb_printf(" pushl %%ebp\n");
}
if (code < pc && instruction[0] == 0x89 && instruction[1] == 0xe5) {
/* movl %esp,%ebp */
if (fp >= sp && (fp & -THREAD_SIZE) == ss)
ar->fp = fp; /* %ebp has been set */
code += 2;
if (KDB_DEBUG(ARA)) {
kdb_printf(" movl %%esp,%%ebp, fp=0x%lx\n", ar->fp);
}
}
}
if (!unwound && code < pc) {
if (instruction[0] == 0x83 && instruction[1] == 0xec) {
/* subl $xx,%esp */
kdb_getword(&(ar->locals), (unsigned long)(instruction+2), 1);
code += 3;
if (KDB_DEBUG(ARA)) {
kdb_printf(" subl $xx,%%esp, locals=%ld\n", ar->locals);
}
} else if (instruction[0] == 0x81 && instruction[1] == 0xec) {
/* subl $xxxxxxxx,%esp */
kdb_getword(&(ar->locals), (unsigned long)(instruction+2), 4);
code += 6;
if (KDB_DEBUG(ARA)) {
kdb_printf(" subl $xxxxxxxx,%%esp, locals=%ld\n", ar->locals);
}
}
}
while (!unwound && code < pc &&
kdb_getarea(instruction, code) == 0 &&
(instruction[0] & 0xf8) == 0x50) {
/* pushl %reg */
ar->regs += 4;
++code;
if (KDB_DEBUG(ARA)) {
kdb_printf(" pushl %%reg, regs=%ld\n", ar->regs);
}
}
/* Check the return address. It must point within the kernel
* and the code at that location must be a valid entry sequence.
*/
if (ar->fp) {
ret_p = ar->fp + ar->setup;
}
else {
ret_p = ar->end + ar->regs + ar->locals + ar->setup;
}
ret_p -= 4;
if (KDB_DEBUG(ARA)) {
kdb_printf(" ret_p(0)=0x%lx\n", ret_p);
}
ar->ret = 0;
if ((ret_p & -THREAD_SIZE) == ss &&
(ret_p = kdba_find_return(ret_p, ss, symtab))) {
kdb_getword(&(ar->ret), ret_p, 4);
}
if (KDB_DEBUG(ARA)) {
kdb_printf(" ret_p(1)=0x%lx ret=0x%lx\n", ret_p, ar->ret);
}
if (ar->ret) {
ar->fp = ret_p - ar->setup + 4; /* "accurate" fp */
ar->start = ret_p + 4;
if (KDB_DEBUG(ARA)) {
kdb_printf(" fp=0x%lx start=0x%lx\n", ar->fp, ar->start);
}
}
if (oldfp_present) {
if (ar->fp)
kdb_getword(&(ar->oldfp), ar->fp, 4);
if (KDB_DEBUG(ARA)) {
kdb_printf(" oldfp=0x%lx", ar->oldfp);
}
if (ar->oldfp <= ar->fp || (ar->oldfp & -THREAD_SIZE) != ss) {
ar->oldfp = 0;
if (KDB_DEBUG(ARA)) {
kdb_printf(" (out of range)");
}
}
if (KDB_DEBUG(ARA)) {
kdb_printf("\n");
}
}
return(1);
}
kdb_machreg_t
kdba_getdr6(void)
{
return kdba_getdr(6);
}
kdb_machreg_t
kdba_getdr7(void)
{
return kdba_getdr(7);
}
void
kdba_putdr6(kdb_machreg_t contents)
{
kdba_putdr(6, contents);
}
static void
kdba_putdr7(kdb_machreg_t contents)
{
kdba_putdr(7, contents);
}
void
kdba_installdbreg(kdb_bp_t *bp)
{
kdb_machreg_t dr7;
dr7 = kdba_getdr7();
kdba_putdr(bp->bp_hard->bph_reg, bp->bp_addr);
dr7 |= DR7_GE;
if (cpu_has_de)
set_in_cr4(X86_CR4_DE);
switch (bp->bp_hard->bph_reg){
case 0:
DR7_RW0SET(dr7,bp->bp_hard->bph_mode);
DR7_LEN0SET(dr7,bp->bp_hard->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_G1SET(dr7);
break;
case 2:
DR7_RW2SET(dr7,bp->bp_hard->bph_mode);
DR7_LEN2SET(dr7,bp->bp_hard->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_G3SET(dr7);
break;
default:
kdb_printf("kdb: Bad debug register!! %ld\n",
bp->bp_hard->bph_reg);
break;
}
kdba_putdr7(dr7);
return;
}
void
kdba_removedbreg(kdb_bp_t *bp)
{
int regnum;
kdb_machreg_t dr7;
if (!bp->bp_hard)
return;
regnum = bp->bp_hard->bph_reg;
dr7 = kdba_getdr7();
kdba_putdr(regnum, 0);
switch (regnum) {
case 0:
DR7_G0CLR(dr7);
DR7_L0CLR(dr7);
break;
case 1:
DR7_G1CLR(dr7);
DR7_L1CLR(dr7);
break;
case 2:
DR7_G2CLR(dr7);
DR7_L2CLR(dr7);
break;
case 3:
DR7_G3CLR(dr7);
DR7_L3CLR(dr7);
break;
default:
kdb_printf("kdb: Bad debug register!! %d\n", regnum);
break;
}
kdba_putdr7(dr7);
}
kdb_machreg_t
kdba_getdr(int regnum)
{
kdb_machreg_t contents = 0;
switch(regnum) {
case 0:
__asm__ ("movl %%db0,%0\n\t":"=r"(contents));
break;
case 1:
__asm__ ("movl %%db1,%0\n\t":"=r"(contents));
break;
case 2:
__asm__ ("movl %%db2,%0\n\t":"=r"(contents));
break;
case 3:
__asm__ ("movl %%db3,%0\n\t":"=r"(contents));
break;
case 4:
case 5:
break;
case 6:
__asm__ ("movl %%db6,%0\n\t":"=r"(contents));
break;
case 7:
__asm__ ("movl %%db7,%0\n\t":"=r"(contents));
break;
default:
break;
}
return contents;
}
kdb_machreg_t
kdb_getcr(int regnum)
{
kdb_machreg_t contents = 0;
switch(regnum) {
case 0:
__asm__ ("movl %%cr0,%0\n\t":"=r"(contents));
break;
case 1:
break;
case 2:
__asm__ ("movl %%cr2,%0\n\t":"=r"(contents));
break;
case 3:
__asm__ ("movl %%cr3,%0\n\t":"=r"(contents));
break;
case 4:
__asm__ ("movl %%cr4,%0\n\t":"=r"(contents));
break;
default:
break;
}
return contents;
}
void
kdba_putdr(int regnum, kdb_machreg_t contents)
{
switch(regnum) {
case 0:
__asm__ ("movl %0,%%db0\n\t"::"r"(contents));
break;
case 1:
__asm__ ("movl %0,%%db1\n\t"::"r"(contents));
break;
case 2:
__asm__ ("movl %0,%%db2\n\t"::"r"(contents));
break;
case 3:
__asm__ ("movl %0,%%db3\n\t"::"r"(contents));
break;
case 4:
case 5:
break;
case 6:
__asm__ ("movl %0,%%db6\n\t"::"r"(contents));
break;
case 7:
__asm__ ("movl %0,%%db7\n\t"::"r"(contents));
break;
default:
break;
}
}
/*
* kdba_getregcontents
*
* Return the contents of the register specified by the
* input string argument. Return an error if the string
* does not match a machine register.
*
* The following pseudo register names are supported:
* ®s - Prints address of exception frame
* kesp - Prints kernel stack pointer at time of fault
* cesp - Prints current kernel stack pointer, inside kdb
* ceflags - Prints current flags, inside kdb
* %<regname> - Uses the value of the registers at the
* last time the user process entered kernel
* mode, instead of the registers at the time
* kdb was entered.
*
* Parameters:
* regname Pointer to string naming register
* regs Pointer to structure containing registers.
* Outputs:
* *contents Pointer to unsigned long to recieve register contents
* Returns:
* 0 Success
* KDB_BADREG Invalid register name
* Locking:
* None.
* Remarks:
* If kdb was entered via an interrupt from the kernel itself then
* ss and esp are *not* on the stack.
*/
static struct kdbregs {
char *reg_name;
size_t reg_offset;
} kdbreglist[] = {
{ "eax", offsetof(struct pt_regs, eax) },
{ "ebx", offsetof(struct pt_regs, ebx) },
{ "ecx", offsetof(struct pt_regs, ecx) },
{ "edx", offsetof(struct pt_regs, edx) },
{ "esi", offsetof(struct pt_regs, esi) },
{ "edi", offsetof(struct pt_regs, edi) },
{ "esp", offsetof(struct pt_regs, esp) },
{ "eip", offsetof(struct pt_regs, eip) },
{ "ebp", offsetof(struct pt_regs, ebp) },
{ "xss", offsetof(struct pt_regs, xss) },
{ "xcs", offsetof(struct pt_regs, xcs) },
{ "eflags", offsetof(struct pt_regs, eflags) },
{ "xds", offsetof(struct pt_regs, xds) },
{ "xes", offsetof(struct pt_regs, xes) },
{ "origeax", offsetof(struct pt_regs, orig_eax) },
};
static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs);
static struct kdbregs dbreglist[] = {
{ "dr0", 0 },
{ "dr1", 1 },
{ "dr2", 2 },
{ "dr3", 3 },
{ "dr6", 6 },
{ "dr7", 7 },
};
static const int ndbreglist = sizeof(dbreglist) / sizeof(struct kdbregs);
int
kdba_getregcontents(const char *regname,
struct pt_regs *regs,
kdb_machreg_t *contents)
{
int i;
if (strcmp(regname, "cesp") == 0) {
asm volatile("movl %%esp,%0":"=m" (*contents));
return 0;
}
if (strcmp(regname, "ceflags") == 0) {
int flags;
__save_flags(flags);
*contents = flags;
return 0;
}
if (regname[0] == '%') {
/* User registers: %%e[a-c]x, etc */
regname++;
regs = (struct pt_regs *)
(kdb_current_task->thread.esp0 - sizeof(struct pt_regs));
}
for (i=0; i<ndbreglist; i++) {
if (strnicmp(dbreglist[i].reg_name,
regname,
strlen(regname)) == 0)
break;
}
if ((i < ndbreglist)
&& (strlen(dbreglist[i].reg_name) == strlen(regname))) {
*contents = kdba_getdr(dbreglist[i].reg_offset);
return 0;
}
if (!regs) {
kdb_printf("%s: pt_regs not available\n", __FUNCTION__);
return KDB_BADREG;
}
if (strcmp(regname, "®s") == 0) {
*contents = (unsigned long)regs;
return 0;
}
if (strcmp(regname, "kesp") == 0) {
*contents = (unsigned long)regs + sizeof(struct pt_regs);
if ((regs->xcs & 0xffff) == __KERNEL_CS) {
/* esp and ss are not on stack */
*contents -= 2*4;
}
return 0;
}
for (i=0; i<nkdbreglist; i++) {
if (strnicmp(kdbreglist[i].reg_name,
regname,
strlen(regname)) == 0)
break;
}
if ((i < nkdbreglist)
&& (strlen(kdbreglist[i].reg_name) == strlen(regname))) {
if ((regs->xcs & 0xffff) == __KERNEL_CS) {
/* No cpl switch, esp and ss are not on stack */
if (strcmp(kdbreglist[i].reg_name, "esp") == 0) {
*contents = (kdb_machreg_t)regs +
sizeof(struct pt_regs) - 2*4;
return(0);
}
if (strcmp(kdbreglist[i].reg_name, "xss") == 0) {
asm volatile(
"pushl %%ss\n"
"popl %0\n"
:"=m" (*contents));
return(0);
}
}
*contents = *(unsigned long *)((unsigned long)regs +
kdbreglist[i].reg_offset);
return(0);
}
return KDB_BADREG;
}
/*
* kdba_setregcontents
*
* Set the contents of the register specified by the
* input string argument. Return an error if the string
* does not match a machine register.
*
* Supports modification of user-mode registers via
* %<register-name>
*
* Parameters:
* regname Pointer to string naming register
* regs Pointer to structure containing registers.
* contents Unsigned long containing new register contents
* Outputs:
* Returns:
* 0 Success
* KDB_BADREG Invalid register name
* Locking:
* None.
* Remarks:
*/
int
kdba_setregcontents(const char *regname,
struct pt_regs *regs,
unsigned long contents)
{
int i;
if (regname[0] == '%') {
regname++;
regs = (struct pt_regs *)
(kdb_current_task->thread.esp0 - sizeof(struct pt_regs));
}
for (i=0; i<ndbreglist; i++) {
if (strnicmp(dbreglist[i].reg_name,
regname,
strlen(regname)) == 0)
break;
}
if ((i < ndbreglist)
&& (strlen(dbreglist[i].reg_name) == strlen(regname))) {
kdba_putdr(dbreglist[i].reg_offset, contents);
return 0;
}
if (!regs) {
kdb_printf("%s: pt_regs not available\n", __FUNCTION__);
return KDB_BADREG;
}
for (i=0; i<nkdbreglist; i++) {
if (strnicmp(kdbreglist[i].reg_name,
regname,
strlen(regname)) == 0)
break;
}
if ((i < nkdbreglist)
&& (strlen(kdbreglist[i].reg_name) == strlen(regname))) {
*(unsigned long *)((unsigned long)regs
+ kdbreglist[i].reg_offset) = contents;
return 0;
}
return KDB_BADREG;
}
/*
* kdba_dumpregs
*
* Dump the specified register set to the display.
*
* Parameters:
* regs Pointer to structure containing registers.
* type Character string identifying register set to dump
* extra string further identifying register (optional)
* Outputs:
* Returns:
* 0 Success
* Locking:
* None.
* Remarks:
* This function will dump the general register set if the type
* argument is NULL (struct pt_regs). The alternate register
* set types supported by this function:
*
* d Debug registers
* c Control registers
* u User registers at most recent entry to kernel
* for the process currently selected with "pid" command.
* Following not yet implemented:
* m Model Specific Registers (extra defines register #)
* r Memory Type Range Registers (extra defines register)
*/
int
kdba_dumpregs(struct pt_regs *regs,
const char *type,
const char *extra)
{
int i;
int count = 0;
if (type
&& (type[0] == 'u')) {
type = NULL;
regs = (struct pt_regs *)
(kdb_current_task->thread.esp0 - sizeof(struct pt_regs));
}
if (type == NULL) {
struct kdbregs *rlp;
kdb_machreg_t contents;
if (!regs) {
kdb_printf("%s: pt_regs not available\n", __FUNCTION__);
return KDB_BADREG;
}
for (i=0, rlp=kdbreglist; i<nkdbreglist; i++,rlp++) {
kdb_printf("%s = ", rlp->reg_name);
kdba_getregcontents(rlp->reg_name, regs, &contents);
kdb_printf("0x%08lx ", contents);
if ((++count % 4) == 0)
kdb_printf("\n");
}
kdb_printf("®s = 0x%p\n", regs);
return 0;
}
switch (type[0]) {
case 'd':
{
unsigned long dr[8];
for(i=0; i<8; i++) {
if ((i == 4) || (i == 5)) continue;
dr[i] = kdba_getdr(i);
}
kdb_printf("dr0 = 0x%08lx dr1 = 0x%08lx dr2 = 0x%08lx dr3 = 0x%08lx\n",
dr[0], dr[1], dr[2], dr[3]);
kdb_printf("dr6 = 0x%08lx dr7 = 0x%08lx\n",
dr[6], dr[7]);
return 0;
}
case 'c':
{
unsigned long cr[5];
for (i=0; i<5; i++) {
cr[i] = kdb_getcr(i);
}
kdb_printf("cr0 = 0x%08lx cr1 = 0x%08lx cr2 = 0x%08lx cr3 = 0x%08lx\ncr4 = 0x%08lx\n",
cr[0], cr[1], cr[2], cr[3], cr[4]);
return 0;
}
case 'm':
break;
case 'r':
break;
default:
return KDB_BADREG;
}
/* NOTREACHED */
return 0;
}
kdb_machreg_t
kdba_getpc(struct pt_regs *regs)
{
return regs ? regs->eip : 0;
}
int
kdba_setpc(struct pt_regs *regs, kdb_machreg_t newpc)
{
if (KDB_NULL_REGS(regs))
return KDB_BADREG;
regs->eip = newpc;
KDB_STATE_SET(IP_ADJUSTED);
return 0;
}
/*
* kdba_main_loop
*
* Do any architecture specific set up before entering the main kdb loop.
* The primary function of this routine is to make all processes look the
* same to kdb, kdb must be able to list a process without worrying if the
* process is running or blocked, so make all process look as though they
* are blocked.
*
* Inputs:
* reason The reason KDB was invoked
* error The hardware-defined error code
* error2 kdb's current reason code. Initially error but can change
* acording to kdb state.
* db_result Result from break or debug point.
* regs The exception frame at time of fault/breakpoint. If reason
* is KDB_REASON_SILENT then regs is NULL, otherwise it should
* always be valid.
* Returns:
* 0 KDB was invoked for an event which it wasn't responsible
* 1 KDB handled the event for which it was invoked.
* Outputs:
* Sets eip and esp in current->thread.
* Locking:
* None.
* Remarks:
* none.
*/
int
kdba_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error,
kdb_dbtrap_t db_result, struct pt_regs *regs)
{
int ret;
kdb_save_running(regs);
ret = kdb_main_loop(reason, reason2, error, db_result, regs);
kdb_unsave_running(regs);
return ret;
}
void
kdba_disableint(kdb_intstate_t *state)
{
int *fp = (int *)state;
int flags;
__save_flags(flags);
__cli();
*fp = flags;
}
void
kdba_restoreint(kdb_intstate_t *state)
{
int flags = *(int *)state;
__restore_flags(flags);
}
void
kdba_setsinglestep(struct pt_regs *regs)
{
if (KDB_NULL_REGS(regs))
return;
if (regs->eflags & EF_IE)
KDB_STATE_SET(A_IF);
else
KDB_STATE_CLEAR(A_IF);
regs->eflags = (regs->eflags | EF_TF) & ~EF_IE;
}
void
kdba_clearsinglestep(struct pt_regs *regs)
{
if (KDB_NULL_REGS(regs))
return;
if (KDB_STATE(A_IF))
regs->eflags |= EF_IE;
else
regs->eflags &= ~EF_IE;
}
#ifdef KDB_HAVE_LONGJMP
int
kdba_setjmp(kdb_jmp_buf *jb)
{
#if defined(CONFIG_FRAME_POINTER)
__asm__ ("movl 8(%esp), %eax\n\t"
"movl %ebx, 0(%eax)\n\t"
"movl %esi, 4(%eax)\n\t"
"movl %edi, 8(%eax)\n\t"
"movl (%esp), %ecx\n\t"
"movl %ecx, 12(%eax)\n\t"
"leal 8(%esp), %ecx\n\t"
"movl %ecx, 16(%eax)\n\t"
"movl 4(%esp), %ecx\n\t"
"movl %ecx, 20(%eax)\n\t");
#else /* CONFIG_FRAME_POINTER */
__asm__ ("movl 4(%esp), %eax\n\t"
"movl %ebx, 0(%eax)\n\t"
"movl %esi, 4(%eax)\n\t"
"movl %edi, 8(%eax)\n\t"
"movl %ebp, 12(%eax)\n\t"
"leal 4(%esp), %ecx\n\t"
"movl %ecx, 16(%eax)\n\t"
"movl 0(%esp), %ecx\n\t"
"movl %ecx, 20(%eax)\n\t");
#endif /* CONFIG_FRAME_POINTER */
return 0;
}
void
kdba_longjmp(kdb_jmp_buf *jb, int reason)
{
#if defined(CONFIG_FRAME_POINTER)
__asm__("movl 8(%esp), %ecx\n\t"
"movl 12(%esp), %eax\n\t"
"movl 20(%ecx), %edx\n\t"
"movl 0(%ecx), %ebx\n\t"
"movl 4(%ecx), %esi\n\t"
"movl 8(%ecx), %edi\n\t"
"movl 12(%ecx), %ebp\n\t"
"movl 16(%ecx), %esp\n\t"
"jmp *%edx\n");
#else /* CONFIG_FRAME_POINTER */
__asm__("movl 4(%esp), %ecx\n\t"
"movl 8(%esp), %eax\n\t"
"movl 20(%ecx), %edx\n\t"
"movl 0(%ecx), %ebx\n\t"
"movl 4(%ecx), %esi\n\t"
"movl 8(%ecx), %edi\n\t"
"movl 12(%ecx), %ebp\n\t"
"movl 16(%ecx), %esp\n\t"
"jmp *%edx\n");
#endif /* CONFIG_FRAME_POINTER */
}
#endif /* KDB_HAVE_LONGJMP */
/*
* kdba_enable_mce
*
* This function is called once on each CPU to enable machine
* check exception handling.
*
* Inputs:
* None.
* Outputs:
* None.
* Returns:
* None.
* Locking:
* None.
* Remarks:
*
*/
void
kdba_enable_mce(void)
{
/* No longer required, arch/i386/kernel/bluesmoke.c does the job now */
}
/*
* kdba_enable_lbr
*
* Enable last branch recording.
*
* Parameters:
* None.
* Returns:
* None
* Locking:
* None
* Remarks:
* None.
*/
static unsigned char lbr_warned;
void
kdba_enable_lbr(void)
{
u32 lv, hv;
if (!test_bit(X86_FEATURE_MCA, boot_cpu_data.x86_capability)) {
if (lbr_warned) {
kdb_printf("kdb: machine does not support last branch recording\n");
lbr_warned = 1;
}
return;
}
rdmsr(MSR_IA32_DEBUGCTLMSR, lv, hv);
lv |= 0x1; /* Set LBR enable */
wrmsr(MSR_IA32_DEBUGCTLMSR, lv, hv);
}
/*
* kdba_disable_lbr
*
* disable last branch recording.
*
* Parameters:
* None.
* Returns:
* None
* Locking:
* None
* Remarks:
* None.
*/
void
kdba_disable_lbr(void)
{
u32 lv, hv;
if (!test_bit(X86_FEATURE_MCA, boot_cpu_data.x86_capability)) {
if (lbr_warned) {
kdb_printf("kdb: machine does not support last branch recording\n");
lbr_warned = 1;
}
return;
}
rdmsr(MSR_IA32_DEBUGCTLMSR, lv, hv);
lv &= ~0x1; /* Set LBR disable */
wrmsr(MSR_IA32_DEBUGCTLMSR, lv, hv);
}
/*
* kdba_print_lbr
*
* Print last branch and last exception addresses
*
* Parameters:
* None.
* Returns:
* None
* Locking:
* None
* Remarks:
* None.
*/
void
kdba_print_lbr(void)
{
u32 from, to, dummy;
if (!test_bit(X86_FEATURE_MCA, boot_cpu_data.x86_capability))
return;
rdmsr(MSR_IA32_LASTBRANCHFROMIP, from, dummy);
rdmsr(MSR_IA32_LASTBRANCHTOIP, to, dummy);
kdb_printf("Last Branch IP, from: ");
kdb_symbol_print(from, NULL, KDB_SP_DEFAULT);
kdb_printf(" to: ");
kdb_symbol_print(to, NULL, KDB_SP_DEFAULT);
kdb_printf("\n");
rdmsr(MSR_IA32_LASTINTFROMIP, from, dummy);
rdmsr(MSR_IA32_LASTINTTOIP, to, dummy);
kdb_printf("Last Int IP, from: ");
kdb_symbol_print(from, NULL, KDB_SP_DEFAULT);
kdb_printf(" to: ");
kdb_symbol_print(to, NULL, KDB_SP_DEFAULT);
kdb_printf("\n");
}
/*
* kdba_pt_regs
*
* Format a struct pt_regs
*
* Inputs:
* argc argument count
* argv argument vector
* envp environment vector
* regs registers at time kdb was entered.
* Outputs:
* None.
* Returns:
* zero for success, a kdb diagnostic if error
* Locking:
* none.
* Remarks:
* If no address is supplied, it uses regs.
*/
static int
kdba_pt_regs(int argc, const char **argv, const char **envp, struct pt_regs *regs)
{
int diag;
kdb_machreg_t addr;
long offset = 0;
int nextarg;
struct pt_regs *p;
static const char *fmt = " %-11.11s 0x%lx\n";
if (argc == 0) {
addr = (kdb_machreg_t) regs;
} else if (argc == 1) {
nextarg = 1;
diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
if (diag)
return diag;
} else {
return KDB_ARGCOUNT;
}
p = (struct pt_regs *) addr;
kdb_printf("struct pt_regs %p-%p\n", p, (unsigned char *)p + sizeof(*p) - 1);
kdb_print_nameval("ebx", p->ebx);
kdb_print_nameval("ecx", p->ecx);
kdb_print_nameval("edx", p->edx);
kdb_print_nameval("esi", p->esi);
kdb_print_nameval("edi", p->edi);
kdb_print_nameval("ebp", p->ebp);
kdb_print_nameval("eax", p->eax);
kdb_printf(fmt, "xds", p->xds);
kdb_printf(fmt, "xes", p->xes);
kdb_print_nameval("orig_eax", p->orig_eax);
kdb_print_nameval("eip", p->eip);
kdb_printf(fmt, "xcs", p->xcs);
kdb_printf(fmt, "eflags", p->eflags);
kdb_printf(fmt, "esp", p->esp);
kdb_printf(fmt, "xss", p->xss);
return 0;
}
/*
* kdba_init
*
* Architecture specific initialization.
*
* Parameters:
* None.
* Returns:
* None.
* Locking:
* None.
* Remarks:
* None.
*/
void __init
kdba_init(void)
{
kdba_enable_lbr();
kdb_register("pt_regs", kdba_pt_regs, "address", "Format struct pt_regs", 0);
return;
}
/*
* kdba_adjust_ip
*
* Architecture specific adjustment of instruction pointer before leaving
* kdb.
*
* Parameters:
* reason The reason KDB was invoked
* error The hardware-defined error code
* regs The exception frame at time of fault/breakpoint. If reason
* is KDB_REASON_SILENT then regs is NULL, otherwise it should
* always be valid.
* Returns:
* None.
* Locking:
* None.
* Remarks:
* noop on ix86.
*/
void
kdba_adjust_ip(kdb_reason_t reason, int error, struct pt_regs *regs)
{
return;
}