crash/alpha.c
2014-01-28 16:46:11 -05:00

2734 lines
75 KiB
C

/* alpha.c - core analysis suite
*
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
* Copyright (C) 2002-2006, 2010-2013 David Anderson
* Copyright (C) 2002-2006, 2010-2013 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 ALPHA
#include "defs.h"
static void alpha_back_trace(struct gnu_request *, struct bt_info *);
static int alpha_trace_status(struct gnu_request *, struct bt_info *);
static void alpha_exception_frame(ulong, ulong,
struct gnu_request *, struct bt_info *);
static void alpha_frame_offset(struct gnu_request *, ulong);
static int alpha_backtrace_resync(struct gnu_request *, ulong,
struct bt_info *);
static void alpha_print_stack_entry(struct gnu_request *, ulong,
char *, ulong, struct bt_info *);
static int alpha_resync_speculate(struct gnu_request *, ulong,struct bt_info *);
static int alpha_dis_filter(ulong, char *, unsigned int);
static void dis_address_translation(ulong, char *, unsigned int);
static void alpha_cmd_mach(void);
static int alpha_get_smp_cpus(void);
static void alpha_display_machine_stats(void);
static void alpha_dump_line_number(char *, ulong);
static void display_hwrpb(unsigned int);
static void alpha_post_init(void);
static struct line_number_hook alpha_line_number_hooks[];
#define ALPHA_CONTINUE_TRACE (1)
#define ALPHA_END_OF_TRACE (2)
#define ALPHA_EXCEPTION_FRAME (3)
#define ALPHA_SYSCALL_FRAME (4)
#define ALPHA_MM_FAULT (5)
#define ALPHA_INTERRUPT_PENDING (6)
#define ALPHA_RESCHEDULE (7)
#define ALPHA_DOWN_FAILED (8)
#define ALPHA_RET_FROM_SMP_FORK (9)
#define ALPHA_SIGNAL_RETURN (10)
#define ALPHA_STRACE (11)
static int alpha_eframe_search(struct bt_info *);
static int alpha_uvtop(struct task_context *, ulong, physaddr_t *, int);
static int alpha_kvtop(struct task_context *, ulong, physaddr_t *, int);
static void alpha_back_trace_cmd(struct bt_info *);
static ulong alpha_get_task_pgd(ulong task);
static ulong alpha_processor_speed(void);
static void alpha_dump_irq(int);
static void alpha_get_stack_frame(struct bt_info *, ulong *, ulong *);
static void get_alpha_frame(struct bt_info *, ulong *, ulong *);
static int verify_user_eframe(struct bt_info *, ulong, ulong);
static int alpha_translate_pte(ulong, void *, ulonglong);
static uint64_t alpha_memory_size(void);
static ulong alpha_vmalloc_start(void);
static int alpha_is_task_addr(ulong);
static int alpha_verify_symbol(const char *, ulong, char);
struct percpu_data {
ulong halt_PC;
ulong halt_ra;
ulong halt_pv;
};
#define GET_HALT_PC 0x1
#define GET_HALT_RA 0x2
#define GET_HALT_PV 0x3
static ulong get_percpu_data(int, ulong, struct percpu_data *);
/*
* Do all necessary machine-specific setup here. This is called three times,
* before symbol table initialization, and before and after GDB has been
* initialized.
*/
void
alpha_init(int when)
{
int tmp;
switch (when)
{
case PRE_SYMTAB:
machdep->verify_symbol = alpha_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 = ~(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->verify_paddr = generic_verify_paddr;
machdep->ptrs_per_pgd = PTRS_PER_PGD;
break;
case PRE_GDB:
switch (symbol_value("_stext") & KSEG_BASE)
{
case KSEG_BASE:
machdep->kvbase = KSEG_BASE;
break;
case KSEG_BASE_48_BIT:
machdep->kvbase = KSEG_BASE_48_BIT;
break;
default:
error(FATAL,
"cannot determine KSEG base from _stext: %lx\n",
symbol_value("_stext"));
}
machdep->identity_map_base = machdep->kvbase;
machdep->is_kvaddr = generic_is_kvaddr;
machdep->is_uvaddr = generic_is_uvaddr;
machdep->eframe_search = alpha_eframe_search;
machdep->back_trace = alpha_back_trace_cmd;
machdep->processor_speed = alpha_processor_speed;
machdep->uvtop = alpha_uvtop;
machdep->kvtop = alpha_kvtop;
machdep->get_task_pgd = alpha_get_task_pgd;
if (symbol_exists("irq_desc"))
machdep->dump_irq = generic_dump_irq;
else
machdep->dump_irq = alpha_dump_irq;
machdep->get_stack_frame = alpha_get_stack_frame;
machdep->get_stackbase = generic_get_stackbase;
machdep->get_stacktop = generic_get_stacktop;
machdep->translate_pte = alpha_translate_pte;
machdep->memory_size = alpha_memory_size;
machdep->vmalloc_start = alpha_vmalloc_start;
machdep->is_task_addr = alpha_is_task_addr;
if (symbol_exists("console_crash")) {
get_symbol_data("console_crash", sizeof(int), &tmp);
if (tmp)
machdep->flags |= HWRESET;
}
machdep->dis_filter = alpha_dis_filter;
machdep->cmd_mach = alpha_cmd_mach;
machdep->get_smp_cpus = alpha_get_smp_cpus;
machdep->line_number_hooks = alpha_line_number_hooks;
machdep->value_to_symbol = generic_machdep_value_to_symbol;
machdep->init_kernel_pgd = NULL;
break;
case POST_GDB:
MEMBER_OFFSET_INIT(thread_struct_ptbr,
"thread_struct", "ptbr");
MEMBER_OFFSET_INIT(hwrpb_struct_cycle_freq,
"hwrpb_struct", "cycle_freq");
MEMBER_OFFSET_INIT(hwrpb_struct_processor_offset,
"hwrpb_struct", "processor_offset");
MEMBER_OFFSET_INIT(hwrpb_struct_processor_size,
"hwrpb_struct", "processor_size");
MEMBER_OFFSET_INIT(percpu_struct_halt_PC,
"percpu_struct", "halt_PC");
MEMBER_OFFSET_INIT(percpu_struct_halt_ra,
"percpu_struct", "halt_ra");
MEMBER_OFFSET_INIT(percpu_struct_halt_pv,
"percpu_struct", "halt_pv");
MEMBER_OFFSET_INIT(switch_stack_r26,
"switch_stack", "r26");
if (symbol_exists("irq_action"))
ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_action,
"irq_action", NULL, 0);
else if (symbol_exists("irq_desc"))
ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc,
"irq_desc", NULL, 0);
else
machdep->nr_irqs = 0;
if (!machdep->hz)
machdep->hz = HZ;
break;
case POST_INIT:
alpha_post_init();
break;
}
}
/*
* Unroll a kernel stack.
*/
static void
alpha_back_trace_cmd(struct bt_info *bt)
{
char buf[BUFSIZE];
struct gnu_request *req;
bt->flags |= BT_EXCEPTION_FRAME;
if (CRASHDEBUG(1) || bt->debug)
fprintf(fp, " => PC: %lx (%s) FP: %lx \n",
bt->instptr, value_to_symstr(bt->instptr, buf, 0),
bt->stkptr );
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
req->command = GNU_STACK_TRACE;
req->flags = GNU_RETURN_ON_ERROR;
req->buf = GETBUF(BUFSIZE);
req->debug = bt->debug;
req->task = bt->task;
req->pc = bt->instptr;
req->sp = bt->stkptr;
if (bt->flags & BT_USE_GDB) {
strcpy(req->buf, "backtrace");
gdb_interface(req);
}
else
alpha_back_trace(req, bt);
FREEBUF(req->buf);
FREEBUF(req);
}
/*
* Unroll the kernel stack.
*/
#define ALPHA_BACKTRACE_SPECULATE(X) \
{ \
speculate_location = X; \
\
if (bt->flags & BT_SPECULATE) \
return; \
\
BZERO(btloc, sizeof(struct bt_info)); \
btloc->task = req->task; \
btloc->tc = bt->tc; \
btloc->stackbase = bt->stackbase; \
btloc->stacktop = bt->stacktop; \
btloc->flags = BT_TEXT_SYMBOLS_NOPRINT; \
hook.eip = 0; \
hook.esp = req->lastsp ? req->lastsp + sizeof(long) : 0; \
btloc->hp = &hook; \
\
back_trace(btloc); \
\
if (hook.esp && hook.eip) { \
req->hookp = &hook; \
if (alpha_resync_speculate(req, bt->flags, bt)) { \
req->pc = hook.eip; \
req->sp = hook.esp; \
continue; \
} \
goto show_remaining_text; \
} \
goto show_remaining_text; \
}
static void
alpha_back_trace(struct gnu_request *req, struct bt_info *bt)
{
char buf[BUFSIZE];
int frame;
int done;
int status;
struct stack_hook hook;
int eframe_same_pc_ra_function;
int speculate_location;
struct bt_info bt_info, *btloc;
frame = 0;
req->curframe = 0;
btloc = &bt_info;
if (!IS_KVADDR(req->pc)) {
if (BT_REFERENCE_CHECK(bt))
return;
if ((machdep->flags & HWRESET) && is_task_active(req->task)) {
fprintf(fp, "(hardware reset while in user space)\n");
return;
}
fprintf(fp, "invalid pc: %lx\n", req->pc);
alpha_exception_frame(USER_EFRAME_ADDR(req->task),
BT_USER_EFRAME, req, bt);
return;
}
for (done = FALSE; !done && (frame < 100); frame++) {
speculate_location = 0;
if ((req->name = closest_symbol(req->pc)) == NULL) {
req->ra = req->pc = 0;
if (alpha_backtrace_resync(req,
bt->flags | BT_FROM_CALLFRAME, bt))
continue;
if (BT_REFERENCE_FOUND(bt))
return;
ALPHA_BACKTRACE_SPECULATE(1);
}
if (!INSTACK(req->sp, bt))
break;
if (!is_kernel_text(req->pc))
ALPHA_BACKTRACE_SPECULATE(2);
alpha_print_stack_entry(req, req->pc, req->name,
bt->flags | BT_SAVE_LASTSP, bt);
if (BT_REFERENCE_FOUND(bt))
return;
switch (status = alpha_trace_status(req, bt))
{
case ALPHA_CONTINUE_TRACE:
alpha_frame_offset(req, 0);
if (!req->value) {
done = TRUE;
break;
}
req->prevpc = req->pc;
req->pc = GET_STACK_ULONG(req->sp);
req->prevsp = req->sp;
req->sp += req->value;
break;
case ALPHA_END_OF_TRACE:
done = TRUE;
break;
case ALPHA_STRACE:
alpha_exception_frame(req->sp,
BT_USER_EFRAME|BT_STRACE, req, bt);
done = TRUE;
break;
case ALPHA_RET_FROM_SMP_FORK:
alpha_exception_frame(USER_EFRAME_ADDR(req->task),
BT_USER_EFRAME|BT_RET_FROM_SMP_FORK, req, bt);
done = TRUE;
break;
case ALPHA_DOWN_FAILED:
frame++;
alpha_print_stack_entry(req,
req->pc, closest_symbol(req->pc),
bt->flags | BT_SAVE_LASTSP, bt);
if (BT_REFERENCE_FOUND(bt))
return;
alpha_frame_offset(req, 0);
if (!req->value) {
done = TRUE;
break;
}
req->prevpc = req->pc;
req->pc = GET_STACK_ULONG(req->sp);
req->prevsp = req->sp;
req->sp += req->value;
break;
case ALPHA_RESCHEDULE:
alpha_exception_frame(USER_EFRAME_ADDR(req->task),
BT_USER_EFRAME|BT_RESCHEDULE, req, bt);
done = TRUE;
break;
case ALPHA_MM_FAULT:
alpha_exception_frame(req->sp, bt->flags, req, bt);
if (!IS_KVADDR(req->pc)) {
done = TRUE;
break;
}
alpha_frame_offset(req, 0);
if (!req->value) {
done = TRUE;
break;
}
frame++;
alpha_print_stack_entry(req,
req->pc, closest_symbol(req->pc),
bt->flags | BT_SAVE_LASTSP, bt);
if (BT_REFERENCE_FOUND(bt))
return;
if (!IS_KVADDR(req->pc)) {
done = TRUE;
break;
}
req->prevpc = req->pc;
req->pc = GET_STACK_ULONG(req->sp);
req->prevsp = req->sp;
req->sp += req->value;
break;
case ALPHA_SYSCALL_FRAME:
req->sp = verify_user_eframe(bt, req->task, req->sp) ?
req->sp : USER_EFRAME_ADDR(req->task);
alpha_exception_frame(req->sp, bt->flags, req, bt);
if (!IS_KVADDR(req->pc)) {
done = TRUE;
break;
}
alpha_frame_offset(req, 0);
if (!req->value) {
done = TRUE;
break;
}
req->prevpc = req->pc;
req->pc = GET_STACK_ULONG(req->sp);
req->prevsp = req->sp;
req->sp += req->value;
break;
case ALPHA_SIGNAL_RETURN:
alpha_exception_frame(USER_EFRAME_ADDR(req->task),
bt->flags, req, bt);
done = TRUE;
break;
case ALPHA_EXCEPTION_FRAME:
alpha_frame_offset(req, 0);
if (!req->value) {
fprintf(fp,
"ALPHA EXCEPTION FRAME w/no frame offset for %lx (%s)\n",
req->pc,
value_to_symstr(req->pc, buf, 0));
done = TRUE;
break;
}
alpha_exception_frame(req->sp + req->value,
bt->flags, req, bt);
if (!IS_KVADDR(req->pc)) {
done = TRUE;
break;
}
alpha_frame_offset(req, 0);
if (!req->value) {
fprintf(fp,
"ALPHA EXCEPTION FRAME w/no frame offset for %lx (%s)\n",
req->pc,
value_to_symstr(req->pc, buf, 0));
done = TRUE;
break;
}
eframe_same_pc_ra_function =
SAME_FUNCTION(req->pc, req->ra);
frame++;
alpha_print_stack_entry(req, req->pc,
closest_symbol(req->pc),
bt->flags | BT_SAVE_LASTSP, bt);
if (BT_REFERENCE_FOUND(bt))
return;
if (!IS_KVADDR(req->pc)) {
done = TRUE;
break;
}
if (STREQ(closest_symbol(req->pc),
"ret_from_reschedule")) {
alpha_exception_frame(
USER_EFRAME_ADDR(req->task),
BT_USER_EFRAME|BT_RESCHEDULE, req, bt);
done = TRUE;
break;
}
req->prevpc = req->pc;
req->pc = GET_STACK_ULONG(req->sp);
if (!is_kernel_text(req->pc)) {
if (alpha_backtrace_resync(req,
bt->flags | BT_FROM_EXCEPTION, bt))
break;
if (BT_REFERENCE_FOUND(bt))
return;
ALPHA_BACKTRACE_SPECULATE(3);
}
if (!eframe_same_pc_ra_function &&
(req->pc != req->ra)) {
req->pc = req->ra;
break;
}
req->prevsp = req->sp;
req->sp += req->value;
break;
case ALPHA_INTERRUPT_PENDING:
alpha_frame_offset(req, 0);
if (!req->value) {
req->prevpc = req->pc;
req->pc = req->addr;
req->prevsp = req->sp;
req->sp = req->frame;
} else {
req->prevpc = req->pc;
req->pc = GET_STACK_ULONG(req->sp);
req->prevsp = req->sp;
req->sp += req->value;
}
break;
}
}
return;
show_remaining_text:
if (BT_REFERENCE_CHECK(bt))
return;
BZERO(btloc, sizeof(struct bt_info));
btloc->task = req->task;
btloc->tc = bt->tc;
btloc->stackbase = bt->stackbase;
btloc->stacktop = bt->stacktop;
btloc->flags = BT_TEXT_SYMBOLS_NOPRINT;
hook.esp = req->lastsp + sizeof(long);
btloc->hp = &hook;
back_trace(btloc);
if (hook.eip) {
fprintf(fp,
"NOTE: cannot resolve trace from this point -- remaining text symbols on stack:\n");
btloc->flags = BT_TEXT_SYMBOLS_PRINT|BT_ERROR_MASK;
hook.esp = req->lastsp + sizeof(long);
back_trace(btloc);
} else
fprintf(fp,
"NOTE: cannot resolve trace from this point -- no remaining text symbols\n");
if (CRASHDEBUG(1))
fprintf(fp, "speculate_location: %d\n", speculate_location);
alpha_exception_frame(USER_EFRAME_ADDR(req->task),
BT_USER_EFRAME, req, bt);
}
/*
* print one entry of a stack trace
*/
static void
alpha_print_stack_entry(struct gnu_request *req,
ulong callpc,
char *name,
ulong flags,
struct bt_info *bt)
{
struct load_module *lm;
if (BT_REFERENCE_CHECK(bt)) {
switch (bt->ref->cmdflags & (BT_REF_SYMBOL|BT_REF_HEXVAL))
{
case BT_REF_SYMBOL:
if (STREQ(name, bt->ref->str) ||
(STREQ(name, "strace") &&
STREQ(bt->ref->str, "entSys"))) {
bt->ref->cmdflags |= BT_REF_FOUND;
}
break;
case BT_REF_HEXVAL:
if (bt->ref->hexval == callpc)
bt->ref->cmdflags |= BT_REF_FOUND;
break;
}
} else {
fprintf(fp, "%s#%d [%lx] %s at %lx",
req->curframe < 10 ? " " : "", req->curframe, req->sp,
STREQ(name, "strace") ? "strace (via entSys)" : name,
callpc);
if (module_symbol(callpc, NULL, &lm, NULL, 0))
fprintf(fp, " [%s]", lm->mod_name);
fprintf(fp, "\n");
}
if (!(flags & BT_SPECULATE))
req->curframe++;
if (flags & BT_SAVE_LASTSP)
req->lastsp = req->sp;
if (BT_REFERENCE_CHECK(bt))
return;
if (flags & BT_LINE_NUMBERS)
alpha_dump_line_number(name, callpc);
}
static const char *hook_files[] = {
"arch/alpha/kernel/entry.S",
"arch/alpha/kernel/head.S",
"init/main.c",
"arch/alpha/kernel/smp.c",
};
#define ENTRY_S ((char **)&hook_files[0])
#define HEAD_S ((char **)&hook_files[1])
#define MAIN_C ((char **)&hook_files[2])
#define SMP_C ((char **)&hook_files[3])
static struct line_number_hook alpha_line_number_hooks[] = {
{"entInt", ENTRY_S},
{"entMM", ENTRY_S},
{"entArith", ENTRY_S},
{"entIF", ENTRY_S},
{"entDbg", ENTRY_S},
{"kernel_clone", ENTRY_S},
{"kernel_thread", ENTRY_S},
{"__kernel_execve", ENTRY_S},
{"do_switch_stack", ENTRY_S},
{"undo_switch_stack", ENTRY_S},
{"entUna", ENTRY_S},
{"entUnaUser", ENTRY_S},
{"sys_fork", ENTRY_S},
{"sys_clone", ENTRY_S},
{"sys_vfork", ENTRY_S},
{"alpha_switch_to", ENTRY_S},
{"entSys", ENTRY_S},
{"ret_from_sys_call", ENTRY_S},
{"ret_from_reschedule", ENTRY_S},
{"restore_all", ENTRY_S},
{"strace", ENTRY_S},
{"strace_success", ENTRY_S},
{"strace_error", ENTRY_S},
{"syscall_error", ENTRY_S},
{"ret_success", ENTRY_S},
{"signal_return", ENTRY_S},
{"ret_from_fork", ENTRY_S},
{"reschedule", ENTRY_S},
{"sys_sigreturn", ENTRY_S},
{"sys_rt_sigreturn", ENTRY_S},
{"sys_sigsuspend", ENTRY_S},
{"sys_rt_sigsuspend", ENTRY_S},
{"ret_from_smpfork", ENTRY_S},
{"_stext", HEAD_S},
{"__start", HEAD_S},
{"__smp_callin", HEAD_S},
{"cserve_ena", HEAD_S},
{"cserve_dis", HEAD_S},
{"halt", HEAD_S},
{"start_kernel", MAIN_C},
{"smp_callin", SMP_C},
{NULL, NULL} /* list must be NULL-terminated */
};
static void
alpha_dump_line_number(char *name, ulong callpc)
{
char buf[BUFSIZE], *p;
int retries;
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 the frame size storage at the beginning of a function.
* If it's not obvious, try gdb.
*
* For future reference, here's where the numbers come from:
*
* 0xfffffc00003217e8 <schedule+8>: subq sp,0x50,sp
* fffffc00003217e8: 43ca153e
* 010000 11110 01010000 1 0101001 11110
*
* 0xfffffc0000321668 <schedule_timeout+8>: subq sp,0x60,sp
* fffffc0000321668: 43cc153e
* 010000 11110 01100000 1 0101001 11110
*
* 0xfffffc000035d028 <do_select+8>: subq sp,0x70,sp
* fffffc000035d028: 43ce153e
* 010000 11110 01110000 1 0101001 11110
*
* 0100 0011 110x xxxx xxx1 0101 0011 1110
* 1111 1111 111x xxxx xxx1 1111 1111 1111
* 0000 0000 0001 1111 1110 0000 0000 0000
* f f e 0 1 f f f instruction mask
* 0 0 1 f e 0 0 0 offset
*
* stq ra,0(sp)
* fffffc000035d034: b75e0000
*/
static void
alpha_frame_offset(struct gnu_request *req, ulong alt_pc)
{
uint *ip, ival;
ulong value;
req->value = value = 0;
if (alt_pc && !is_kernel_text(alt_pc))
error(FATAL,
"trying to get frame offset of non-text address: %lx\n",
alt_pc);
else if (!alt_pc && !is_kernel_text(req->pc))
error(FATAL,
"trying to get frame offset of non-text address: %lx\n",
req->pc);
ip = alt_pc ? (int *)closest_symbol_value(alt_pc) :
(int *)closest_symbol_value(req->pc);
if (!ip)
goto use_gdb;
ival = 0;
/*
* Don't go any farther than "stq ra,0(sp)" (0xb75e0000)
*/
while (ival != 0xb75e0000) {
if (!text_value_cache((ulong)ip, 0, &ival)) {
readmem((ulong)ip, KVADDR, &ival,
sizeof(uint), "uncached text value",
FAULT_ON_ERROR);
text_value_cache((ulong)ip, ival, NULL);
}
if ((ival & 0xffe01fff) == 0x43c0153e) {
value = (ival & 0x1fe000) >> 13;
break;
}
ip++;
}
if (value) {
req->value = value;
return;
}
use_gdb:
#ifndef GDB_5_3
{
static int gdb_frame_offset_warnings = 10;
if (gdb_frame_offset_warnings-- > 0)
error(WARNING,
"GNU_ALPHA_FRAME_OFFSET functionality not ported to gdb\n");
}
#endif
req->command = GNU_ALPHA_FRAME_OFFSET;
if (alt_pc) {
ulong pc_save;
pc_save = req->pc;
req->pc = alt_pc;
gdb_interface(req);
req->pc = pc_save;
} else
gdb_interface(req);
}
/*
* Look for key routines that either mean the trace has ended or has
* bumped into an exception frame.
*/
int
alpha_trace_status(struct gnu_request *req, struct bt_info *bt)
{
ulong value;
char *func;
ulong frame;
req->addr = 0;
func = req->name;
frame = req->sp;
if (STREQ(func, "start_kernel") ||
STREQ(func, "smp_callin") ||
STREQ(func, "kernel_thread") ||
STREQ(func, "__kernel_thread"))
return ALPHA_END_OF_TRACE;
if (STREQ(func, "ret_from_smp_fork") ||
STREQ(func, "ret_from_smpfork"))
return ALPHA_RET_FROM_SMP_FORK;
if (STREQ(func, "entSys"))
return ALPHA_SYSCALL_FRAME;
if (STREQ(func, "entMM")) {
req->sp += 56; /* see entMM in entry.S */
return ALPHA_MM_FAULT;
}
if (STREQ(func, "do_entInt"))
return ALPHA_EXCEPTION_FRAME;
if (STREQ(func, "do_entArith"))
return ALPHA_EXCEPTION_FRAME;
if (STREQ(func, "do_entIF"))
return ALPHA_EXCEPTION_FRAME;
if (STREQ(func, "do_entDbg"))
return ALPHA_EXCEPTION_FRAME;
if (STREQ(func, "handle_bottom_half"))
return ALPHA_EXCEPTION_FRAME;
if (STREQ(func, "handle_softirq"))
return ALPHA_EXCEPTION_FRAME;
if (STREQ(func, "reschedule"))
return ALPHA_RESCHEDULE;
if (STREQ(func, "ret_from_reschedule"))
return ALPHA_RESCHEDULE;
if (STREQ(func, "signal_return"))
return ALPHA_SIGNAL_RETURN;
if (STREQ(func, "strace"))
return ALPHA_STRACE;
if (STREQ(func, "__down_failed") ||
STREQ(func, "__down_failed_interruptible")) {
readmem(req->sp + 144, KVADDR, &req->pc, sizeof(ulong),
"__down_failed r26", FAULT_ON_ERROR);
req->sp += 160;
return ALPHA_DOWN_FAILED;
}
value = GET_STACK_ULONG(frame);
if (STREQ(closest_symbol(value), "do_entInt") ||
STREQ(closest_symbol(value), "do_entArith") ||
STREQ(closest_symbol(value), "do_entIF") ||
STREQ(closest_symbol(value), "do_entDbg")) {
req->addr = value;
req->frame = 0;
while (INSTACK(frame, bt)) {
frame += sizeof(ulong);
value = GET_STACK_ULONG(frame);
if (STREQ(closest_symbol(value), "ret_from_sys_call")) {
alpha_frame_offset(req, req->addr);
/* req->frame = frame + req->value; XXX */
break;
}
}
return ALPHA_INTERRUPT_PENDING;
}
return ALPHA_CONTINUE_TRACE;
}
/*
* Redo the gdb pt_regs structure output.
*/
enum regnames { _r0_, _r1_, _r2_, _r3_, _r4_, _r5_, _r6_, _r7_, _r8_,
_r19_, _r20_, _r21_, _r22_, _r23_, _r24_, _r25_, _r26_,
_r27_, _r28_, _hae_, _trap_a0_, _trap_a1_, _trap_a2_,
_ps_, _pc_, _gp_, _r16_, _r17_, _r18_, NUMREGS};
struct alpha_eframe {
char regs[30][30];
ulong value[29];
};
static void
alpha_exception_frame(ulong addr,
ulong flags,
struct gnu_request *req,
struct bt_info *bt)
{
int i, j;
char buf[BUFSIZE];
ulong value;
physaddr_t paddr;
struct alpha_eframe eframe;
if (CRASHDEBUG(4))
fprintf(fp, "alpha_exception_frame: %lx\n", addr);
if (flags & BT_SPECULATE) {
req->pc = 0;
fprintf(fp, "ALPHA EXCEPTION FRAME\n");
return;
}
BZERO(&eframe, sizeof(struct alpha_eframe));
open_tmpfile();
dump_struct("pt_regs", addr, RADIX(16));
rewind(pc->tmpfile);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
strip_comma(clean_line(buf));
if (!strstr(buf, "0x"))
continue;
extract_hex(buf, &value, NULLCHAR, TRUE);
if (CRASHDEBUG(4))
fprintf(pc->saved_fp, "<%s> %lx\n", buf, value);
if (STRNEQ(buf, "r0 = ")) {
sprintf(eframe.regs[_r0_], " V0/R0: %016lx", value);
eframe.value[_r0_] = value;
}
if (STRNEQ(buf, "r1 = ")) {
sprintf(eframe.regs[_r1_], " T0/R1: %016lx", value);
eframe.value[_r1_] = value;
}
if (STRNEQ(buf, "r2 = ")) {
sprintf(eframe.regs[_r2_], " T1/R2: %016lx", value);
eframe.value[_r2_] = value;
}
if (STRNEQ(buf, "r3 = ")) {
sprintf(eframe.regs[_r3_], " T2/R3: %016lx", value);
eframe.value[_r3_] = value;
}
if (STRNEQ(buf, "r4 = ")) {
sprintf(eframe.regs[_r4_], " T3/R4: %016lx", value);
eframe.value[_r4_] = value;
}
if (STRNEQ(buf, "r5 = ")) {
sprintf(eframe.regs[_r5_], " T4/R5: %016lx", value);
eframe.value[_r5_] = value;
}
if (STRNEQ(buf, "r6 = ")) {
sprintf(eframe.regs[_r6_], " T5/R6: %016lx", value);
eframe.value[_r6_] = value;
}
if (STRNEQ(buf, "r7 = ")) {
sprintf(eframe.regs[_r7_], " T6/R7: %016lx", value);
eframe.value[_r7_] = value;
}
if (STRNEQ(buf, "r8 = ")) {
sprintf(eframe.regs[_r8_], " T7/R8: %016lx", value);
eframe.value[_r8_] = value;
}
if (STRNEQ(buf, "r19 = ")) {
sprintf(eframe.regs[_r19_], " A3/R19: %016lx", value);
eframe.value[_r19_] = value;
}
if (STRNEQ(buf, "r20 = ")) {
sprintf(eframe.regs[_r20_], " A4/R20: %016lx", value);
eframe.value[_r20_] = value;
}
if (STRNEQ(buf, "r21 = ")) {
sprintf(eframe.regs[_r21_], " A5/R21: %016lx", value);
eframe.value[_r21_] = value;
}
if (STRNEQ(buf, "r22 = ")) {
sprintf(eframe.regs[_r22_], " T8/R22: %016lx", value);
eframe.value[_r22_] = value;
}
if (STRNEQ(buf, "r23 = ")) {
sprintf(eframe.regs[_r23_], " T9/R23: %016lx", value);
eframe.value[_r23_] = value;
}
if (STRNEQ(buf, "r24 = ")) {
sprintf(eframe.regs[_r24_], "T10/R24: %016lx", value);
eframe.value[_r24_] = value;
}
if (STRNEQ(buf, "r25 = ")) {
sprintf(eframe.regs[_r25_], "T11/R25: %016lx", value);
eframe.value[_r25_] = value;
}
if (STRNEQ(buf, "r26 = ")) {
sprintf(eframe.regs[_r26_], " RA/R26: %016lx", value);
eframe.value[_r26_] = value;
}
if (STRNEQ(buf, "r27 = ")) {
sprintf(eframe.regs[_r27_], "T12/R27: %016lx", value);
eframe.value[_r27_] = value;
}
if (STRNEQ(buf, "r28 = ")) {
sprintf(eframe.regs[_r28_], " AT/R28: %016lx", value);
eframe.value[_r28_] = value;
}
if (STRNEQ(buf, "hae = ")) {
sprintf(eframe.regs[_hae_], " HAE: %016lx", value);
eframe.value[_hae_] = value;
}
if (STRNEQ(buf, "trap_a0 = ")) {
sprintf(eframe.regs[_trap_a0_], "TRAP_A0: %016lx",
value);
eframe.value[_trap_a0_] = value;
}
if (STRNEQ(buf, "trap_a1 = ")) {
sprintf(eframe.regs[_trap_a1_], "TRAP_A1: %016lx",
value);
eframe.value[_trap_a1_] = value;
}
if (STRNEQ(buf, "trap_a2 = ")) {
sprintf(eframe.regs[_trap_a2_], "TRAP_A2: %016lx",
value);
eframe.value[_trap_a2_] = value;
}
if (STRNEQ(buf, "ps = ")) {
sprintf(eframe.regs[_ps_], " PS: %016lx", value);
eframe.value[_ps_] = value;
}
if (STRNEQ(buf, "pc = ")) {
sprintf(eframe.regs[_pc_], " PC: %016lx", value);
eframe.value[_pc_] = value;
}
if (STRNEQ(buf, "gp = ")) {
sprintf(eframe.regs[_gp_], " GP/R29: %016lx", value);
eframe.value[_gp_] = value;
}
if (STRNEQ(buf, "r16 = ")) {
sprintf(eframe.regs[_r16_], " A0/R16: %016lx", value);
eframe.value[_r16_] = value;
}
if (STRNEQ(buf, "r17 = ")) {
sprintf(eframe.regs[_r17_], " A1/R17: %016lx", value);
eframe.value[_r17_] = value;
}
if (STRNEQ(buf, "r18 =")) {
sprintf(eframe.regs[_r18_], " A2/R18: %016lx", value);
eframe.value[_r18_] = value;
}
}
close_tmpfile();
if ((flags & BT_EXCEPTION_FRAME) && !BT_REFERENCE_CHECK(bt)) {
dump_eframe:
fprintf(fp, " EFRAME: %lx ", addr);
fprintf(fp, "%s\n", eframe.regs[_r24_]);
for (i = 0; i < (((NUMREGS+1)/2)-1); i++) {
fprintf(fp, "%s ", eframe.regs[i]);
pad_line(fp, 21 - strlen(eframe.regs[i]), ' ');
j = i+((NUMREGS+1)/2);
fprintf(fp, "%s", eframe.regs[j]);
if (((j == _pc_) || (j == _r26_)) &&
is_kernel_text(eframe.value[j]))
fprintf(fp, " <%s>",
value_to_symstr(eframe.value[j], buf, 0));
fprintf(fp, "\n");
}
}
req->ra = eframe.value[_r26_];
req->pc = eframe.value[_pc_];
req->sp = addr + (29 * sizeof(ulong));
if (flags & BT_USER_EFRAME) {
flags &= ~BT_USER_EFRAME;
if (!BT_REFERENCE_CHECK(bt) && (eframe.value[_ps_] == 8) &&
(((uvtop(task_to_context(req->task), req->pc, &paddr, 0) ||
(volatile ulong)paddr) &&
(uvtop(task_to_context(req->task), req->ra, &paddr, 0) ||
(volatile ulong)paddr)) ||
(IS_ZOMBIE(req->task) || IS_EXITING(req->task)))) {
if (!(flags &
(BT_RESCHEDULE|BT_RET_FROM_SMP_FORK|BT_STRACE)))
fprintf(fp,
"NOTE: kernel-entry exception frame:\n");
goto dump_eframe;
}
}
}
/*
* Look for likely exception frames in a stack.
*/
struct alpha_pt_regs {
ulong reg_value[NUMREGS];
};
static int
alpha_eframe_search(struct bt_info *bt)
{
ulong *first, *last;
ulong eframe;
struct alpha_pt_regs *pt;
struct gnu_request *req; /* needed for alpha_exception_frame */
ulong *stack;
int cnt;
stack = (ulong *)bt->stackbuf;
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
req->task = bt->task;
first = stack +
(roundup(SIZE(task_struct), sizeof(ulong)) / sizeof(ulong));
last = stack +
(((bt->stacktop - bt->stackbase) - SIZE(pt_regs)) / sizeof(ulong));
for (cnt = 0; first <= last; first++) {
pt = (struct alpha_pt_regs *)first;
/* check for kernel exception frame */
if (!(pt->reg_value[_ps_] & 0xfffffffffffffff8) &&
(is_kernel_text(pt->reg_value[_pc_]) ||
IS_MODULE_VADDR(pt->reg_value[_pc_])) &&
(is_kernel_text(pt->reg_value[_r26_]) ||
IS_MODULE_VADDR(pt->reg_value[_r26_])) &&
IS_KVADDR(pt->reg_value[_gp_])) {
cnt++;
if (bt->flags & BT_EFRAME_COUNT)
continue;
fprintf(fp, "\nKERNEL-MODE EXCEPTION FRAME:\n");
eframe = bt->task + ((ulong)first - (ulong)stack);
alpha_exception_frame(eframe, BT_EXCEPTION_FRAME,
req, bt);
continue;
}
/* check for user exception frame */
if ((pt->reg_value[_ps_] == 0x8) &&
((IN_TASK_VMA(bt->task, pt->reg_value[_pc_]) &&
IN_TASK_VMA(bt->task, pt->reg_value[_r26_]) &&
IS_UVADDR(pt->reg_value[_gp_], bt->tc)) ||
((first == last) &&
(IS_ZOMBIE(bt->task) || IS_EXITING(bt->task))))) {
cnt++;
if (bt->flags & BT_EFRAME_COUNT)
continue;
fprintf(fp, "\nUSER-MODE EXCEPTION FRAME:\n");
eframe = bt->task + ((ulong)first - (ulong)stack);
alpha_exception_frame(eframe, BT_EXCEPTION_FRAME,
req, bt);
}
}
FREEBUF(req);
return cnt;
}
/*
* Before dumping a nonsensical exception frame, give it a quick test.
*/
static int
verify_user_eframe(struct bt_info *bt, ulong task, ulong sp)
{
struct alpha_pt_regs ptbuf, *pt;
readmem(sp, KVADDR, &ptbuf, sizeof(struct alpha_pt_regs),
"pt_regs", FAULT_ON_ERROR);
pt = &ptbuf;
if ((pt->reg_value[_ps_] == 0x8) &&
((IN_TASK_VMA(task, pt->reg_value[_pc_]) &&
IN_TASK_VMA(task, pt->reg_value[_r26_]) &&
IS_UVADDR(pt->reg_value[_gp_], bt->tc)) ||
((pt == (struct alpha_pt_regs *)USER_EFRAME_ADDR(task)) &&
(IS_ZOMBIE(task) || IS_EXITING(task))))) {
return TRUE;
}
return FALSE;
}
/*
* Try to resync the stack location when there is no valid stack frame,
* typically just above an exception frame. Use the req->ra value from the
* exception frame as the new starting req->pc. Then walk up the stack until
* a text routine that calls the newly-assigned pc is found -- that stack
* location then becomes the new req->sp.
*
* If we're not coming from an exception frame, req-ra and req->pc will be
* purposely zeroed out. In that case, use the prevsp value to find the
* first pc that called the last frame's pc.
*
* Add any other repeatable "special-case" frames to the beginning of this
* routine (ex. debug_spin_lock). Last ditch -- at the end of this routine,
* speculate what might have happened (possibly in the background) -- and
* if it looks good, run with it.
*/
static int
alpha_backtrace_resync(struct gnu_request *req, ulong flags, struct bt_info *bt)
{
char addr[BUFSIZE];
char buf[BUFSIZE];
char lookfor1[BUFSIZE];
char lookfor2[BUFSIZE];
ulong newpc;
ulong *stkp;
ulong *stkp_newpc, *stkp_next;
ulong value;
int found;
char *name;
int exception;
if (CRASHDEBUG(1))
fprintf(fp,
"RESYNC1: [%lx-%d] ra: %lx pc: %lx sp: %lx\n",
flags, req->curframe, req->ra, req->pc, req->sp);
if (!req->ra && !req->pc) {
req->ra = req->prevpc;
exception = FALSE;
} else
exception = TRUE;
if (!IS_KVADDR(req->ra))
return FALSE;
name = closest_symbol(req->ra);
sprintf(lookfor1, "<%s>", name);
sprintf(lookfor2, "<%s+", name);
if (CRASHDEBUG(1))
fprintf(fp, "RESYNC2: exception: %s lookfor: %s or %s\n",
exception ? "TRUE" : "FALSE",
lookfor1, lookfor2);
/*
* This is common when a non-panicking active CPU is spinning
* in debug_spin_lock(). The next pc is offset by 0x30 from
* the top of the exception frame, and the next sp is equal
* to the frame offset of debug_spin_lock(). I can't explain it...
*/
if ((flags & BT_FROM_EXCEPTION) && STREQ(name, "debug_spin_lock")) {
alpha_print_stack_entry(req, req->ra,
closest_symbol(req->ra), flags, bt);
if (BT_REFERENCE_FOUND(bt))
return FALSE;
alpha_frame_offset(req, req->ra);
stkp = (ulong *)(req->sp + 0x30);
value = GET_STACK_ULONG(stkp);
if (!is_kernel_text(value)) {
req->sp = req->prevsp;
return FALSE;
}
req->pc = value;
req->sp += req->value;
return TRUE;
}
/*
* If the ra is a system call, then all we should have to do is
* find the next reference to entSys on the stack, and set the
* sp to that value.
*/
if (is_system_call(name, 0)) {
/* stkp = (ulong *)req->sp; */
stkp = (ulong *)req->prevsp;
for (stkp++; INSTACK(stkp, bt); stkp++) {
value = GET_STACK_ULONG(stkp);
if (IS_KVADDR(value) && is_kernel_text(value)) {
if (STREQ(closest_symbol(value), "entSys")) {
req->pc = value;
req->sp = USER_EFRAME_ADDR(req->task);
return TRUE;
}
}
}
}
/*
* Just find the next location containing text. (?)
*/
if (STREQ(name, "do_coredump")) {
stkp = (ulong *)(req->sp + sizeof(long));
for (stkp++; INSTACK(stkp, bt); stkp++) {
value = GET_STACK_ULONG(stkp);
if (IS_KVADDR(value) && is_kernel_text(value)) {
req->pc = req->ra;
req->sp = (ulong)stkp;
return TRUE;
}
}
}
if (flags & BT_SPECULATE)
return FALSE;
if (CRASHDEBUG(1)) {
fprintf(fp, "RESYNC3: prevsp: %lx ra: %lx name: %s\n",
req->prevsp, req->ra, name);
fprintf(fp, "RESYNC3: prevpc: %lx\n", req->prevpc);
}
stkp_newpc = stkp_next = 0;
newpc = 0;
found = FALSE;
if (exception) {
newpc = req->ra;
stkp = (ulong *)req->sp;
} else
stkp = (ulong *)req->prevsp;
if (CRASHDEBUG(1))
fprintf(fp, "RESYNC4: stkp: %lx newpc: %lx\n",
(ulong)stkp, newpc);
for (stkp++; INSTACK(stkp, bt); stkp++) {
value = GET_STACK_ULONG(stkp);
/*
* First find the new pc on the stack.
*/
if (!found) {
if (!exception && is_kernel_text(value)) {
found = TRUE;
} else if (value == newpc) {
found = TRUE;
stkp_newpc = stkp;
continue;
}
}
if (!IS_KVADDR(value))
continue;
if (is_kernel_text(value)) {
if (!stkp_next)
stkp_next = stkp;
if (CRASHDEBUG(2)) {
fprintf(fp,
"RESYNC6: disassemble %lx (%s)\n",
value - sizeof(uint),
value_to_symstr(value - sizeof(uint),
buf, 0));
}
req->command = GNU_DISASSEMBLE;
req->addr = value - sizeof(uint);
sprintf(addr, "0x%lx", req->addr);
open_tmpfile();
req->fp = pc->tmpfile;
gdb_interface(req);
rewind(pc->tmpfile);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
clean_line(buf);
if (STRNEQ(buf, "Dump of") ||
STRNEQ(buf, "End of"))
continue;
if (STRNEQ(buf, addr)) {
if (LASTCHAR(buf) == ':') {
fgets(buf, BUFSIZE,
pc->tmpfile);
clean_line(buf);
}
if (CRASHDEBUG(2) &&
(strstr(buf, "jsr")
|| strstr(buf, "bsr")))
fprintf(pc->saved_fp, "%s\n",
buf);
if ((strstr(buf, "jsr") ||
strstr(buf, "bsr")) &&
(strstr(buf, lookfor1) ||
strstr(buf, lookfor2))) {
if (exception) {
req->pc = newpc;
req->sp = (ulong)stkp;
} else
req->pc = req->addr;
close_tmpfile();
return TRUE;
}
}
}
close_tmpfile();
}
}
if (CRASHDEBUG(1)) {
fprintf(fp, "RESYNC9: [%d] name: %s pc: %lx ra: %lx\n",
req->curframe, name, req->pc, req->ra);
fprintf(fp, "RESYNC9: sp: %lx lastsp: %lx\n",
req->sp, req->lastsp);
fprintf(fp, "RESYNC9: prevpc: %lx prevsp: %lx\n",
req->prevpc, req->prevsp);
}
/*
* At this point, all we can do is speculate based upon
* past experiences...
*/
return (alpha_resync_speculate(req, flags, bt));
}
/*
* Try one level of speculation. If it works, fine -- if not, give up.
*/
static int
alpha_resync_speculate(struct gnu_request *req, ulong flags, struct bt_info *bt)
{
ulong *stkp;
ulong value;
ulong found_sp, found_ra;
struct stack_hook hook;
struct bt_info bt_info, *btloc;
char buf[BUFSIZE];
int kernel_thread;
int looks_good;
if (flags & BT_SPECULATE) /* already been here on this trace... */
return FALSE;
if (pc->tmpfile)
return FALSE;
found_ra = found_sp = 0;
kernel_thread = is_kernel_thread(req->task);
/*
* Add "known" possibilities here.
*/
switch (flags & (BT_FROM_EXCEPTION|BT_FROM_CALLFRAME))
{
case BT_FROM_EXCEPTION:
if (STREQ(closest_symbol(req->prevpc), "read_lock") ||
STREQ(closest_symbol(req->ra), "do_select") ||
STREQ(closest_symbol(req->ra), "schedule")) {
stkp = (ulong *)req->sp;
for (stkp++; INSTACK(stkp, bt); stkp++) {
value = GET_STACK_ULONG(stkp);
if (found_ra) {
if (is_kernel_text_offset(value)) {
found_sp = (ulong)stkp;
break;
}
continue;
}
if (value == req->ra)
found_ra = value;
}
}
break;
case BT_FROM_CALLFRAME:
if (STREQ(closest_symbol(req->ra), "sys_read")) {
value = GET_STACK_ULONG(req->prevsp - 32);
if (STREQ(closest_symbol(value), "entSys")) {
found_ra = value;
found_sp = req->prevsp - 32;
}
} else if (STREQ(closest_symbol(req->ra), "exit_autofs4_fs")) {
stkp = (ulong *)req->sp;
for (stkp++; INSTACK(stkp, bt); stkp++) {
value = GET_STACK_ULONG(stkp);
if (found_ra && (value != found_ra)) {
if (is_kernel_text_offset(value)) {
found_sp = (ulong)stkp;
break;
}
continue;
}
if (is_kernel_text_offset(value))
found_ra = value;
}
}
break;
default:
if (req->hookp &&
STREQ(closest_symbol(req->prevpc), "filemap_nopage") &&
!STREQ(closest_symbol(req->hookp->eip), "do_no_page")) {
found_ra = found_sp = 0;
stkp = (ulong *)req->prevsp;
for (stkp++; INSTACK(stkp, bt); stkp++) {
value = GET_STACK_ULONG(stkp);
if (found_ra && (value != found_ra)) {
if (is_kernel_text_offset(value)) {
found_sp = (ulong)stkp;
break;
}
continue;
}
if (is_kernel_text_offset(value) &&
STREQ(closest_symbol(value), "do_no_page"))
found_ra = value;
}
if (found_ra && found_sp) {
req->hookp->eip = found_ra;
req->hookp->esp = found_sp;
return TRUE;
}
}
if (req->hookp) {
found_ra = req->hookp->eip;
found_sp = req->hookp->esp;
}
break;
}
if (found_ra && found_sp) {
looks_good = FALSE;
hook.esp = found_sp;
hook.eip = found_ra;
if (CRASHDEBUG(1))
fprintf(pc->saved_fp,
"----- RESYNC SPECULATE START -----\n");
open_tmpfile();
btloc = &bt_info;
BZERO(btloc, sizeof(struct bt_info));
btloc->task = req->task;
btloc->tc = bt->tc;
btloc->stackbase = bt->stackbase;
btloc->stacktop = bt->stacktop;
btloc->flags = BT_SPECULATE;
btloc->hp = &hook;
back_trace(btloc);
rewind(pc->tmpfile);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
if (CRASHDEBUG(1))
fprintf(pc->saved_fp, "%s", buf);
if (strstr(buf, "NOTE: cannot resolve")) {
looks_good = FALSE;
break;
}
if (strstr(buf, "ALPHA EXCEPTION FRAME")) {
looks_good = TRUE;
break;
}
if (kernel_thread) {
if (strstr(buf, " kernel_thread ") ||
strstr(buf, " __kernel_thread ") ||
strstr(buf, " start_kernel ") ||
strstr(buf, " smp_callin ")) {
looks_good = TRUE;
break;
}
}
}
close_tmpfile();
if (CRASHDEBUG(1))
fprintf(pc->saved_fp,
"----- RESYNC SPECULATE DONE ------\n");
if (looks_good) {
req->pc = found_ra;
req->sp = found_sp;
return TRUE;
}
}
return FALSE;
}
/*
* 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.
*/
static int
alpha_uvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose)
{
ulong mm;
ulong *pgd;
ulong *page_dir;
ulong *page_middle;
ulong *page_table;
ulong pgd_pte;
ulong pmd_pte;
ulong pte;
if (!tc)
error(FATAL, "current context invalid\n");
*paddr = 0;
if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) {
pgd = (ulong *)machdep->get_task_pgd(tc->task);
} else {
if (!tc->mm_struct)
pgd = (ulong *)machdep->get_task_pgd(tc->task);
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) & (PTRS_PER_PAGE - 1));
FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE());
pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir));
if (verbose)
fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte);
if (!(pgd_pte & _PAGE_VALID))
goto no_upage;
page_middle = (ulong *)
(PTOV((pgd_pte & _PFN_MASK) >> (32-PAGESHIFT()))) +
((vaddr >> PMD_SHIFT) & (PTRS_PER_PAGE - 1));
FILL_PMD(PAGEBASE(page_middle), KVADDR, PAGESIZE());
pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle));
if (verbose)
fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte);
if (!(pmd_pte & _PAGE_VALID))
goto no_upage;
page_table = (ulong *)
(PTOV((pmd_pte & _PFN_MASK) >> (32-PAGESHIFT()))) +
(BTOP(vaddr) & (PTRS_PER_PAGE - 1));
FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE());
pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table));
if (verbose)
fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte);
if (!(pte & (_PAGE_VALID))) {
*paddr = pte;
if (pte && verbose) {
fprintf(fp, "\n");
alpha_translate_pte(pte, 0, 0);
}
goto no_upage;
}
*paddr = ((pte & _PFN_MASK) >> (32-PAGESHIFT())) + PAGEOFFSET(vaddr);
if (verbose) {
fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr));
alpha_translate_pte(pte, 0, 0);
}
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
alpha_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;
if (!IS_KVADDR(kvaddr))
return FALSE;
if (!vt->vmalloc_start) { /* presume KSEG this early */
*paddr = VTOP(kvaddr);
return TRUE;
}
if (!IS_VMALLOC_ADDR(kvaddr)) {
*paddr = VTOP(kvaddr);
return TRUE;
}
pgd = (ulong *)vt->kernel_pgd[0];
if (verbose)
fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd);
page_dir = pgd + ((kvaddr >> PGDIR_SHIFT) & (PTRS_PER_PAGE - 1));
FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE());
pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir));
if (verbose)
fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte);
if (!(pgd_pte & _PAGE_VALID))
goto no_kpage;
page_middle = (ulong *)
(PTOV((pgd_pte & _PFN_MASK) >> (32-PAGESHIFT()))) +
((kvaddr >> PMD_SHIFT) & (PTRS_PER_PAGE - 1));
FILL_PMD(PAGEBASE(page_middle), KVADDR, PAGESIZE());
pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle));
if (verbose)
fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte);
if (!(pmd_pte & _PAGE_VALID))
goto no_kpage;
page_table = (ulong *)
(PTOV((pmd_pte & _PFN_MASK) >> (32-PAGESHIFT()))) +
(BTOP(kvaddr) & (PTRS_PER_PAGE - 1));
FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE());
pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table));
if (verbose)
fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte);
if (!(pte & (_PAGE_VALID))) {
if (pte && verbose) {
fprintf(fp, "\n");
alpha_translate_pte(pte, 0, 0);
}
goto no_kpage;
}
*paddr = ((pte & _PFN_MASK) >> (32-PAGESHIFT())) + PAGEOFFSET(kvaddr);
if (verbose) {
fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr));
alpha_translate_pte(pte, 0, 0);
}
return TRUE;
no_kpage:
return FALSE;
}
/*
* Get the relevant page directory pointer from a task structure.
*/
static ulong
alpha_get_task_pgd(ulong task)
{
long offset;
ulong ptbr;
offset = OFFSET_OPTION(task_struct_thread, task_struct_tss);
offset += OFFSET(thread_struct_ptbr);
readmem(task + offset, KVADDR, &ptbr,
sizeof(ulong), "task thread ptbr", FAULT_ON_ERROR);
return(PTOV(PTOB(ptbr)));
}
/*
* Calculate and return the speed of the processor.
*/
static ulong
alpha_processor_speed(void)
{
ulong hwrpb;
long offset;
long cycle_freq;
ulong mhz;
if (machdep->mhz)
return machdep->mhz;
mhz = 0;
get_symbol_data("hwrpb", sizeof(void *), &hwrpb);
offset = OFFSET(hwrpb_struct_cycle_freq);
if (!hwrpb || (offset == -1) ||
!readmem(hwrpb+offset, KVADDR, &cycle_freq,
sizeof(ulong), "hwrpb cycle_freq", RETURN_ON_ERROR))
return (machdep->mhz = mhz);
mhz = cycle_freq/1000000;
return (machdep->mhz = mhz);
}
void
alpha_dump_machdep_table(ulong arg)
{
int others;
others = 0;
fprintf(fp, " flags: %lx (", machdep->flags);
if (machdep->flags & HWRESET)
fprintf(fp, "%sHWRESET", 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: %ld (0x%lx)\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: alpha_eframe_search()\n");
fprintf(fp, " back_trace: alpha_back_trace_cmd()\n");
fprintf(fp, " processor_speed: alpha_processor_speed()\n");
fprintf(fp, " uvtop: alpha_uvtop()\n");
fprintf(fp, " kvtop: alpha_uvtop()\n");
fprintf(fp, " get_task_pgd: alpha_get_task_pgd()\n");
if (machdep->dump_irq == generic_dump_irq)
fprintf(fp, " dump_irq: generic_dump_irq()\n");
else
fprintf(fp, " dump_irq: alpha_dump_irq()\n");
fprintf(fp, " get_stack_frame: alpha_get_stack_frame()\n");
fprintf(fp, " get_stackbase: generic_get_stackbase()\n");
fprintf(fp, " get_stacktop: generic_get_stacktop()\n");
fprintf(fp, " translate_pte: alpha_translate_pte()\n");
fprintf(fp, " memory_size: alpha_get_memory_size()\n");
fprintf(fp, " vmalloc_start: alpha_get_vmalloc_start()\n");
fprintf(fp, " is_task_addr: alpha_is_task_addr()\n");
fprintf(fp, " verify_symbol: alpha_verify_symbol()\n");
fprintf(fp, " dis_filter: alpha_dis_filter()\n");
fprintf(fp, " cmd_mach: alpha_cmd_mach()\n");
fprintf(fp, " get_smp_cpus: alpha_get_smp_cpus()\n");
fprintf(fp, " is_kvaddr: generic_is_kvaddr()\n");
fprintf(fp, " is_uvaddr: generic_is_uvaddr()\n");
fprintf(fp, " verify_paddr: generic_verify_paddr()\n");
fprintf(fp, " init_kernel_pgd: NULL\n");
fprintf(fp, " value_to_symbol: generic_machdep_value_to_symbol()\n");
fprintf(fp, " line_number_hooks: alpha_line_number_hooks\n");
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, " machspec: %lx\n", (ulong)machdep->machspec);
}
/*
* Fix up jsr's to show the right target.
*
* If a value is passed with no buf, then cmd_dis is fishing for whether
* the GP can be calculated from the first couple of instructions of the
* target routine:
*
* 0xfffffc0000349fa0 <sys_read>: ldah gp,35(t12)
* 0xfffffc0000349fa4 <sys_read+4>: lda gp,6216(gp)
*
* If a buf pointer is passed, then check whether the t12 register
* is being set up as an offset from gp, then calculate the target address:
*
* 0xfffffc000042c364 <start_tty+228>: ldq t12,-29336(gp)
* 0xfffffc000042c368 <start_tty+232>:
* jsr ra,(t12),0xfffffc0000429dc0 <decr_console+96>
*
* If the next instruction is a jsr ra,(t12), then correct the bracketed
* target address translation.
*
*/
#define LDAH_GP_T12 (0x27bb0000)
#define LDA_GP_GP (0x23bd0000)
#define LDQ_T12_GP (0xa77d0000)
#define JSR_RA_T12 (0x6b5b0000)
#define OPCODE_OPERAND_MASK (0xffff0000)
#define OPCODE_MEM_DISP_MASK (0x0000ffff)
static struct instruction_data {
uint inst[2];
short mem_disp[2];
ulong gp;
ulong target;
char *curfunc;
} instruction_data = { {0} };
static int
alpha_dis_filter(ulong vaddr, char *buf, unsigned int output_radix)
{
struct syment *sp;
struct instruction_data *id;
char buf2[BUFSIZE], *p1;
id = &instruction_data;
if (!buf) {
BZERO(id, sizeof(struct instruction_data));
if (!(sp = value_search(vaddr, NULL)))
return FALSE;
readmem(sp->value, KVADDR, &id->inst[0],
sizeof(uint) * 2, "two instructions", FAULT_ON_ERROR);
if (((id->inst[0] & OPCODE_OPERAND_MASK) == LDAH_GP_T12) &&
((id->inst[1] & OPCODE_OPERAND_MASK) == LDA_GP_GP)) {
id->mem_disp[0] = (short)(id->inst[0] &
OPCODE_MEM_DISP_MASK);
id->mem_disp[1] = (short)(id->inst[1] &
OPCODE_MEM_DISP_MASK);
id->gp = sp->value + (65536*id->mem_disp[0]) +
id->mem_disp[1];
id->curfunc = sp->name;
if (CRASHDEBUG(1))
console("%s: ldah(%d) and lda(%d) gp: %lx\n",
id->curfunc,
id->mem_disp[0], id->mem_disp[1],
id->gp);
return TRUE;
}
/* send all lines through the generic */
return TRUE; /* dis_address_translation() filter */
}
dis_address_translation(vaddr, buf, output_radix);
if (!id->gp || !(sp = value_search(vaddr, NULL)) ||
!STREQ(id->curfunc, sp->name)) {
BZERO(id, sizeof(struct instruction_data));
return FALSE;
}
readmem(vaddr, KVADDR, &id->inst[0],
sizeof(uint), "one instruction", FAULT_ON_ERROR);
if ((id->inst[0] & OPCODE_OPERAND_MASK) == JSR_RA_T12) {
if (!id->target || !strstr(buf, "jsr\tra,(t12)") ||
!strstr(buf, "<"))
return FALSE;
p1 = strstr(strstr(buf, "jsr"), "0x");
sprintf(p1, "0x%lx <%s>%s",
id->target,
value_to_symstr(id->target, buf2, output_radix),
CRASHDEBUG(1) ? " [PATCHED]\n" : "\n");
return TRUE;
}
if ((id->inst[0] & OPCODE_OPERAND_MASK) == LDQ_T12_GP) {
id->mem_disp[0] = (short)(id->inst[0] & OPCODE_MEM_DISP_MASK);
readmem(id->gp + id->mem_disp[0], KVADDR, &id->target,
sizeof(ulong), "jsr target", FAULT_ON_ERROR);
} else
id->target = 0;
return TRUE;
}
/*
* For some reason gdb can go off into the weeds translating text addresses,
* so this routine both fixes the references as well as imposing the current
* output radix on the translations.
*/
static void
dis_address_translation(ulong vaddr, char *inbuf, unsigned int output_radix)
{
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char *colon, *p1;
int argc;
char *argv[MAXARGS];
ulong value;
console("IN: %s", inbuf);
colon = strstr(inbuf, ":");
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) && (*p1 != ','))
p1--;
if (!STRNEQ(p1, ",0x"))
return;
p1++;
if (!extract_hex(p1, &value, NULLCHAR, TRUE))
return;
sprintf(buf1, "0x%lx <%s>\n", value,
value_to_symstr(value, buf2, output_radix));
sprintf(p1, "%s", buf1);
}
console(" %s", inbuf);
}
/*
* If we're generically-inclined, call generic_dump_irq(). Otherwise
* dump the IRQ table the old-fashioned way.
*/
static void
alpha_dump_irq(int irq)
{
ulong action;
ulong value;
char *arglist[MAXARGS];
int argc, others;
char buf[BUFSIZE];
if (symbol_exists("irq_desc")) {
machdep->dump_irq = generic_dump_irq;
return(generic_dump_irq(irq));
}
action = symbol_value("irq_action") + (sizeof(void *) * irq);
readmem(action, KVADDR, &action,
sizeof(void *), "irq_action pointer", FAULT_ON_ERROR);
if (!action) {
fprintf(fp, " IRQ: %d\n", irq);
fprintf(fp, "handler:\n");
fprintf(fp, " flags: \n");
fprintf(fp, " mask: \n");
fprintf(fp, " name: \n");
fprintf(fp, " dev_id: \n");
fprintf(fp, " next: \n\n");
return;
}
fprintf(fp, " IRQ: %d\n", irq);
open_tmpfile();
do_linked_action:
dump_struct("irqaction", action, RADIX(16));
action = 0;
rewind(pc->tmpfile);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
strip_comma(buf);
argc = parse_line(buf, arglist);
if (STREQ(arglist[0], "struct") || STREQ(buf, "};"))
continue;
if (STREQ(arglist[0], "handler")) {
fprintf(pc->saved_fp, "handler: %s ",
strip_hex(arglist[2]));
if (argc == 4)
fprintf(pc->saved_fp, "%s", arglist[3]);
fprintf(pc->saved_fp, "\n");
}
if (STREQ(arglist[0], "flags")) {
value = htol(strip_comma(arglist[2]),
FAULT_ON_ERROR, NULL);
fprintf(pc->saved_fp,
" flags: %lx ", value);
if (value) {
others = 0;
fprintf(pc->saved_fp, "(");
if (value & SA_INTERRUPT)
fprintf(pc->saved_fp,
"%sSA_INTERRUPT",
others++ ? "|" : "");
if (value & SA_PROBE)
fprintf(pc->saved_fp,
"%sSA_PROBE",
others++ ? "|" : "");
if (value & SA_SAMPLE_RANDOM)
fprintf(pc->saved_fp,
"%sSA_SAMPLE_RANDOM",
others++ ? "|" : "");
if (value & SA_SHIRQ)
fprintf(pc->saved_fp,
"%sSA_SHIRQ",
others++ ? "|" : "");
fprintf(pc->saved_fp, ")");
if (value & ~ACTION_FLAGS) {
fprintf(pc->saved_fp,
" (bits %lx not translated)",
value & ~ACTION_FLAGS);
}
}
fprintf(pc->saved_fp, "\n");
}
if (STREQ(arglist[0], "mask")) {
value = htol(strip_comma(arglist[2]),
FAULT_ON_ERROR, NULL);
fprintf(pc->saved_fp,
" mask: %lx\n", value);
}
if (STREQ(arglist[0], "name")) {
fprintf(pc->saved_fp, " name: %s ",
strip_hex(arglist[2]));
if (argc == 4)
fprintf(pc->saved_fp, "\"%s\"", arglist[3]);
fprintf(pc->saved_fp, "\n");
}
if (STREQ(arglist[0], "dev_id")) {
value = htol(strip_comma(arglist[2]),
FAULT_ON_ERROR, NULL);
fprintf(pc->saved_fp,
" dev_id: %lx\n", value);
}
if (STREQ(arglist[0], "next")) {
value = htol(strip_comma(arglist[2]),
FAULT_ON_ERROR, NULL);
fprintf(pc->saved_fp,
" next: %s\n",
strip_hex(arglist[2]));
if (value)
action = value;
}
}
close_tmpfile();
fprintf(fp, "\n");
if (action)
goto do_linked_action;
}
/*
* Get a stack frame combination of pc and ra from the most relevent spot.
*/
static void
alpha_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
{
struct syment *sp;
ulong ksp;
ulong ip;
if (pcp) {
if (DUMPFILE() && is_panic_thread(bt->task)) {
sp = next_symbol("crash_save_current_state", NULL);
if (HWRESET_TASK(bt->task))
ip = get_percpu_data(0, GET_HALT_PC, 0);
else if (sp)
ip = sp->value - 4;
else
ip = symbol_value("crash_save_current_state")
+ 16;
} else
get_alpha_frame(bt, &ip, NULL);
*pcp = ip;
}
if (spp) {
ip = 0;
if (!get_panic_ksp(bt, &ksp))
get_alpha_frame(bt,
HWRESET_TASK(bt->task) ? &ip : NULL, &ksp);
if (!INSTACK(ksp, bt))
error(FATAL,
"cannot determine starting stack address\n",
bt->task);
*spp = ksp;
if (ip)
*pcp = ip;
}
}
/*
* Do the work formerly done by alpha_get_sp() and alpha_get_pc().
*/
static void
get_alpha_frame(struct bt_info *bt, ulong *getpc, ulong *getsp)
{
int i;
ulong ip;
ulong r26;
ulong ksp, sp;
ulong *spp;
ulong percpu_ra;
ulong percpu_pv;
struct percpu_data percpu_data;
char buf[BUFSIZE];
ulong task;
ulong *stack;
task = bt->task;
stack = (ulong *)bt->stackbuf;
if (tt->flags & THREAD_INFO) { /* pcb.ksp is 1st word in thread_info */
readmem(bt->tc->thread_info, KVADDR, &ksp, sizeof(ulong),
"thread_info pcb ksp", FAULT_ON_ERROR);
sp = ksp;
} else if (VALID_MEMBER(task_struct_tss_ksp))
ksp = sp = stack[OFFSET(task_struct_tss_ksp)/sizeof(long)];
else
ksp = sp = stack[OFFSET(task_struct_thread_ksp)/sizeof(long)];
ip = 0;
percpu_ra = percpu_pv = 0;
spp = &stack[(sp - task)/sizeof(long)];
if (DUMPFILE() && getsp) {
if (HWRESET_TASK(task)) {
if (INSTACK(sp, bt)) {
*getsp = sp;
return;
} else {
get_percpu_data(0, 0, &percpu_data);
percpu_ra = percpu_data.halt_ra;
percpu_pv = percpu_data.halt_pv;
spp = &stack[roundup(SIZE(task_struct),
sizeof(ulong)) / sizeof(ulong)];
}
}
if (!percpu_ra && (STREQ(closest_symbol(*spp), "panic") ||
STREQ(closest_symbol(*spp), "handle_ipi"))) {
*getsp = sp;
return;
}
}
percpu_retry:
if (CRASHDEBUG(1) && percpu_ra) {
fprintf(fp, "get_alpha_frame: look for %lx (%s)\n",
percpu_ra, value_to_symstr(percpu_ra, buf, 0));
}
for (i = 0, spp++; spp < &stack[LONGS_PER_STACK]; spp++,i++) {
if (CRASHDEBUG(1) && (percpu_ra || percpu_pv) &&
is_kernel_text(*spp)) {
fprintf(fp, "%lx: %lx (%s)\n",
((ulong)spp - (ulong)stack) + task,
*spp, value_to_symstr(*spp, buf, 0));
}
if (percpu_ra) {
if (*spp == percpu_ra) {
*getsp = ((ulong)spp - (ulong)stack) + task;
return;
}
continue;
} else if (percpu_pv) {
if (*spp == percpu_pv) {
*getsp = ((ulong)spp - (ulong)stack) + task;
if (getpc)
*getpc = percpu_pv;
return;
}
continue;
}
if (!INSTACK(*spp, bt))
continue;
if (is_kernel_text(*(spp+1))) {
sp = *spp;
ip = *(spp+1);
break;
}
}
if (percpu_ra) {
percpu_ra = 0;
error(INFO,
"cannot find return address (percpu_ra) in HARDWARE RESET stack\n");
error(INFO,
"looking for procedure address (percpu_pv) in HARDWARE RESET stack\n");
if (CRASHDEBUG(1)) {
fprintf(fp, "get_alpha_frame: look for %lx (%s)\n",
percpu_pv, value_to_symstr(percpu_pv, buf, 0));
}
spp = &stack[roundup(SIZE(task_struct),
sizeof(ulong)) / sizeof(ulong)];
goto percpu_retry;
}
if (percpu_pv) {
error(INFO,
"cannot find procedure address (percpu_pv) in HARDWARE RESET stack\n");
}
/*
* Check for a forked task that has not yet run in user space.
*/
if (!ip) {
if (INSTACK(ksp + OFFSET(switch_stack_r26), bt)) {
readmem(ksp + OFFSET(switch_stack_r26), KVADDR,
&r26, sizeof(ulong),
"ret_from_smp_fork check", FAULT_ON_ERROR);
if (STREQ(closest_symbol(r26), "ret_from_smp_fork") ||
STREQ(closest_symbol(r26), "ret_from_smpfork")) {
ip = r26;
sp = ksp;
}
}
}
if (getsp)
*getsp = sp;
if (getpc)
*getpc = ip;
}
/*
* Fill the percpu_data structure with information from the
* hwrpb/percpu_data structures for a given CPU. If requested,
* return one of the specified entries.
*/
static ulong
get_percpu_data(int cpu, ulong flag, struct percpu_data *pd)
{
ulong hwrpb, halt_ra, halt_PC, halt_pv;
unsigned long processor_offset, processor_size;
get_symbol_data("hwrpb", sizeof(void *), &hwrpb);
readmem(hwrpb+OFFSET(hwrpb_struct_processor_offset), KVADDR,
&processor_offset, sizeof(ulong),
"hwrpb processor_offset", FAULT_ON_ERROR);
readmem(hwrpb+OFFSET(hwrpb_struct_processor_size), KVADDR,
&processor_size, sizeof(ulong),
"hwrpb processor_size", FAULT_ON_ERROR);
readmem(hwrpb + processor_offset + (cpu * processor_size) +
OFFSET(percpu_struct_halt_PC),
KVADDR, &halt_PC, sizeof(ulong),
"percpu halt_PC", FAULT_ON_ERROR);
readmem(hwrpb + processor_offset + (cpu * processor_size) +
OFFSET(percpu_struct_halt_ra),
KVADDR, &halt_ra, sizeof(ulong),
"percpu halt_ra", FAULT_ON_ERROR);
readmem(hwrpb + processor_offset + (cpu * processor_size) +
OFFSET(percpu_struct_halt_pv),
KVADDR, &halt_pv, sizeof(ulong),
"percpu halt_pv", FAULT_ON_ERROR);
if (pd) {
pd->halt_PC = halt_PC;
pd->halt_ra = halt_ra;
pd->halt_pv = halt_pv;
}
switch (flag)
{
case GET_HALT_PC:
return halt_PC;
case GET_HALT_RA:
return halt_ra;
case GET_HALT_PV:
return halt_pv;
default:
return 0;
}
}
/*
* Translate a PTE, returning TRUE if the page is _PAGE_VALID or _PAGE_PRESENT,
* whichever is appropriate for the machine type. If a physaddr pointer is
* passed in, don't print anything.
*/
static int
alpha_translate_pte(ulong pte, void *physaddr, ulonglong unused)
{
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];
physaddr_t paddr;
paddr = PTOB(pte >> 32);
page_present = (pte & _PAGE_VALID);
if (physaddr) {
*((ulong *)physaddr) = paddr;
return page_present;
}
sprintf(ptebuf, "%lx", pte);
len1 = MAX(strlen(ptebuf), strlen("PTE"));
fprintf(fp, "%s ", mkstring(buf, len1, CENTER|LJUST, "PTE"));
if (!page_present && pte) {
swap_location(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_VALID)
fprintf(fp, "%sVALID", others++ ? "|" : "");
if (pte & _PAGE_FOR)
fprintf(fp, "%sFOR", others++ ? "|" : "");
if (pte & _PAGE_FOW)
fprintf(fp, "%sFOW", others++ ? "|" : "");
if (pte & _PAGE_FOE)
fprintf(fp, "%sFOE", others++ ? "|" : "");
if (pte & _PAGE_ASM)
fprintf(fp, "%sASM", others++ ? "|" : "");
if (pte & _PAGE_KRE)
fprintf(fp, "%sKRE", others++ ? "|" : "");
if (pte & _PAGE_URE)
fprintf(fp, "%sURE", others++ ? "|" : "");
if (pte & _PAGE_KWE)
fprintf(fp, "%sKWE", others++ ? "|" : "");
if (pte & _PAGE_UWE)
fprintf(fp, "%sUWE", others++ ? "|" : "");
if (pte & _PAGE_DIRTY)
fprintf(fp, "%sDIRTY", others++ ? "|" : "");
if (pte & _PAGE_ACCESSED)
fprintf(fp, "%sACCESSED", others++ ? "|" : "");
} else {
fprintf(fp, "no mapping");
}
fprintf(fp, ")\n");
return page_present;
}
/*
* This is currently not machine-dependent, but eventually I'd prefer to use
* the HWPCB for the real physical memory size.
*/
static uint64_t
alpha_memory_size(void)
{
return (generic_memory_size());
}
/*
* Determine where vmalloc'd memory starts.
*/
static ulong
alpha_vmalloc_start(void)
{
return VMALLOC_START;
}
/*
* ALPHA tasks are all stacksize-aligned.
*/
static int
alpha_is_task_addr(ulong task)
{
return (IS_KVADDR(task) && (ALIGNED_STACK_OFFSET(task) == 0));
}
/*
* Keep or reject a symbol from the kernel namelist.
*/
int
alpha_verify_symbol(const char *name, ulong value, char type)
{
if (CRASHDEBUG(8) && name && strlen(name))
fprintf(fp, "%016lx %s\n", value, name);
return (name && strlen(name) && (value > MIN_SYMBOL_VALUE));
}
/*
* Override smp_num_cpus if possible and necessary.
*/
int
alpha_get_smp_cpus(void)
{
int cpus;
if ((cpus = get_cpus_online()))
return cpus;
else
return kt->cpus;
}
/*
* Machine dependent command.
*/
void
alpha_cmd_mach(void)
{
int c, cflag;
unsigned int radix;
cflag = radix = 0;
while ((c = getopt(argcnt, args, "cxd")) != EOF) {
switch(c)
{
case 'c':
cflag++;
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)
display_hwrpb(radix);
else
alpha_display_machine_stats();
}
/*
* "mach" command output.
*/
static void
alpha_display_machine_stats(void)
{
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);
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());
}
/*
* Display the hwrpb_struct and each percpu_struct.
*/
static void
display_hwrpb(unsigned int radix)
{
int cpu;
ulong hwrpb, percpu;
ulong processor_offset, processor_size;
get_symbol_data("hwrpb", sizeof(void *), &hwrpb);
readmem(hwrpb+OFFSET(hwrpb_struct_processor_offset), KVADDR,
&processor_offset, sizeof(ulong),
"hwrpb processor_offset", FAULT_ON_ERROR);
readmem(hwrpb+OFFSET(hwrpb_struct_processor_size), KVADDR,
&processor_size, sizeof(ulong),
"hwrpb processor_size", FAULT_ON_ERROR);
fprintf(fp, "HWRPB:\n");
dump_struct("hwrpb_struct", hwrpb, radix);
for (cpu = 0; cpu < kt->cpus; cpu++) {
fprintf(fp, "\nCPU %d:\n", cpu);
percpu = hwrpb + processor_offset + (processor_size * cpu);
dump_struct("percpu_struct", percpu, radix);
}
}
/*
* Perform any leftover pre-prompt machine-specific initialization tasks here.
*/
static void
alpha_post_init(void)
{
modify_signame(7, "SIGEMT", NULL);
modify_signame(10, "SIGBUS", NULL);
modify_signame(12, "SIGSYS", NULL);
modify_signame(16, "SIGURG", NULL);
modify_signame(17, "SIGSTOP", NULL);
modify_signame(18, "SIGTSTP", NULL);
modify_signame(19, "SIGCONT", NULL);
modify_signame(20, "SIGCHLD", NULL);
modify_signame(23, "SIGIO", "SIGPOLL");
modify_signame(29, "SIGINFO", "SIGPWR");
modify_signame(30, "SIGUSR1", NULL);
modify_signame(31, "SIGUSR2", NULL);
}
#endif /* ALPHA */