/* x86.c - core analysis suite * * Portions Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. * Copyright (C) 2002-2014,2017-2018 David Anderson * Copyright (C) 2002-2014,2017-2018 Red Hat, Inc. All rights reserved. * * 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 of the License, 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. */ #ifdef X86 /* * NOTICE OF APPRECIATION * * The stack-trace related code in this file is an extension of the stack * trace code from the Mach in-kernel debugger "ddb". Sincere thanks to * the author(s). * */ /* * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "defs.h" #include "xen_hyper_defs.h" #ifndef MCLX #include #include #include #include #include #include #include #include #include #include /* * Machine register set. */ struct db_variable db_regs[] = { "cs", &ddb_regs.tf_cs, FCN_NULL, "ds", &ddb_regs.tf_ds, FCN_NULL, "es", &ddb_regs.tf_es, FCN_NULL, #if 0 "fs", &ddb_regs.tf_fs, FCN_NULL, "gs", &ddb_regs.tf_gs, FCN_NULL, #endif "ss", &ddb_regs.tf_ss, FCN_NULL, "eax", &ddb_regs.tf_eax, FCN_NULL, "ecx", &ddb_regs.tf_ecx, FCN_NULL, "edx", &ddb_regs.tf_edx, FCN_NULL, "ebx", &ddb_regs.tf_ebx, FCN_NULL, "esp", &ddb_regs.tf_esp, FCN_NULL, "ebp", &ddb_regs.tf_ebp, FCN_NULL, "esi", &ddb_regs.tf_esi, FCN_NULL, "edi", &ddb_regs.tf_edi, FCN_NULL, "eip", &ddb_regs.tf_eip, FCN_NULL, "efl", &ddb_regs.tf_eflags, FCN_NULL, }; struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); #else typedef int db_strategy_t; /* search strategy */ #define DB_STGY_ANY 0 /* anything goes */ #define DB_STGY_XTRN 1 /* only external symbols */ #define DB_STGY_PROC 2 /* only procedures */ typedef ulong db_addr_t; /* address - unsigned */ typedef int db_expr_t; /* expression - signed */ /* * Symbol representation is specific to the symtab style: * BSD compilers use dbx' nlist, other compilers might use * a different one */ typedef char * db_sym_t; /* opaque handle on symbols */ #define DB_SYM_NULL ((db_sym_t)0) typedef uint boolean_t; #endif /* !MCLX */ /* * Stack trace. */ #ifdef MCLX static db_expr_t db_get_value(db_addr_t, int, boolean_t, struct bt_info *); #define INKERNEL(va) (machdep->kvtop(CURRENT_CONTEXT(), va, &phys, 0)) #else #define INKERNEL(va) (((vm_offset_t)(va)) >= USRSTACK) #endif struct i386_frame { struct i386_frame *f_frame; int f_retaddr; int f_arg0; }; #ifdef MCLX #define NORMAL 0 #define IDT_DIRECT_ENTRY 1 #define IDT_JMP_ERROR_CODE 2 #define RET_FROM_INTR 3 #define SIGNAL_RETURN 4 #else #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 #endif #ifndef MCLX typedef vm_offset_t db_addr_t; #endif #ifdef MCLX struct eframe { int eframe_found; int eframe_type; ulong eframe_addr; ulong jmp_error_code_eip; }; static void db_nextframe(struct i386_frame **, db_addr_t *, struct eframe *, struct bt_info *); static int dump_eframe(struct eframe *, int, struct bt_info *); static int eframe_numargs(ulong eip, struct bt_info *); static int check_for_eframe(char *, struct bt_info *); static void x86_user_eframe(struct bt_info *); static ulong x86_next_eframe(ulong addr, struct bt_info *bt); static void x86_cmd_mach(void); static int x86_get_smp_cpus(void); static void x86_display_machine_stats(void); static void x86_display_cpu_data(unsigned int); static void x86_display_memmap(void); static int x86_omit_frame_pointer(void); static void x86_back_trace_cmd(struct bt_info *); static int is_rodata_text(ulong); static int mach_CRASHDEBUG(ulong); static db_sym_t db_search_symbol(db_addr_t, db_strategy_t,db_expr_t *); static void db_symbol_values(db_sym_t, char **, db_expr_t *); static int db_sym_numargs(db_sym_t, int *, char **); static void x86_dump_line_number(ulong); static void x86_clear_machdep_cache(void); static void x86_parse_cmdline_args(void); static ulong mach_debug = 0; static int mach_CRASHDEBUG(ulong dval) { if (CRASHDEBUG(dval)) return TRUE; return (mach_debug >= dval); } #else static void db_nextframe(struct i386_frame **, db_addr_t *); #endif #ifdef MCLX static int db_numargs(struct i386_frame *, struct bt_info *bt); static void db_print_stack_entry(char *, int, char **, int *, db_addr_t, struct bt_info *, struct eframe *, int, struct i386_frame *); #else static void db_print_stack_entry (char *, int, char **, int *, db_addr_t); #endif /* * Figure out how many arguments were passed into the frame at "fp". */ static int db_numargs(fp, bt) struct i386_frame *fp; struct bt_info *bt; { int *argp; int inst; int args; argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE, bt); /* * etext is wrong for LKMs. We should attempt to interpret * the instruction at the return address in all cases. This * may require better fault handling. */ #ifdef MCLX if (!is_kernel_text((ulong)argp)) { #else if (argp < (int *)btext || argp >= (int *)etext) { #endif args = 5; } else { inst = db_get_value((int)argp, 4, FALSE, bt); if ((inst & 0xff) == 0x59) /* popl %ecx */ args = 1; else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */ args = ((inst >> 16) & 0xff) / 4; else args = 5; } return (args); } #ifdef MCLX static int eframe_numargs(ulong eip, struct bt_info *bt) { int inst; int args; if (!is_kernel_text(eip)) args = 5; else { inst = db_get_value((int)eip, 4, FALSE, bt); if ((inst & 0xff) == 0x59) /* popl %ecx */ args = 1; else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */ args = ((inst >> 16) & 0xff) / 4; else args = 5; } return args; } #endif static void #ifdef MCLX db_print_stack_entry(name, narg, argnp, argp, callpc, bt, ep, fnum, frame) #else db_print_stack_entry(name, narg, argnp, argp, callpc) #endif char *name; int narg; char **argnp; int *argp; db_addr_t callpc; #ifdef MCLX struct bt_info *bt; struct eframe *ep; int fnum; struct i386_frame *frame; #endif { #ifdef MCLX int i; db_expr_t arg; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char *sp; if (!name) { if (IS_MODULE_VADDR(callpc) && module_symbol(callpc, NULL, NULL, buf1, *gdb_output_radix)) { sprintf(buf2, "(%s)", buf1); name = buf2; } else name = "(unknown module)"; } if (strstr(name, "_MODULE_START_")) { sprintf(buf3, "(%s module)", name + strlen("_MODULE_START_")); name = buf3; } if (BT_REFERENCE_CHECK(bt)) { switch (bt->ref->cmdflags & (BT_REF_SYMBOL|BT_REF_HEXVAL)) { case BT_REF_SYMBOL: if (ep->eframe_found && ep->jmp_error_code_eip) { if (STREQ(closest_symbol(ep->jmp_error_code_eip), bt->ref->str) || STREQ(closest_symbol(callpc), bt->ref->str)) bt->ref->cmdflags |= BT_REF_FOUND; } else if (STREQ(name, bt->ref->str)) bt->ref->cmdflags |= BT_REF_FOUND; break; case BT_REF_HEXVAL: if (ep->eframe_found && ep->jmp_error_code_eip && (bt->ref->hexval == ep->jmp_error_code_eip)) bt->ref->cmdflags |= BT_REF_FOUND; else if (bt->ref->hexval == callpc) bt->ref->cmdflags |= BT_REF_FOUND; break; } return; } else { fprintf(fp, "%s#%d [%08lx] ", fnum < 10 ? " " : "", fnum, (ulong)frame); if (ep->eframe_found && ep->jmp_error_code_eip) fprintf(fp, "%s (via %s)", closest_symbol(callpc), closest_symbol(ep->jmp_error_code_eip)); else fprintf(fp, "%s", name); fprintf(fp, " at %lx\n", callpc); } if (ep->eframe_found) goto done_entry; if (STREQ(name, "L6")) goto done_entry; fprintf(fp, " ("); if ((i = get_function_numargs(callpc)) >= 0) narg = i; while (narg) { if (argnp) fprintf(fp, "%s=", *argnp++); arg = db_get_value((int)argp, 4, FALSE, bt); if ((sp = value_symbol(arg))) fprintf(fp, "%s", sp); else if ((bt->flags & BT_SYMBOLIC_ARGS) && strlen(value_to_symstr(arg, buf1, 0))) fprintf(fp, "%s", buf1); else fprintf(fp, "%x", arg); argp++; if (--narg != 0) fprintf(fp, ", "); } if (i == 0) fprintf(fp, "void"); fprintf(fp, ")\n"); done_entry: if (bt->flags & BT_LINE_NUMBERS) x86_dump_line_number(callpc); return; #else db_printf("%s(", name); while (narg) { if (argnp) db_printf("%s=", *argnp++); db_printf("%r", db_get_value((int)argp, 4, FALSE, bt)); argp++; if (--narg != 0) db_printf(","); } db_printf(") at "); db_printsym(callpc, DB_STGY_PROC); db_printf("\n"); return; #endif } #ifdef MCLX static db_sym_t db_search_symbol(db_addr_t val, db_strategy_t strategy, db_expr_t *offp) { struct syment *sp; ulong offset; if ((sp = value_search(val, &offset))) { *offp = (db_expr_t)offset; return(sp->name); } else return DB_SYM_NULL; } /* * Return name and value of a symbol */ static void db_symbol_values(db_sym_t sym, char **namep, db_expr_t *valuep) { struct syment *sp; if (sym == DB_SYM_NULL) { *namep = 0; return; } if ((sp = symbol_search(sym)) == NULL) { error(INFO, "db_symbol_values: cannot find symbol: %s\n", sym); *namep = 0; return; } *namep = sp->name; if (valuep) *valuep = sp->value; #ifndef MCLX X_db_symbol_values(db_last_symtab, sym, namep, &value); if (db_symbol_is_ambiguous(sym)) *namep = db_qualify(sym, db_last_symtab->name); if (valuep) *valuep = value; #endif } static unsigned db_extend[] = { /* table for sign-extending */ 0, 0xFFFFFF80U, 0xFFFF8000U, 0xFF800000U }; static db_expr_t db_get_value(addr, size, is_signed, bt) db_addr_t addr; int size; boolean_t is_signed; struct bt_info * bt; { char data[sizeof(int)]; db_expr_t value; int i; #ifndef MCLX db_read_bytes(addr, size, data); #else BZERO(data, sizeof(int)); if (INSTACK(addr, bt)) { if (size == sizeof(ulong)) return (db_expr_t)GET_STACK_ULONG(addr); else GET_STACK_DATA(addr, data, size); } else { if ((size == sizeof(int)) && text_value_cache(addr, 0, (uint32_t *)&value)) return value; if (!readmem(addr, KVADDR, &value, size, "db_get_value", RETURN_ON_ERROR)) error(FATAL, "db_get_value: read error: address: %lx\n", addr); if (size == sizeof(int)) text_value_cache(addr, value, NULL); } #endif value = 0; #if BYTE_MSF for (i = 0; i < size; i++) #else /* BYTE_LSF */ for (i = size - 1; i >= 0; i--) #endif { value = (value << 8) + (data[i] & 0xFF); } if (size < 4) { if (is_signed && (value & db_extend[size]) != 0) value |= db_extend[size]; } return (value); } static int db_sym_numargs(db_sym_t sym, int *nargp, char **argnames) { return FALSE; } #endif /* * Figure out the next frame up in the call stack. */ #ifdef MCLX static void db_nextframe(fp, ip, ep, bt) struct i386_frame **fp; /* in/out */ db_addr_t *ip; /* out */ struct eframe *ep; struct bt_info *bt; #else static void db_nextframe(fp, ip) struct i386_frame **fp; /* in/out */ db_addr_t *ip; /* out */ #endif { int eip, ebp; db_expr_t offset; char *sym, *name; #ifdef MCLX static int last_ebp; static int last_eip; struct syment *sp; #endif eip = db_get_value((int) &(*fp)->f_retaddr, 4, FALSE, bt); ebp = db_get_value((int) &(*fp)->f_frame, 4, FALSE, bt); /* * Figure out frame type, presuming normal. */ BZERO(ep, sizeof(struct eframe)); ep->eframe_type = NORMAL; sym = db_search_symbol(eip, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (name != NULL) { ep->eframe_type = check_for_eframe(name, bt); #ifndef MCLX if (!strcmp(name, "calltrap")) { frame_type = TRAP; } else if (!strncmp(name, "Xresume", 7)) { frame_type = INTERRUPT; } else if (!strcmp(name, "_Xsyscall")) { frame_type = SYSCALL; } #endif } switch (ep->eframe_type) { case NORMAL: ep->eframe_found = FALSE; break; case IDT_DIRECT_ENTRY: case RET_FROM_INTR: case SIGNAL_RETURN: ep->eframe_found = TRUE; ep->eframe_addr = x86_next_eframe(last_ebp + sizeof(ulong)*2, bt); break; case IDT_JMP_ERROR_CODE: ep->eframe_found = TRUE; ep->eframe_addr = x86_next_eframe(last_ebp + sizeof(ulong) * 4, bt); if ((sp = x86_jmp_error_code(last_eip))) ep->jmp_error_code_eip = sp->value; break; default: error(FATAL, "unknown exception frame type?\n"); } *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; last_ebp = ebp; last_eip = eip; return; #ifndef MCLX db_print_stack_entry(name, 0, 0, 0, eip); /* * Point to base of trapframe which is just above the * current frame. */ tf = (struct trapframe *) ((int)*fp + 8); esp = (ISPL(tf->tf_cs) == SEL_UPL) ? tf->tf_esp : (int)&tf->tf_esp; switch (frame_type) { case TRAP: if (INKERNEL((int) tf)) { eip = tf->tf_eip; ebp = tf->tf_ebp; db_printf( "--- trap %#r, eip = %#r, esp = %#r, ebp = %#r ---\n", tf->tf_trapno, eip, esp, ebp); } break; case SYSCALL: if (INKERNEL((int) tf)) { eip = tf->tf_eip; ebp = tf->tf_ebp; db_printf( "--- syscall %#r, eip = %#r, esp = %#r, ebp = %#r ---\n", tf->tf_eax, eip, esp, ebp); } break; case INTERRUPT: tf = (struct trapframe *)((int)*fp + 16); if (INKERNEL((int) tf)) { eip = tf->tf_eip; ebp = tf->tf_ebp; db_printf( "--- interrupt, eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); } break; default: break; } *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; #endif } #ifdef MCLX void x86_back_trace_cmd(struct bt_info *bt) #else ulong db_stack_trace_cmd(addr, have_addr, count, modif, task, flags) db_expr_t addr; boolean_t have_addr; db_expr_t count; char *modif; ulong task; ulong flags; #endif /* MCLX */ { struct i386_frame *frame; int *argp; db_addr_t callpc; boolean_t first; #ifdef MCLX db_expr_t addr; boolean_t have_addr; db_expr_t count; char *modif; db_addr_t last_callpc; ulong lastframe; physaddr_t phys; int frame_number; int forced; struct eframe eframe, *ep; char dbuf[BUFSIZE]; if (!(bt->flags & BT_USER_SPACE) && (!bt->stkptr || !accessible(bt->stkptr))) { error(INFO, "cannot determine starting stack pointer\n"); if (KVMDUMP_DUMPFILE()) kvmdump_display_regs(bt->tc->processor, fp); else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) diskdump_display_regs(bt->tc->processor, fp); else if (SADUMP_DUMPFILE()) sadump_display_regs(bt->tc->processor, fp); return; } if (bt->flags & BT_USER_SPACE) { if (KVMDUMP_DUMPFILE()) kvmdump_display_regs(bt->tc->processor, fp); else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) diskdump_display_regs(bt->tc->processor, fp); else if (SADUMP_DUMPFILE()) sadump_display_regs(bt->tc->processor, fp); fprintf(fp, " #0 [user space]\n"); return; } else if ((bt->flags & BT_KERNEL_SPACE)) { if (KVMDUMP_DUMPFILE()) kvmdump_display_regs(bt->tc->processor, fp); else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) diskdump_display_regs(bt->tc->processor, fp); else if (SADUMP_DUMPFILE()) sadump_display_regs(bt->tc->processor, fp); } addr = bt->stkptr; have_addr = TRUE; count = 50; modif = (char *)bt->instptr; mach_debug = bt->debug; if ((machdep->flags & OMIT_FRAME_PTR) || bt->debug || (bt->flags & BT_FRAMESIZE_DEBUG) || !(bt->flags & BT_OLD_BACK_TRACE)) { bt->flags &= ~BT_OLD_BACK_TRACE; lkcd_x86_back_trace(bt, 0, fp); return; } if (mach_CRASHDEBUG(2)) { fprintf(fp, "--> stkptr: %lx instptr: %lx (%s)\n", bt->stkptr, bt->instptr, closest_symbol(bt->instptr)); } #endif if (count == -1) count = 65535; if (!have_addr) { #ifndef MCLX frame = (struct i386_frame *)ddb_regs.tf_ebp; if (frame == NULL) frame = (struct i386_frame *)(ddb_regs.tf_esp - 4); callpc = (db_addr_t)ddb_regs.tf_eip; #endif } else { frame = (struct i386_frame *)addr; lastframe = (ulong)frame; ep = &eframe; BZERO(ep, sizeof(struct eframe)); ep->eframe_found = FALSE; callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE, bt); if (modif) { frame_number = 0; forced = TRUE; callpc = (db_addr_t)modif; } else { frame_number = 1; forced = FALSE; if (!is_kernel_text(callpc)) error(INFO, "callpc from stack is not a text address\n"); } } first = TRUE; while (count--) { struct i386_frame *actframe; int narg; char * name; db_expr_t offset; db_sym_t sym; #define MAXNARG 16 char *argnames[MAXNARG], **argnp = NULL; sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); /* * Attempt to determine a (possibly fake) frame that gives * the caller's pc. It may differ from `frame' if the * current function never sets up a standard frame or hasn't * set one up yet or has just discarded one. The last two * cases can be guessed fairly reliably for code generated * by gcc. The first case is too much trouble to handle in * general because the amount of junk on the stack depends * on the pc (the special handling of "calltrap", etc. in * db_nextframe() works because the `next' pc is special). */ actframe = frame; if (first && !have_addr) { #ifdef MCLX error(FATAL, "cannot handle \"!have_addr\" path #2\n"); #else int instr; instr = db_get_value(callpc, 4, FALSE); if ((instr & 0x00ffffff) == 0x00e58955) { /* pushl %ebp; movl %esp, %ebp */ actframe = (struct i386_frame *) (ddb_regs.tf_esp - 4); } else if ((instr & 0x0000ffff) == 0x0000e589) { /* movl %esp, %ebp */ actframe = (struct i386_frame *) ddb_regs.tf_esp; if (ddb_regs.tf_ebp == 0) { /* Fake the caller's frame better. */ frame = actframe; } } else if ((instr & 0x000000ff) == 0x000000c3) { /* ret */ actframe = (struct i386_frame *) (ddb_regs.tf_esp - 4); } else if (offset == 0) { /* Probably a symbol in assembler code. */ actframe = (struct i386_frame *) (ddb_regs.tf_esp - 4); } #endif } first = FALSE; argp = &actframe->f_arg0; narg = MAXNARG; if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) { argnp = argnames; } else { narg = db_numargs(frame, bt); } #ifdef MCLX if (is_kernel_text(callpc) || IS_MODULE_VADDR(callpc)) { if (mach_CRASHDEBUG(2)) fprintf(fp, "--> (1) lastframe: %lx => frame: %lx\n", lastframe, (ulong)frame); db_print_stack_entry(name, narg, argnp, argp, callpc, bt, ep, frame_number++, frame); if (STREQ(closest_symbol(callpc), "start_secondary")) break; if (BT_REFERENCE_FOUND(bt)) return; if ((ulong)frame < lastframe) { break; } if (INSTACK(frame, bt) && ((ulong)frame > lastframe)) lastframe = (ulong)frame; } else { if (!(forced && frame_number == 1)) { if (is_kernel_data(callpc)) { if (mach_CRASHDEBUG(2)) fprintf(fp, "--> break(1): callpc %lx is data?\n", callpc); if (!is_rodata_text(callpc)) break; } if (mach_CRASHDEBUG(2)) fprintf(fp, "--> (2) lastframe: %lx => frame: %lx\n", lastframe, (ulong)frame); db_print_stack_entry(name, narg, argnp, argp, callpc, bt, ep, frame_number++, frame); if (BT_REFERENCE_FOUND(bt)) return; if ((ulong)frame < lastframe) { break; } if (INSTACK(frame, bt) && ((ulong)frame > lastframe)) lastframe = (ulong)frame; } } if (!INSTACK(frame, bt)) { if (mach_CRASHDEBUG(2)) fprintf(fp, "--> break: !INSTACK(frame: %lx, task: %lx)\n", (ulong)frame, bt->task); break; } #else db_print_stack_entry(name, narg, argnp, argp, callpc); #endif if (actframe != frame) { /* `frame' belongs to caller. */ callpc = (db_addr_t) db_get_value((int)&actframe->f_retaddr, 4, FALSE, bt); continue; } if (ep->eframe_found) frame_number = dump_eframe(ep, frame_number, bt); last_callpc = callpc; skip_frame: db_nextframe(&frame, &callpc, ep, bt); if (mach_CRASHDEBUG(2)) { fprintf(fp, "--> db_nextframe: frame: %lx callpc: %lx [%s]\n", (ulong)frame, callpc, value_to_symstr(callpc, dbuf,0)); if (callpc == last_callpc) fprintf(fp, "last callpc == callpc!\n"); } if ((callpc == last_callpc) && STREQ(closest_symbol(callpc), "smp_stop_cpu_interrupt")) goto skip_frame; if (INSTACK(frame, bt) && ((ulong)frame < lastframe)) if (mach_CRASHDEBUG(2)) fprintf(fp, "--> frame pointer reversion?\n"); if (INKERNEL((int) callpc) && !INKERNEL((int) frame)) { sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (is_kernel_data(callpc)) { if (mach_CRASHDEBUG(2)) fprintf(fp, "--> break(2): callpc %lx is data?\n", callpc); if (!is_rodata_text(callpc)) break; } if (mach_CRASHDEBUG(2)) fprintf(fp, "--> (3) lastframe: %lx => frame: %lx\n", lastframe, (ulong)frame); db_print_stack_entry(name, 0, 0, 0, callpc, bt, ep, frame_number++, frame); if (BT_REFERENCE_FOUND(bt)) return; if ((ulong)frame < lastframe) { if (STREQ(closest_symbol(callpc), "reschedule")) x86_user_eframe(bt); break; } if (INSTACK(frame, bt) && ((ulong)frame > lastframe)) lastframe = (ulong)frame; if (mach_CRASHDEBUG(2)) fprintf(fp, "--> break: INKERNEL(callpc: %lx [%s]) && !INKERNEL(frame: %lx)\n", callpc, value_to_symstr(callpc, dbuf, 0), (ulong)frame); break; } if (!INKERNEL((int) frame)) { if (mach_CRASHDEBUG(2)) fprintf(fp, "--> break: !INKERNEL(frame: %lx)\n", (ulong)frame); break; } } if (mach_CRASHDEBUG(2)) { fprintf(fp, "--> returning lastframe: %lx\n", lastframe); } if (ep->eframe_found) frame_number = dump_eframe(ep, frame_number, bt); #ifndef MCLX return(lastframe); #endif } /* * The remainder of this file was generated at MCL to segregate * x86-specific needs. */ static int x86_uvtop(struct task_context *, ulong, physaddr_t *, int); static int x86_kvtop(struct task_context *, ulong, physaddr_t *, int); static int x86_uvtop_PAE(struct task_context *, ulong, physaddr_t *, int); static int x86_kvtop_PAE(struct task_context *, ulong, physaddr_t *, int); static int x86_uvtop_xen_wpt(struct task_context *, ulong, physaddr_t *, int); static int x86_kvtop_xen_wpt(struct task_context *, ulong, physaddr_t *, int); static int x86_uvtop_xen_wpt_PAE(struct task_context *, ulong, physaddr_t *, int); static int x86_kvtop_xen_wpt_PAE(struct task_context *, ulong, physaddr_t *, int); static int x86_kvtop_remap(ulong, physaddr_t *); static ulong x86_get_task_pgd(ulong); static ulong x86_processor_speed(void); static ulong x86_get_pc(struct bt_info *); static ulong x86_get_sp(struct bt_info *); static void x86_get_stack_frame(struct bt_info *, ulong *, ulong *); static int x86_translate_pte(ulong, void *, ulonglong); static uint64_t x86_memory_size(void); static ulong x86_vmalloc_start(void); static ulong *read_idt_table(int); static void eframe_init(void); static int remap_init(void); #define READ_IDT_INIT 1 #define READ_IDT_RUNTIME 2 static char *extract_idt_function(ulong *, char *, ulong *); static int x86_is_task_addr(ulong); static int x86_verify_symbol(const char *, ulong, char); static int x86_eframe_search(struct bt_info *); static ulong x86_in_irqstack(ulong); static int x86_dis_filter(ulong, char *, unsigned int); static struct line_number_hook x86_line_number_hooks[]; static int x86_is_uvaddr(ulong, struct task_context *); static void x86_init_kernel_pgd(void); static ulong xen_m2p_nonPAE(ulong); static int x86_xendump_p2m_create(struct xendump_data *); static int x86_pvops_xendump_p2m_create(struct xendump_data *); static int x86_pvops_xendump_p2m_l2_create(struct xendump_data *); static int x86_pvops_xendump_p2m_l3_create(struct xendump_data *); static void x86_debug_dump_page(FILE *, char *, char *); static int x86_xen_kdump_p2m_create(struct xen_kdump_data *); static char *x86_xen_kdump_load_page(ulong, char *); static char *x86_xen_kdump_load_page_PAE(ulong, char *); static ulong x86_xen_kdump_page_mfn(ulong); static ulong x86_xen_kdump_page_mfn_PAE(ulong); static ulong x86_xendump_panic_task(struct xendump_data *); static void x86_get_xendump_regs(struct xendump_data *, struct bt_info *, ulong *, ulong *); static char *x86_xendump_load_page(ulong, char *); static char *x86_xendump_load_page_PAE(ulong, char *); static int x86_xendump_page_index(ulong); static int x86_xendump_page_index_PAE(ulong); static void x86_init_hyper(int); static ulong x86_get_stackbase_hyper(ulong); static ulong x86_get_stacktop_hyper(ulong); int INT_EFRAME_SS = 14; int INT_EFRAME_ESP = 13; int INT_EFRAME_EFLAGS = 12; /* CS lcall7 */ int INT_EFRAME_CS = 11; /* EIP lcall7 */ int INT_EFRAME_EIP = 10; /* EFLAGS lcall7 */ int INT_EFRAME_ERR = 9; int INT_EFRAME_ES = 8; int INT_EFRAME_DS = 7; int INT_EFRAME_EAX = 6; int INT_EFRAME_EBP = 5; int INT_EFRAME_EDI = 4; int INT_EFRAME_ESI = 3; int INT_EFRAME_EDX = 2; int INT_EFRAME_ECX = 1; int INT_EFRAME_EBX = 0; int INT_EFRAME_GS = -1; #define MAX_USER_EFRAME_SIZE (17) #define KERNEL_EFRAME_SIZE (INT_EFRAME_EFLAGS+1) #define EFRAME_USER (1) #define EFRAME_KERNEL (2) #define DPL_BITS (0x3) static int dump_eframe(struct eframe *ep, int frame_number, struct bt_info *bt) { int i; char buf[BUFSIZE], *sp; ulong int_eframe[MAX_USER_EFRAME_SIZE]; int eframe_type, args; ulong value, *argp; eframe_type = 0; if (STACK_OFFSET_TYPE(ep->eframe_addr) > STACKSIZE()) return(frame_number); GET_STACK_DATA(ep->eframe_addr, (char *)int_eframe, SIZE(pt_regs)); if (int_eframe[INT_EFRAME_CS] & DPL_BITS) { if (!INSTACK(ep->eframe_addr + SIZE(pt_regs) - 1, bt)) return(frame_number); /* error(FATAL, "read of exception frame would go beyond stack\n"); */ eframe_type = EFRAME_USER; } else { if (!INSTACK(ep->eframe_addr + (KERNEL_EFRAME_SIZE*sizeof(ulong)) - 1, bt)) return(frame_number); /* error(FATAL, "read of exception frame would go beyond stack\n"); */ eframe_type = EFRAME_KERNEL; } x86_dump_eframe_common(bt, int_eframe, (eframe_type == EFRAME_KERNEL)); if (bt->flags & BT_EFRAME_SEARCH) return 0; if (eframe_type == EFRAME_USER) return(frame_number); if (BT_REFERENCE_CHECK(bt)) return(++frame_number); /* * The exception occurred while executing in kernel mode. * Pull out the EIP from the exception frame and display * the frame line. Then figure out whether it's possible to * show any arguments. */ fprintf(fp, "%s#%d [%08lx] %s at %08lx\n", frame_number < 10 ? " " : "", frame_number, int_eframe[INT_EFRAME_EBP], value_to_symstr(int_eframe[INT_EFRAME_EIP], buf, 0), int_eframe[INT_EFRAME_EIP]); frame_number++; if ((sp = closest_symbol(int_eframe[INT_EFRAME_EIP])) == NULL) return(frame_number); value = symbol_value(sp); argp = (ulong *)(int_eframe[INT_EFRAME_EBP] + (sizeof(long)*2)); args = is_system_call(NULL, value) ? 4 : eframe_numargs(int_eframe[INT_EFRAME_EIP], bt); fprintf(fp, " ("); for (i = 0; i < args; i++, argp++) { if (INSTACK(argp, bt)) value = GET_STACK_ULONG((ulong)argp); else /* impossible! */ readmem((ulong)argp, KVADDR, &value, sizeof(ulong), "syscall arg", FAULT_ON_ERROR); if (i) fprintf(fp, ", "); if ((sp = value_symbol(value))) fprintf(fp, "%s", sp); else if ((bt->flags & BT_SYMBOLIC_ARGS) && strlen(value_to_symstr(value, buf, 0))) fprintf(fp, "%s", buf); else fprintf(fp, "%lx", value); } fprintf(fp, ")\n"); if (bt->flags & BT_LINE_NUMBERS) x86_dump_line_number(int_eframe[INT_EFRAME_EIP]); return(frame_number); } /* * Dump an exception frame, coming from either source of stack trace code. * (i.e., -fomit-frame-pointer or not) */ void x86_dump_eframe_common(struct bt_info *bt, ulong *int_eframe, int kernel) { struct syment *sp; ulong offset; if (bt && BT_REFERENCE_CHECK(bt)) { if (!(bt->ref->cmdflags & BT_REF_HEXVAL)) return; if ((int_eframe[INT_EFRAME_EAX] == bt->ref->hexval) || (int_eframe[INT_EFRAME_EBX] == bt->ref->hexval) || (int_eframe[INT_EFRAME_ECX] == bt->ref->hexval) || (int_eframe[INT_EFRAME_EDX] == bt->ref->hexval) || (int_eframe[INT_EFRAME_EBP] == bt->ref->hexval) || (int_eframe[INT_EFRAME_ESI] == bt->ref->hexval) || (int_eframe[INT_EFRAME_EDI] == bt->ref->hexval) || ((short)int_eframe[INT_EFRAME_ES] == (short)bt->ref->hexval) || ((short)int_eframe[INT_EFRAME_DS] == (short)bt->ref->hexval) || ((short)int_eframe[INT_EFRAME_CS] == (short)bt->ref->hexval) || (int_eframe[INT_EFRAME_EIP] == bt->ref->hexval) || (int_eframe[INT_EFRAME_ERR] == bt->ref->hexval) || (int_eframe[INT_EFRAME_EFLAGS] == bt->ref->hexval)) bt->ref->cmdflags |= BT_REF_FOUND; if (!kernel) { if ((int_eframe[INT_EFRAME_ESP] == bt->ref->hexval) || ((short)int_eframe[INT_EFRAME_SS] == (short)bt->ref->hexval)) bt->ref->cmdflags |= BT_REF_FOUND; } return; } if (kernel) { if (bt && (bt->flags & BT_EFRAME_SEARCH)) { fprintf(fp, " [exception EIP: "); if ((sp = value_search(int_eframe[INT_EFRAME_EIP], &offset))) { fprintf(fp, "%s", sp->name); if (offset) fprintf(fp, (*gdb_output_radix == 16) ? "+0x%lx" : "+%ld", offset); } else fprintf(fp, "unknown or invalid address"); fprintf(fp, "]\n"); } fprintf(fp, " EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx EBP: %08lx \n", int_eframe[INT_EFRAME_EAX], int_eframe[INT_EFRAME_EBX], int_eframe[INT_EFRAME_ECX], int_eframe[INT_EFRAME_EDX], int_eframe[INT_EFRAME_EBP]); } else fprintf(fp, " EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx \n", int_eframe[INT_EFRAME_EAX], int_eframe[INT_EFRAME_EBX], int_eframe[INT_EFRAME_ECX], int_eframe[INT_EFRAME_EDX]); fprintf(fp, " DS: %04x ESI: %08lx ES: %04x EDI: %08lx", (short)int_eframe[INT_EFRAME_DS], int_eframe[INT_EFRAME_ESI], (short)int_eframe[INT_EFRAME_ES], int_eframe[INT_EFRAME_EDI]); if (kernel && (INT_EFRAME_GS != -1)) fprintf(fp, " GS: %04x", (short)int_eframe[INT_EFRAME_GS]); fprintf(fp, "\n"); if (!kernel) { fprintf(fp, " SS: %04x ESP: %08lx EBP: %08lx", (short)int_eframe[INT_EFRAME_SS], int_eframe[INT_EFRAME_ESP], int_eframe[INT_EFRAME_EBP]); if (INT_EFRAME_GS != -1) fprintf(fp, " GS: %04x", (short)int_eframe[INT_EFRAME_GS]); fprintf(fp, "\n"); } fprintf(fp, " CS: %04x EIP: %08lx ERR: %08lx EFLAGS: %08lx \n", (short)int_eframe[INT_EFRAME_CS], int_eframe[INT_EFRAME_EIP], int_eframe[INT_EFRAME_ERR], int_eframe[INT_EFRAME_EFLAGS]); } /* * Catch a few functions that show up as rodata but really are * functions. */ int is_rodata_text(ulong callpc) { struct syment *sp; if (!is_rodata(callpc, &sp)) return FALSE; if (strstr(sp->name, "interrupt") || strstr(sp->name, "call_")) return TRUE; return FALSE; } static int check_for_eframe(char *name, struct bt_info *bt) { int i; ulong *ip; char buf[BUFSIZE]; ip = read_idt_table(READ_IDT_RUNTIME); for (i = 0; i < 256; i++, ip += 2) { if (STREQ(name, extract_idt_function(ip, buf, NULL))) return IDT_DIRECT_ENTRY; } if (STREQ(name, "ret_from_intr") || STREQ(name, "call_call_function_interrupt") || STREQ(name, "call_reschedule_interrupt") || STREQ(name, "call_invalidate_interrupt")) return RET_FROM_INTR; if (STREQ(name, "error_code")) return IDT_JMP_ERROR_CODE; if (STREQ(name, "signal_return")) return SIGNAL_RETURN; return FALSE; } /* * Return the syment of the function that did the "jmp error_code". */ struct syment * x86_jmp_error_code(ulong callpc) { struct syment *sp; if (!(sp = value_search(callpc, NULL)) || !STRNEQ(sp->name, "do_")) return NULL; return (symbol_search(sp->name + strlen("do_"))); } static const char *hook_files[] = { "arch/i386/kernel/entry.S", "arch/i386/kernel/head.S", "arch/i386/kernel/semaphore.c" }; #define ENTRY_S ((char **)&hook_files[0]) #define HEAD_S ((char **)&hook_files[1]) #define SEMAPHORE_C ((char **)&hook_files[2]) static struct line_number_hook x86_line_number_hooks[] = { {"lcall7", ENTRY_S}, {"lcall27", ENTRY_S}, {"ret_from_fork", ENTRY_S}, {"system_call", ENTRY_S}, {"ret_from_sys_call", ENTRY_S}, {"ret_from_intr", ENTRY_S}, {"divide_error", ENTRY_S}, {"coprocessor_error", ENTRY_S}, {"simd_coprocessor_error", ENTRY_S}, {"device_not_available", ENTRY_S}, {"debug", ENTRY_S}, {"nmi", ENTRY_S}, {"int3", ENTRY_S}, {"overflow", ENTRY_S}, {"bounds", ENTRY_S}, {"invalid_op", ENTRY_S}, {"coprocessor_segment_overrun", ENTRY_S}, {"double_fault", ENTRY_S}, {"invalid_TSS", ENTRY_S}, {"segment_not_present", ENTRY_S}, {"stack_segment", ENTRY_S}, {"general_protection", ENTRY_S}, {"alignment_check", ENTRY_S}, {"page_fault", ENTRY_S}, {"machine_check", ENTRY_S}, {"spurious_interrupt_bug", ENTRY_S}, {"v86_signal_return", ENTRY_S}, {"tracesys", ENTRY_S}, {"tracesys_exit", ENTRY_S}, {"badsys", ENTRY_S}, {"ret_from_exception", ENTRY_S}, {"reschedule", ENTRY_S}, {"error_code", ENTRY_S}, {"device_not_available_emulate", ENTRY_S}, {"restore_all", ENTRY_S}, {"signal_return", ENTRY_S}, {"L6", HEAD_S}, {"_text", HEAD_S}, {"startup_32", HEAD_S}, {"checkCPUtype", HEAD_S}, {"is486", HEAD_S}, {"is386", HEAD_S}, {"ready", HEAD_S}, {"check_x87", HEAD_S}, {"setup_idt", HEAD_S}, {"rp_sidt", HEAD_S}, {"stack_start", HEAD_S}, {"int_msg", HEAD_S}, {"ignore_int", HEAD_S}, {"idt_descr", HEAD_S}, {"idt", HEAD_S}, {"gdt_descr", HEAD_S}, {"gdt", HEAD_S}, {"swapper_pg_dir", HEAD_S}, {"pg0", HEAD_S}, {"pg1", HEAD_S}, {"empty_zero_page", HEAD_S}, {"__down_failed", SEMAPHORE_C}, {"__down_failed_interruptible", SEMAPHORE_C}, {"__down_failed_trylock", SEMAPHORE_C}, {"__up_wakeup", SEMAPHORE_C}, {"__write_lock_failed", SEMAPHORE_C}, {"__read_lock_failed", SEMAPHORE_C}, {NULL, NULL} /* list must be NULL-terminated */ }; static void x86_dump_line_number(ulong callpc) { int retries; char buf[BUFSIZE], *p; retries = 0; try_closest: get_line_number(callpc, buf, FALSE); if (strlen(buf)) { if (retries) { p = strstr(buf, ": "); if (p) *p = NULLCHAR; } fprintf(fp, " %s\n", buf); } else { if (retries) { fprintf(fp, GDB_PATCHED() ? "" : " (cannot determine file and line number)\n"); } else { retries++; callpc = closest_symbol_value(callpc); goto try_closest; } } } /* * Look for likely exception frames in a stack. */ struct x86_pt_regs { ulong reg_value[MAX_USER_EFRAME_SIZE]; }; /* * Searches from addr within the stackframe defined by bt * for the next set of bytes that matches an exception frame pattern. * Returns either the address of the frame or 0. */ static ulong x86_next_eframe(ulong addr, struct bt_info *bt) { ulong *first, *last; struct x86_pt_regs *pt; ulong *stack; ulong rv; stack = (ulong *)bt->stackbuf; if (!INSTACK(addr, bt)) { return(0); } rv = 0; first = stack + ((addr - bt->stackbase) / sizeof(ulong)); last = stack + (((bt->stacktop - bt->stackbase) - SIZE(pt_regs)) / sizeof(ulong)); for ( ; first <= last; first++) { pt = (struct x86_pt_regs *)first; /* check for kernel exception frame */ if (((short)pt->reg_value[INT_EFRAME_CS] == 0x10) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x18) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x18) && IS_KVADDR(pt->reg_value[INT_EFRAME_EIP])) { if (!(machdep->flags & OMIT_FRAME_PTR) && !INSTACK(pt->reg_value[INT_EFRAME_EBP], bt)) continue; rv = bt->stackbase + sizeof(ulong) * (first - stack); break; } if (((short)pt->reg_value[INT_EFRAME_CS] == 0x60) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x68) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x68) && IS_KVADDR(pt->reg_value[INT_EFRAME_EIP])) { if (!(machdep->flags & OMIT_FRAME_PTR) && !INSTACK(pt->reg_value[INT_EFRAME_EBP], bt)) continue; rv = bt->stackbase + sizeof(ulong) * (first - stack); break; } if (((short)pt->reg_value[INT_EFRAME_CS] == 0x60) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x7b) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x7b) && IS_KVADDR(pt->reg_value[INT_EFRAME_EIP])) { if (!(machdep->flags & OMIT_FRAME_PTR) && !INSTACK(pt->reg_value[INT_EFRAME_EBP], bt)) continue; rv = bt->stackbase + sizeof(ulong) * (first - stack); break; } if (XEN() && ((short)pt->reg_value[INT_EFRAME_CS] == 0x61) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x7b) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x7b) && IS_KVADDR(pt->reg_value[INT_EFRAME_EIP])) { if (!(machdep->flags & OMIT_FRAME_PTR) && !INSTACK(pt->reg_value[INT_EFRAME_EBP], bt)) continue; rv = bt->stackbase + sizeof(ulong) * (first - stack); break; } /* check for user exception frame */ if (((short)pt->reg_value[INT_EFRAME_CS] == 0x23) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x2b) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x2b) && ((short)pt->reg_value[INT_EFRAME_SS] == 0x2b) && IS_UVADDR(pt->reg_value[INT_EFRAME_EIP], bt->tc) && IS_UVADDR(pt->reg_value[INT_EFRAME_ESP], bt->tc)) { rv = bt->stackbase + sizeof(ulong) * (first - stack); break; } if (((short)pt->reg_value[INT_EFRAME_CS] == 0x73) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x7b) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x7b) && ((short)pt->reg_value[INT_EFRAME_SS] == 0x7b) && IS_UVADDR(pt->reg_value[INT_EFRAME_EIP], bt->tc) && IS_UVADDR(pt->reg_value[INT_EFRAME_ESP], bt->tc)) { rv = bt->stackbase + sizeof(ulong) * (first - stack); break; } /* * 2.6 kernels using sysenter_entry instead of system_call * have a funky trampoline EIP address. */ if (((short)pt->reg_value[INT_EFRAME_CS] == 0x73) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x7b) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x7b) && ((short)pt->reg_value[INT_EFRAME_SS] == 0x7b) && (pt->reg_value[INT_EFRAME_EFLAGS] == 0x246) && IS_UVADDR(pt->reg_value[INT_EFRAME_ESP], bt->tc)) { rv = bt->stackbase + sizeof(ulong) * (first - stack); break; } } return(rv); } static int x86_eframe_search(struct bt_info *bt_in) { ulong addr; struct x86_pt_regs *pt; struct eframe eframe, *ep; struct bt_info bt_local, *bt; ulong flagsave; ulong irqstack; short cs; char *mode, *ibuf; int c, cnt; bt = bt_in; ibuf = NULL; cnt = 0; if (bt->flags & BT_EFRAME_SEARCH2) { if (!(tt->flags & IRQSTACKS)) { error(FATAL, "this kernel does not have IRQ stacks\n"); return 0; } BCOPY(bt_in, &bt_local, sizeof(struct bt_info)); bt = &bt_local; bt->flags &= ~(ulonglong)BT_EFRAME_SEARCH2; for (c = 0; c < NR_CPUS; c++) { if (tt->hardirq_ctx[c]) { if ((bt->flags & BT_CPUMASK) && !(NUM_IN_BITMAP(bt->cpumask, c))) continue; bt->hp->esp = tt->hardirq_ctx[c]; fprintf(fp, "CPU %d HARD IRQ STACK:\n", c); if ((cnt = x86_eframe_search(bt))) fprintf(fp, "\n"); else fprintf(fp, "(none found)\n\n"); } } for (c = 0; c < NR_CPUS; c++) { if (tt->softirq_ctx[c]) { if ((bt->flags & BT_CPUMASK) && !(NUM_IN_BITMAP(bt->cpumask, c))) continue; bt->hp->esp = tt->softirq_ctx[c]; fprintf(fp, "CPU %d SOFT IRQ STACK:\n", c); if ((cnt = x86_eframe_search(bt))) fprintf(fp, "\n"); else fprintf(fp, "(none found)\n\n"); } } return 0; } if (bt->hp && bt->hp->esp) { BCOPY(bt_in, &bt_local, sizeof(struct bt_info)); bt = &bt_local; addr = bt->hp->esp; if ((irqstack = x86_in_irqstack(addr))) { bt->stackbase = irqstack; bt->stacktop = irqstack + SIZE(irq_ctx); if (SIZE(irq_ctx) > STACKSIZE()) { ibuf = (char *)GETBUF(SIZE(irq_ctx)); bt->stackbuf = ibuf; } alter_stackbuf(bt); } else if (!INSTACK(addr, bt)) error(FATAL, "unrecognized stack address for this task: %lx\n", bt->hp->esp); } else if (tt->flags & THREAD_INFO) addr = bt->stackbase + roundup(SIZE(thread_info), sizeof(ulong)); else addr = bt->stackbase + roundup(SIZE(task_struct), sizeof(ulong)); ep = &eframe; BZERO(ep, sizeof(struct eframe)); while ((addr = x86_next_eframe(addr, bt)) != 0) { cnt++; if (bt->flags & BT_EFRAME_COUNT) { addr += 4; continue; } pt = (struct x86_pt_regs *) (bt->stackbuf + (addr - bt->stackbase)); ep->eframe_addr = addr; cs = pt->reg_value[INT_EFRAME_CS]; if ((cs == 0x23) || (cs == 0x73)) { mode = "USER-MODE"; } else if ((cs == 0x10) || (cs == 0x60)) { mode = "KERNEL-MODE"; } else if (XEN() && (cs == 0x61)) { mode = "KERNEL-MODE"; } else { mode = "UNKNOWN-MODE"; } fprintf(fp, "%s %s EXCEPTION FRAME AT %lx:\n", bt->flags & BT_EFRAME_SEARCH ? "\n" : "", mode, ep->eframe_addr); flagsave = bt->flags; bt->flags |= BT_EFRAME_SEARCH; dump_eframe(ep, 0, bt); bt->flags = flagsave; addr += 4; } if (ibuf) FREEBUF(ibuf); return cnt; } static ulong x86_in_irqstack(ulong addr) { int c; if (!(tt->flags & IRQSTACKS)) return 0; for (c = 0; c < NR_CPUS; c++) { if (tt->hardirq_ctx[c]) { if ((addr >= tt->hardirq_ctx[c]) && (addr < (tt->hardirq_ctx[c] + SIZE(irq_ctx)))) return(tt->hardirq_ctx[c]); } if (tt->softirq_ctx[c]) { if ((addr >= tt->softirq_ctx[c]) && (addr < (tt->softirq_ctx[c] + SIZE(irq_ctx)))) return(tt->softirq_ctx[c]); } } return 0; } /* * Dump the kernel-entry user-mode exception frame. */ static void x86_user_eframe(struct bt_info *bt) { struct eframe eframe, *ep; struct x86_pt_regs x86_pt_regs, *pt; ulong pt_regs_addr; pt_regs_addr = USER_EFRAME_ADDR(bt->task); readmem(pt_regs_addr, KVADDR, &x86_pt_regs, sizeof(struct x86_pt_regs), "x86 pt_regs", FAULT_ON_ERROR); pt = &x86_pt_regs; if (((short)pt->reg_value[INT_EFRAME_CS] == 0x23) && ((short)pt->reg_value[INT_EFRAME_DS] == 0x2b) && ((short)pt->reg_value[INT_EFRAME_ES] == 0x2b) && ((short)pt->reg_value[INT_EFRAME_SS] == 0x2b) && IS_UVADDR(pt->reg_value[INT_EFRAME_EIP], bt->tc) && IS_UVADDR(pt->reg_value[INT_EFRAME_ESP], bt->tc) && IS_UVADDR(pt->reg_value[INT_EFRAME_EBP], bt->tc)) { ep = &eframe; BZERO(ep, sizeof(struct eframe)); ep->eframe_addr = pt_regs_addr; bt->flags |= BT_EFRAME_SEARCH; dump_eframe(ep, 0, bt); bt->flags &= ~(ulonglong)BT_EFRAME_SEARCH; } } /* * Do all necessary machine-specific setup here. This is called three times, * during symbol table initialization, and before and after GDB has been * initialized. */ struct machine_specific x86_machine_specific = { 0 }; static int PGDIR_SHIFT; static int PTRS_PER_PTE; static int PTRS_PER_PGD; void x86_init(int when) { struct syment *sp, *spn; if (XEN_HYPER_MODE()) { x86_init_hyper(when); return; } switch (when) { case SETUP_ENV: machdep->process_elf_notes = x86_process_elf_notes; break; case PRE_SYMTAB: machdep->verify_symbol = x86_verify_symbol; if (pc->flags & KERNEL_DEBUG_QUERY) return; machdep->pagesize = memory_page_size(); machdep->pageshift = ffs(machdep->pagesize) - 1; machdep->pageoffset = machdep->pagesize - 1; machdep->pagemask = ~((ulonglong)machdep->pageoffset); machdep->stacksize = machdep->pagesize * 2; if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc pgd space."); if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc pmd space."); if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc ptbl space."); machdep->last_pgd_read = 0; machdep->last_pmd_read = 0; machdep->last_ptbl_read = 0; machdep->machspec = &x86_machine_specific; machdep->verify_paddr = generic_verify_paddr; x86_parse_cmdline_args(); break; case PRE_GDB: if (symbol_exists("pae_pgd_cachep") || ((sp = symbol_search("pkmap_count")) && (spn = next_symbol(NULL, sp)) && (((spn->value - sp->value)/sizeof(int)) == 512))) { machdep->flags |= PAE; PGDIR_SHIFT = PGDIR_SHIFT_3LEVEL; PTRS_PER_PTE = PTRS_PER_PTE_3LEVEL; PTRS_PER_PGD = PTRS_PER_PGD_3LEVEL; machdep->uvtop = x86_uvtop_PAE; machdep->kvtop = x86_kvtop_PAE; } else { PGDIR_SHIFT = PGDIR_SHIFT_2LEVEL; PTRS_PER_PTE = PTRS_PER_PTE_2LEVEL; PTRS_PER_PGD = PTRS_PER_PGD_2LEVEL; machdep->uvtop = x86_uvtop; machdep->kvtop = x86_kvtop; free(machdep->pmd); machdep->pmd = machdep->pgd; } machdep->ptrs_per_pgd = PTRS_PER_PGD; if (!machdep->kvbase) { if (kernel_symbol_exists("module_kaslr_mutex")) machdep->kvbase = 0xc0000000; else machdep->kvbase = symbol_value("_stext") & ~KVBASE_MASK; } if (machdep->kvbase & 0x80000000) machdep->is_uvaddr = generic_is_uvaddr; else { vt->flags |= COMMON_VADDR; machdep->is_uvaddr = x86_is_uvaddr; } machdep->identity_map_base = machdep->kvbase; machdep->is_kvaddr = generic_is_kvaddr; machdep->eframe_search = x86_eframe_search; machdep->back_trace = x86_back_trace_cmd; machdep->processor_speed = x86_processor_speed; machdep->get_task_pgd = x86_get_task_pgd; machdep->dump_irq = generic_dump_irq; machdep->get_irq_affinity = generic_get_irq_affinity; machdep->show_interrupts = generic_show_interrupts; machdep->get_stack_frame = x86_get_stack_frame; machdep->get_stackbase = generic_get_stackbase; machdep->get_stacktop = generic_get_stacktop; machdep->translate_pte = x86_translate_pte; machdep->memory_size = x86_memory_size; machdep->vmalloc_start = x86_vmalloc_start; machdep->is_task_addr = x86_is_task_addr; machdep->dis_filter = x86_dis_filter; machdep->cmd_mach = x86_cmd_mach; machdep->get_smp_cpus = x86_get_smp_cpus; machdep->flags |= FRAMESIZE_DEBUG; machdep->value_to_symbol = generic_machdep_value_to_symbol; machdep->init_kernel_pgd = x86_init_kernel_pgd; machdep->xendump_p2m_create = x86_xendump_p2m_create; machdep->xen_kdump_p2m_create = x86_xen_kdump_p2m_create; machdep->xendump_panic_task = x86_xendump_panic_task; machdep->get_xendump_regs = x86_get_xendump_regs; machdep->clear_machdep_cache = x86_clear_machdep_cache; break; case POST_GDB: if (x86_omit_frame_pointer()) machdep->flags |= OMIT_FRAME_PTR; STRUCT_SIZE_INIT(user_regs_struct, "user_regs_struct"); if (MEMBER_EXISTS("user_regs_struct", "ebp")) MEMBER_OFFSET_INIT(user_regs_struct_ebp, "user_regs_struct", "ebp"); else MEMBER_OFFSET_INIT(user_regs_struct_ebp, "user_regs_struct", "bp"); if (MEMBER_EXISTS("user_regs_struct", "esp")) MEMBER_OFFSET_INIT(user_regs_struct_esp, "user_regs_struct", "esp"); else MEMBER_OFFSET_INIT(user_regs_struct_esp, "user_regs_struct", "sp"); if (MEMBER_EXISTS("user_regs_struct", "eip")) MEMBER_OFFSET_INIT(user_regs_struct_eip, "user_regs_struct", "eip"); else MEMBER_OFFSET_INIT(user_regs_struct_eip, "user_regs_struct", "ip"); if (MEMBER_EXISTS("user_regs_struct", "eax")) MEMBER_OFFSET_INIT(user_regs_struct_eax, "user_regs_struct", "eax"); else MEMBER_OFFSET_INIT(user_regs_struct_eax, "user_regs_struct", "ax"); if (MEMBER_EXISTS("user_regs_struct", "ebx")) MEMBER_OFFSET_INIT(user_regs_struct_ebx, "user_regs_struct", "ebx"); else MEMBER_OFFSET_INIT(user_regs_struct_ebx, "user_regs_struct", "bx"); if (MEMBER_EXISTS("user_regs_struct", "ecx")) MEMBER_OFFSET_INIT(user_regs_struct_ecx, "user_regs_struct", "ecx"); else MEMBER_OFFSET_INIT(user_regs_struct_ecx, "user_regs_struct", "cx"); if (MEMBER_EXISTS("user_regs_struct", "edx")) MEMBER_OFFSET_INIT(user_regs_struct_edx, "user_regs_struct", "edx"); else MEMBER_OFFSET_INIT(user_regs_struct_edx, "user_regs_struct", "dx"); if (MEMBER_EXISTS("user_regs_struct", "esi")) MEMBER_OFFSET_INIT(user_regs_struct_esi, "user_regs_struct", "esi"); else MEMBER_OFFSET_INIT(user_regs_struct_esi, "user_regs_struct", "si"); if (MEMBER_EXISTS("user_regs_struct", "edi")) MEMBER_OFFSET_INIT(user_regs_struct_edi, "user_regs_struct", "edi"); else MEMBER_OFFSET_INIT(user_regs_struct_edi, "user_regs_struct", "di"); if (MEMBER_EXISTS("user_regs_struct", "eflags")) MEMBER_OFFSET_INIT(user_regs_struct_eflags, "user_regs_struct", "eflags"); else MEMBER_OFFSET_INIT(user_regs_struct_eflags, "user_regs_struct", "flags"); MEMBER_OFFSET_INIT(user_regs_struct_cs, "user_regs_struct", "cs"); MEMBER_OFFSET_INIT(user_regs_struct_ds, "user_regs_struct", "ds"); MEMBER_OFFSET_INIT(user_regs_struct_es, "user_regs_struct", "es"); MEMBER_OFFSET_INIT(user_regs_struct_fs, "user_regs_struct", "fs"); MEMBER_OFFSET_INIT(user_regs_struct_gs, "user_regs_struct", "gs"); MEMBER_OFFSET_INIT(user_regs_struct_ss, "user_regs_struct", "ss"); if (!VALID_STRUCT(user_regs_struct)) { /* Use this hardwired version -- sometimes the * debuginfo doesn't pick this up even though * it exists in the kernel; it shouldn't change. */ struct x86_user_regs_struct { long ebx, ecx, edx, esi, edi, ebp, eax; unsigned short ds, __ds, es, __es; unsigned short fs, __fs, gs, __gs; long orig_eax, eip; unsigned short cs, __cs; long eflags, esp; unsigned short ss, __ss; }; ASSIGN_SIZE(user_regs_struct) = sizeof(struct x86_user_regs_struct); ASSIGN_OFFSET(user_regs_struct_ebp) = offsetof(struct x86_user_regs_struct, ebp); ASSIGN_OFFSET(user_regs_struct_esp) = offsetof(struct x86_user_regs_struct, esp); ASSIGN_OFFSET(user_regs_struct_eip) = offsetof(struct x86_user_regs_struct, eip); ASSIGN_OFFSET(user_regs_struct_eax) = offsetof(struct x86_user_regs_struct, eax); ASSIGN_OFFSET(user_regs_struct_ebx) = offsetof(struct x86_user_regs_struct, ebx); ASSIGN_OFFSET(user_regs_struct_ecx) = offsetof(struct x86_user_regs_struct, ecx); ASSIGN_OFFSET(user_regs_struct_edx) = offsetof(struct x86_user_regs_struct, edx); ASSIGN_OFFSET(user_regs_struct_esi) = offsetof(struct x86_user_regs_struct, esi); ASSIGN_OFFSET(user_regs_struct_edi) = offsetof(struct x86_user_regs_struct, edi); ASSIGN_OFFSET(user_regs_struct_eflags) = offsetof(struct x86_user_regs_struct, eflags); ASSIGN_OFFSET(user_regs_struct_cs) = offsetof(struct x86_user_regs_struct, cs); ASSIGN_OFFSET(user_regs_struct_ds) = offsetof(struct x86_user_regs_struct, ds); ASSIGN_OFFSET(user_regs_struct_es) = offsetof(struct x86_user_regs_struct, es); ASSIGN_OFFSET(user_regs_struct_fs) = offsetof(struct x86_user_regs_struct, fs); ASSIGN_OFFSET(user_regs_struct_gs) = offsetof(struct x86_user_regs_struct, gs); ASSIGN_OFFSET(user_regs_struct_ss) = offsetof(struct x86_user_regs_struct, ss); } MEMBER_OFFSET_INIT(thread_struct_cr3, "thread_struct", "cr3"); STRUCT_SIZE_INIT(cpuinfo_x86, "cpuinfo_x86"); STRUCT_SIZE_INIT(irq_ctx, "irq_ctx"); if (STRUCT_EXISTS("e820map")) { STRUCT_SIZE_INIT(e820map, "e820map"); MEMBER_OFFSET_INIT(e820map_nr_map, "e820map", "nr_map"); } else { STRUCT_SIZE_INIT(e820map, "e820_table"); MEMBER_OFFSET_INIT(e820map_nr_map, "e820_table", "nr_entries"); } if (STRUCT_EXISTS("e820entry")) { STRUCT_SIZE_INIT(e820entry, "e820entry"); MEMBER_OFFSET_INIT(e820entry_addr, "e820entry", "addr"); MEMBER_OFFSET_INIT(e820entry_size, "e820entry", "size"); MEMBER_OFFSET_INIT(e820entry_type, "e820entry", "type"); } else { STRUCT_SIZE_INIT(e820entry, "e820_entry"); MEMBER_OFFSET_INIT(e820entry_addr, "e820_entry", "addr"); MEMBER_OFFSET_INIT(e820entry_size, "e820_entry", "size"); MEMBER_OFFSET_INIT(e820entry_type, "e820_entry", "type"); } if (!VALID_STRUCT(irq_ctx)) STRUCT_SIZE_INIT(irq_ctx, "irq_stack"); if (KVMDUMP_DUMPFILE()) set_kvm_iohole(NULL); if (symbol_exists("irq_desc")) ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc, "irq_desc", NULL, 0); else if (kernel_symbol_exists("nr_irqs")) get_symbol_data("nr_irqs", sizeof(unsigned int), &machdep->nr_irqs); else machdep->nr_irqs = 224; /* NR_IRQS */ if (!machdep->hz) { machdep->hz = HZ; if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) machdep->hz = 1000; } if (machdep->flags & PAE) { if (THIS_KERNEL_VERSION < LINUX(2,6,26)) machdep->section_size_bits = _SECTION_SIZE_BITS_PAE_ORIG; else machdep->section_size_bits = _SECTION_SIZE_BITS_PAE_2_6_26; machdep->max_physmem_bits = _MAX_PHYSMEM_BITS_PAE; } else { machdep->section_size_bits = _SECTION_SIZE_BITS; machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; } if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) { if (machdep->flags & PAE) machdep->uvtop = x86_uvtop_xen_wpt_PAE; else machdep->uvtop = x86_uvtop_xen_wpt; } if (XEN()) { MEMBER_OFFSET_INIT(vcpu_guest_context_user_regs, "vcpu_guest_context", "user_regs"); MEMBER_OFFSET_INIT(cpu_user_regs_esp, "cpu_user_regs", "esp"); MEMBER_OFFSET_INIT(cpu_user_regs_eip, "cpu_user_regs", "eip"); } if (THIS_KERNEL_VERSION < LINUX(2,6,24)) machdep->line_number_hooks = x86_line_number_hooks; eframe_init(); if (THIS_KERNEL_VERSION >= LINUX(2,6,28)) machdep->machspec->page_protnone = _PAGE_GLOBAL; else machdep->machspec->page_protnone = _PAGE_PSE; STRUCT_SIZE_INIT(note_buf, "note_buf_t"); STRUCT_SIZE_INIT(elf_prstatus, "elf_prstatus"); MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus", "pr_reg"); STRUCT_SIZE_INIT(percpu_data, "percpu_data"); if (!remap_init()) machdep->machspec->max_numnodes = -1; MEMBER_OFFSET_INIT(inactive_task_frame_ret_addr, "inactive_task_frame", "ret_addr"); break; case POST_INIT: read_idt_table(READ_IDT_INIT); break; case LOG_ONLY: machdep->kvbase = kt->vmcoreinfo._stext_SYMBOL & ~KVBASE_MASK; break; } } /* * Handle non-default (c0000000) values of CONFIG_PAGE_OFFSET * with "--machdep page_offset=
" */ static void x86_parse_cmdline_args(void) { int index, i, c, err; char *arglist[MAXARGS]; char buf[BUFSIZE]; char *p; ulong value = 0; for (index = 0; index < MAX_MACHDEP_ARGS; index++) { if (!machdep->cmdline_args[index]) break; if (!strstr(machdep->cmdline_args[index], "=")) { error(WARNING, "ignoring --machdep option: %x\n", machdep->cmdline_args[index]); continue; } strcpy(buf, machdep->cmdline_args[index]); for (p = buf; *p; p++) { if (*p == ',') *p = ' '; } c = parse_line(buf, arglist); for (i = 0; i < c; i++) { err = 0; if (STRNEQ(arglist[i], "page_offset=")) { int flags = RETURN_ON_ERROR | QUIET; p = arglist[i] + strlen("page_offset="); if (strlen(p)) value = htol(p, flags, &err); if (!err) { machdep->kvbase = value; error(NOTE, "setting PAGE_OFFSET to: 0x%lx\n\n", machdep->kvbase); continue; } } error(WARNING, "ignoring --machdep option: %s\n", arglist[i]); } } } /* * Account for addition of pt_regs.xgs field in 2.6.20+ kernels. */ static void eframe_init(void) { if (INVALID_SIZE(pt_regs)) { if (THIS_KERNEL_VERSION < LINUX(2,6,20)) ASSIGN_SIZE(pt_regs) = (MAX_USER_EFRAME_SIZE-2)*sizeof(ulong); else { ASSIGN_SIZE(pt_regs) = MAX_USER_EFRAME_SIZE*sizeof(ulong); INT_EFRAME_SS = 15; INT_EFRAME_ESP = 14; INT_EFRAME_EFLAGS = 13; INT_EFRAME_CS = 12; INT_EFRAME_EIP = 11; INT_EFRAME_ERR = 10; INT_EFRAME_GS = 9; } return; } if (MEMBER_EXISTS("pt_regs", "esp")) { INT_EFRAME_SS = MEMBER_OFFSET("pt_regs", "xss") / 4; INT_EFRAME_ESP = MEMBER_OFFSET("pt_regs", "esp") / 4; INT_EFRAME_EFLAGS = MEMBER_OFFSET("pt_regs", "eflags") / 4; INT_EFRAME_CS = MEMBER_OFFSET("pt_regs", "xcs") / 4; INT_EFRAME_EIP = MEMBER_OFFSET("pt_regs", "eip") / 4; INT_EFRAME_ERR = MEMBER_OFFSET("pt_regs", "orig_eax") / 4; if ((INT_EFRAME_GS = MEMBER_OFFSET("pt_regs", "xgs")) != -1) INT_EFRAME_GS /= 4; INT_EFRAME_ES = MEMBER_OFFSET("pt_regs", "xes") / 4; INT_EFRAME_DS = MEMBER_OFFSET("pt_regs", "xds") / 4; INT_EFRAME_EAX = MEMBER_OFFSET("pt_regs", "eax") / 4; INT_EFRAME_EBP = MEMBER_OFFSET("pt_regs", "ebp") / 4; INT_EFRAME_EDI = MEMBER_OFFSET("pt_regs", "edi") / 4; INT_EFRAME_ESI = MEMBER_OFFSET("pt_regs", "esi") / 4; INT_EFRAME_EDX = MEMBER_OFFSET("pt_regs", "edx") / 4; INT_EFRAME_ECX = MEMBER_OFFSET("pt_regs", "ecx") / 4; INT_EFRAME_EBX = MEMBER_OFFSET("pt_regs", "ebx") / 4; } else { INT_EFRAME_SS = MEMBER_OFFSET("pt_regs", "ss") / 4; INT_EFRAME_ESP = MEMBER_OFFSET("pt_regs", "sp") / 4; INT_EFRAME_EFLAGS = MEMBER_OFFSET("pt_regs", "flags") / 4; INT_EFRAME_CS = MEMBER_OFFSET("pt_regs", "cs") / 4; INT_EFRAME_EIP = MEMBER_OFFSET("pt_regs", "ip") / 4; INT_EFRAME_ERR = MEMBER_OFFSET("pt_regs", "orig_ax") / 4; if ((INT_EFRAME_GS = MEMBER_OFFSET("pt_regs", "gs")) != -1) INT_EFRAME_GS /= 4; INT_EFRAME_ES = MEMBER_OFFSET("pt_regs", "es") / 4; INT_EFRAME_DS = MEMBER_OFFSET("pt_regs", "ds") / 4; INT_EFRAME_EAX = MEMBER_OFFSET("pt_regs", "ax") / 4; INT_EFRAME_EBP = MEMBER_OFFSET("pt_regs", "bp") / 4; INT_EFRAME_EDI = MEMBER_OFFSET("pt_regs", "di") / 4; INT_EFRAME_ESI = MEMBER_OFFSET("pt_regs", "si") / 4; INT_EFRAME_EDX = MEMBER_OFFSET("pt_regs", "dx") / 4; INT_EFRAME_ECX = MEMBER_OFFSET("pt_regs", "cx") / 4; INT_EFRAME_EBX = MEMBER_OFFSET("pt_regs", "bx") / 4; } } /* * Locate regions remapped by the remap allocator */ static int remap_init(void) { ulong start_vaddr, end_vaddr, start_pfn; int max_numnodes; struct machine_specific *ms; struct syment *sp; if (! (sp = symbol_search("node_remap_start_vaddr")) ) return FALSE; start_vaddr = sp->value; if (! (sp = symbol_search("node_remap_end_vaddr")) ) return FALSE; end_vaddr = sp->value; if (! (sp = symbol_search("node_remap_start_pfn")) ) return FALSE; start_pfn = sp->value; max_numnodes = get_array_length("node_remap_start_pfn", NULL, sizeof(ulong)); if (max_numnodes < 1) max_numnodes = 1; ms = machdep->machspec; ms->remap_start_vaddr = calloc(3 * max_numnodes, sizeof(ulong)); if (!ms->remap_start_vaddr) error(FATAL, "cannot malloc remap array"); ms->remap_end_vaddr = ms->remap_start_vaddr + max_numnodes; ms->remap_start_pfn = ms->remap_end_vaddr + max_numnodes; readmem(start_vaddr, KVADDR, ms->remap_start_vaddr, max_numnodes * sizeof(ulong), "node_remap_start_vaddr", FAULT_ON_ERROR); readmem(end_vaddr, KVADDR, ms->remap_end_vaddr, max_numnodes * sizeof(ulong), "node_remap_end_vaddr", FAULT_ON_ERROR); readmem(start_pfn, KVADDR, ms->remap_start_pfn, max_numnodes * sizeof(ulong), "node_remap_end_vaddr", FAULT_ON_ERROR); ms->max_numnodes = max_numnodes; return TRUE; } static int x86_kvtop_remap(ulong kvaddr, physaddr_t *paddr) { struct machine_specific *ms; int i; ms = machdep->machspec; /* ms->max_numnodes is -1 when unused. */ for (i = 0; i < ms->max_numnodes; ++i) { if (kvaddr >= ms->remap_start_vaddr[i] && kvaddr < ms->remap_end_vaddr[i]) { *paddr = PTOB(ms->remap_start_pfn[i]) + kvaddr - ms->remap_start_vaddr[i]; return TRUE; } } return FALSE; } /* * Needs to be done this way because of potential 4G/4G split. */ static int x86_is_uvaddr(ulong vaddr, struct task_context *tc) { return IN_TASK_VMA(tc->task, vaddr); } /* * Translates a user virtual address to its physical address. cmd_vtop() * sets the verbose flag so that the pte translation gets displayed; all * other callers quietly accept the translation. * * This routine can also take mapped kernel virtual addresses if the -u flag * was passed to cmd_vtop(). If so, it makes the translation using the * kernel-memory PGD entry instead of swapper_pg_dir. */ #define _4MB_PAGE_MASK (~((MEGABYTES(4))-1)) #define _2MB_PAGE_MASK (~((MEGABYTES(2))-1)) static int x86_uvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) { ulong mm, active_mm; ulong *pgd; ulong *page_dir; ulong *page_middle; ulong *page_table; ulong pgd_pte; ulong pmd_pte; ulong pte; char buf[BUFSIZE]; if (!tc) error(FATAL, "current context invalid\n"); *paddr = 0; if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { if (VALID_MEMBER(thread_struct_cr3)) pgd = (ulong *)machdep->get_task_pgd(tc->task); else { if (INVALID_MEMBER(task_struct_active_mm)) error(FATAL, "no cr3 or active_mm?\n"); readmem(tc->task + OFFSET(task_struct_active_mm), KVADDR, &active_mm, sizeof(void *), "task active_mm contents", FAULT_ON_ERROR); if (!active_mm) error(FATAL, "no active_mm for this kernel thread\n"); readmem(active_mm + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } } else { if ((mm = task_mm(tc->task, TRUE))) pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); else readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); page_dir = pgd + (vaddr >> PGDIR_SHIFT); FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); if (verbose) fprintf(fp, " PGD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_dir)), pgd_pte); if (!(pgd_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) goto no_upage; if (pgd_pte & _PAGE_4M) { if (verbose) { fprintf(fp, " PAGE: %s (4MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pgd_pte)))); x86_translate_pte(pgd_pte, 0, 0); } *paddr = NONPAE_PAGEBASE(pgd_pte) + (vaddr & ~_4MB_PAGE_MASK); return TRUE; } page_middle = page_dir; FILL_PMD(NONPAE_PAGEBASE(page_middle), KVADDR, PAGESIZE()); pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); if (verbose) fprintf(fp, " PMD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_middle)), pmd_pte); if (!pmd_pte) goto no_upage; #ifdef PTES_IN_LOWMEM page_table = (ulong *)(PTOV(NONPAE_PAGEBASE(pmd_pte)) + ((vaddr>>10) & ((PTRS_PER_PTE-1)<<2))); FILL_PTBL(NONPAE_PAGEBASE(page_table), KVADDR, PAGESIZE()); pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); #else page_table = (ulong *)((NONPAE_PAGEBASE(pmd_pte)) + ((vaddr>>10) & ((PTRS_PER_PTE-1)<<2))); FILL_PTBL(NONPAE_PAGEBASE(page_table), PHYSADDR, PAGESIZE()); pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); #endif if (verbose) fprintf(fp, " PTE: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_table)), pte); if (!(pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) { *paddr = pte; if (pte && verbose) { fprintf(fp, "\n"); x86_translate_pte(pte, 0, 0); } goto no_upage; } *paddr = NONPAE_PAGEBASE(pte) + PAGEOFFSET(vaddr); if (verbose) { fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pte)))); x86_translate_pte(pte, 0, 0); } return TRUE; no_upage: return FALSE; } static int x86_uvtop_xen_wpt(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) { ulong mm, active_mm; ulong *pgd; ulong *page_dir; ulong *page_middle; ulong *machine_page_table, *pseudo_page_table; ulong pgd_pte, pseudo_pgd_pte; ulong pmd_pte; ulong machine_pte, pseudo_pte; char buf[BUFSIZE]; if (!tc) error(FATAL, "current context invalid\n"); *paddr = 0; if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { if (VALID_MEMBER(thread_struct_cr3)) pgd = (ulong *)machdep->get_task_pgd(tc->task); else { if (INVALID_MEMBER(task_struct_active_mm)) error(FATAL, "no cr3 or active_mm?\n"); readmem(tc->task + OFFSET(task_struct_active_mm), KVADDR, &active_mm, sizeof(void *), "task active_mm contents", FAULT_ON_ERROR); if (!active_mm) error(FATAL, "no active_mm for this kernel thread\n"); readmem(active_mm + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } } else { if ((mm = task_mm(tc->task, TRUE))) pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); else readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); page_dir = pgd + (vaddr >> PGDIR_SHIFT); FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); if (verbose) fprintf(fp, " PGD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_dir)), pgd_pte); if (!pgd_pte) goto no_upage; if (pgd_pte & _PAGE_4M) { if (verbose) fprintf(fp, " PAGE: %s (4MB) [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pgd_pte)))); pseudo_pgd_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(pgd_pte)); if (pseudo_pgd_pte == XEN_MFN_NOT_FOUND) { if (verbose) fprintf(fp, " PAGE: page not available\n"); *paddr = PADDR_NOT_AVAILABLE; return FALSE; } pseudo_pgd_pte |= PAGEOFFSET(pgd_pte); if (verbose) { fprintf(fp, " PAGE: %s (4MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pseudo_pgd_pte)))); x86_translate_pte(pseudo_pgd_pte, 0, 0); } *paddr = NONPAE_PAGEBASE(pseudo_pgd_pte) + (vaddr & ~_4MB_PAGE_MASK); return TRUE; } page_middle = page_dir; FILL_PMD(NONPAE_PAGEBASE(page_middle), KVADDR, PAGESIZE()); pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); if (verbose) fprintf(fp, " PMD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_middle)), pmd_pte); if (!pmd_pte) goto no_upage; machine_page_table = (ulong *)((NONPAE_PAGEBASE(pmd_pte)) + ((vaddr>>10) & ((PTRS_PER_PTE-1)<<2))); pseudo_page_table = (ulong *) xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_page_table)); FILL_PTBL(NONPAE_PAGEBASE(pseudo_page_table), PHYSADDR, PAGESIZE()); machine_pte = ULONG(machdep->ptbl + PAGEOFFSET(machine_page_table)); if (verbose) { fprintf(fp, " PTE: %s [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)machine_page_table))); fprintf(fp, " PTE: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pseudo_page_table + PAGEOFFSET(machine_page_table))), machine_pte); } if (!(machine_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) { *paddr = machine_pte; if (machine_pte && verbose) { fprintf(fp, "\n"); x86_translate_pte(machine_pte, 0, 0); } goto no_upage; } pseudo_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_pte)); pseudo_pte |= PAGEOFFSET(machine_pte); *paddr = NONPAE_PAGEBASE(pseudo_pte) + PAGEOFFSET(vaddr); if (verbose) { fprintf(fp, " PAGE: %s [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(machine_pte)))); fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pseudo_pte)))); x86_translate_pte(pseudo_pte, 0, 0); } return TRUE; no_upage: return FALSE; } static int x86_uvtop_PAE(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) { ulong mm, active_mm; ulonglong *pgd; ulonglong page_dir_entry; ulonglong page_middle; ulonglong page_middle_entry; ulonglong page_table; ulonglong page_table_entry; ulonglong physpage; ulonglong ull; ulong offset; char buf[BUFSIZE]; if (!tc) error(FATAL, "current context invalid\n"); *paddr = 0; if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { if (VALID_MEMBER(thread_struct_cr3)) pgd = (ulonglong *)machdep->get_task_pgd(tc->task); else { if (INVALID_MEMBER(task_struct_active_mm)) error(FATAL, "no cr3 or active_mm?\n"); readmem(tc->task + OFFSET(task_struct_active_mm), KVADDR, &active_mm, sizeof(void *), "task active_mm contents", FAULT_ON_ERROR); if (!active_mm) error(FATAL, "no active_mm for this kernel thread\n"); readmem(active_mm + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } } else { if ((mm = task_mm(tc->task, TRUE))) pgd = (ulonglong *)(ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd))); else readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); FILL_PGD(pgd, KVADDR, PTRS_PER_PGD * sizeof(ulonglong)); offset = ((vaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) * sizeof(ulonglong); page_dir_entry = *((ulonglong *)&machdep->pgd[offset]); if (verbose) fprintf(fp, " PGD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pgd + offset)), page_dir_entry); if (!(page_dir_entry & _PAGE_PRESENT)) { goto no_upage; } page_middle = PAE_PAGEBASE(page_dir_entry); FILL_PMD_PAE(page_middle, PHYSADDR, PAGESIZE()); offset = ((vaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); page_middle_entry = *((ulonglong *)&machdep->pmd[offset]); if (verbose) { ull = page_middle + offset; fprintf(fp, " PMD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_middle_entry); } if (!(page_middle_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { goto no_upage; } if (page_middle_entry & _PAGE_PSE) { if (verbose) { ull = PAE_PAGEBASE(page_middle_entry); fprintf(fp, " PAGE: %s (2MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull))); x86_translate_pte(0, 0, page_middle_entry); } physpage = PAE_PAGEBASE(page_middle_entry) + (vaddr & ~_2MB_PAGE_MASK); *paddr = physpage; return TRUE; } page_table = PAE_PAGEBASE(page_middle_entry); FILL_PTBL_PAE(page_table, PHYSADDR, PAGESIZE()); offset = ((vaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * sizeof(ulonglong); page_table_entry = *((ulonglong *)&machdep->ptbl[offset]); if (verbose) { ull = page_table + offset; fprintf(fp, " PTE: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_table_entry); } if (!(page_table_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { *paddr = page_table_entry; if (page_table_entry && verbose) { fprintf(fp, "\n"); x86_translate_pte(0, 0, page_table_entry); } goto no_upage; } physpage = PAE_PAGEBASE(page_table_entry) + PAGEOFFSET(vaddr); *paddr = physpage; if (verbose) { ull = PAE_PAGEBASE(page_table_entry); fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull))); x86_translate_pte(0, 0, page_table_entry); } return TRUE; no_upage: return FALSE; } static int x86_uvtop_xen_wpt_PAE(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) { ulong mm, active_mm; ulonglong *pgd; ulonglong page_dir_entry; ulonglong page_middle, pseudo_page_middle; ulonglong page_middle_entry; ulonglong page_table, pseudo_page_table; ulonglong page_table_entry, pte; ulonglong physpage, pseudo_physpage; ulonglong ull; ulong offset; char buf[BUFSIZE]; if (!tc) error(FATAL, "current context invalid\n"); *paddr = 0; if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { if (VALID_MEMBER(thread_struct_cr3)) pgd = (ulonglong *)machdep->get_task_pgd(tc->task); else { if (INVALID_MEMBER(task_struct_active_mm)) error(FATAL, "no cr3 or active_mm?\n"); readmem(tc->task + OFFSET(task_struct_active_mm), KVADDR, &active_mm, sizeof(void *), "task active_mm contents", FAULT_ON_ERROR); if (!active_mm) error(FATAL, "no active_mm for this kernel thread\n"); readmem(active_mm + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } } else { if ((mm = task_mm(tc->task, TRUE))) pgd = (ulonglong *)(ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd))); else readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); } if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); FILL_PGD(pgd, KVADDR, PTRS_PER_PGD * sizeof(ulonglong)); offset = ((vaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) * sizeof(ulonglong); page_dir_entry = *((ulonglong *)&machdep->pgd[offset]); if (verbose) fprintf(fp, " PGD: %s => %llx [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pgd + offset)), page_dir_entry); if (!(page_dir_entry & _PAGE_PRESENT)) { goto no_upage; } page_middle = PAE_PAGEBASE(page_dir_entry); pseudo_page_middle = xen_m2p(page_middle); if (verbose) fprintf(fp, " PGD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pgd + offset)), pseudo_page_middle | PAGEOFFSET(page_dir_entry) | (page_dir_entry & _PAGE_NX)); FILL_PMD_PAE(pseudo_page_middle, PHYSADDR, PAGESIZE()); offset = ((vaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); page_middle_entry = *((ulonglong *)&machdep->pmd[offset]); if (verbose) { ull = page_middle + offset; fprintf(fp, " PMD: %s => %llx [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_middle_entry); } if (!(page_middle_entry & _PAGE_PRESENT)) { goto no_upage; } if (page_middle_entry & _PAGE_PSE) { error(FATAL, "_PAGE_PSE in an mfn not supported\n"); /* XXX */ if (verbose) { ull = PAE_PAGEBASE(page_middle_entry); fprintf(fp, " PAGE: %s (2MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull))); x86_translate_pte(0, 0, page_middle_entry); } physpage = PAE_PAGEBASE(page_middle_entry) + (vaddr & ~_2MB_PAGE_MASK); *paddr = physpage; return TRUE; } page_table = PAE_PAGEBASE(page_middle_entry); pseudo_page_table = xen_m2p(page_table); if (verbose) { ull = page_middle + offset; fprintf(fp, " PMD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), pseudo_page_table | PAGEOFFSET(page_middle_entry) | (page_middle_entry & _PAGE_NX)); } FILL_PTBL_PAE(pseudo_page_table, PHYSADDR, PAGESIZE()); offset = ((vaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * sizeof(ulonglong); page_table_entry = *((ulonglong *)&machdep->ptbl[offset]); if (verbose) { ull = page_table + offset; fprintf(fp, " PTE: %s => %llx [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_table_entry); } if (!(page_table_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { *paddr = page_table_entry; if (page_table_entry && verbose) { fprintf(fp, "\n"); x86_translate_pte(0, 0, page_table_entry); } goto no_upage; } physpage = PAE_PAGEBASE(page_table_entry) + PAGEOFFSET(vaddr); pseudo_physpage = xen_m2p(physpage); if (verbose) { ull = page_table + offset; fprintf(fp, " PTE: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), pseudo_physpage | PAGEOFFSET(page_table_entry) | (page_table_entry & _PAGE_NX)); } *paddr = pseudo_physpage + PAGEOFFSET(vaddr); if (verbose) { physpage = PAE_PAGEBASE(physpage); fprintf(fp, " PAGE: %s [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&physpage))); fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&pseudo_physpage))); pte = pseudo_physpage | PAGEOFFSET(page_table_entry) | (page_table_entry & _PAGE_NX); x86_translate_pte(0, 0, pte); } return TRUE; no_upage: return FALSE; } /* * Translates a kernel virtual address to its physical address. cmd_vtop() * sets the verbose flag so that the pte translation gets displayed; all * other callers quietly accept the translation. */ static int x86_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { ulong *pgd; ulong *page_dir; ulong *page_middle; ulong *page_table; ulong pgd_pte; ulong pmd_pte; ulong pte; char buf[BUFSIZE]; if (!IS_KVADDR(kvaddr)) return FALSE; if (XEN_HYPER_MODE()) { if (DIRECTMAP_VIRT_ADDR(kvaddr)) { *paddr = kvaddr - DIRECTMAP_VIRT_START; return TRUE; } pgd = (ulong *)symbol_value("idle_pg_table_l2"); } else { if (x86_kvtop_remap(kvaddr, paddr)) { if (!verbose) return TRUE; } else if (!vt->vmalloc_start) { *paddr = VTOP(kvaddr); return TRUE; } else if (!IS_VMALLOC_ADDR(kvaddr)) { *paddr = VTOP(kvaddr); if (!verbose) return TRUE; } if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) return (x86_kvtop_xen_wpt(tc, kvaddr, paddr, verbose)); pgd = (ulong *)vt->kernel_pgd[0]; } if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); page_dir = pgd + (kvaddr >> PGDIR_SHIFT); FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); if (verbose) fprintf(fp, " PGD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_dir)), pgd_pte); if (!pgd_pte) goto no_kpage; if (pgd_pte & _PAGE_4M) { if (verbose) { fprintf(fp, " PAGE: %s (4MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pgd_pte)))); x86_translate_pte(pgd_pte, 0, 0); } *paddr = NONPAE_PAGEBASE(pgd_pte) + (kvaddr & ~_4MB_PAGE_MASK); return TRUE; } page_middle = page_dir; FILL_PMD(NONPAE_PAGEBASE(page_middle), KVADDR, PAGESIZE()); pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); if (verbose) fprintf(fp, " PMD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_middle)), pmd_pte); if (!pmd_pte) goto no_kpage; #ifdef PTES_IN_LOWMEM page_table = (ulong *)(PTOV(NONPAE_PAGEBASE(pmd_pte)) + ((kvaddr>>10) & ((PTRS_PER_PTE-1)<<2))); FILL_PTBL(NONPAE_PAGEBASE(page_table), KVADDR, PAGESIZE()); pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); #else page_table = (ulong *)((NONPAE_PAGEBASE(pmd_pte)) + ((kvaddr>>10) & ((PTRS_PER_PTE-1)<<2))); FILL_PTBL(NONPAE_PAGEBASE(page_table), PHYSADDR, PAGESIZE()); pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); #endif if (verbose) fprintf(fp, " PTE: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_table)), pte); if (!(pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) { if (pte && verbose) { fprintf(fp, "\n"); x86_translate_pte(pte, 0, 0); } goto no_kpage; } if (verbose) { fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pte)))); x86_translate_pte(pte, 0, 0); } *paddr = NONPAE_PAGEBASE(pte) + PAGEOFFSET(kvaddr); return TRUE; no_kpage: return FALSE; } static int x86_kvtop_xen_wpt(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { ulong *pgd; ulong *page_dir; ulong *page_middle; ulong *machine_page_table, *pseudo_page_table; ulong pgd_pte, pseudo_pgd_pte; ulong pmd_pte; ulong machine_pte, pseudo_pte; char buf[BUFSIZE]; pgd = (ulong *)vt->kernel_pgd[0]; if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); page_dir = pgd + (kvaddr >> PGDIR_SHIFT); FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); if (verbose) fprintf(fp, " PGD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_dir)), pgd_pte); if (!pgd_pte) goto no_kpage; if (pgd_pte & _PAGE_4M) { if (verbose) fprintf(fp, " PAGE: %s (4MB) [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pgd_pte)))); pseudo_pgd_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(pgd_pte)); if (pseudo_pgd_pte == XEN_MFN_NOT_FOUND) { if (verbose) fprintf(fp, " PAGE: page not available\n"); *paddr = PADDR_NOT_AVAILABLE; return FALSE; } pseudo_pgd_pte |= PAGEOFFSET(pgd_pte); if (verbose) { fprintf(fp, " PAGE: %s (4MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pseudo_pgd_pte)))); x86_translate_pte(pseudo_pgd_pte, 0, 0); } *paddr = NONPAE_PAGEBASE(pseudo_pgd_pte) + (kvaddr & ~_4MB_PAGE_MASK); return TRUE; } page_middle = page_dir; FILL_PMD(NONPAE_PAGEBASE(page_middle), KVADDR, PAGESIZE()); pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); if (verbose) fprintf(fp, " PMD: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)page_middle)), pmd_pte); if (!pmd_pte) goto no_kpage; machine_page_table = (ulong *)((NONPAE_PAGEBASE(pmd_pte)) + ((kvaddr>>10) & ((PTRS_PER_PTE-1)<<2))); pseudo_page_table = (ulong *) xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_page_table)); FILL_PTBL(NONPAE_PAGEBASE(pseudo_page_table), PHYSADDR, PAGESIZE()); machine_pte = ULONG(machdep->ptbl + PAGEOFFSET(machine_page_table)); if (verbose) { fprintf(fp, " PTE: %s [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)machine_page_table))); fprintf(fp, " PTE: %s => %lx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pseudo_page_table + PAGEOFFSET(machine_page_table))), machine_pte); } if (!(machine_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) { if (machine_pte && verbose) { fprintf(fp, "\n"); x86_translate_pte(machine_pte, 0, 0); } goto no_kpage; } pseudo_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_pte)); pseudo_pte |= PAGEOFFSET(machine_pte); if (verbose) { fprintf(fp, " PAGE: %s [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(machine_pte)))); fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(NONPAE_PAGEBASE(pseudo_pte)))); x86_translate_pte(pseudo_pte, 0, 0); } *paddr = NONPAE_PAGEBASE(pseudo_pte) + PAGEOFFSET(kvaddr); return TRUE; no_kpage: return FALSE; } static int x86_kvtop_PAE(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { ulonglong *pgd; ulonglong page_dir_entry; ulonglong page_middle; ulonglong page_middle_entry; ulonglong page_table; ulonglong page_table_entry; ulonglong physpage; ulonglong ull; char buf[BUFSIZE]; ulong offset; if (!IS_KVADDR(kvaddr)) return FALSE; if (XEN_HYPER_MODE()) { if (DIRECTMAP_VIRT_ADDR(kvaddr)) { *paddr = kvaddr - DIRECTMAP_VIRT_START; return TRUE; } if (symbol_exists("idle_pg_table_l3")) pgd = (ulonglong *)symbol_value("idle_pg_table_l3"); else pgd = (ulonglong *)symbol_value("idle_pg_table"); } else { if (x86_kvtop_remap(kvaddr, paddr)) { if (!verbose) return TRUE; } else if (!vt->vmalloc_start) { *paddr = VTOP(kvaddr); return TRUE; } else if (!IS_VMALLOC_ADDR(kvaddr)) { *paddr = VTOP(kvaddr); if (!verbose) return TRUE; } if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) return (x86_kvtop_xen_wpt_PAE(tc, kvaddr, paddr, verbose)); pgd = (ulonglong *)vt->kernel_pgd[0]; } if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); FILL_PGD(pgd, KVADDR, PTRS_PER_PGD * sizeof(ulonglong)); offset = ((kvaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) * sizeof(ulonglong); page_dir_entry = *((ulonglong *)&machdep->pgd[offset]); if (verbose) fprintf(fp, " PGD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pgd + offset)), page_dir_entry); if (!(page_dir_entry & _PAGE_PRESENT)) { goto no_kpage; } page_middle = PAE_PAGEBASE(page_dir_entry); FILL_PMD_PAE(page_middle, PHYSADDR, PAGESIZE()); offset = ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); page_middle_entry = *((ulonglong *)&machdep->pmd[offset]); if (verbose) { ull = page_middle + offset; fprintf(fp, " PMD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_middle_entry); } if (!(page_middle_entry & _PAGE_PRESENT)) { goto no_kpage; } if (page_middle_entry & _PAGE_PSE) { if (verbose) { ull = PAE_PAGEBASE(page_middle_entry); fprintf(fp, " PAGE: %s (2MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull))); x86_translate_pte(0, 0, page_middle_entry); } physpage = PAE_PAGEBASE(page_middle_entry) + (kvaddr & ~_2MB_PAGE_MASK); *paddr = physpage; return TRUE; } page_table = PAE_PAGEBASE(page_middle_entry); FILL_PTBL_PAE(page_table, PHYSADDR, PAGESIZE()); offset = ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * sizeof(ulonglong); page_table_entry = *((ulonglong *)&machdep->ptbl[offset]); if (verbose) { ull = page_table + offset; fprintf(fp, " PTE: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_table_entry); } if (!(page_table_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { if (page_table_entry && verbose) { fprintf(fp, "\n"); x86_translate_pte(0, 0, page_table_entry); } goto no_kpage; } physpage = PAE_PAGEBASE(page_table_entry) + PAGEOFFSET(kvaddr); *paddr = physpage; if (verbose) { ull = PAE_PAGEBASE(page_table_entry); fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull))); x86_translate_pte(0, 0, page_table_entry); } return TRUE; no_kpage: return FALSE; } static int x86_kvtop_xen_wpt_PAE(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { ulonglong *pgd; ulonglong page_dir_entry; ulonglong page_middle, pseudo_page_middle; ulonglong page_middle_entry; ulonglong page_table, pseudo_page_table; ulonglong page_table_entry, pte; ulonglong physpage, pseudo_physpage; ulonglong ull; ulong offset; char buf[BUFSIZE]; pgd = (ulonglong *)vt->kernel_pgd[0]; if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); FILL_PGD(pgd, KVADDR, PTRS_PER_PGD * sizeof(ulonglong)); offset = ((kvaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) * sizeof(ulonglong); page_dir_entry = *((ulonglong *)&machdep->pgd[offset]); if (verbose) fprintf(fp, " PGD: %s => %llx [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pgd + offset)), page_dir_entry); if (!(page_dir_entry & _PAGE_PRESENT)) { goto no_kpage; } page_middle = PAE_PAGEBASE(page_dir_entry); pseudo_page_middle = xen_m2p(page_middle); if (verbose) fprintf(fp, " PGD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR((ulong)pgd + offset)), pseudo_page_middle | PAGEOFFSET(page_dir_entry) | (page_dir_entry & _PAGE_NX)); FILL_PMD_PAE(pseudo_page_middle, PHYSADDR, PAGESIZE()); offset = ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); page_middle_entry = *((ulonglong *)&machdep->pmd[offset]); if (verbose) { ull = page_middle + offset; fprintf(fp, " PMD: %s => %llx [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_middle_entry); } if (!(page_middle_entry & _PAGE_PRESENT)) { goto no_kpage; } if (page_middle_entry & _PAGE_PSE) { error(FATAL, "_PAGE_PSE in an mfn not supported\n"); /* XXX */ if (verbose) { ull = PAE_PAGEBASE(page_middle_entry); fprintf(fp, " PAGE: %s (2MB)\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull))); x86_translate_pte(0, 0, page_middle_entry); } physpage = PAE_PAGEBASE(page_middle_entry) + (kvaddr & ~_2MB_PAGE_MASK); *paddr = physpage; return TRUE; } page_table = PAE_PAGEBASE(page_middle_entry); pseudo_page_table = xen_m2p(page_table); if (verbose) { ull = page_middle + offset; fprintf(fp, " PMD: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), pseudo_page_table | PAGEOFFSET(page_middle_entry) | (page_middle_entry & _PAGE_NX)); } FILL_PTBL_PAE(pseudo_page_table, PHYSADDR, PAGESIZE()); offset = ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * sizeof(ulonglong); page_table_entry = *((ulonglong *)&machdep->ptbl[offset]); if (verbose) { ull = page_table + offset; fprintf(fp, " PTE: %s => %llx [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), page_table_entry); } if (!(page_table_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { if (page_table_entry && verbose) { fprintf(fp, "\n"); x86_translate_pte(0, 0, page_table_entry); } goto no_kpage; } physpage = PAE_PAGEBASE(page_table_entry) + PAGEOFFSET(kvaddr); pseudo_physpage = xen_m2p(physpage); if (verbose) { ull = page_table + offset; fprintf(fp, " PTE: %s => %llx\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&ull)), pseudo_physpage | PAGEOFFSET(page_table_entry) | (page_table_entry & _PAGE_NX)); } *paddr = pseudo_physpage + PAGEOFFSET(kvaddr); if (verbose) { physpage = PAE_PAGEBASE(physpage); fprintf(fp, " PAGE: %s [machine]\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&physpage))); fprintf(fp, " PAGE: %s\n\n", mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, MKSTR(&pseudo_physpage))); pte = pseudo_physpage | PAGEOFFSET(page_table_entry) | (page_table_entry & _PAGE_NX); x86_translate_pte(0, 0, pte); } return TRUE; no_kpage: return FALSE; } void x86_clear_machdep_cache(void) { machdep->machspec->last_pmd_read_PAE = 0; machdep->machspec->last_ptbl_read_PAE = 0; } /* * Get the relevant page directory pointer from a task structure. */ static ulong x86_get_task_pgd(ulong task) { long offset; ulong cr3; offset = OFFSET_OPTION(task_struct_thread, task_struct_tss); if (INVALID_MEMBER(thread_struct_cr3)) error(FATAL, "cr3 does not exist in this kernel's thread_struct\n"); offset += OFFSET(thread_struct_cr3); readmem(task + offset, KVADDR, &cr3, sizeof(ulong), "task thread cr3", FAULT_ON_ERROR); return(PTOV(cr3)); } /* * Calculate and return the speed of the processor. */ ulong x86_processor_speed(void) { unsigned long cpu_hz, cpu_khz; if (machdep->mhz) return (machdep->mhz); if (symbol_exists("cpu_hz")) { get_symbol_data("cpu_hz", sizeof(long), &cpu_hz); if (cpu_hz) return (machdep->mhz = cpu_hz/1000000); } if (symbol_exists("cpu_khz")) { get_symbol_data("cpu_khz", sizeof(long), &cpu_khz); if (cpu_khz) return(machdep->mhz = cpu_khz/1000); } return 0; } void x86_dump_machdep_table(ulong arg) { int others; ulong xen_wpt; char buf[BUFSIZE]; struct machine_specific *ms; int i, max_numnodes; switch (arg) { default: break; } others = 0; fprintf(fp, " flags: %lx (", machdep->flags); if (machdep->flags & KSYMS_START) fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); if (machdep->flags & PAE) fprintf(fp, "%sPAE", others++ ? "|" : ""); if (machdep->flags & OMIT_FRAME_PTR) fprintf(fp, "%sOMIT_FRAME_PTR", others++ ? "|" : ""); if (machdep->flags & FRAMESIZE_DEBUG) fprintf(fp, "%sFRAMESIZE_DEBUG", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " kvbase: %lx\n", machdep->kvbase); fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base); fprintf(fp, " pagesize: %d\n", machdep->pagesize); fprintf(fp, " pageshift: %d\n", machdep->pageshift); fprintf(fp, " pagemask: %llx\n", machdep->pagemask); fprintf(fp, " pageoffset: %lx\n", machdep->pageoffset); fprintf(fp, " stacksize: %ld\n", machdep->stacksize); fprintf(fp, " hz: %d\n", machdep->hz); fprintf(fp, " mhz: %ld\n", machdep->mhz); fprintf(fp, " memsize: %lld (0x%llx)\n", machdep->memsize, machdep->memsize); fprintf(fp, " bits: %d\n", machdep->bits); fprintf(fp, " nr_irqs: %d\n", machdep->nr_irqs); fprintf(fp, " eframe_search: x86_eframe_search()\n"); fprintf(fp, " back_trace: x86_back_trace_cmd()\n"); fprintf(fp, "get_processor_speed: x86_processor_speed()\n"); xen_wpt = XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES); if (machdep->flags & PAE) { fprintf(fp, " uvtop: %s()\n", xen_wpt ? "x86_uvtop_xen_wpt_PAE" : "x86_uvtop_PAE"); fprintf(fp, " kvtop: x86_kvtop_PAE()%s\n", xen_wpt ? " -> x86_kvtop_xen_wpt_PAE()" : ""); } else { fprintf(fp, " uvtop: %s()\n", xen_wpt ? "x86_uvtop_xen_wpt" : "x86_uvtop"); fprintf(fp, " kvtop: x86_kvtop()%s\n", xen_wpt ? " -> x86_kvtop_xen_wpt()" : ""); } fprintf(fp, " get_task_pgd: x86_get_task_pgd()\n"); fprintf(fp, " dump_irq: generic_dump_irq()\n"); fprintf(fp, " get_irq_affinity: generic_get_irq_affinity()\n"); fprintf(fp, " show_interrupts: generic_show_interrupts()\n"); fprintf(fp, " get_stack_frame: x86_get_stack_frame()\n"); fprintf(fp, " get_stackbase: generic_get_stackbase()\n"); fprintf(fp, " get_stacktop: generic_get_stacktop()\n"); fprintf(fp, " translate_pte: x86_translate_pte()\n"); fprintf(fp, " memory_size: x86_memory_size()\n"); fprintf(fp, " vmalloc_start: x86_vmalloc_start()\n"); fprintf(fp, " is_task_addr: x86_is_task_addr()\n"); fprintf(fp, " verify_symbol: x86_verify_symbol()\n"); fprintf(fp, " dis_filter: x86_dis_filter()\n"); fprintf(fp, " cmd_mach: x86_cmd_mach()\n"); fprintf(fp, " get_smp_cpus: x86_get_smp_cpus()\n"); fprintf(fp, " is_kvaddr: generic_is_kvaddr()\n"); fprintf(fp, " is_uvaddr: %s\n", COMMON_VADDR_SPACE() ? "x86_is_uvaddr()" : "generic_is_uvaddr()"); fprintf(fp, " verify_paddr: generic_verify_paddr()\n"); fprintf(fp, " init_kernel_pgd: x86_init_kernel_pgd()\n"); fprintf(fp, " value_to_symbol: %s\n", machdep->value_to_symbol == generic_machdep_value_to_symbol ? "generic_machdep_value_to_symbol()" : "x86_is_entry_tramp_address()"); fprintf(fp, " line_number_hooks: %s\n", machdep->line_number_hooks ? "x86_line_number_hooks" : "(not used)"); fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); fprintf(fp, " last_pmd_read: %lx\n", machdep->last_pmd_read); fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read); fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); fprintf(fp, " xendump_p2m_create: x86_xendump_p2m_create()\n"); fprintf(fp, " xendump_p2m_create: %s\n", PVOPS_XEN() ? "x86_pvops_xendump_p2m_create()" : "x86_xendump_p2m_create()"); fprintf(fp, " xendump_panic_task: x86_xendump_panic_task()\n"); fprintf(fp, " get_xendump_regs: x86_get_xendump_regs()\n"); fprintf(fp, "xen_kdump_p2m_create: x86_xen_kdump_p2m_create()\n"); fprintf(fp, "clear_machdep_cache: x86_clear_machdep_cache()\n"); fprintf(fp, " INT_EFRAME_[reg]:\n"); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "SS: "), INT_EFRAME_SS); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "ESP: "), INT_EFRAME_ESP); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "EFLAGS: "), INT_EFRAME_EFLAGS); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "CS: "), INT_EFRAME_CS); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "IP: "), INT_EFRAME_EIP); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "ERR: "), INT_EFRAME_ERR); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "ES: "), INT_EFRAME_ES); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "DS: "), INT_EFRAME_DS); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "EAX: "), INT_EFRAME_EAX); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "EBP: "), INT_EFRAME_EBP); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "EDI: "), INT_EFRAME_EDI); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "ESI: "), INT_EFRAME_ESI); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "EDX: "), INT_EFRAME_EDX); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "ECX: "), INT_EFRAME_ECX); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "EBX: "), INT_EFRAME_EBX); fprintf(fp, "%s %d\n", mkstring(buf, 21, RJUST, "GS: "), INT_EFRAME_GS); fprintf(fp, " machspec: x86_machine_specific\n"); fprintf(fp, " idt_table: %lx\n", (ulong)machdep->machspec->idt_table); fprintf(fp, " entry_tramp_start: %lx\n", machdep->machspec->entry_tramp_start); fprintf(fp, " entry_tramp_end: %lx\n", machdep->machspec->entry_tramp_end); fprintf(fp, " entry_tramp_start_phys: %llx\n", machdep->machspec->entry_tramp_start_phys); fprintf(fp, " last_pmd_read_PAE: %llx\n", machdep->machspec->last_pmd_read_PAE); fprintf(fp, " last_ptbl_read_PAE: %llx\n", machdep->machspec->last_ptbl_read_PAE); fprintf(fp, " page_protnone: %lx\n", machdep->machspec->page_protnone); ms = machdep->machspec; max_numnodes = ms->max_numnodes; fprintf(fp, " MAX_NUMNODES: "); if (max_numnodes < 0) { fprintf(fp, "(unused)\n"); } else { fprintf(fp, "%d\n", max_numnodes); fprintf(fp, " remap_start_vaddr:"); for (i = 0; i < max_numnodes; ++i) { if ((i % 8) == 0) fprintf(fp, "\n "); fprintf(fp, "%08lx ", ms->remap_start_vaddr[i]); } fprintf(fp, "\n"); fprintf(fp, " remap_end_vaddr:"); for (i = 0; i < max_numnodes; ++i) { if ((i % 8) == 0) fprintf(fp, "\n "); fprintf(fp, "%08lx ", ms->remap_end_vaddr[i]); } fprintf(fp, "\n"); fprintf(fp, " remap_start_pfn:"); for (i = 0; i < max_numnodes; ++i) { if ((i % 8) == 0) fprintf(fp, "\n "); fprintf(fp, "%08lx ", ms->remap_start_pfn[i]); } fprintf(fp, "\n"); } } /* * Get a stack frame combination of pc and ra from the most relevent spot. */ static void x86_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp) { if (pcp) *pcp = x86_get_pc(bt); if (spp) *spp = x86_get_sp(bt); } /* * Get the saved PC from a user-space copy of the kernel stack. */ static ulong x86_get_pc(struct bt_info *bt) { ulong offset; ulong eip, inactive_task_frame; if (tt->flags & THREAD_INFO) { if (VALID_MEMBER(task_struct_thread_eip)) readmem(bt->task + OFFSET(task_struct_thread_eip), KVADDR, &eip, sizeof(void *), "thread_struct eip", FAULT_ON_ERROR); else if (VALID_MEMBER(inactive_task_frame_ret_addr)) { readmem(bt->task + OFFSET(task_struct_thread_esp), KVADDR, &inactive_task_frame, sizeof(void *), "task_struct.inactive_task_frame", FAULT_ON_ERROR); readmem(inactive_task_frame + OFFSET(inactive_task_frame_ret_addr), KVADDR, &eip, sizeof(void *), "inactive_task_frame.ret_addr", FAULT_ON_ERROR); } else error(FATAL, "cannot determine ip address\n"); return eip; } offset = OFFSET_OPTION(task_struct_thread_eip, task_struct_tss_eip); return GET_STACK_ULONG(offset); } /* * Get the saved SP from a user-space copy of the kernel stack if it * cannot be found in the panic_ksp array. */ static ulong x86_get_sp(struct bt_info *bt) { ulong offset, ksp; if (get_panic_ksp(bt, &ksp)) return ksp; if (tt->flags & THREAD_INFO) { readmem(bt->task + OFFSET(task_struct_thread_esp), KVADDR, &ksp, sizeof(void *), "thread_struct esp", FAULT_ON_ERROR); if (VALID_MEMBER(inactive_task_frame_ret_addr)) ksp += OFFSET(inactive_task_frame_ret_addr); return ksp; } offset = OFFSET_OPTION(task_struct_thread_esp, task_struct_tss_esp); return GET_STACK_ULONG(offset); } /* * Translate a PTE, returning TRUE if the page is _PAGE_PRESENT. * If a physaddr pointer is passed in, don't print anything. */ static int x86_translate_pte(ulong pte, void *physaddr, ulonglong pae_pte) { int c, len1, len2, len3, others, page_present; char buf[BUFSIZE]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; char ptebuf[BUFSIZE]; char physbuf[BUFSIZE]; char *arglist[MAXARGS]; ulonglong paddr; int nx_bit_set; nx_bit_set = FALSE; if (machdep->flags & PAE) { paddr = PAE_PAGEBASE(pae_pte); sprintf(ptebuf, "%llx", pae_pte); if (pae_pte & _PAGE_NX) nx_bit_set = TRUE; pte = (ulong)pae_pte; } else { paddr = NONPAE_PAGEBASE(pte); sprintf(ptebuf, "%lx", pte); } page_present = (pte & (_PAGE_PRESENT|_PAGE_PROTNONE)); if (physaddr) { if (machdep->flags & PAE) *((ulonglong *)physaddr) = paddr; else *((ulong *)physaddr) = (ulong)paddr; return page_present; } len1 = MAX(strlen(ptebuf), strlen("PTE")); fprintf(fp, "%s ", mkstring(buf, len1, CENTER|LJUST, "PTE")); if (!page_present && pte) { swap_location(machdep->flags & PAE ? pae_pte : pte, buf); if ((c = parse_line(buf, arglist)) != 3) error(FATAL, "cannot determine swap location\n"); len2 = MAX(strlen(arglist[0]), strlen("SWAP")); len3 = MAX(strlen(arglist[2]), strlen("OFFSET")); fprintf(fp, "%s %s\n", mkstring(buf2, len2, CENTER|LJUST, "SWAP"), mkstring(buf3, len3, CENTER|LJUST, "OFFSET")); strcpy(buf2, arglist[0]); strcpy(buf3, arglist[2]); fprintf(fp, "%s %s %s\n", mkstring(ptebuf, len1, CENTER|RJUST, NULL), mkstring(buf2, len2, CENTER|RJUST, NULL), mkstring(buf3, len3, CENTER|RJUST, NULL)); return page_present; } sprintf(physbuf, "%llx", paddr); len2 = MAX(strlen(physbuf), strlen("PHYSICAL")); fprintf(fp, "%s ", mkstring(buf, len2, CENTER|LJUST, "PHYSICAL")); fprintf(fp, "FLAGS\n"); fprintf(fp, "%s %s ", mkstring(ptebuf, len1, CENTER|RJUST, NULL), mkstring(physbuf, len2, CENTER|RJUST, NULL)); fprintf(fp, "("); others = 0; if (pte) { if (pte & _PAGE_PRESENT) fprintf(fp, "%sPRESENT", others++ ? "|" : ""); if (pte & _PAGE_RW) fprintf(fp, "%sRW", others++ ? "|" : ""); if (pte & _PAGE_USER) fprintf(fp, "%sUSER", others++ ? "|" : ""); if (pte & _PAGE_PWT) fprintf(fp, "%sPWT", others++ ? "|" : ""); if (pte & _PAGE_PCD) fprintf(fp, "%sPCD", others++ ? "|" : ""); if (pte & _PAGE_ACCESSED) fprintf(fp, "%sACCESSED", others++ ? "|" : ""); if (pte & _PAGE_DIRTY) fprintf(fp, "%sDIRTY", others++ ? "|" : ""); if ((pte & _PAGE_PSE) && (pte && _PAGE_PRESENT)) fprintf(fp, "%sPSE", others++ ? "|" : ""); if (pte & _PAGE_GLOBAL) fprintf(fp, "%sGLOBAL", others++ ? "|" : ""); if (pte & _PAGE_PROTNONE && !(pte && _PAGE_PRESENT)) fprintf(fp, "%sPROTNONE", others++ ? "|" : ""); if (nx_bit_set) fprintf(fp, "%sNX", others++ ? "|" : ""); } else { fprintf(fp, "no mapping"); } fprintf(fp, ")\n"); return page_present; } /* * For the time being, walk through the kernel page directory looking * for the 4MB PTEs. Zones might make this common code in the future. */ static uint64_t x86_memory_size(void) { int i, j; ulong *pp; ulong kpgd[PTRS_PER_PGD]; uint64_t vm_total; uint64_t pgd_total; if (machdep->memsize) return machdep->memsize; if (!(machdep->flags & PAE)) { readmem(vt->kernel_pgd[0], KVADDR, kpgd, sizeof(ulong) * PTRS_PER_PGD, "kernel page directory", FAULT_ON_ERROR); for (i = j = 0, pp = &kpgd[0]; i < PTRS_PER_PGD; i++, pp++) { if ((*pp & (_PAGE_PRESENT|_PAGE_4M)) == (_PAGE_PRESENT|_PAGE_4M) ) { j++; } } pgd_total = (uint64_t)j * (uint64_t)(MEGABYTES(4)); } else pgd_total = 0; /* * Use the memory node data (or its equivalent) if it's larger than * the page directory total. */ vm_total = total_node_memory(); machdep->memsize = MAX(pgd_total, vm_total); return (machdep->memsize); } /* * Determine where vmalloc'd memory starts. */ static ulong x86_vmalloc_start(void) { return (first_vmalloc_address()); } /* * Do the work for cmd_irq() -d option. */ void x86_display_idt_table(void) { int i; ulong *ip; char buf[BUFSIZE]; ip = read_idt_table(READ_IDT_RUNTIME); for (i = 0; i < 256; i++, ip += 2) { if (i < 10) fprintf(fp, " "); else if (i < 100) fprintf(fp, " "); fprintf(fp, "[%d] %s\n", i, extract_idt_function(ip, buf, NULL)); } } /* * Extract the function name out of the IDT entry. */ static char * extract_idt_function(ulong *ip, char *buf, ulong *retaddr) { ulong i1, i2, addr; char locbuf[BUFSIZE]; physaddr_t phys; if (buf) BZERO(buf, BUFSIZE); i1 = *ip; i2 = *(ip+1); i1 &= 0x0000ffff; i2 &= 0xffff0000; addr = i1 | i2; if (retaddr) *retaddr = addr; if (!buf) return NULL; value_to_symstr(addr, locbuf, 0); if (strlen(locbuf)) sprintf(buf, "%s", locbuf); else { sprintf(buf, "%08lx", addr); if (kvtop(NULL, addr, &phys, 0)) { addr = machdep->kvbase + (ulong)phys; if (value_to_symstr(addr, locbuf, 0)) { strcat(buf, " <"); strcat(buf, locbuf); strcat(buf, ">"); } } } return buf; } /* * Read the IDT table into a (hopefully) malloc'd buffer. */ static ulong * read_idt_table(int flag) { ulong *idt, addr, offset; physaddr_t phys; long desc_struct_size; struct syment *sp; struct machine_specific *ms; idt = NULL; ms = machdep->machspec; if (ms->idt_table) return ms->idt_table; desc_struct_size = SIZE(desc_struct) * 256; switch (flag) { case READ_IDT_INIT: if (!symbol_exists("idt_table")) return NULL; if (!(idt = (ulong *)malloc(desc_struct_size))) { error(WARNING, "cannot malloc idt_table\n\n"); return NULL; } if (!readmem(symbol_value("idt_table"), KVADDR, idt, desc_struct_size, "idt_table", RETURN_ON_ERROR)) { error(WARNING, "cannot read idt_table\n\n"); return NULL; } ms->idt_table = idt; addr = 0; extract_idt_function(idt, NULL, &addr); if (addr) { if (symbol_exists("__entry_tramp_start") && symbol_exists("__entry_tramp_end") && symbol_exists("__start___entry_text")) { ms->entry_tramp_start = symbol_value("__start___entry_text"); ms->entry_tramp_end = ms->entry_tramp_start + (symbol_value("__entry_tramp_end") - symbol_value("__entry_tramp_start")); ms->entry_tramp_start_phys = 0; machdep->value_to_symbol = x86_is_entry_tramp_address; } else if (!(sp = value_search(addr, &offset))) { addr = VIRTPAGEBASE(addr); if (kvtop(NULL, addr, &phys, 0) && (sp = value_search(PTOV(phys), &offset)) && STREQ(sp->name, "entry_tramp_start")) { ms->entry_tramp_start = addr; ms->entry_tramp_start_phys = phys; ms->entry_tramp_end = addr + (symbol_value("entry_tramp_end") - symbol_value("entry_tramp_start")); machdep->value_to_symbol = x86_is_entry_tramp_address; } } } break; case READ_IDT_RUNTIME: if (!symbol_exists("idt_table")) error(FATAL, "idt_table does not exist on this architecture\n"); idt = (ulong *)GETBUF(desc_struct_size); readmem(symbol_value("idt_table"), KVADDR, idt, desc_struct_size, "idt_table", FAULT_ON_ERROR); break; } return idt; } /* * If the address fits in the entry_tramp_start page, find the syment * associated with it. */ struct syment * x86_is_entry_tramp_address(ulong vaddr, ulong *retoffset) { struct syment *sp; struct machine_specific *ms; ulong addr, offset; ms = machdep->machspec; if (!ms->entry_tramp_start || !((vaddr >= ms->entry_tramp_start) && (vaddr <= ms->entry_tramp_end))) return NULL; /* * Check new vs. old style handling of entry_tramp addresses: * * - The old way requires creation of the real symbol address from * the entry_tramp address passed in. * - The new way just uses the absolute (A) symbols that are built * in using the entry_tramp addresses, w/no phys address required. */ if (ms->entry_tramp_start_phys) /* old */ addr = machdep->kvbase + (ulong)ms->entry_tramp_start_phys + PAGEOFFSET(vaddr); else /* new */ addr = vaddr; if ((sp = value_search_base_kernel(addr, &offset))) { if (retoffset) *retoffset = offset; if (CRASHDEBUG(4)) console("x86_is_entry_tramp_address: %lx: %s %lx+%ld\n", vaddr, sp->name, sp->value, offset); if (STREQ(sp->name, "entry_tramp_start")) sp++; } return sp; } /* * X86 tasks are all stacksize-aligned, except when split from the stack. */ static int x86_is_task_addr(ulong task) { if (tt->flags & THREAD_INFO) return IS_KVADDR(task); else return (IS_KVADDR(task) && (ALIGNED_STACK_OFFSET(task) == 0)); } /* * Keep or reject a symbol from the namelist. */ static int x86_verify_symbol(const char *name, ulong value, char type) { if (XEN_HYPER_MODE() && STREQ(name, "__per_cpu_shift")) return TRUE; if (CRASHDEBUG(8) && name && strlen(name)) fprintf(fp, "%08lx %s\n", value, name); if (STREQ(name, "_text") || STREQ(name, "_stext")) machdep->flags |= KSYMS_START; if (!name || !strlen(name) || !(machdep->flags & KSYMS_START)) return FALSE; if ((type == 'A') && STRNEQ(name, "__crc_")) return FALSE; if (STREQ(name, "Letext") || STREQ(name, "gcc2_compiled.")) return FALSE; return TRUE; } /* * Filter disassembly output if the output radix is not gdb's default 10 */ static int x86_dis_filter(ulong vaddr, char *inbuf, unsigned int output_radix) { char buf1[BUFSIZE]; char buf2[BUFSIZE]; char *colon, *p1; int argc; char *argv[MAXARGS]; ulong value; if (!inbuf) return TRUE; /* * For some reason gdb can go off into the weeds translating text addresses, * (on alpha -- not necessarily seen on x86) so this routine both fixes the * references as well as imposing the current output radix on the translations. */ if (CRASHDEBUG(1)) console("IN: %s", inbuf); colon = (inbuf[0] != ' ') ? strstr(inbuf, ":") : NULL; if (colon) { sprintf(buf1, "0x%lx <%s>", vaddr, value_to_symstr(vaddr, buf2, output_radix)); sprintf(buf2, "%s%s", buf1, colon); strcpy(inbuf, buf2); } strcpy(buf1, inbuf); argc = parse_line(buf1, argv); if ((FIRSTCHAR(argv[argc-1]) == '<') && (LASTCHAR(argv[argc-1]) == '>')) { p1 = rindex(inbuf, '<'); while ((p1 > inbuf) && !STRNEQ(p1, " 0x")) p1--; if (!STRNEQ(p1, " 0x")) return FALSE; p1++; if (!extract_hex(p1, &value, NULLCHAR, TRUE)) return FALSE; sprintf(buf1, "0x%lx <%s>\n", value, value_to_symstr(value, buf2, output_radix)); sprintf(p1, "%s", buf1); } else if (STREQ(argv[argc-2], "call") && hexadecimal(argv[argc-1], 0)) { /* * Update module code of the form: * * call 0xe081e1e0 * * to show a bracketed direct call target. */ p1 = &LASTCHAR(inbuf); if (extract_hex(argv[argc-1], &value, NULLCHAR, TRUE)) { sprintf(buf1, " <%s>\n", value_to_symstr(value, buf2, output_radix)); if (IS_MODULE_VADDR(value) && !strstr(buf2, "+")) sprintf(p1, "%s", buf1); } } else if (STREQ(argv[2], "ud2a")) pc->curcmd_flags |= UD2A_INSTRUCTION; else if (STREQ(argv[2], "(bad)")) pc->curcmd_flags |= BAD_INSTRUCTION; if (CRASHDEBUG(1)) console(" %s", inbuf); return TRUE; } /* * Override smp_num_cpus if possible and necessary. */ int x86_get_smp_cpus(void) { int count, cpucount; if ((count = get_cpus_online()) == 0) { count = kt->cpus; if (symbol_exists("cpucount")) { get_symbol_data("cpucount", sizeof(int), &cpucount); cpucount++; count = MAX(cpucount, kt->cpus); } } if (XEN() && (count == 1) && symbol_exists("cpu_present_map")) { ulong cpu_present_map; get_symbol_data("cpu_present_map", sizeof(ulong), &cpu_present_map); cpucount = count_bits_long(cpu_present_map); count = MAX(cpucount, kt->cpus); } if (KVMDUMP_DUMPFILE() && (count < get_cpus_present())) return(get_highest_cpu_present()+1); return MAX(count, get_highest_cpu_online()+1); } /* * Machine dependent command. */ void x86_cmd_mach(void) { int c, cflag, mflag; unsigned int radix; cflag = mflag = radix = 0; while ((c = getopt(argcnt, args, "cmxd")) != EOF) { switch(c) { case 'c': cflag++; break; case 'm': mflag++; x86_display_memmap(); break; case 'x': if (radix == 10) error(FATAL, "-d and -x are mutually exclusive\n"); radix = 16; break; case 'd': if (radix == 16) error(FATAL, "-d and -x are mutually exclusive\n"); radix = 10; break; default: argerrs++; break; } } if (argerrs) cmd_usage(pc->curcmd, SYNOPSIS); if (cflag) x86_display_cpu_data(radix); if (!cflag && !mflag) x86_display_machine_stats(); } /* * "mach" command output. */ static void x86_display_machine_stats(void) { int c; struct new_utsname *uts; char buf[BUFSIZE]; ulong mhz; uts = &kt->utsname; fprintf(fp, " MACHINE TYPE: %s\n", uts->machine); fprintf(fp, " MEMORY SIZE: %s\n", get_memory_size(buf)); fprintf(fp, " CPUS: %d\n", kt->cpus); if (!STREQ(kt->hypervisor, "(undetermined)") && !STREQ(kt->hypervisor, "bare hardware")) fprintf(fp, " HYPERVISOR: %s\n", kt->hypervisor); fprintf(fp, " PROCESSOR SPEED: "); if ((mhz = machdep->processor_speed())) fprintf(fp, "%ld Mhz\n", mhz); else fprintf(fp, "(unknown)\n"); fprintf(fp, " HZ: %d\n", machdep->hz); fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); // fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); fprintf(fp, "KERNEL VIRTUAL BASE: %lx\n", machdep->kvbase); fprintf(fp, "KERNEL VMALLOC BASE: %lx\n", vt->vmalloc_start); fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); if (tt->flags & IRQSTACKS) { fprintf(fp, "HARD IRQ STACK SIZE: %ld\n", STACKSIZE()); fprintf(fp, " HARD IRQ STACKS:\n"); for (c = 0; c < kt->cpus; c++) { if (!tt->hardirq_ctx[c]) break; sprintf(buf, "CPU %d", c); fprintf(fp, "%19s: %lx\n", buf, tt->hardirq_ctx[c]); } fprintf(fp, "SOFT IRQ STACK SIZE: %ld\n", STACKSIZE()); fprintf(fp, " SOFT IRQ STACKS:\n"); for (c = 0; c < kt->cpus; c++) { if (!tt->softirq_ctx) break; sprintf(buf, "CPU %d", c); fprintf(fp, "%19s: %lx\n", buf, tt->softirq_ctx[c]); } } } static void x86_display_cpu_data(unsigned int radix) { int cpu; ulong cpu_data = 0; if (symbol_exists("cpu_data")) cpu_data = symbol_value("cpu_data"); else if (symbol_exists("boot_cpu_data")) cpu_data = symbol_value("boot_cpu_data"); for (cpu = 0; cpu < kt->cpus; cpu++) { fprintf(fp, "%sCPU %d:\n", cpu ? "\n" : "", cpu); dump_struct("cpuinfo_x86", cpu_data, radix); cpu_data += SIZE(cpuinfo_x86); } } static char *e820type[] = { "(invalid type)", "E820_RAM", "E820_RESERVED", "E820_ACPI", "E820_NVS", "E820_UNUSABLE", }; static void x86_display_memmap(void) { ulong e820; int nr_map, i; char *buf, *e820entry_ptr; ulonglong addr, size; uint type; if (kernel_symbol_exists("e820")) { if (get_symbol_type("e820", NULL, NULL) == TYPE_CODE_PTR) get_symbol_data("e820", sizeof(void *), &e820); else e820 = symbol_value("e820"); } else if (kernel_symbol_exists("e820_table")) get_symbol_data("e820_table", sizeof(void *), &e820); else error(FATAL, "neither e820 or e820_table symbols exist\n"); if (CRASHDEBUG(1)) { if (STRUCT_EXISTS("e820map")) dump_struct("e820map", e820, RADIX(16)); else if (STRUCT_EXISTS("e820_table")) dump_struct("e820_table", e820, RADIX(16)); } buf = (char *)GETBUF(SIZE(e820map)); readmem(e820, KVADDR, &buf[0], SIZE(e820map), "e820map", FAULT_ON_ERROR); nr_map = INT(buf + OFFSET(e820map_nr_map)); fprintf(fp, " PHYSICAL ADDRESS RANGE TYPE\n"); for (i = 0; i < nr_map; i++) { e820entry_ptr = buf + sizeof(int) + (SIZE(e820entry) * i); addr = ULONGLONG(e820entry_ptr + OFFSET(e820entry_addr)); size = ULONGLONG(e820entry_ptr + OFFSET(e820entry_size)); type = UINT(e820entry_ptr + OFFSET(e820entry_type)); fprintf(fp, "%016llx - %016llx ", addr, addr+size); if (type >= (sizeof(e820type)/sizeof(char *))) { if (type == 12) fprintf(fp, "E820_PRAM\n"); else if (type == 128) fprintf(fp, "E820_RESERVED_KERN\n"); else fprintf(fp, "type %d\n", type); } else fprintf(fp, "%s\n", e820type[type]); } } /* * Check a few functions to determine whether the kernel was built * with the -fomit-frame-pointer flag. */ #define PUSH_BP_MOV_ESP_BP 0xe58955 #define PUSH_BP_CLR_EAX_MOV_ESP_BP 0xe589c03155ULL static int x86_omit_frame_pointer(void) { ulonglong push_bp_mov_esp_bp; int i; char *checkfuncs[] = {"sys_open", "sys_fork", "sys_read"}; if (pc->flags & KERNEL_DEBUG_QUERY) return FALSE; for (i = 0; i < 2; i++) { if (!readmem(symbol_value(checkfuncs[i]), KVADDR, &push_bp_mov_esp_bp, sizeof(ulonglong), "x86_omit_frame_pointer", RETURN_ON_ERROR)) return TRUE; if (!(((push_bp_mov_esp_bp & 0x0000ffffffULL) == PUSH_BP_MOV_ESP_BP) || ((push_bp_mov_esp_bp & 0xffffffffffULL) == PUSH_BP_CLR_EAX_MOV_ESP_BP))) return TRUE; } return FALSE; } /* * Disassemble an address and determine whether the instruction calls * a function; if so, return a pointer to the name of the called function. */ char * x86_function_called_by(ulong eip) { struct syment *sp; char buf[BUFSIZE], *p1, *p2, *funcname; ulong value, offset; unsigned char byte; funcname = NULL; if (!readmem(eip, KVADDR, &byte, sizeof(unsigned char), "call byte", RETURN_ON_ERROR)) return funcname; if (byte != 0xe8) return funcname; sprintf(buf, "x/i 0x%lx", eip); open_tmpfile2(); if (gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { rewind(pc->tmpfile2); while (fgets(buf, BUFSIZE, pc->tmpfile2)) { if ((p1 = strstr(buf, "call "))) { p1 += strlen("call "); if ((p2 = strstr(p1, " <"))) { p2 += strlen(" <"); if ((p1 = strstr(p2, ">"))) *p1 = NULLCHAR; if ((sp = symbol_search(p2))) funcname = sp->name; } else if ((p2 = strstr(p1, "0x"))) { if (!extract_hex(strip_linefeeds(p2), &value, NULLCHAR, TRUE)) continue; if ((sp = value_search(value, &offset)) && !offset) funcname = sp->name; } } } } close_tmpfile2(); return funcname; } struct syment * x86_text_lock_jmp(ulong eip, ulong *offset) { int i, c; char buf1[BUFSIZE]; char buf2[BUFSIZE]; char *arglist[MAXARGS]; struct syment *sp; ulong value; sprintf(buf1, "x/10i 0x%lx", eip); buf2[0] = NULLCHAR; value = 0; open_tmpfile2(); if (gdb_pass_through(buf1, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { rewind(pc->tmpfile2); while (fgets(buf1, BUFSIZE, pc->tmpfile2)) { if (!(c = parse_line(buf1, arglist))) continue; for (i = 0; i < c; i++) { if (STREQ(arglist[i], "jmp") && ((i+1)kernel_pgd[i] = value; } static ulong xen_m2p_nonPAE(ulong machine) { ulonglong pseudo; pseudo = xen_m2p((ulonglong)machine); if (pseudo == XEN_MACHADDR_NOT_FOUND) return XEN_MFN_NOT_FOUND; return ((ulong)pseudo); } #include "netdump.h" #include "xen_dom0.h" /* * From the xen vmcore, create an index of mfns for each page that makes * up the dom0 kernel's complete phys_to_machine_mapping[max_pfn] array. */ #define MAX_X86_FRAMES (16) #define MFNS_PER_FRAME (PAGESIZE()/sizeof(ulong)) static int x86_xen_kdump_p2m_create(struct xen_kdump_data *xkd) { int i, j; ulong kvaddr; ulong *up; ulonglong *ulp; ulong frames; ulong frame_mfn[MAX_X86_FRAMES] = { 0 }; int mfns[MAX_X86_FRAMES] = { 0 }; /* * Temporarily read physical (machine) addresses from vmcore. */ pc->curcmd_flags |= XEN_MACHINE_ADDR; if (CRASHDEBUG(1)) fprintf(fp, "readmem (temporary): force XEN_MACHINE_ADDR\n"); if (xkd->flags & KDUMP_CR3) goto use_cr3; xkd->p2m_frames = 0; if (CRASHDEBUG(1)) fprintf(fp, "x86_xen_kdump_p2m_create: p2m_mfn: %lx\n", xkd->p2m_mfn); if (!readmem(PTOB(xkd->p2m_mfn), PHYSADDR, xkd->page, PAGESIZE(), "xen kdump p2m mfn page", RETURN_ON_ERROR)) error(FATAL, "cannot read xen kdump p2m mfn page\n"); if (CRASHDEBUG(1)) { up = (ulong *)xkd->page; for (i = 0; i < 4; i++) { fprintf(fp, "%08lx: %08lx %08lx %08lx %08lx\n", (ulong)((i * 4) * sizeof(ulong)), *up, *(up+1), *(up+2), *(up+3)); up += 4; } fprintf(fp, "\n"); } for (i = 0, up = (ulong *)xkd->page; i < MAX_X86_FRAMES; i++, up++) frame_mfn[i] = *up; for (i = 0; i < MAX_X86_FRAMES; i++) { if (!frame_mfn[i]) break; if (!readmem(PTOB(frame_mfn[i]), PHYSADDR, xkd->page, PAGESIZE(), "xen kdump p2m mfn list page", RETURN_ON_ERROR)) error(FATAL, "cannot read xen kdump p2m mfn list page\n"); for (j = 0, up = (ulong *)xkd->page; j < MFNS_PER_FRAME; j++, up++) if (*up) mfns[i]++; xkd->p2m_frames += mfns[i]; if (CRASHDEBUG(7)) { up = (ulong *)xkd->page; for (j = 0; j < 256; j++) { fprintf(fp, "%08lx: %08lx %08lx %08lx %08lx\n", (ulong)((j * 4) * sizeof(ulong)), *up, *(up+1), *(up+2), *(up+3)); up += 4; } } } if (CRASHDEBUG(1)) fprintf(fp, "p2m_frames: %d\n", xkd->p2m_frames); if ((xkd->p2m_mfn_frame_list = (ulong *) malloc(xkd->p2m_frames * sizeof(ulong))) == NULL) error(FATAL, "cannot malloc p2m_frame_index_list"); for (i = 0, frames = xkd->p2m_frames; frames; i++) { if (!readmem(PTOB(frame_mfn[i]), PHYSADDR, &xkd->p2m_mfn_frame_list[i * MFNS_PER_FRAME], mfns[i] * sizeof(ulong), "xen kdump p2m mfn list page", RETURN_ON_ERROR)) error(FATAL, "cannot read xen kdump p2m mfn list page\n"); frames -= mfns[i]; } if (CRASHDEBUG(2)) { for (i = 0; i < xkd->p2m_frames; i++) fprintf(fp, "%lx ", xkd->p2m_mfn_frame_list[i]); fprintf(fp, "\n"); } pc->curcmd_flags &= ~XEN_MACHINE_ADDR; if (CRASHDEBUG(1)) fprintf(fp, "readmem (restore): p2m translation\n"); return TRUE; use_cr3: if (CRASHDEBUG(1)) fprintf(fp, "x86_xen_kdump_p2m_create: cr3: %lx\n", xkd->cr3); if (!readmem(PTOB(xkd->cr3), PHYSADDR, machdep->pgd, PAGESIZE(), "xen kdump cr3 page", RETURN_ON_ERROR)) error(FATAL, "cannot read xen kdump cr3 page\n"); if (CRASHDEBUG(7)) { fprintf(fp, "contents of page directory page:\n"); if (machdep->flags & PAE) { ulp = (ulonglong *)machdep->pgd; fprintf(fp, "%016llx %016llx %016llx %016llx\n", *ulp, *(ulp+1), *(ulp+2), *(ulp+3)); } else { up = (ulong *)machdep->pgd; for (i = 0; i < 256; i++) { fprintf(fp, "%08lx: %08lx %08lx %08lx %08lx\n", (ulong)((i * 4) * sizeof(ulong)), *up, *(up+1), *(up+2), *(up+3)); up += 4; } } } kvaddr = symbol_value("max_pfn"); if (!x86_xen_kdump_load_page(kvaddr, xkd->page)) return FALSE; up = (ulong *)(xkd->page + PAGEOFFSET(kvaddr)); xkd->p2m_frames = (*up/(PAGESIZE()/sizeof(ulong))) + ((*up%(PAGESIZE()/sizeof(ulong))) ? 1 : 0); if (CRASHDEBUG(1)) fprintf(fp, "max_pfn at %lx: %lx (%ld) -> %d p2m_frames\n", kvaddr, *up, *up, xkd->p2m_frames); if ((xkd->p2m_mfn_frame_list = (ulong *) malloc(xkd->p2m_frames * sizeof(ulong))) == NULL) error(FATAL, "cannot malloc p2m_frame_index_list"); kvaddr = symbol_value("phys_to_machine_mapping"); if (!x86_xen_kdump_load_page(kvaddr, xkd->page)) return FALSE; up = (ulong *)(xkd->page + PAGEOFFSET(kvaddr)); kvaddr = *up; if (CRASHDEBUG(1)) fprintf(fp, "phys_to_machine_mapping: %lx\n", kvaddr); if (CRASHDEBUG(7)) { fprintf(fp, "contents of first phys_to_machine_mapping page:\n"); if (!x86_xen_kdump_load_page(kvaddr, xkd->page)) error(INFO, "cannot read first phys_to_machine_mapping page\n"); up = (ulong *)xkd->page; for (i = 0; i < 256; i++) { fprintf(fp, "%08lx: %08lx %08lx %08lx %08lx\n", (ulong)((i * 4) * sizeof(ulong)), *up, *(up+1), *(up+2), *(up+3)); up += 4; } } machdep->last_ptbl_read = BADADDR; machdep->last_pmd_read = BADADDR; machdep->last_pgd_read = BADADDR; for (i = 0; i < xkd->p2m_frames; i++) { xkd->p2m_mfn_frame_list[i] = x86_xen_kdump_page_mfn(kvaddr); kvaddr += PAGESIZE(); } if (CRASHDEBUG(1)) { for (i = 0; i < xkd->p2m_frames; i++) fprintf(fp, "%lx ", xkd->p2m_mfn_frame_list[i]); fprintf(fp, "\n"); } machdep->last_ptbl_read = 0; machdep->last_pmd_read = 0; machdep->last_pgd_read = 0; pc->curcmd_flags &= ~XEN_MACHINE_ADDR; if (CRASHDEBUG(1)) fprintf(fp, "readmem (restore): p2m translation\n"); return TRUE; } /* * Find the page associate with the kvaddr, and read its contents * into the passed-in buffer. */ static char * x86_xen_kdump_load_page(ulong kvaddr, char *pgbuf) { ulong *entry; ulong *up; ulong mfn; if (machdep->flags & PAE) return x86_xen_kdump_load_page_PAE(kvaddr, pgbuf); up = (ulong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (*entry) >> PAGESHIFT(); if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), "xen kdump pgd entry", RETURN_ON_ERROR)) { error(INFO, "cannot read/find pgd entry from cr3 page\n"); return NULL; } up = (ulong *)pgbuf; entry = up + ((kvaddr >> 12) & (PTRS_PER_PTE-1)); mfn = (*entry) >> PAGESHIFT(); if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), "xen page table page", RETURN_ON_ERROR)) { error(INFO, "cannot read/find page table page\n"); return NULL; } return pgbuf; } static char * x86_xen_kdump_load_page_PAE(ulong kvaddr, char *pgbuf) { ulonglong *entry; ulonglong *up; ulong mfn; up = (ulonglong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (ulong)((*entry) >> PAGESHIFT()); if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), "xen kdump pgd entry", RETURN_ON_ERROR)) { error(INFO, "cannot read/find pgd entry from cr3 page\n"); return NULL; } up = (ulonglong *)pgbuf; entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), "xen kdump pmd entry", RETURN_ON_ERROR)) { error(INFO, "cannot read/find pmd entry from pgd\n"); return NULL; } up = (ulonglong *)pgbuf; entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), "xen kdump page table page", RETURN_ON_ERROR)) { error(INFO, "cannot read/find page table page from pmd\n"); return NULL; } return pgbuf; } /* * Return the mfn value associated with a virtual address. */ static ulong x86_xen_kdump_page_mfn(ulong kvaddr) { ulong *entry; ulong *up; ulong mfn; if (machdep->flags & PAE) return x86_xen_kdump_page_mfn_PAE(kvaddr); up = (ulong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (*entry) >> PAGESHIFT(); if ((mfn != machdep->last_ptbl_read) && !readmem(PTOB(mfn), PHYSADDR, machdep->ptbl, PAGESIZE(), "xen kdump pgd entry", RETURN_ON_ERROR)) error(FATAL, "cannot read/find pgd entry from cr3 page (mfn: %lx)\n", mfn); machdep->last_ptbl_read = mfn; up = (ulong *)machdep->ptbl; entry = up + ((kvaddr >> 12) & (PTRS_PER_PTE-1)); mfn = (*entry) >> PAGESHIFT(); return mfn; } static ulong x86_xen_kdump_page_mfn_PAE(ulong kvaddr) { ulonglong *entry; ulonglong *up; ulong mfn; up = (ulonglong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (ulong)((*entry) >> PAGESHIFT()); if ((mfn != machdep->last_pmd_read) && !readmem(PTOB(mfn), PHYSADDR, machdep->pmd, PAGESIZE(), "xen kdump pgd entry", RETURN_ON_ERROR)) error(FATAL, "cannot read/find pgd entry from cr3 page (mfn: %lx)\n", mfn); machdep->last_pmd_read = mfn; up = (ulonglong *)machdep->pmd; entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); if ((mfn != machdep->last_ptbl_read) && !readmem(PTOB(mfn), PHYSADDR, machdep->ptbl, PAGESIZE(), "xen kdump pmd entry", RETURN_ON_ERROR)) error(FATAL, "cannot read/find pmd entry from pgd (mfn: %lx)\n", mfn); machdep->last_ptbl_read = mfn; up = (ulonglong *)machdep->ptbl; entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); return mfn; } #include "xendump.h" /* * Create an index of mfns for each page that makes up the * kernel's complete phys_to_machine_mapping[max_pfn] array. */ static int x86_xendump_p2m_create(struct xendump_data *xd) { int i, idx; ulong mfn, kvaddr, ctrlreg[8], ctrlreg_offset; ulong *up; ulonglong *ulp; off_t offset; /* * Check for pvops Xen kernel before presuming it's HVM. */ if (symbol_exists("pv_init_ops") && symbol_exists("xen_patch") && (xd->xc_core.header.xch_magic == XC_CORE_MAGIC)) return x86_pvops_xendump_p2m_create(xd); if (!symbol_exists("phys_to_machine_mapping")) { xd->flags |= XC_CORE_NO_P2M; return TRUE; } if ((ctrlreg_offset = MEMBER_OFFSET("vcpu_guest_context", "ctrlreg")) == INVALID_OFFSET) error(FATAL, "cannot determine vcpu_guest_context.ctrlreg offset\n"); else if (CRASHDEBUG(1)) fprintf(xd->ofp, "MEMBER_OFFSET(vcpu_guest_context, ctrlreg): %ld\n", ctrlreg_offset); offset = xd->xc_core.header.xch_ctxt_offset + (off_t)ctrlreg_offset; if (lseek(xd->xfd, offset, SEEK_SET) == -1) error(FATAL, "cannot lseek to xch_ctxt_offset\n"); if (read(xd->xfd, &ctrlreg, sizeof(ctrlreg)) != sizeof(ctrlreg)) error(FATAL, "cannot read vcpu_guest_context ctrlreg[8]\n"); mfn = (ctrlreg[3] >> PAGESHIFT()) | (ctrlreg[3] << (BITS()-PAGESHIFT())); for (i = 0; CRASHDEBUG(1) && (i < 8); i++) { fprintf(xd->ofp, "ctrlreg[%d]: %lx", i, ctrlreg[i]); if (i == 3) fprintf(xd->ofp, " -> mfn: %lx", mfn); fprintf(xd->ofp, "\n"); } if (!xc_core_mfn_to_page(mfn, machdep->pgd)) error(FATAL, "cannot read/find cr3 page\n"); machdep->last_pgd_read = mfn; if (CRASHDEBUG(1)) { fprintf(xd->ofp, "contents of page directory page:\n"); if (machdep->flags & PAE) { ulp = (ulonglong *)machdep->pgd; fprintf(xd->ofp, "%016llx %016llx %016llx %016llx\n", *ulp, *(ulp+1), *(ulp+2), *(ulp+3)); } else { up = (ulong *)machdep->pgd; for (i = 0; i < 256; i++) { fprintf(xd->ofp, "%08lx: %08lx %08lx %08lx %08lx\n", (ulong)((i * 4) * sizeof(ulong)), *up, *(up+1), *(up+2), *(up+3)); up += 4; } } } kvaddr = symbol_value("max_pfn"); if (!x86_xendump_load_page(kvaddr, xd->page)) return FALSE; up = (ulong *)(xd->page + PAGEOFFSET(kvaddr)); if (CRASHDEBUG(1)) fprintf(xd->ofp, "max_pfn: %lx\n", *up); xd->xc_core.p2m_frames = (*up/(PAGESIZE()/sizeof(ulong))) + ((*up%(PAGESIZE()/sizeof(ulong))) ? 1 : 0); if ((xd->xc_core.p2m_frame_index_list = (ulong *) malloc(xd->xc_core.p2m_frames * sizeof(int))) == NULL) error(FATAL, "cannot malloc p2m_frame_index_list"); kvaddr = symbol_value("phys_to_machine_mapping"); if (!x86_xendump_load_page(kvaddr, xd->page)) return FALSE; up = (ulong *)(xd->page + PAGEOFFSET(kvaddr)); if (CRASHDEBUG(1)) fprintf(fp, "phys_to_machine_mapping: %lx\n", *up); kvaddr = *up; machdep->last_ptbl_read = BADADDR; machdep->last_pmd_read = BADADDR; for (i = 0; i < xd->xc_core.p2m_frames; i++) { if ((idx = x86_xendump_page_index(kvaddr)) == MFN_NOT_FOUND) return FALSE; xd->xc_core.p2m_frame_index_list[i] = idx; kvaddr += PAGESIZE(); } machdep->last_ptbl_read = 0; machdep->last_pmd_read = 0; return TRUE; } static int x86_pvops_xendump_p2m_create(struct xendump_data *xd) { int i; ulong mfn, kvaddr, ctrlreg[8], ctrlreg_offset; ulong *up; ulonglong *ulp; off_t offset; if ((ctrlreg_offset = MEMBER_OFFSET("vcpu_guest_context", "ctrlreg")) == INVALID_OFFSET) error(FATAL, "cannot determine vcpu_guest_context.ctrlreg offset\n"); else if (CRASHDEBUG(1)) fprintf(xd->ofp, "MEMBER_OFFSET(vcpu_guest_context, ctrlreg): %ld\n", ctrlreg_offset); offset = xd->xc_core.header.xch_ctxt_offset + (off_t)ctrlreg_offset; if (lseek(xd->xfd, offset, SEEK_SET) == -1) error(FATAL, "cannot lseek to xch_ctxt_offset\n"); if (read(xd->xfd, &ctrlreg, sizeof(ctrlreg)) != sizeof(ctrlreg)) error(FATAL, "cannot read vcpu_guest_context ctrlreg[8]\n"); mfn = (ctrlreg[3] >> PAGESHIFT()) | (ctrlreg[3] << (BITS()-PAGESHIFT())); for (i = 0; CRASHDEBUG(1) && (i < 8); i++) { fprintf(xd->ofp, "ctrlreg[%d]: %lx", i, ctrlreg[i]); if (i == 3) fprintf(xd->ofp, " -> mfn: %lx", mfn); fprintf(xd->ofp, "\n"); } if (!xc_core_mfn_to_page(mfn, machdep->pgd)) error(FATAL, "cannot read/find cr3 page\n"); machdep->last_pgd_read = mfn; if (CRASHDEBUG(1)) { fprintf(xd->ofp, "contents of page directory page:\n"); if (machdep->flags & PAE) { ulp = (ulonglong *)machdep->pgd; fprintf(xd->ofp, "%016llx %016llx %016llx %016llx\n", *ulp, *(ulp+1), *(ulp+2), *(ulp+3)); } else { up = (ulong *)machdep->pgd; for (i = 0; i < 256; i++) { fprintf(xd->ofp, "%08lx: %08lx %08lx %08lx %08lx\n", (ulong)((i * 4) * sizeof(ulong)), *up, *(up+1), *(up+2), *(up+3)); up += 4; } } } kvaddr = symbol_value("max_pfn"); if (!x86_xendump_load_page(kvaddr, xd->page)) return FALSE; up = (ulong *)(xd->page + PAGEOFFSET(kvaddr)); if (CRASHDEBUG(1)) fprintf(xd->ofp, "max_pfn: %lx\n", *up); xd->xc_core.p2m_frames = (*up/(PAGESIZE()/sizeof(ulong))) + ((*up%(PAGESIZE()/sizeof(ulong))) ? 1 : 0); if ((xd->xc_core.p2m_frame_index_list = (ulong *) malloc(xd->xc_core.p2m_frames * sizeof(int))) == NULL) error(FATAL, "cannot malloc p2m_frame_index_list"); if (symbol_exists("p2m_mid_missing")) return x86_pvops_xendump_p2m_l3_create(xd); else return x86_pvops_xendump_p2m_l2_create(xd); } static int x86_pvops_xendump_p2m_l2_create(struct xendump_data *xd) { int i, idx, p; ulong kvaddr, *up; machdep->last_ptbl_read = BADADDR; machdep->last_pmd_read = BADADDR; kvaddr = symbol_value("p2m_top"); for (p = 0; p < xd->xc_core.p2m_frames; p += XEN_PFNS_PER_PAGE) { if (!x86_xendump_load_page(kvaddr, xd->page)) return FALSE; if (CRASHDEBUG(7)) x86_debug_dump_page(xd->ofp, xd->page, "contents of page:"); up = (ulong *)(xd->page); for (i = 0; i < XEN_PFNS_PER_PAGE; i++, up++) { if ((p+i) >= xd->xc_core.p2m_frames) break; if ((idx = x86_xendump_page_index(*up)) == MFN_NOT_FOUND) return FALSE; xd->xc_core.p2m_frame_index_list[p+i] = idx; } kvaddr += PAGESIZE(); } machdep->last_ptbl_read = 0; machdep->last_pmd_read = 0; return TRUE; } static int x86_pvops_xendump_p2m_l3_create(struct xendump_data *xd) { int i, idx, j, p2m_frame, ret = FALSE; ulong kvaddr, *p2m_mid, p2m_mid_missing, p2m_missing, *p2m_top; p2m_top = NULL; machdep->last_ptbl_read = BADADDR; machdep->last_pmd_read = BADADDR; kvaddr = symbol_value("p2m_missing"); if (!x86_xendump_load_page(kvaddr, xd->page)) goto err; p2m_missing = *(ulong *)(xd->page + PAGEOFFSET(kvaddr)); kvaddr = symbol_value("p2m_mid_missing"); if (!x86_xendump_load_page(kvaddr, xd->page)) goto err; p2m_mid_missing = *(ulong *)(xd->page + PAGEOFFSET(kvaddr)); kvaddr = symbol_value("p2m_top"); if (!x86_xendump_load_page(kvaddr, xd->page)) goto err; kvaddr = *(ulong *)(xd->page + PAGEOFFSET(kvaddr)); if (!x86_xendump_load_page(kvaddr, xd->page)) goto err; if (CRASHDEBUG(7)) x86_debug_dump_page(xd->ofp, xd->page, "contents of p2m_top page:"); p2m_top = (ulong *)GETBUF(PAGESIZE()); memcpy(p2m_top, xd->page, PAGESIZE()); for (i = 0; i < XEN_P2M_TOP_PER_PAGE; ++i) { p2m_frame = i * XEN_P2M_MID_PER_PAGE; if (p2m_frame >= xd->xc_core.p2m_frames) break; if (p2m_top[i] == p2m_mid_missing) continue; if (!x86_xendump_load_page(p2m_top[i], xd->page)) goto err; if (CRASHDEBUG(7)) x86_debug_dump_page(xd->ofp, xd->page, "contents of p2m_mid page:"); p2m_mid = (ulong *)xd->page; for (j = 0; j < XEN_P2M_MID_PER_PAGE; ++j, ++p2m_frame) { if (p2m_frame >= xd->xc_core.p2m_frames) break; if (p2m_mid[j] == p2m_missing) continue; idx = x86_xendump_page_index(p2m_mid[j]); if (idx == MFN_NOT_FOUND) goto err; xd->xc_core.p2m_frame_index_list[p2m_frame] = idx; } } machdep->last_ptbl_read = 0; machdep->last_pmd_read = 0; ret = TRUE; err: if (p2m_top) FREEBUF(p2m_top); return ret; } static void x86_debug_dump_page(FILE *ofp, char *page, char *name) { int i; ulong *up; fprintf(ofp, "%s\n", name); up = (ulong *)page; for (i = 0; i < 256; i++) { fprintf(ofp, "%016lx: %08lx %08lx %08lx %08lx\n", (ulong)((i * 4) * sizeof(ulong)), *up, *(up+1), *(up+2), *(up+3)); up += 4; } } /* * Find the page associate with the kvaddr, and read its contents * into the passed-in buffer. */ static char * x86_xendump_load_page(ulong kvaddr, char *pgbuf) { ulong *entry; ulong *up; ulong mfn; if (machdep->flags & PAE) return x86_xendump_load_page_PAE(kvaddr, pgbuf); up = (ulong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (*entry) >> PAGESHIFT(); if (!xc_core_mfn_to_page(mfn, pgbuf)) { error(INFO, "cannot read/find pgd entry from cr3 page\n"); return NULL; } up = (ulong *)pgbuf; entry = up + ((kvaddr >> 12) & (PTRS_PER_PTE-1)); mfn = (*entry) >> PAGESHIFT(); if (!xc_core_mfn_to_page(mfn, pgbuf)) { error(INFO, "cannot read/find page table page\n"); return NULL; } return pgbuf; } static char * x86_xendump_load_page_PAE(ulong kvaddr, char *pgbuf) { ulonglong *entry; ulonglong *up; ulong mfn; up = (ulonglong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (ulong)((*entry) >> PAGESHIFT()); if (!xc_core_mfn_to_page(mfn, pgbuf)) { error(INFO, "cannot read/find pgd entry from cr3 page\n"); return NULL; } up = (ulonglong *)pgbuf; entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); if (!xc_core_mfn_to_page(mfn, pgbuf)) { error(INFO, "cannot read/find pmd entry from pgd\n"); return NULL; } up = (ulonglong *)pgbuf; entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); if (!xc_core_mfn_to_page(mfn, pgbuf)) { error(INFO, "cannot read/find page table page from pmd\n"); return NULL; } return pgbuf; } /* * Find the dumpfile page index associated with the kvaddr. */ static int x86_xendump_page_index(ulong kvaddr) { int idx; ulong *entry; ulong *up; ulong mfn; if (machdep->flags & PAE) return x86_xendump_page_index_PAE(kvaddr); up = (ulong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (*entry) >> PAGESHIFT(); if ((mfn != machdep->last_ptbl_read) && !xc_core_mfn_to_page(mfn, machdep->ptbl)) { error(INFO, "cannot read/find pgd entry from cr3 page\n"); return MFN_NOT_FOUND; } machdep->last_ptbl_read = mfn; up = (ulong *)machdep->ptbl; entry = up + ((kvaddr>>12) & (PTRS_PER_PTE-1)); mfn = (*entry) >> PAGESHIFT(); if ((idx = xc_core_mfn_to_page_index(mfn)) == MFN_NOT_FOUND) error(INFO, "cannot determine page index for %lx\n", kvaddr); return idx; } static int x86_xendump_page_index_PAE(ulong kvaddr) { int idx; ulonglong *entry; ulonglong *up; ulong mfn; up = (ulonglong *)machdep->pgd; entry = up + (kvaddr >> PGDIR_SHIFT); mfn = (ulong)((*entry) >> PAGESHIFT()); if ((mfn != machdep->last_pmd_read) && !xc_core_mfn_to_page(mfn, machdep->pmd)) { error(INFO, "cannot read/find pgd entry from cr3 page\n"); return MFN_NOT_FOUND; } machdep->last_pmd_read = mfn; up = (ulonglong *)machdep->pmd; entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); if ((mfn != machdep->last_ptbl_read) && !xc_core_mfn_to_page(mfn, machdep->ptbl)) { error(INFO, "cannot read/find pmd entry from pgd\n"); return MFN_NOT_FOUND; } machdep->last_ptbl_read = mfn; up = (ulonglong *)machdep->ptbl; entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); mfn = (ulong)((*entry) >> PAGESHIFT()); if ((idx = xc_core_mfn_to_page_index(mfn)) == MFN_NOT_FOUND) error(INFO, "cannot determine page index for %lx\n", kvaddr); return idx; } /* * Pull the esp from the cpu_user_regs struct in the header * turn it into a task, and match it with the active_set. * Unfortunately, the registers in the vcpu_guest_context * are not necessarily those of the panic task, so for now * let get_active_set_panic_task() get the right task. */ static ulong x86_xendump_panic_task(struct xendump_data *xd) { return NO_TASK; #ifdef TO_BE_REVISITED int i; ulong esp; off_t offset; ulong task; if (INVALID_MEMBER(vcpu_guest_context_user_regs) || INVALID_MEMBER(cpu_user_regs_esp)) return NO_TASK; offset = xd->xc_core.header.xch_ctxt_offset + (off_t)OFFSET(vcpu_guest_context_user_regs) + (off_t)OFFSET(cpu_user_regs_esp); if (lseek(xd->xfd, offset, SEEK_SET) == -1) return NO_TASK; if (read(xd->xfd, &esp, sizeof(ulong)) != sizeof(ulong)) return NO_TASK; if (IS_KVADDR(esp) && (task = stkptr_to_task(esp))) { for (i = 0; i < NR_CPUS; i++) { if (task == tt->active_set[i]) { if (CRASHDEBUG(0)) error(INFO, "x86_xendump_panic_task: esp: %lx -> task: %lx\n", esp, task); return task; } } error(WARNING, "x86_xendump_panic_task: esp: %lx -> task: %lx (not active)\n", esp); } return NO_TASK; #endif } /* * Because of an off-by-one vcpu bug in early xc_domain_dumpcore() * instantiations, the registers in the vcpu_guest_context are not * necessarily those of the panic task. If not, the eip/esp will be * in stop_this_cpu, as a result of the IP interrupt in panic(), * but the trace is strange because it comes out of the hypervisor * at least if the vcpu had been idle. */ static void x86_get_xendump_regs(struct xendump_data *xd, struct bt_info *bt, ulong *eip, ulong *esp) { ulong task, xeip, xesp; off_t offset; if (INVALID_MEMBER(vcpu_guest_context_user_regs) || INVALID_MEMBER(cpu_user_regs_eip) || INVALID_MEMBER(cpu_user_regs_esp)) goto generic; offset = xd->xc_core.header.xch_ctxt_offset + (off_t)OFFSET(vcpu_guest_context_user_regs) + (off_t)OFFSET(cpu_user_regs_esp); if (lseek(xd->xfd, offset, SEEK_SET) == -1) goto generic; if (read(xd->xfd, &xesp, sizeof(ulong)) != sizeof(ulong)) goto generic; offset = xd->xc_core.header.xch_ctxt_offset + (off_t)OFFSET(vcpu_guest_context_user_regs) + (off_t)OFFSET(cpu_user_regs_eip); if (lseek(xd->xfd, offset, SEEK_SET) == -1) goto generic; if (read(xd->xfd, &xeip, sizeof(ulong)) != sizeof(ulong)) goto generic; if (IS_KVADDR(xesp) && (task = stkptr_to_task(xesp)) && (task == bt->task)) { if (CRASHDEBUG(1)) fprintf(xd->ofp, "hooks from vcpu_guest_context: eip: %lx esp: %lx\n", xeip, xesp); *eip = xeip; *esp = xesp; return; } generic: return machdep->get_stack_frame(bt, eip, esp); } /* for Xen Hypervisor analysis */ static int x86_xenhyper_is_kvaddr(ulong addr) { if (machdep->flags & PAE) { return (addr >= HYPERVISOR_VIRT_START_PAE); } return (addr >= HYPERVISOR_VIRT_START); } static ulong x86_get_stackbase_hyper(ulong task) { struct xen_hyper_vcpu_context *vcc; int pcpu; ulong init_tss; ulong esp, base; char *buf; /* task means vcpu here */ vcc = xen_hyper_vcpu_to_vcpu_context(task); if (!vcc) error(FATAL, "invalid vcpu\n"); pcpu = vcc->processor; if (!xen_hyper_test_pcpu_id(pcpu)) { error(FATAL, "invalid pcpu number\n"); } if (symbol_exists("init_tss")) { init_tss = symbol_value("init_tss"); init_tss += XEN_HYPER_SIZE(tss) * pcpu; } else { init_tss = symbol_value("per_cpu__init_tss"); init_tss = xen_hyper_per_cpu(init_tss, pcpu); } buf = GETBUF(XEN_HYPER_SIZE(tss)); if (!readmem(init_tss, KVADDR, buf, XEN_HYPER_SIZE(tss), "init_tss", RETURN_ON_ERROR)) { error(FATAL, "cannot read init_tss.\n"); } esp = ULONG(buf + XEN_HYPER_OFFSET(tss_esp0)); FREEBUF(buf); base = esp & (~(STACKSIZE() - 1)); return base; } static ulong x86_get_stacktop_hyper(ulong task) { return x86_get_stackbase_hyper(task) + STACKSIZE(); } static void x86_get_stack_frame_hyper(struct bt_info *bt, ulong *pcp, ulong *spp) { struct xen_hyper_vcpu_context *vcc; int pcpu; ulong *regs; ulong esp, eip; /* task means vcpu here */ vcc = xen_hyper_vcpu_to_vcpu_context(bt->task); if (!vcc) error(FATAL, "invalid vcpu\n"); pcpu = vcc->processor; if (!xen_hyper_test_pcpu_id(pcpu)) { error(FATAL, "invalid pcpu number\n"); } if (bt->flags & BT_TEXT_SYMBOLS_ALL) { if (spp) *spp = x86_get_stackbase_hyper(bt->task); if (pcp) *pcp = 0; bt->flags &= ~BT_TEXT_SYMBOLS_ALL; return; } regs = (ulong *)xen_hyper_id_to_dumpinfo_context(pcpu)->pr_reg_ptr; esp = XEN_HYPER_X86_NOTE_ESP(regs); eip = XEN_HYPER_X86_NOTE_EIP(regs); if (spp) { if (esp < x86_get_stackbase_hyper(bt->task) || esp >= x86_get_stacktop_hyper(bt->task)) *spp = x86_get_stackbase_hyper(bt->task); else *spp = esp; } if (pcp) { if (is_kernel_text(eip)) *pcp = eip; else *pcp = 0; } } static void x86_init_hyper(int when) { switch (when) { case PRE_SYMTAB: machdep->verify_symbol = x86_verify_symbol; if (pc->flags & KERNEL_DEBUG_QUERY) return; machdep->pagesize = memory_page_size(); machdep->pageshift = ffs(machdep->pagesize) - 1; machdep->pageoffset = machdep->pagesize - 1; machdep->pagemask = ~((ulonglong)machdep->pageoffset); machdep->stacksize = machdep->pagesize * 4; /* ODA: magic num */ if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc pgd space."); if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc pmd space."); if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc ptbl space."); machdep->last_pgd_read = 0; machdep->last_pmd_read = 0; machdep->last_ptbl_read = 0; machdep->machspec = &x86_machine_specific; /* some members used */ break; case PRE_GDB: if (symbol_exists("create_pae_xen_mappings") || symbol_exists("idle_pg_table_l3")) { machdep->flags |= PAE; PGDIR_SHIFT = PGDIR_SHIFT_3LEVEL; PTRS_PER_PTE = PTRS_PER_PTE_3LEVEL; PTRS_PER_PGD = PTRS_PER_PGD_3LEVEL; machdep->kvtop = x86_kvtop_PAE; machdep->kvbase = HYPERVISOR_VIRT_START_PAE; } else { PGDIR_SHIFT = PGDIR_SHIFT_2LEVEL; PTRS_PER_PTE = PTRS_PER_PTE_2LEVEL; PTRS_PER_PGD = PTRS_PER_PGD_2LEVEL; machdep->kvtop = x86_kvtop; free(machdep->pmd); machdep->pmd = machdep->pgd; machdep->kvbase = HYPERVISOR_VIRT_START; } machdep->ptrs_per_pgd = PTRS_PER_PGD; machdep->identity_map_base = DIRECTMAP_VIRT_START; machdep->is_kvaddr = x86_xenhyper_is_kvaddr; machdep->eframe_search = x86_eframe_search; machdep->back_trace = x86_back_trace_cmd; machdep->processor_speed = x86_processor_speed; /* ODA: check */ machdep->dump_irq = generic_dump_irq; /* ODA: check */ machdep->get_stack_frame = x86_get_stack_frame_hyper; machdep->get_stackbase = x86_get_stackbase_hyper; machdep->get_stacktop = x86_get_stacktop_hyper; machdep->translate_pte = x86_translate_pte; machdep->memory_size = xen_hyper_x86_memory_size; machdep->dis_filter = x86_dis_filter; // machdep->cmd_mach = x86_cmd_mach; /* ODA: check */ machdep->get_smp_cpus = xen_hyper_x86_get_smp_cpus; // machdep->line_number_hooks = x86_line_number_hooks; /* ODA: check */ machdep->flags |= FRAMESIZE_DEBUG; /* ODA: check */ machdep->value_to_symbol = generic_machdep_value_to_symbol; machdep->clear_machdep_cache = x86_clear_machdep_cache; /* machdep table for Xen Hypervisor */ xhmachdep->pcpu_init = xen_hyper_x86_pcpu_init; break; case POST_GDB: #if 0 /* ODA: need this ? */ if (x86_omit_frame_pointer()) { machdep->flags |= OMIT_FRAME_PTR; #endif XEN_HYPER_STRUCT_SIZE_INIT(cpu_time, "cpu_time"); XEN_HYPER_STRUCT_SIZE_INIT(cpuinfo_x86, "cpuinfo_x86"); XEN_HYPER_STRUCT_SIZE_INIT(tss, "tss_struct"); XEN_HYPER_MEMBER_OFFSET_INIT(tss_esp0, "tss_struct", "esp0"); XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_local_tsc_stamp, "cpu_time", "local_tsc_stamp"); XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_stime_local_stamp, "cpu_time", "stime_local_stamp"); XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_stime_master_stamp, "cpu_time", "stime_master_stamp"); XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_tsc_scale, "cpu_time", "tsc_scale"); XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_calibration_timer, "cpu_time", "calibration_timer"); if (symbol_exists("cpu_data")) { xht->cpu_data_address = symbol_value("cpu_data"); } /* KAK Can this be calculated? */ if (!machdep->hz) { machdep->hz = XEN_HYPER_HZ; } break; case POST_INIT: break; } } #endif /* X86 */