crash/kernel.c
Dave Anderson 4119e19053 Fix for the DATE display in the initial system banner and by the
"sys" command to account for the Linux 3.17 change that moved
the "timekeeper" symbol and structure into a containing tk_core
structure; the "shadow_timekeeper" timekeeper will be used as an
alternative.  Without the patch, the DATE shows something within
a few hours of the Linux epoch, such as "Wed Dec 31 18:00:00 1969".
(kmcmartin@redhat.com)
2015-05-19 17:09:06 -04:00

9593 lines
266 KiB
C

/* kernel.c - core analysis suite
*
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
* Copyright (C) 2002-2015 David Anderson
* Copyright (C) 2002-2015 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.
*/
#include "defs.h"
#include "xen_hyper_defs.h"
#include <elf.h>
#include <libgen.h>
#include <ctype.h>
static void do_module_cmd(ulong, char *, ulong, char *, char *);
static void show_module_taint(void);
static char *find_module_objfile(char *, char *, char *);
static char *module_objfile_search(char *, char *, char *);
static char *get_loadavg(char *);
static void get_lkcd_regs(struct bt_info *, ulong *, ulong *);
static void dump_sys_call_table(char *, int);
static int get_NR_syscalls(int *);
static ulong get_irq_desc_addr(int);
static void display_cpu_affinity(ulong *);
static void display_bh_1(void);
static void display_bh_2(void);
static void display_bh_3(void);
static void display_bh_4(void);
static void dump_hrtimer_data(void);
static void dump_hrtimer_clock_base(const void *, const int);
static void dump_hrtimer_base(const void *, const int);
static void dump_active_timers(const void *, ulonglong);
static int get_expires_len(const int, const ulong *, const int);
static void print_timer(const void *);
static ulonglong ktime_to_ns(const void *);
static void dump_timer_data(void);
static void dump_timer_data_tvec_bases_v1(void);
static void dump_timer_data_tvec_bases_v2(void);
struct tv_range;
static void init_tv_ranges(struct tv_range *, int, int, int);
static int do_timer_list(ulong,int, ulong *, void *,ulong *,struct tv_range *);
static int compare_timer_data(const void *, const void *);
static void panic_this_kernel(void);
static void dump_waitq(ulong, char *);
static void reinit_modules(void);
static int verify_modules(void);
static void verify_namelist(void);
static char *debug_kernel_version(char *);
static int restore_stack(struct bt_info *);
static ulong __xen_m2p(ulonglong, ulong);
static ulong __xen_pvops_m2p_l2(ulonglong, ulong);
static ulong __xen_pvops_m2p_l3(ulonglong, ulong);
static int search_mapping_page(ulong, ulong *, ulong *, ulong *);
static void read_in_kernel_config_err(int, char *);
static void BUG_bytes_init(void);
static int BUG_x86(void);
static int BUG_x86_64(void);
static void cpu_maps_init(void);
static void get_xtime(struct timespec *);
static char *log_from_idx(uint32_t, char *);
static uint32_t log_next(uint32_t, char *);
static void dump_log_entry(char *, int);
static void dump_variable_length_record_log(int);
static void hypervisor_init(void);
static void dump_log_legacy(void);
static void dump_variable_length_record(void);
static int is_livepatch(void);
static void show_kernel_taints(char *, int);
/*
* Gather a few kernel basics.
*/
void
kernel_init()
{
int i, c;
char *p1, *p2, buf[BUFSIZE];
struct syment *sp1, *sp2;
char *rqstruct;
char *rq_timestamp_name = NULL;
char *irq_desc_type_name;
ulong pv_init_ops;
if (pc->flags & KERNEL_DEBUG_QUERY)
return;
if (!(kt->cpu_flags = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc cpu_flags array");
cpu_maps_init();
kt->stext = symbol_value("_stext");
kt->etext = symbol_value("_etext");
get_text_init_space();
if (symbol_exists("__init_begin")) {
kt->init_begin = symbol_value("__init_begin");
kt->init_end = symbol_value("__init_end");
}
if (symbol_exists("_end"))
kt->end = symbol_value("_end");
else
kt->end = highest_bss_symbol();
/*
* For the traditional (non-pv_ops) Xen architecture, default to writable
* page tables unless:
*
* (1) it's an "xm save" CANONICAL_PAGE_TABLES dumpfile, or
* (2) the --shadow_page_tables option was explicitly entered.
*
* But if the "phys_to_maching_mapping" array does not exist, and
* it's not an "xm save" canonical dumpfile, then we have no choice
* but to presume shadow page tables.
*/
if (!PVOPS() && symbol_exists("xen_start_info")) {
kt->flags |= ARCH_XEN;
if (!(kt->xen_flags & (SHADOW_PAGE_TABLES|CANONICAL_PAGE_TABLES)))
kt->xen_flags |= WRITABLE_PAGE_TABLES;
if (symbol_exists("phys_to_machine_mapping"))
get_symbol_data("phys_to_machine_mapping", sizeof(ulong),
&kt->phys_to_machine_mapping);
else if (!(kt->xen_flags & CANONICAL_PAGE_TABLES)) {
kt->xen_flags &= ~WRITABLE_PAGE_TABLES;
kt->xen_flags |= SHADOW_PAGE_TABLES;
}
if (machine_type("X86"))
get_symbol_data("max_pfn", sizeof(ulong), &kt->p2m_table_size);
if (machine_type("X86_64")) {
/*
* kernel version < 2.6.27 => end_pfn
* kernel version >= 2.6.27 => max_pfn
*/
if (!try_get_symbol_data("end_pfn", sizeof(ulong), &kt->p2m_table_size))
get_symbol_data("max_pfn", sizeof(ulong), &kt->p2m_table_size);
}
if ((kt->m2p_page = (char *)malloc(PAGESIZE())) == NULL)
error(FATAL, "cannot malloc m2p page.");
}
if (PVOPS() && readmem(symbol_value("pv_init_ops"), KVADDR, &pv_init_ops,
sizeof(void *), "pv_init_ops", RETURN_ON_ERROR) &&
(p1 = value_symbol(pv_init_ops)) &&
STREQ(p1, "xen_patch")) {
kt->flags |= ARCH_XEN | ARCH_PVOPS_XEN;
kt->xen_flags |= WRITABLE_PAGE_TABLES;
if (machine_type("X86"))
get_symbol_data("max_pfn", sizeof(ulong), &kt->p2m_table_size);
if (machine_type("X86_64")) {
if (!try_get_symbol_data("end_pfn", sizeof(ulong), &kt->p2m_table_size))
get_symbol_data("max_pfn", sizeof(ulong), &kt->p2m_table_size);
}
if ((kt->m2p_page = (char *)malloc(PAGESIZE())) == NULL)
error(FATAL, "cannot malloc m2p page.");
if (symbol_exists("p2m_mid_missing")) {
kt->pvops_xen.p2m_top_entries = XEN_P2M_TOP_PER_PAGE;
get_symbol_data("p2m_top", sizeof(ulong),
&kt->pvops_xen.p2m_top);
get_symbol_data("p2m_mid_missing", sizeof(ulong),
&kt->pvops_xen.p2m_mid_missing);
get_symbol_data("p2m_missing", sizeof(ulong),
&kt->pvops_xen.p2m_missing);
} else {
kt->pvops_xen.p2m_top_entries = get_array_length("p2m_top", NULL, 0);
kt->pvops_xen.p2m_top = symbol_value("p2m_top");
kt->pvops_xen.p2m_missing = symbol_value("p2m_missing");
}
}
if (symbol_exists("smp_num_cpus")) {
kt->flags |= SMP;
get_symbol_data("smp_num_cpus", sizeof(int), &kt->cpus);
if (kt->cpus < 1 || kt->cpus > NR_CPUS)
error(WARNING,
"invalid value: smp_num_cpus: %d\n",
kt->cpus);
} else if (symbol_exists("__per_cpu_offset")) {
kt->flags |= SMP;
kt->cpus = 1;
} else
kt->cpus = 1;
if ((sp1 = symbol_search("__per_cpu_start")) &&
(sp2 = symbol_search("__per_cpu_end")) &&
(sp1->type == 'A' || sp1->type == 'D') &&
(sp2->type == 'A' || sp2->type == 'D') &&
(sp2->value > sp1->value))
kt->flags |= SMP|PER_CPU_OFF;
MEMBER_OFFSET_INIT(timekeeper_xtime, "timekeeper", "xtime");
MEMBER_OFFSET_INIT(timekeeper_xtime_sec, "timekeeper", "xtime_sec");
get_xtime(&kt->date);
if (kt->flags2 & GET_TIMESTAMP) {
fprintf(fp, "%s\n\n",
strip_linefeeds(ctime(&kt->date.tv_sec)));
clean_exit(0);
}
if (symbol_exists("system_utsname"))
readmem(symbol_value("system_utsname"), KVADDR, &kt->utsname,
sizeof(struct new_utsname), "system_utsname",
RETURN_ON_ERROR);
else if (symbol_exists("init_uts_ns"))
readmem(symbol_value("init_uts_ns") + sizeof(int),
KVADDR, &kt->utsname, sizeof(struct new_utsname),
"init_uts_ns", RETURN_ON_ERROR);
else
error(INFO, "cannot access utsname information\n\n");
strncpy(buf, kt->utsname.release, MIN(strlen(kt->utsname.release), 65));
if (ascii_string(kt->utsname.release)) {
char separator;
p1 = p2 = buf;
while (*p2 != '.')
p2++;
*p2 = NULLCHAR;
kt->kernel_version[0] = atoi(p1);
p1 = ++p2;
while (*p2 != '.' && *p2 != '-' && *p2 != '\0')
p2++;
separator = *p2;
*p2 = NULLCHAR;
kt->kernel_version[1] = atoi(p1);
*p2 = separator;
if (*p2 == '.') {
p1 = ++p2;
while ((*p2 >= '0') && (*p2 <= '9'))
p2++;
*p2 = NULLCHAR;
kt->kernel_version[2] = atoi(p1);
} else
kt->kernel_version[2] = 0;
if (CRASHDEBUG(1))
fprintf(fp, "base kernel version: %d.%d.%d\n",
kt->kernel_version[0],
kt->kernel_version[1],
kt->kernel_version[2]);
} else
error(INFO, "cannot determine base kernel version\n");
verify_version();
if (symbol_exists("__per_cpu_offset")) {
if (LKCD_KERNTYPES())
i = get_cpus_possible();
else
i = get_array_length("__per_cpu_offset", NULL, 0);
get_symbol_data("__per_cpu_offset",
sizeof(long)*((i && (i <= NR_CPUS)) ? i : NR_CPUS),
&kt->__per_cpu_offset[0]);
kt->flags |= PER_CPU_OFF;
}
MEMBER_OFFSET_INIT(percpu_counter_count, "percpu_counter", "count");
if (STRUCT_EXISTS("runqueue")) {
rqstruct = "runqueue";
rq_timestamp_name = "timestamp_last_tick";
} else if (STRUCT_EXISTS("rq")) {
rqstruct = "rq";
if (MEMBER_EXISTS("rq", "clock"))
rq_timestamp_name = "clock";
else if (MEMBER_EXISTS("rq", "most_recent_timestamp"))
rq_timestamp_name = "most_recent_timestamp";
else if (MEMBER_EXISTS("rq", "timestamp_last_tick"))
rq_timestamp_name = "timestamp_last_tick";
} else {
rqstruct = NULL;
error(FATAL, "neither runqueue nor rq structures exist\n");
}
MEMBER_OFFSET_INIT(runqueue_cpu, rqstruct, "cpu");
/*
* 'cpu' does not exist in 'struct rq'.
*/
if (VALID_MEMBER(runqueue_cpu) &&
(get_array_length("runqueue.cpu", NULL, 0) > 0)) {
MEMBER_OFFSET_INIT(cpu_s_curr, "cpu_s", "curr");
MEMBER_OFFSET_INIT(cpu_s_idle, "cpu_s", "idle");
STRUCT_SIZE_INIT(cpu_s, "cpu_s");
kt->runq_siblings = get_array_length("runqueue.cpu",
NULL, 0);
if (symbol_exists("__cpu_idx") &&
symbol_exists("__rq_idx")) {
if (!(kt->__cpu_idx = (long *)
calloc(NR_CPUS, sizeof(long))))
error(FATAL, "cannot malloc __cpu_idx array");
if (!(kt->__rq_idx = (long *)
calloc(NR_CPUS, sizeof(long))))
error(FATAL, "cannot malloc __rq_idx array");
if (!readmem(symbol_value("__cpu_idx"), KVADDR,
&kt->__cpu_idx[0], sizeof(long) * NR_CPUS,
"__cpu_idx[NR_CPUS]", RETURN_ON_ERROR))
error(INFO,
"cannot read __cpu_idx[NR_CPUS] array\n");
if (!readmem(symbol_value("__rq_idx"), KVADDR,
&kt->__rq_idx[0], sizeof(long) * NR_CPUS,
"__rq_idx[NR_CPUS]", RETURN_ON_ERROR))
error(INFO,
"cannot read __rq_idx[NR_CPUS] array\n");
} else if (kt->runq_siblings > 1)
error(INFO,
"runq_siblings: %d: __cpu_idx and __rq_idx arrays don't exist?\n",
kt->runq_siblings);
} else {
MEMBER_OFFSET_INIT(runqueue_idle, rqstruct, "idle");
MEMBER_OFFSET_INIT(runqueue_curr, rqstruct, "curr");
ASSIGN_OFFSET(runqueue_cpu) = INVALID_OFFSET;
}
MEMBER_OFFSET_INIT(runqueue_active, rqstruct, "active");
MEMBER_OFFSET_INIT(runqueue_expired, rqstruct, "expired");
MEMBER_OFFSET_INIT(runqueue_arrays, rqstruct, "arrays");
MEMBER_OFFSET_INIT(rq_timestamp, rqstruct, rq_timestamp_name);
MEMBER_OFFSET_INIT(prio_array_queue, "prio_array", "queue");
MEMBER_OFFSET_INIT(prio_array_nr_active, "prio_array", "nr_active");
STRUCT_SIZE_INIT(runqueue, rqstruct);
STRUCT_SIZE_INIT(prio_array, "prio_array");
MEMBER_OFFSET_INIT(rq_cfs, "rq", "cfs");
MEMBER_OFFSET_INIT(task_group_cfs_rq, "task_group", "cfs_rq");
MEMBER_OFFSET_INIT(task_group_rt_rq, "task_group", "rt_rq");
MEMBER_OFFSET_INIT(task_group_parent, "task_group", "parent");
/*
* In 2.4, smp_send_stop() sets smp_num_cpus back to 1
* in some, but not all, architectures. So if a count
* of 1 is found, be suspicious, and check the
* init_tasks[NR_CPUS] array (also intro'd in 2.4),
* for idle thread addresses. For 2.2, prepare for the
* eventuality by verifying the cpu count with the machine
* dependent count.
*/
if ((kt->flags & SMP) && DUMPFILE() && (kt->cpus == 1)) {
if (symbol_exists("init_tasks")) {
ulong init_tasks[NR_CPUS];
int nr_cpus;
BZERO(&init_tasks[0], sizeof(ulong) * NR_CPUS);
nr_cpus = get_array_length("init_tasks", NULL, 0);
if ((nr_cpus < 1) || (nr_cpus > NR_CPUS))
nr_cpus = NR_CPUS;
get_idle_threads(&init_tasks[0], nr_cpus);
for (i = kt->cpus = 0; i < nr_cpus; i++)
if (init_tasks[i])
kt->cpus++;
} else
kt->cpus = machdep->get_smp_cpus();
}
if ((kt->flags & SMP) && ACTIVE() && (kt->cpus == 1) &&
(kt->flags & PER_CPU_OFF))
kt->cpus = machdep->get_smp_cpus();
if (kt->cpus_override && (c = atoi(kt->cpus_override))) {
error(WARNING, "forcing cpu count to: %d\n\n", c);
kt->cpus = c;
}
if (kt->cpus > NR_CPUS) {
error(WARNING,
"%s number of cpus (%d) greater than compiled-in NR_CPUS (%d)\n",
kt->cpus_override && atoi(kt->cpus_override) ?
"configured" : "calculated", kt->cpus, NR_CPUS);
error(FATAL, "recompile crash with larger NR_CPUS\n");
}
hypervisor_init();
STRUCT_SIZE_INIT(spinlock_t, "spinlock_t");
verify_spinlock();
if (STRUCT_EXISTS("atomic_t"))
if (MEMBER_EXISTS("atomic_t", "counter"))
MEMBER_OFFSET_INIT(atomic_t_counter,
"atomic_t", "counter");
STRUCT_SIZE_INIT(list_head, "list_head");
MEMBER_OFFSET_INIT(list_head_next, "list_head", "next");
MEMBER_OFFSET_INIT(list_head_prev, "list_head", "prev");
if (OFFSET(list_head_next) != 0)
error(WARNING,
"list_head.next offset: %ld: list command may fail\n",
OFFSET(list_head_next));
MEMBER_OFFSET_INIT(hlist_node_next, "hlist_node", "next");
MEMBER_OFFSET_INIT(hlist_node_pprev, "hlist_node", "pprev");
STRUCT_SIZE_INIT(hlist_head, "hlist_head");
STRUCT_SIZE_INIT(hlist_node, "hlist_node");
if (STRUCT_EXISTS("irq_desc_t"))
irq_desc_type_name = "irq_desc_t";
else
irq_desc_type_name = "irq_desc";
STRUCT_SIZE_INIT(irq_desc_t, irq_desc_type_name);
if (MEMBER_EXISTS(irq_desc_type_name, "irq_data"))
MEMBER_OFFSET_INIT(irq_desc_t_irq_data, irq_desc_type_name, "irq_data");
else
MEMBER_OFFSET_INIT(irq_desc_t_affinity, irq_desc_type_name, "affinity");
if (MEMBER_EXISTS(irq_desc_type_name, "kstat_irqs"))
MEMBER_OFFSET_INIT(irq_desc_t_kstat_irqs, irq_desc_type_name, "kstat_irqs");
MEMBER_OFFSET_INIT(irq_desc_t_name, irq_desc_type_name, "name");
MEMBER_OFFSET_INIT(irq_desc_t_status, irq_desc_type_name, "status");
if (MEMBER_EXISTS(irq_desc_type_name, "handler"))
MEMBER_OFFSET_INIT(irq_desc_t_handler, irq_desc_type_name, "handler");
else if (MEMBER_EXISTS(irq_desc_type_name, "chip"))
MEMBER_OFFSET_INIT(irq_desc_t_chip, irq_desc_type_name, "chip");
MEMBER_OFFSET_INIT(irq_desc_t_action, irq_desc_type_name, "action");
MEMBER_OFFSET_INIT(irq_desc_t_depth, irq_desc_type_name, "depth");
STRUCT_SIZE_INIT(kernel_stat, "kernel_stat");
MEMBER_OFFSET_INIT(kernel_stat_irqs, "kernel_stat", "irqs");
if (STRUCT_EXISTS("hw_interrupt_type")) {
MEMBER_OFFSET_INIT(hw_interrupt_type_typename,
"hw_interrupt_type", "typename");
MEMBER_OFFSET_INIT(hw_interrupt_type_startup,
"hw_interrupt_type", "startup");
MEMBER_OFFSET_INIT(hw_interrupt_type_shutdown,
"hw_interrupt_type", "shutdown");
MEMBER_OFFSET_INIT(hw_interrupt_type_handle,
"hw_interrupt_type", "handle");
MEMBER_OFFSET_INIT(hw_interrupt_type_enable,
"hw_interrupt_type", "enable");
MEMBER_OFFSET_INIT(hw_interrupt_type_disable,
"hw_interrupt_type", "disable");
MEMBER_OFFSET_INIT(hw_interrupt_type_ack,
"hw_interrupt_type", "ack");
MEMBER_OFFSET_INIT(hw_interrupt_type_end,
"hw_interrupt_type", "end");
MEMBER_OFFSET_INIT(hw_interrupt_type_set_affinity,
"hw_interrupt_type", "set_affinity");
} else { /*
* On later kernels where hw_interrupt_type was replaced
* by irq_chip
*/
MEMBER_OFFSET_INIT(irq_chip_typename,
"irq_chip", "name");
MEMBER_OFFSET_INIT(irq_chip_startup,
"irq_chip", "startup");
MEMBER_OFFSET_INIT(irq_chip_shutdown,
"irq_chip", "shutdown");
MEMBER_OFFSET_INIT(irq_chip_enable,
"irq_chip", "enable");
MEMBER_OFFSET_INIT(irq_chip_disable,
"irq_chip", "disable");
MEMBER_OFFSET_INIT(irq_chip_ack,
"irq_chip", "ack");
MEMBER_OFFSET_INIT(irq_chip_mask,
"irq_chip", "mask");
MEMBER_OFFSET_INIT(irq_chip_mask_ack,
"irq_chip", "mask_ack");
MEMBER_OFFSET_INIT(irq_chip_unmask,
"irq_chip", "unmask");
MEMBER_OFFSET_INIT(irq_chip_eoi,
"irq_chip", "eoi");
MEMBER_OFFSET_INIT(irq_chip_end,
"irq_chip", "end");
MEMBER_OFFSET_INIT(irq_chip_set_affinity,
"irq_chip", "set_affinity");
MEMBER_OFFSET_INIT(irq_chip_retrigger,
"irq_chip", "retrigger");
MEMBER_OFFSET_INIT(irq_chip_set_type,
"irq_chip", "set_type");
MEMBER_OFFSET_INIT(irq_chip_set_wake,
"irq_chip", "set_wake");
}
MEMBER_OFFSET_INIT(irqaction_handler, "irqaction", "handler");
MEMBER_OFFSET_INIT(irqaction_flags, "irqaction", "flags");
MEMBER_OFFSET_INIT(irqaction_mask, "irqaction", "mask");
MEMBER_OFFSET_INIT(irqaction_name, "irqaction", "name");
MEMBER_OFFSET_INIT(irqaction_dev_id, "irqaction", "dev_id");
MEMBER_OFFSET_INIT(irqaction_next, "irqaction", "next");
if (kernel_symbol_exists("irq_desc_tree"))
kt->flags |= IRQ_DESC_TREE;
STRUCT_SIZE_INIT(irq_data, "irq_data");
if (VALID_STRUCT(irq_data)) {
MEMBER_OFFSET_INIT(irq_data_chip, "irq_data", "chip");
MEMBER_OFFSET_INIT(irq_data_affinity, "irq_data", "affinity");
}
STRUCT_SIZE_INIT(irq_cpustat_t, "irq_cpustat_t");
MEMBER_OFFSET_INIT(irq_cpustat_t___softirq_active,
"irq_cpustat_t", "__softirq_active");
MEMBER_OFFSET_INIT(irq_cpustat_t___softirq_mask,
"irq_cpustat_t", "__softirq_mask");
STRUCT_SIZE_INIT(timer_list, "timer_list");
MEMBER_OFFSET_INIT(timer_list_list, "timer_list", "list");
MEMBER_OFFSET_INIT(timer_list_next, "timer_list", "next");
MEMBER_OFFSET_INIT(timer_list_entry, "timer_list", "entry");
MEMBER_OFFSET_INIT(timer_list_expires, "timer_list", "expires");
MEMBER_OFFSET_INIT(timer_list_function, "timer_list", "function");
STRUCT_SIZE_INIT(timer_vec_root, "timer_vec_root");
if (VALID_STRUCT(timer_vec_root))
MEMBER_OFFSET_INIT(timer_vec_root_vec,
"timer_vec_root", "vec");
STRUCT_SIZE_INIT(timer_vec, "timer_vec");
if (VALID_STRUCT(timer_vec))
MEMBER_OFFSET_INIT(timer_vec_vec, "timer_vec", "vec");
STRUCT_SIZE_INIT(tvec_root_s, "tvec_root_s");
if (VALID_STRUCT(tvec_root_s)) {
STRUCT_SIZE_INIT(tvec_t_base_s, "tvec_t_base_s");
MEMBER_OFFSET_INIT(tvec_t_base_s_tv1,
"tvec_t_base_s", "tv1");
MEMBER_OFFSET_INIT(tvec_root_s_vec,
"tvec_root_s", "vec");
STRUCT_SIZE_INIT(tvec_s, "tvec_s");
MEMBER_OFFSET_INIT(tvec_s_vec, "tvec_s", "vec");
} else {
STRUCT_SIZE_INIT(tvec_root_s, "tvec_root");
if (VALID_STRUCT(tvec_root_s)) {
STRUCT_SIZE_INIT(tvec_t_base_s, "tvec_base");
MEMBER_OFFSET_INIT(tvec_t_base_s_tv1,
"tvec_base", "tv1");
MEMBER_OFFSET_INIT(tvec_root_s_vec,
"tvec_root", "vec");
STRUCT_SIZE_INIT(tvec_s, "tvec");
MEMBER_OFFSET_INIT(tvec_s_vec, "tvec", "vec");
}
}
STRUCT_SIZE_INIT(__wait_queue, "__wait_queue");
if (VALID_STRUCT(__wait_queue)) {
if (MEMBER_EXISTS("__wait_queue", "task"))
MEMBER_OFFSET_INIT(__wait_queue_task,
"__wait_queue", "task");
else
MEMBER_OFFSET_INIT(__wait_queue_task,
"__wait_queue", "private");
MEMBER_OFFSET_INIT(__wait_queue_head_task_list,
"__wait_queue_head", "task_list");
MEMBER_OFFSET_INIT(__wait_queue_task_list,
"__wait_queue", "task_list");
} else {
STRUCT_SIZE_INIT(wait_queue, "wait_queue");
if (VALID_STRUCT(wait_queue)) {
MEMBER_OFFSET_INIT(wait_queue_task,
"wait_queue", "task");
MEMBER_OFFSET_INIT(wait_queue_next,
"wait_queue", "next");
}
}
STRUCT_SIZE_INIT(pt_regs, "pt_regs");
STRUCT_SIZE_INIT(softirq_state, "softirq_state");
STRUCT_SIZE_INIT(softirq_action, "softirq_action");
STRUCT_SIZE_INIT(desc_struct, "desc_struct");
STRUCT_SIZE_INIT(char_device_struct, "char_device_struct");
if (VALID_STRUCT(char_device_struct)) {
MEMBER_OFFSET_INIT(char_device_struct_next,
"char_device_struct", "next");
MEMBER_OFFSET_INIT(char_device_struct_name,
"char_device_struct", "name");
MEMBER_OFFSET_INIT(char_device_struct_fops,
"char_device_struct", "fops");
MEMBER_OFFSET_INIT(char_device_struct_major,
"char_device_struct", "major");
MEMBER_OFFSET_INIT(char_device_struct_baseminor,
"char_device_struct", "baseminor");
MEMBER_OFFSET_INIT(char_device_struct_cdev,
"char_device_struct", "cdev");
}
STRUCT_SIZE_INIT(cdev, "cdev");
if (VALID_STRUCT(cdev))
MEMBER_OFFSET_INIT(cdev_ops, "cdev", "ops");
STRUCT_SIZE_INIT(probe, "probe");
if (VALID_STRUCT(probe)) {
MEMBER_OFFSET_INIT(probe_next, "probe", "next");
MEMBER_OFFSET_INIT(probe_dev, "probe", "dev");
MEMBER_OFFSET_INIT(probe_data, "probe", "data");
}
STRUCT_SIZE_INIT(kobj_map, "kobj_map");
if (VALID_STRUCT(kobj_map))
MEMBER_OFFSET_INIT(kobj_map_probes, "kobj_map", "probes");
MEMBER_OFFSET_INIT(module_kallsyms_start, "module",
"kallsyms_start");
STRUCT_SIZE_INIT(kallsyms_header, "kallsyms_header");
if (VALID_MEMBER(module_kallsyms_start) &&
VALID_SIZE(kallsyms_header)) {
MEMBER_OFFSET_INIT(kallsyms_header_sections,
"kallsyms_header", "sections");
MEMBER_OFFSET_INIT(kallsyms_header_section_off,
"kallsyms_header", "section_off");
MEMBER_OFFSET_INIT(kallsyms_header_symbols,
"kallsyms_header", "symbols");
MEMBER_OFFSET_INIT(kallsyms_header_symbol_off,
"kallsyms_header", "symbol_off");
MEMBER_OFFSET_INIT(kallsyms_header_string_off,
"kallsyms_header", "string_off");
MEMBER_OFFSET_INIT(kallsyms_symbol_section_off,
"kallsyms_symbol", "section_off");
MEMBER_OFFSET_INIT(kallsyms_symbol_symbol_addr,
"kallsyms_symbol", "symbol_addr");
MEMBER_OFFSET_INIT(kallsyms_symbol_name_off,
"kallsyms_symbol", "name_off");
MEMBER_OFFSET_INIT(kallsyms_section_start,
"kallsyms_section", "start");
MEMBER_OFFSET_INIT(kallsyms_section_size,
"kallsyms_section", "size");
MEMBER_OFFSET_INIT(kallsyms_section_name_off,
"kallsyms_section", "name_off");
STRUCT_SIZE_INIT(kallsyms_symbol, "kallsyms_symbol");
STRUCT_SIZE_INIT(kallsyms_section, "kallsyms_section");
if (!(kt->flags & NO_KALLSYMS))
kt->flags |= KALLSYMS_V1;
}
MEMBER_OFFSET_INIT(module_num_symtab, "module", "num_symtab");
if (VALID_MEMBER(module_num_symtab)) {
MEMBER_OFFSET_INIT(module_symtab, "module", "symtab");
MEMBER_OFFSET_INIT(module_strtab, "module", "strtab");
if (!(kt->flags & NO_KALLSYMS))
kt->flags |= KALLSYMS_V2;
}
if (!(kt->flags & DWARF_UNWIND))
kt->flags |= NO_DWARF_UNWIND;
/*
* OpenVZ
*/
if (kernel_symbol_exists("pcpu_info") &&
STRUCT_EXISTS("pcpu_info") && STRUCT_EXISTS("vcpu_struct")) {
MEMBER_OFFSET_INIT(pcpu_info_vcpu, "pcpu_info", "vcpu");
MEMBER_OFFSET_INIT(pcpu_info_idle, "pcpu_info", "idle");
MEMBER_OFFSET_INIT(vcpu_struct_rq, "vcpu_struct", "rq");
STRUCT_SIZE_INIT(pcpu_info, "pcpu_info");
STRUCT_SIZE_INIT(vcpu_struct, "vcpu_struct");
kt->flags |= ARCH_OPENVZ;
}
STRUCT_SIZE_INIT(mem_section, "mem_section");
BUG_bytes_init();
/*
* for hrtimer
*/
STRUCT_SIZE_INIT(hrtimer_clock_base, "hrtimer_clock_base");
if (VALID_STRUCT(hrtimer_clock_base)) {
MEMBER_OFFSET_INIT(hrtimer_clock_base_offset,
"hrtimer_clock_base", "offset");
MEMBER_OFFSET_INIT(hrtimer_clock_base_active,
"hrtimer_clock_base", "active");
MEMBER_OFFSET_INIT(hrtimer_clock_base_first,
"hrtimer_clock_base", "first");
MEMBER_OFFSET_INIT(hrtimer_clock_base_get_time,
"hrtimer_clock_base", "get_time");
}
STRUCT_SIZE_INIT(hrtimer_base, "hrtimer_base");
if (VALID_STRUCT(hrtimer_base)) {
MEMBER_OFFSET_INIT(hrtimer_base_first,
"hrtimer_base", "first");
MEMBER_OFFSET_INIT(hrtimer_base_pending,
"hrtimer_base", "pending");
MEMBER_OFFSET_INIT(hrtimer_base_get_time,
"hrtimer_base", "get_time");
}
MEMBER_OFFSET_INIT(hrtimer_cpu_base_clock_base, "hrtimer_cpu_base",
"clock_base");
MEMBER_OFFSET_INIT(hrtimer_node, "hrtimer", "node");
MEMBER_OFFSET_INIT(hrtimer_list, "hrtimer", "list");
MEMBER_OFFSET_INIT(hrtimer_expires, "hrtimer", "expires");
if (INVALID_MEMBER(hrtimer_expires))
MEMBER_OFFSET_INIT(hrtimer_expires, "hrtimer", "_expires");
if (INVALID_MEMBER(hrtimer_expires)) {
MEMBER_OFFSET_INIT(timerqueue_head_next,
"timerqueue_head", "next");
MEMBER_OFFSET_INIT(timerqueue_node_expires,
"timerqueue_node", "expires");
MEMBER_OFFSET_INIT(timerqueue_node_node,
"timerqueue_node_node", "node");
}
MEMBER_OFFSET_INIT(hrtimer_softexpires, "hrtimer", "_softexpires");
MEMBER_OFFSET_INIT(hrtimer_function, "hrtimer", "function");
MEMBER_OFFSET_INIT(ktime_t_tv64, "ktime", "tv64");
if (INVALID_MEMBER(ktime_t_tv64))
MEMBER_OFFSET_INIT(ktime_t_tv64, "ktime_t", "tv64");
MEMBER_OFFSET_INIT(ktime_t_sec, "ktime", "sec");
if (INVALID_MEMBER(ktime_t_sec))
MEMBER_OFFSET_INIT(ktime_t_sec, "ktime_t", "sec");
MEMBER_OFFSET_INIT(ktime_t_nsec, "ktime", "nsec");
if (INVALID_MEMBER(ktime_t_nsec))
MEMBER_OFFSET_INIT(ktime_t_nsec, "ktime_t", "nsec");
kt->flags &= ~PRE_KERNEL_INIT;
}
/*
* Get cpu map address. Types are: possible, online, present and active.
* They exist as either:
*
* (1) cpu_<type>_map symbols, or
* (2) what is pointed to by cpu_<type>_mask
*/
ulong
cpu_map_addr(const char *type)
{
char map_symbol[32];
ulong addr;
sprintf(map_symbol, "cpu_%s_map", type);
if (kernel_symbol_exists(map_symbol))
return symbol_value(map_symbol);
sprintf(map_symbol, "cpu_%s_mask", type);
if (kernel_symbol_exists(map_symbol)) {
get_symbol_data(map_symbol, sizeof(ulong), &addr);
return addr;
}
return 0;
}
static char *
cpu_map_type(char *name)
{
char map_symbol[32];
sprintf(map_symbol, "cpu_%s_map", name);
if (kernel_symbol_exists(map_symbol))
return "map";
sprintf(map_symbol, "cpu_%s_mask", name);
if (kernel_symbol_exists(map_symbol))
return "mask";
return NULL;
}
/*
* Get cpu map (possible, online, etc.) size
*/
static int
cpu_map_size(const char *type)
{
int len;
char map_symbol[32];
struct gnu_request req;
if (LKCD_KERNTYPES()) {
if ((len = STRUCT_SIZE("cpumask_t")) < 0)
error(FATAL, "cannot determine type cpumask_t\n");
return len;
}
sprintf(map_symbol, "cpu_%s_map", type);
if (kernel_symbol_exists(map_symbol)) {
len = get_symbol_type(map_symbol, NULL, &req) ==
TYPE_CODE_UNDEF ? sizeof(ulong) : req.length;
return len;
}
len = STRUCT_SIZE("cpumask_t");
if (len < 0)
return sizeof(ulong);
else
return len;
}
/*
* If the cpu_present_map, cpu_online_map and cpu_possible_maps exist,
* set up the kt->cpu_flags[NR_CPUS] with their settings.
*/
static void
cpu_maps_init(void)
{
int i, c, m, cpu, len;
char *buf;
ulong *maskptr, addr;
struct mapinfo {
ulong cpu_flag;
char *name;
} mapinfo[] = {
{ POSSIBLE_MAP, "possible" },
{ PRESENT_MAP, "present" },
{ ONLINE_MAP, "online" },
{ ACTIVE_MAP, "active" },
};
if ((len = STRUCT_SIZE("cpumask_t")) < 0)
len = sizeof(ulong);
buf = GETBUF(len);
for (m = 0; m < sizeof(mapinfo)/sizeof(struct mapinfo); m++) {
if (!(addr = cpu_map_addr(mapinfo[m].name)))
continue;
if (!readmem(addr, KVADDR, buf, len,
mapinfo[m].name, RETURN_ON_ERROR)) {
error(WARNING, "cannot read cpu_%s_map\n",
mapinfo[m].name);
continue;
}
maskptr = (ulong *)buf;
for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++) {
if (*maskptr == 0)
continue;
for (c = 0; c < BITS_PER_LONG; c++)
if (*maskptr & (0x1UL << c)) {
cpu = (i * BITS_PER_LONG) + c;
if (cpu >= NR_CPUS) {
error(WARNING,
"cpu_%s_%s indicates more than"
" %d (NR_CPUS) cpus\n",
mapinfo[m].name,
cpu_map_type(mapinfo[m].name),
NR_CPUS);
break;
}
kt->cpu_flags[cpu] |= mapinfo[m].cpu_flag;
}
}
if (CRASHDEBUG(1)) {
fprintf(fp, "cpu_%s_%s: ", mapinfo[m].name,
cpu_map_type(mapinfo[m].name));
for (i = 0; i < NR_CPUS; i++) {
if (kt->cpu_flags[i] & mapinfo[m].cpu_flag)
fprintf(fp, "%d ", i);
}
fprintf(fp, "\n");
}
}
FREEBUF(buf);
}
/*
* Determine whether a cpu is in one of the cpu masks.
*/
int
in_cpu_map(int map, int cpu)
{
if (cpu >= (kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS)) {
error(INFO, "in_cpu_map: invalid cpu: %d\n", cpu);
return FALSE;
}
switch (map)
{
case POSSIBLE_MAP:
if (!cpu_map_addr("possible")) {
error(INFO, "cpu_possible_map does not exist\n");
return FALSE;
}
return (kt->cpu_flags[cpu] & POSSIBLE_MAP);
case PRESENT_MAP:
if (!cpu_map_addr("present")) {
error(INFO, "cpu_present_map does not exist\n");
return FALSE;
}
return (kt->cpu_flags[cpu] & PRESENT_MAP);
case ONLINE_MAP:
if (!cpu_map_addr("online")) {
error(INFO, "cpu_online_map does not exist\n");
return FALSE;
}
return (kt->cpu_flags[cpu] & ONLINE_MAP);
case ACTIVE_MAP:
if (!cpu_map_addr("active")) {
error(INFO, "cpu_active_map does not exist\n");
return FALSE;
}
return (kt->cpu_flags[cpu] & ACTIVE_MAP);
}
return FALSE;
}
/*
* For lack of a better manner of verifying that the namelist and dumpfile
* (or live kernel) match up, verify that the Linux banner is where
* the namelist says it is. Since this is common place to bail, extra
* debug statements are available.
*/
void
verify_version(void)
{
char buf[BUFSIZE];
ulong linux_banner;
int argc, len;
char *arglist[MAXARGS];
char *p1, *p2;
struct syment *sp;
if (pc->flags & KERNEL_DEBUG_QUERY)
return;
BZERO(buf, BUFSIZE);
if (!(sp = symbol_search("linux_banner")))
error(FATAL, "linux_banner symbol does not exist?\n");
else if ((sp->type == 'R') || (sp->type == 'r') ||
(machine_type("ARM") && sp->type == 'T') ||
(machine_type("ARM64")))
linux_banner = symbol_value("linux_banner");
else
get_symbol_data("linux_banner", sizeof(ulong), &linux_banner);
if (!IS_KVADDR(linux_banner))
error(WARNING, "invalid linux_banner pointer: %lx\n",
linux_banner);
if (!accessible(linux_banner))
goto bad_match;
if (!read_string(linux_banner, buf, BUFSIZE-1))
error(WARNING, "cannot read linux_banner string\n");
if (ACTIVE()) {
len = strlen(kt->proc_version);
if ((len > 0) && (strncmp(buf, kt->proc_version, len) != 0)) {
if (CRASHDEBUG(1)) {
fprintf(fp, "/proc/version:\n%s\n",
kt->proc_version);
fprintf(fp, "linux_banner:\n%s\n", buf);
}
goto bad_match;
} else if (CRASHDEBUG(1))
fprintf(fp, "linux_banner:\n%s\n", buf);
}
if (DUMPFILE()) {
if (!STRNEQ(buf, "Linux version")) {
if (CRASHDEBUG(1))
fprintf(fp, "linux_banner:\n%s\n", buf);
goto bad_match;
}
strcpy(kt->proc_version, strip_linefeeds(buf));
}
verify_namelist();
if (strstr(kt->proc_version, "gcc version 3.3.3"))
kt->flags |= GCC_3_3_3;
if (strstr(kt->proc_version, "gcc version 3.3.2"))
kt->flags |= GCC_3_3_2;
else if (strstr(kt->proc_version, "gcc version 3.2.3"))
kt->flags |= GCC_3_2_3;
else if (strstr(kt->proc_version, "gcc version 3.2"))
kt->flags |= GCC_3_2;
else if (strstr(kt->proc_version, "gcc version 2.96"))
kt->flags |= GCC_2_96;
/*
* Keeping the gcc version with #define's is getting out of hand.
*/
if ((p1 = strstr(kt->proc_version, "gcc version "))) {
BZERO(buf, BUFSIZE);
p1 += strlen("gcc version ");
p2 = buf;
while (((*p1 >= '0') && (*p1 <= '9')) || (*p1 == '.')) {
if (*p1 == '.')
*p2++ = ' ';
else
*p2++ = *p1;
p1++;
}
argc = parse_line(buf, arglist);
switch (argc)
{
case 0:
case 1:
break;
case 2:
kt->gcc_version[0] = atoi(arglist[0]);
kt->gcc_version[1] = atoi(arglist[1]);
break;
default:
kt->gcc_version[0] = atoi(arglist[0]);
kt->gcc_version[1] = atoi(arglist[1]);
kt->gcc_version[2] = atoi(arglist[2]);
break;
}
}
if (CRASHDEBUG(1))
gdb_readnow_warning();
return;
bad_match:
if (REMOTE())
sprintf(buf, "%s:%s", pc->server, pc->server_memsrc);
else
sprintf(buf, "%s", ACTIVE() ? pc->live_memsrc : pc->dumpfile);
error(INFO, "%s and %s do not match!\n",
pc->system_map ? pc->system_map :
pc->namelist_debug ? pc->namelist_debug : pc->namelist, buf);
program_usage(SHORT_FORM);
}
/*
* Quick test to verify that we're not using a UP debug kernel on
* an SMP system.
*/
void
verify_spinlock(void)
{
char buf[BUFSIZE];
if ((kt->flags & SMP) && (SIZE(spinlock_t) == 0)) {
error(INFO,
"debug data shows spinlock_t as an incomplete type (undefined),\n");
fprintf(fp, "%sbut \"%s\" is an SMP kernel.\n",
space(strlen(pc->program_name)+2),
pc->namelist);
if (CRASHDEBUG(1)) {
fprintf(fp, "\ngdb> ptype spinlock_t\n");
sprintf(buf, "ptype spinlock_t");
gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR);
}
non_matching_kernel();
}
}
/*
* Something doesn't jive.
*/
void
non_matching_kernel(void)
{
int kernels = 0;
if (pc->namelist)
kernels++;
if (pc->namelist_debug)
kernels++;
if (pc->debuginfo_file)
kernels++;
fprintf(fp,
"\nErrors like the one above typically occur when the kernel%s and memory source\ndo not match. These are the files being used:\n\n", kernels > 1 ? "s" : "");
if (REMOTE()) {
switch (pc->flags &
(NAMELIST_LOCAL|NAMELIST_UNLINKED|NAMELIST_SAVED))
{
case NAMELIST_UNLINKED:
fprintf(fp, " KERNEL: %s (temporary)\n",
pc->namelist);
break;
case (NAMELIST_UNLINKED|NAMELIST_SAVED):
fprintf(fp, " KERNEL: %s\n", pc->namelist);
break;
case NAMELIST_LOCAL:
fprintf(fp, " KERNEL: %s\n", pc->namelist);
break;
}
} else {
if (pc->system_map) {
fprintf(fp, " SYSTEM MAP: %s\n", pc->system_map);
fprintf(fp, "DEBUG KERNEL: %s %s\n", pc->namelist,
debug_kernel_version(pc->namelist));
} else
fprintf(fp, " KERNEL: %s\n", pc->namelist);
if (pc->namelist_orig)
fprintf(fp, " (uncompressed from %s)\n",
pc->namelist_orig);
}
if (pc->debuginfo_file) {
fprintf(fp, " DEBUGINFO: %s\n", pc->debuginfo_file);
if (STREQ(pc->debuginfo_file, pc->namelist_debug) &&
pc->namelist_debug_orig)
fprintf(fp, " (uncompressed from %s)\n",
pc->namelist_debug_orig);
} else if (pc->namelist_debug) {
fprintf(fp, "DEBUG KERNEL: %s %s\n", pc->namelist_debug,
debug_kernel_version(pc->namelist_debug));
if (pc->namelist_debug_orig)
fprintf(fp, " (uncompressed from %s)\n",
pc->namelist_debug_orig);
}
if (dumpfile_is_split() || sadump_is_diskset() || is_ramdump_image())
fprintf(fp, " DUMPFILES: ");
else
fprintf(fp, " DUMPFILE: ");
if (ACTIVE()) {
if (REMOTE_ACTIVE())
fprintf(fp, "%s@%s (remote live system)\n",
pc->server_memsrc, pc->server);
else
fprintf(fp, "%s\n", pc->live_memsrc);
} else {
if (REMOTE_DUMPFILE())
fprintf(fp, "%s@%s (remote dumpfile)\n",
pc->server_memsrc, pc->server);
else if (REMOTE_PAUSED())
fprintf(fp, "%s %s (remote paused system)\n",
pc->server_memsrc, pc->server);
else {
if (dumpfile_is_split())
show_split_dumpfiles();
else if (sadump_is_diskset())
sadump_show_diskset();
else if (is_ramdump_image())
show_ramdump_files();
else
fprintf(fp, "%s", pc->dumpfile);
}
if (LIVE())
fprintf(fp, " [LIVE DUMP]");
}
fprintf(fp, "\n\n");
if ((pc->flags & FINDKERNEL) && !(pc->system_map)) {
fprintf(fp,
"The kernel \"%s\" is most likely incorrect.\n",
pc->namelist);
fprintf(fp,
"Try a different kernel name, or use a System.map file argument.\n\n");
}
clean_exit(1);
}
/*
* Only two checks are made here:
*
* 1. if the namelist is SMP and the memory source isn't, bail out.
* 2. if the basic gcc versions differ, issue a warning only.
*/
static void
verify_namelist()
{
int i;
char command[BUFSIZE];
char buffer[BUFSIZE];
char buffer2[BUFSIZE];
char buffer3[BUFSIZE];
char buffer4[BUFSIZE];
char buffer5[BUFSIZE];
char *p1;
FILE *pipe;
int found;
char *namelist;
int namelist_smp;
int target_smp;
if (pc->flags & KERNEL_DEBUG_QUERY)
return;
/* the kerntypes may not match in terms of gcc version or SMP */
if (LKCD_KERNTYPES())
return;
if (!strlen(kt->utsname.version))
return;
namelist = pc->namelist ? pc->namelist : pc->namelist_debug;
target_smp = strstr(kt->utsname.version, " SMP ") ? TRUE : FALSE;
namelist_smp = FALSE;
sprintf(command, "/usr/bin/strings %s", namelist);
if ((pipe = popen(command, "r")) == NULL) {
error(INFO, "%s: %s\n", namelist, strerror(errno));
return;
}
found = FALSE;
sprintf(buffer3, "(unknown)");
while (fgets(buffer, BUFSIZE-1, pipe)) {
if (!strstr(buffer, "Linux version 2.") &&
!strstr(buffer, "Linux version 3.") &&
!strstr(buffer, "Linux version 4.") &&
!strstr(buffer, "Linux version 5."))
continue;
if (strstr(buffer, kt->proc_version)) {
found = TRUE;
break;
}
if (strstr(buffer, " SMP ")) {
namelist_smp = TRUE;
strcpy(buffer2, buffer);
}
if ((p1 = strstr(buffer, "(gcc version "))) {
p1 += strlen("(gcc version ");
i = 0;
while (*p1 != ' ')
buffer3[i++] = *p1++;
buffer3[i] = NULLCHAR;
}
break;
}
pclose(pipe);
if (!found && (p1 = strstr(kt->proc_version, "(gcc version "))) {
p1 += strlen("(gcc version ");
i = 0;
while (*p1 != ' ')
buffer4[i++] = *p1++;
buffer4[i] = NULLCHAR;
if (!STREQ(buffer3, buffer4)) {
if (REMOTE())
sprintf(buffer, "%s:%s kernel",
pc->server, pc->server_memsrc);
else
sprintf(buffer, "%s kernel", ACTIVE() ?
"live system" : pc->dumpfile);
sprintf(buffer5, " %s: %s\n %s: %s\n\n",
namelist, buffer3,
buffer, buffer4);
error(WARNING,
"kernels compiled by different gcc versions:\n%s",
buffer5);
}
}
if (found) {
if (CRASHDEBUG(1)) {
fprintf(fp, "verify_namelist:\n");
fprintf(fp, "%s /proc/version:\n%s\n",
ACTIVE() ? "live memory" : "dumpfile",
kt->proc_version);
fprintf(fp, "%s:\n%s\n", namelist, buffer);
}
return;
}
if (!(pc->flags & SYSMAP_ARG))
error(WARNING,
"kernel version inconsistency between vmlinux and %s\n\n",
ACTIVE() ? "live memory" : "dumpfile");
if (CRASHDEBUG(1)) {
error(WARNING,
"\ncannot find matching kernel version in %s file:\n\n",
namelist);
fprintf(fp, "verify_namelist:\n");
fprintf(fp, "%s /proc/version:\n%s\n",
ACTIVE() ? "live memory" : "dumpfile",
kt->proc_version);
fprintf(fp, "%s:\n%s\n", namelist, buffer2);
}
if (target_smp == namelist_smp)
return;
if (REMOTE())
sprintf(buffer, "%s:%s", pc->server, pc->server_memsrc);
else
sprintf(buffer, "%s", ACTIVE() ? "live system" : pc->dumpfile);
sprintf(buffer2, " %s is %s -- %s is %s\n",
namelist, namelist_smp ? "SMP" : "not SMP",
buffer, target_smp ? "SMP" : "not SMP");
error(INFO, "incompatible arguments: %s%s",
strlen(buffer2) > 48 ? "\n " : "", buffer2);
program_usage(SHORT_FORM);
}
/*
* This routine disassembles text in one of four manners. A starting
* address, an expression, or symbol must be entered. Then:
*
* 1. if a count is appended, disassemble that many instructions starting
* at the target address.
* 2. if a count is NOT entered, and the target address is the starting
* address of a function, disassemble the whole function.
* 3. if the target address is other than the starting address of a
* function, and no count argument is appended, then disassemble one
* instruction.
* 4. If the -r option is used, disassemble all instructions in a routine
* up to and including the target address.
* 5. If -u option, just pass the user address and count, ignoring any of
* the above.
*/
static char *dis_err = "gdb unable to disassemble kernel virtual address %lx\n";
void
cmd_dis(void)
{
int c;
int do_load_module_filter, do_machdep_filter, reverse;
int unfiltered, user_mode, count_entered, bug_bytes_entered;
unsigned int radix;
ulong curaddr;
ulong revtarget;
ulong count;
ulong offset;
struct syment *sp;
struct gnu_request *req;
char *savename;
char *ret ATTRIBUTE_UNUSED;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char buf4[BUFSIZE];
char buf5[BUFSIZE];
if ((argcnt == 2) && STREQ(args[1], "-b")) {
fprintf(fp, "encoded bytes being skipped after ud2a: ");
if (kt->BUG_bytes < 0)
fprintf(fp, "undetermined\n");
else
fprintf(fp, "%d\n", kt->BUG_bytes);
return;
}
reverse = count_entered = bug_bytes_entered = FALSE;
sp = NULL;
unfiltered = user_mode = do_machdep_filter = do_load_module_filter = 0;
radix = 0;
req = (struct gnu_request *)getbuf(sizeof(struct gnu_request));
req->buf = GETBUF(BUFSIZE);
req->flags |= GNU_FROM_TTY_OFF|GNU_RETURN_ON_ERROR;
req->count = 1;
while ((c = getopt(argcnt, args, "dxhulrUb:B:")) != EOF) {
switch(c)
{
case 'd':
if (radix == 16)
error(FATAL,
"-d and -x are mutually exclusive\n");
radix = 10;
break;
case 'x':
case 'h':
if (radix == 10)
error(FATAL,
"-d and -x are mutually exclusive\n");
radix = 16;
break;
case 'U':
unfiltered = TRUE;
break;
case 'u':
user_mode = TRUE;
break;
case 'r':
reverse = TRUE;
break;
case 'l':
if (NO_LINE_NUMBERS())
error(INFO, "line numbers are not available\n");
else
req->flags |= GNU_PRINT_LINE_NUMBERS;
BZERO(buf4, BUFSIZE);
break;
case 'B':
case 'b':
kt->BUG_bytes = atoi(optarg);
bug_bytes_entered = TRUE;
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (!radix)
radix = pc->output_radix;
if (args[optind]) {
if (can_eval(args[optind]))
req->addr = eval(args[optind], FAULT_ON_ERROR, NULL);
else if (hexadecimal(args[optind], 0)) {
req->addr = htol(args[optind], FAULT_ON_ERROR, NULL);
if (!user_mode &&
!(sp = value_search(req->addr, &offset))) {
error(WARNING,
"%lx: no associated kernel symbol found\n",
req->addr);
unfiltered = TRUE;
}
} else if ((sp = symbol_search(args[optind]))) {
req->addr = sp->value;
req->flags |= GNU_FUNCTION_ONLY;
} else {
fprintf(fp, "symbol not found: %s\n", args[optind]);
fprintf(fp, "possible alternatives:\n");
if (!symbol_query(args[optind], " ", NULL))
fprintf(fp, " (none found)\n");
FREEBUF(req->buf);
FREEBUF(req);
return;
}
if (args[++optind]) {
if (reverse) {
error(INFO,
"count argument ignored with -r option\n");
} else {
req->count = stol(args[optind],
FAULT_ON_ERROR, NULL);
req->flags &= ~GNU_FUNCTION_ONLY;
count_entered++;
}
}
if (unfiltered) {
sprintf(buf1, "x/%ldi 0x%lx",
req->count ? req->count : 1, req->addr);
gdb_pass_through(buf1, NULL, GNU_RETURN_ON_ERROR);
return;
}
if (!user_mode && !IS_KVADDR(req->addr))
error(FATAL, "%lx is not a kernel virtual address\n",
req->addr);
if (user_mode) {
sprintf(buf1, "x/%ldi 0x%lx",
req->count ? req->count : 1, req->addr);
pc->curcmd_flags |= MEMTYPE_UVADDR;
gdb_pass_through(buf1, NULL, GNU_RETURN_ON_ERROR);
return;
}
do_load_module_filter = module_symbol(req->addr, NULL, NULL,
NULL, *gdb_output_radix);
if (!reverse) {
req->command = GNU_RESOLVE_TEXT_ADDR;
gdb_interface(req);
if ((req->flags & GNU_COMMAND_FAILED) ||
do_load_module_filter ||
(req->flags & GNU_FUNCTION_ONLY)) {
req->flags &= ~GNU_COMMAND_FAILED;
if (sp) {
savename = sp->name;
if ((sp = next_symbol(NULL, sp)))
req->addr2 = sp->value;
else
error(FATAL,
"unable to determine symbol after %s\n",
savename);
} else {
if ((sp = value_search(req->addr, NULL))
&& (sp = next_symbol(NULL, sp)))
req->addr2 = sp->value;
else
error(FATAL, dis_err, req->addr);
}
}
do_machdep_filter = machdep->dis_filter(req->addr, NULL, radix);
count = 0;
open_tmpfile();
#ifdef OLDWAY
req->command = GNU_DISASSEMBLE;
req->fp = pc->tmpfile;
gdb_interface(req);
#else
sprintf(buf1, "x/%ldi 0x%lx",
count_entered && req->count ? req->count :
req->flags & GNU_FUNCTION_ONLY ?
req->addr2 - req->addr : 1,
req->addr);
gdb_pass_through(buf1, NULL, GNU_RETURN_ON_ERROR);
#endif
if (req->flags & GNU_COMMAND_FAILED) {
close_tmpfile();
error(FATAL, dis_err, req->addr);
}
rewind(pc->tmpfile);
while (fgets(buf2, BUFSIZE, pc->tmpfile)) {
if (STRNEQ(buf2, "Dump of") ||
STRNEQ(buf2, "End of"))
continue;
strip_beginning_whitespace(buf2);
if (do_load_module_filter)
load_module_filter(buf2, LM_DIS_FILTER);
if (STRNEQ(buf2, "0x"))
extract_hex(buf2, &curaddr, ':', TRUE);
if ((req->flags & GNU_FUNCTION_ONLY) &&
(curaddr >= req->addr2))
break;
if (do_machdep_filter)
machdep->dis_filter(curaddr, buf2, radix);
if (req->flags & GNU_FUNCTION_ONLY) {
if (req->flags &
GNU_PRINT_LINE_NUMBERS) {
get_line_number(curaddr, buf3,
FALSE);
if (!STREQ(buf3, buf4)) {
print_verbatim(
pc->saved_fp, buf3);
print_verbatim(
pc->saved_fp, "\n");
strcpy(buf4, buf3);
}
}
print_verbatim(pc->saved_fp, buf2);
continue;
} else {
if (curaddr < req->addr)
continue;
if (req->flags &
GNU_PRINT_LINE_NUMBERS) {
get_line_number(curaddr, buf3,
FALSE);
if (!STREQ(buf3, buf4)) {
print_verbatim(
pc->saved_fp, buf3);
print_verbatim(
pc->saved_fp, "\n");
strcpy(buf4, buf3);
}
}
print_verbatim(pc->saved_fp, buf2);
if (LASTCHAR(clean_line(buf2))
!= ':') {
if (++count == req->count)
break;
}
}
}
close_tmpfile();
}
}
else if (bug_bytes_entered)
return;
else cmd_usage(pc->curcmd, SYNOPSIS);
if (!reverse) {
FREEBUF(req->buf);
FREEBUF(req);
return;
}
revtarget = req->addr;
if ((sp = value_search(revtarget, NULL)) == NULL)
error(FATAL, "cannot resolve address: %lx\n", revtarget);
sprintf(buf1, "0x%lx", revtarget);
open_tmpfile();
req->addr = sp->value;
req->flags |= GNU_FUNCTION_ONLY;
req->command = GNU_RESOLVE_TEXT_ADDR;
gdb_interface(req);
req->flags &= ~GNU_COMMAND_FAILED;
savename = sp->name;
if ((sp = next_symbol(NULL, sp)))
req->addr2 = sp->value;
else {
close_tmpfile();
error(FATAL, "unable to determine symbol after %s\n", savename);
}
do_machdep_filter = machdep->dis_filter(req->addr, NULL, radix);
#ifdef OLDWAY
req->command = GNU_DISASSEMBLE;
req->fp = pc->tmpfile;
gdb_interface(req);
#else
sprintf(buf5, "x/%ldi 0x%lx",
(revtarget - req->addr) ? revtarget - req->addr : 1,
req->addr);
gdb_pass_through(buf5, NULL, GNU_RETURN_ON_ERROR);
#endif
if (req->flags & GNU_COMMAND_FAILED) {
close_tmpfile();
error(FATAL, dis_err, req->addr);
}
rewind(pc->tmpfile);
while (fgets(buf2, BUFSIZE, pc->tmpfile)) {
if (STRNEQ(buf2, "Dump of") || STRNEQ(buf2, "End of"))
continue;
strip_beginning_whitespace(buf2);
if (do_load_module_filter)
load_module_filter(buf2, LM_DIS_FILTER);
if (STRNEQ(buf2, "0x"))
extract_hex(buf2, &curaddr, ':', TRUE);
if (do_machdep_filter)
machdep->dis_filter(curaddr, buf2, radix);
if (req->flags & GNU_PRINT_LINE_NUMBERS) {
get_line_number(curaddr, buf3, FALSE);
if (!STREQ(buf3, buf4)) {
print_verbatim(pc->saved_fp, buf3);
print_verbatim(pc->saved_fp, "\n");
strcpy(buf4, buf3);
}
}
print_verbatim(pc->saved_fp, buf2);
if (STRNEQ(buf2, buf1)) {
if (LASTCHAR(clean_line(buf2)) != ':')
break;
ret = fgets(buf2, BUFSIZE, pc->tmpfile);
if (do_load_module_filter)
load_module_filter(buf2, LM_DIS_FILTER);
if (do_machdep_filter)
machdep->dis_filter(curaddr, buf2, radix);
print_verbatim(pc->saved_fp, buf2);
break;
}
}
close_tmpfile();
FREEBUF(req->buf);
FREEBUF(req);
}
/*
* x86 and x86_64 kernels may have file/line-number encoding
* asm()'d in just after the "ud2a" instruction, which confuses
* the disassembler and the x86 backtracer. Determine the
* number of bytes to skip.
*/
static void
BUG_bytes_init(void)
{
if (machine_type("X86"))
kt->BUG_bytes = BUG_x86();
else if (machine_type("X86_64"))
kt->BUG_bytes = BUG_x86_64();
}
static int
BUG_x86(void)
{
struct syment *sp, *spn;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char *arglist[MAXARGS];
ulong vaddr, fileptr;
int found;
/*
* Prior to 2.4.19, a call to do_BUG() preceded
* the standalone ud2a instruction.
*/
if (THIS_KERNEL_VERSION < LINUX(2,4,19))
return 0;
/*
* 2.6.20 introduced __bug_table support for i386,
* but even if CONFIG_DEBUG_BUGVERBOSE is not configured,
* the ud2a stands alone.
*/
if (THIS_KERNEL_VERSION >= LINUX(2,6,20))
return 0;
/*
* For previous kernel versions, it may depend upon
* whether CONFIG_DEBUG_BUGVERBOSE was configured:
*
* #ifdef CONFIG_DEBUG_BUGVERBOSE
* #define BUG() \
* __asm__ __volatile__( "ud2\n" \
* "\t.word %c0\n" \
* "\t.long %c1\n" \
* : : "i" (__LINE__), "i" (__FILE__))
* #else
* #define BUG() __asm__ __volatile__("ud2\n")
* #endif
*
* But that's not necessarily true, since there are
* pre-2.6.11 versions that force it like so:
*
* #if 1 /- Set to zero for a slightly smaller kernel -/
* #define BUG() \
* __asm__ __volatile__( "ud2\n" \
* "\t.word %c0\n" \
* "\t.long %c1\n" \
* : : "i" (__LINE__), "i" (__FILE__))
* #else
* #define BUG() __asm__ __volatile__("ud2\n")
* #endif
*/
/*
* This works if in-kernel config data is available.
*/
if ((THIS_KERNEL_VERSION >= LINUX(2,6,11)) &&
(kt->flags & BUGVERBOSE_OFF))
return 0;
/*
* At this point, it's a pretty safe bet that it's configured,
* but to be sure, disassemble a known BUG() caller and
* verify that the encoding is there.
*/
#define X86_BUG_BYTES (6) /* sizeof(short) + sizeof(pointer) */
if (!(sp = symbol_search("do_exit")) ||
!(spn = next_symbol(NULL, sp)))
return X86_BUG_BYTES;
sprintf(buf1, "x/%ldi 0x%lx", spn->value - sp->value, sp->value);
found = FALSE;
vaddr = 0;
open_tmpfile();
gdb_pass_through(buf1, pc->tmpfile, GNU_RETURN_ON_ERROR);
rewind(pc->tmpfile);
while (fgets(buf2, BUFSIZE, pc->tmpfile)) {
if (parse_line(buf2, arglist) < 3)
continue;
if ((vaddr = htol(strip_ending_char(arglist[0], ':'),
RETURN_ON_ERROR|QUIET, NULL)) >= spn->value)
continue;
if (STREQ(arglist[2], "ud2a")) {
found = TRUE;
break;
}
}
close_tmpfile();
if (!found || !readmem(vaddr+4, KVADDR, &fileptr, sizeof(ulong),
"BUG filename pointer", RETURN_ON_ERROR|QUIET))
return X86_BUG_BYTES;
if (!IS_KVADDR(fileptr)) {
if (CRASHDEBUG(1))
fprintf(fp,
"no filename pointer: kt->BUG_bytes: 0\n");
return 0;
}
if (!read_string(fileptr, buf1, BUFSIZE-1))
error(WARNING,
"cannot read BUG (ud2a) encoded filename address: %lx\n",
fileptr);
else if (CRASHDEBUG(1))
fprintf(fp, "BUG bytes filename encoding: [%s]\n", buf1);
return X86_BUG_BYTES;
}
static int
BUG_x86_64(void)
{
/*
* 2.6.20 introduced __bug_table support for x86_64,
* but even if CONFIG_DEBUG_BUGVERBOSE is not configured,
* the ud2a stands alone.
*/
if (THIS_KERNEL_VERSION >= LINUX(2,6,20))
return 0;
/*
* The original bug_frame structure looks like this, which
* causes the disassembler to go off into the weeds:
*
* struct bug_frame {
* unsigned char ud2[2];
* char *filename;
* unsigned short line;
* }
*
* In 2.6.13, fake push and ret instructions were encoded
* into the frame so that the disassembly would at least
* "work", although the two fake instructions show nonsensical
* arguments:
*
* struct bug_frame {
* unsigned char ud2[2];
* unsigned char push;
* signed int filename;
* unsigned char ret;
* unsigned short line;
* }
*/
if (STRUCT_EXISTS("bug_frame"))
return (int)(STRUCT_SIZE("bug_frame") - 2);
return 0;
}
/*
* Callback from gdb disassembly code.
*/
int
kernel_BUG_encoding_bytes(void)
{
return kt->BUG_bytes;
}
#ifdef NOT_USED
/*
* To avoid premature stoppage/extension of a dis <function> that includes
* one of the following x86/gcc 3.2 constant declarations, don't allow them
* to be considered the next text symbol.
*/
static struct syment *
next_text_symbol(struct syment *sp_in)
{
return next_symbol(NULL, sp_in);
struct syment *sp;
sp = sp_in;
while ((sp = next_symbol(NULL, sp))) {
if (STREQ(sp->name, "__constant_c_and_count_memset") ||
STREQ(sp->name, "__constant_copy_from_user") ||
STREQ(sp->name, "__constant_copy_from_user_nocheck") ||
STREQ(sp->name, "__constant_copy_to_user") ||
STREQ(sp->name, "__constant_copy_to_user_nocheck") ||
STREQ(sp->name, "__constant_memcpy") ||
STREQ(sp->name, "__constant_c_and_count_memset") ||
STREQ(sp->name, "__constant_c_x_memset") ||
STREQ(sp->name, "__constant_memcpy")) {
continue;
}
break;
}
return sp;
}
#endif /* NOT_USED */
/*
* Nothing to do.
*/
int
generic_dis_filter(ulong value, char *buf, unsigned int output_radix)
{
return TRUE;
}
#define FRAMESIZE_DEBUG_MESSAGE \
"\nx86 usage: bt -D [size|clear|dump|seek|noseek|validate|novalidate] [-I eip]\n If eip: set its associated framesize to size.\n \"validate/novalidate\" will turn on/off V bit for this eip entry.\n If !eip: \"clear\" will clear the framesize cache and RA seek/noseek flags.\n \"dump\" will dump the current framesize cache entries.\n \"seek/noseek\" turns on/off RA seeking.\n \"validate/novalidate\" turns on/off V bit for all current entries.\n\nx86_64 usage: bt -D [clear|dump|validate|framepointer|noframepointer] [-I rip]\n If rip: \"validate\" will verbosely recalculate the framesize without\n framepointers (no stack reference).\n If !rip: \"clear\" will clear the framesize cache.\n \"dump\" will dump the current framesize cache entries.\n \"framepointer/noframepointer\" toggle the FRAMEPOINTER flag and\n clear the framesize cache."
/*
* Display a kernel stack backtrace. Arguments may be any number pid or task
* values, or, if no arguments are given, the stack trace of the current
* context will be displayed. Alternatively:
*
* -a displays the stack traces of the active tasks on each CPU.
* (only applicable to crash dumps)
* -r display raw stack data, consisting of a memory dump of the two
* pages of memory containing the task_union structure.
* -s displays arguments symbolically.
*/
void
clone_bt_info(struct bt_info *orig, struct bt_info *new,
struct task_context *tc)
{
BCOPY(orig, new, sizeof(*new));
new->stackbuf = NULL;
new->tc = tc;
new->task = tc->task;
new->stackbase = GET_STACKBASE(tc->task);
new->stacktop = GET_STACKTOP(tc->task);
}
#define BT_SETUP(TC) \
clone_bt_info(&bt_setup, bt, (TC)); \
if (refptr) { \
BZERO(&reference, sizeof(struct reference)); \
bt->ref = &reference; \
bt->ref->str = refptr; \
}
#define DO_TASK_BACKTRACE() \
{ \
BT_SETUP(tc); \
if (!BT_REFERENCE_CHECK(bt)) \
print_task_header(fp, tc, subsequent++); \
back_trace(bt); \
}
#define DO_THREAD_GROUP_BACKTRACE() \
{ \
tc = pid_to_context(tgid); \
BT_SETUP(tc); \
if (!BT_REFERENCE_CHECK(bt)) \
print_task_header(fp, tc, subsequent++); \
if (setjmp(pc->foreach_loop_env)) { \
pc->flags &= ~IN_FOREACH; \
free_all_bufs(); \
} else { \
pc->flags |= IN_FOREACH; \
back_trace(bt); \
pc->flags &= ~IN_FOREACH; \
} \
tc = FIRST_CONTEXT(); \
for (i = 0; i < RUNNING_TASKS(); i++, tc++) { \
if (tc->pid == tgid) \
continue; \
if (task_tgid(tc->task) != tgid) \
continue; \
BT_SETUP(tc); \
if (!BT_REFERENCE_CHECK(bt)) \
print_task_header(fp, tc, subsequent++);\
if (setjmp(pc->foreach_loop_env)) { \
pc->flags &= ~IN_FOREACH; \
free_all_bufs(); \
} else { \
pc->flags |= IN_FOREACH; \
back_trace(bt); \
pc->flags &= ~IN_FOREACH; \
} \
} \
pc->flags &= ~IN_FOREACH; \
}
void
cmd_bt(void)
{
int i, c;
ulong value, *cpus;
struct task_context *tc;
int subsequent, active;
struct stack_hook hook;
struct bt_info bt_info, bt_setup, *bt;
struct reference reference;
char *refptr;
ulong tgid, task;
char arg_buf[BUFSIZE];
tc = NULL;
cpus = NULL;
subsequent = active = 0;
hook.eip = hook.esp = 0;
refptr = 0;
bt = &bt_info;
BZERO(bt, sizeof(struct bt_info));
if (kt->flags & USE_OLD_BT)
bt->flags |= BT_OLD_BACK_TRACE;
while ((c = getopt(argcnt, args, "D:fFI:S:c:aAloreEgstTdxR:O")) != EOF) {
switch (c)
{
case 'f':
bt->flags |= BT_FULL;
break;
case 'F':
if (bt->flags & BT_FULL_SYM_SLAB)
bt->flags |= BT_FULL_SYM_SLAB2;
else
bt->flags |= (BT_FULL|BT_FULL_SYM_SLAB);
break;
case 'o':
if (XEN_HYPER_MODE())
option_not_supported(c);
bt->flags |= BT_OLD_BACK_TRACE;
break;
case 'O':
if (!(machine_type("X86") || machine_type("X86_64")) ||
XEN_HYPER_MODE())
option_not_supported(c);
else if (kt->flags & USE_OLD_BT) {
/*
* Make this setting idempotent across the use of
* $HOME/.crashrc, ./.crashrc, and "-i input" files.
* If we've been here before during initialization,
* leave it alone.
*/
if (pc->flags & INIT_IFILE) {
error(INFO, "use old bt method by default (already set)\n");
return;
}
kt->flags &= ~USE_OLD_BT;
error(INFO, "use new bt method by default\n");
} else {
kt->flags |= USE_OLD_BT;
error(INFO, "use old bt method by default\n");
}
return;
case 'R':
if (refptr)
error(INFO, "only one -R option allowed\n");
else
refptr = optarg;
break;
case 'l':
if (NO_LINE_NUMBERS())
error(INFO, "line numbers are not available\n");
else
bt->flags |= BT_LINE_NUMBERS;
break;
case 'E':
if (XEN_HYPER_MODE())
option_not_supported(c);
bt->flags |= BT_EFRAME_SEARCH|BT_EFRAME_SEARCH2;
bt->hp = &hook;
break;
case 'e':
if (XEN_HYPER_MODE())
option_not_supported(c);
bt->flags |= BT_EFRAME_SEARCH;
break;
case 'g':
#ifdef GDB_5_3
bt->flags |= BT_USE_GDB;
#else
bt->flags |= BT_THREAD_GROUP;
#endif
break;
case 'x':
if (bt->radix == 10)
error(FATAL,
"-d and -x are mutually exclusive\n");
bt->radix = 16;
break;
case 'd':
if (bt->radix == 16)
error(FATAL,
"-d and -x are mutually exclusive\n");
bt->radix = 10;
break;
case 'I':
bt->hp = &hook;
hook.eip = convert(optarg, FAULT_ON_ERROR,
NULL, NUM_HEX|NUM_EXPR);
break;
case 'D':
if (STREQ(optarg, "seek")) {
kt->flags |= RA_SEEK;
kt->flags &= ~NO_RA_SEEK;
return;
} else if (STREQ(optarg, "noseek")) {
kt->flags |= NO_RA_SEEK;
kt->flags &= ~RA_SEEK;
return;
}
bt->hp = &hook;
bt->flags |= BT_FRAMESIZE_DEBUG;
if (STREQ(optarg, "dump"))
hook.esp = 1;
else if (STRNEQ(optarg, "level-"))
bt->debug = dtol(optarg+6, FAULT_ON_ERROR, NULL);
else if (STREQ(optarg, "validate"))
hook.esp = (ulong)-1;
else if (STREQ(optarg, "novalidate"))
hook.esp = (ulong)-2;
else if (STREQ(optarg, "framepointer"))
hook.esp = (ulong)-3;
else if (STREQ(optarg, "noframepointer"))
hook.esp = (ulong)-4;
else if (STREQ(optarg, "clear")) {
kt->flags &= ~(RA_SEEK|NO_RA_SEEK);
hook.esp = 0;
} else if (*optarg == '-') {
hook.esp = dtol(optarg+1, FAULT_ON_ERROR, NULL);
hook.esp = (ulong)(0 - (long)hook.esp);
} else if (STREQ(optarg, "dwarf") || STREQ(optarg, "cfi")) {
if (!(kt->flags & DWARF_UNWIND_CAPABLE))
return;
} else
hook.esp = dtol(optarg, FAULT_ON_ERROR, NULL);
break;
case 'S':
bt->hp = &hook;
hook.esp = htol(optarg, FAULT_ON_ERROR, NULL);
if (!hook.esp)
error(FATAL,
"invalid stack address for this task: 0\n");
break;
case 'c':
if (bt->flags & BT_CPUMASK) {
error(INFO, "only one -c option allowed\n");
argerrs++;
} else {
bt->flags |= BT_CPUMASK;
BZERO(arg_buf, BUFSIZE);
strncpy(arg_buf, optarg, strlen(optarg));
cpus = get_cpumask_buf();
}
break;
case 'A':
if (!machine_type("S390X"))
option_not_supported(c);
bt->flags |= BT_SHOW_ALL_REGS; /* FALLTHROUGH */
case 'a':
active++;
break;
case 'r':
bt->flags |= BT_RAW;
break;
case 's':
bt->flags |= BT_SYMBOL_OFFSET;
break;
case 'T':
bt->flags |= BT_TEXT_SYMBOLS_ALL;
case 't':
bt->flags |= BT_TEXT_SYMBOLS;
break;
default:
argerrs++;
if (optopt == 'D') {
fprintf(fp, FRAMESIZE_DEBUG_MESSAGE);
return;
}
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (bt->flags & BT_FRAMESIZE_DEBUG) {
if (machdep->flags & FRAMESIZE_DEBUG) {
while (args[optind]) {
if (!hook.eip)
hook.eip = convert(args[optind],
FAULT_ON_ERROR, NULL,
NUM_HEX|NUM_EXPR);
else {
fprintf(fp, FRAMESIZE_DEBUG_MESSAGE);
return;
}
optind++;
}
machdep->back_trace(bt);
return;
}
error(FATAL, "framesize debug not available\n");
}
BCOPY(bt, &bt_setup, sizeof(struct bt_info));
if (bt->flags & BT_EFRAME_SEARCH2) {
tc = CURRENT_CONTEXT(); /* borrow stack */
BT_SETUP(tc);
if (bt->flags & BT_CPUMASK) {
make_cpumask(arg_buf, cpus, FAULT_ON_ERROR, NULL);
bt->cpumask = cpus;
}
back_trace(bt);
return;
}
if (XEN_HYPER_MODE()) {
#ifdef XEN_HYPERVISOR_ARCH
/* "task" means vcpu for xen hypervisor */
if (active) {
for (c = 0; c < XEN_HYPER_MAX_CPUS(); c++) {
if (!xen_hyper_test_pcpu_id(c))
continue;
fake_tc.task = xen_hyper_pcpu_to_active_vcpu(c);
BT_SETUP(&fake_tc);
if (!BT_REFERENCE_CHECK(bt))
xen_hyper_print_bt_header(fp, fake_tc.task,
subsequent++);
back_trace(bt);
}
} else {
if (args[optind]) {
fake_tc.task = xen_hyper_pcpu_to_active_vcpu(
convert(args[optind], 0, NULL, NUM_DEC | NUM_HEX));
} else {
fake_tc.task = XEN_HYPER_VCPU_LAST_CONTEXT()->vcpu;
}
BT_SETUP(&fake_tc);
if (!BT_REFERENCE_CHECK(bt))
xen_hyper_print_bt_header(fp, fake_tc.task, 0);
back_trace(bt);
}
return;
#else
error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
#endif
}
if (bt->flags & BT_CPUMASK) {
if (LIVE())
error(FATAL,
"-c option not supported on a live system or live dump\n");
if (bt->flags & BT_THREAD_GROUP)
error(FATAL,
"-c option cannot be used with the -g option\n");
make_cpumask(arg_buf, cpus, FAULT_ON_ERROR, NULL);
for (i = 0; i < kt->cpus; i++) {
if (NUM_IN_BITMAP(cpus, i)) {
if (hide_offline_cpu(i)) {
error(INFO, "%sCPU %d is OFFLINE.\n",
subsequent++ ? "\n" : "", i);
continue;
}
if ((task = get_active_task(i)))
tc = task_to_context(task);
else
error(FATAL, "cannot determine active task on cpu %ld\n", i);
DO_TASK_BACKTRACE();
}
}
FREEBUF(cpus);
return;
}
if (active) {
if (LIVE())
error(FATAL,
"-%c option not supported on a live system or live dump\n",
bt->flags & BT_SHOW_ALL_REGS ? 'A' : 'a');
if (bt->flags & BT_THREAD_GROUP)
error(FATAL,
"-a option cannot be used with the -g option\n");
for (c = 0; c < NR_CPUS; c++) {
if (setjmp(pc->foreach_loop_env)) {
pc->flags &= ~IN_FOREACH;
free_all_bufs();
continue;
}
if ((tc = task_to_context(tt->panic_threads[c]))) {
pc->flags |= IN_FOREACH;
DO_TASK_BACKTRACE();
pc->flags &= ~IN_FOREACH;
}
}
return;
}
if (!args[optind]) {
if (CURRENT_PID() && (bt->flags & BT_THREAD_GROUP)) {
tgid = task_tgid(CURRENT_TASK());
DO_THREAD_GROUP_BACKTRACE();
} else {
tc = CURRENT_CONTEXT();
DO_TASK_BACKTRACE();
}
return;
}
while (args[optind]) {
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
for (tc = pid_to_context(value); tc; tc = tc->tc_next) {
if (tc->pid && (bt->flags & BT_THREAD_GROUP)) {
tgid = task_tgid(tc->task);
DO_THREAD_GROUP_BACKTRACE();
break;
} else if (tc->tc_next) {
if (setjmp(pc->foreach_loop_env)) {
pc->flags &= ~IN_FOREACH;
free_all_bufs();
continue;
}
pc->flags |= IN_FOREACH;
DO_TASK_BACKTRACE();
pc->flags &= ~IN_FOREACH;
} else
DO_TASK_BACKTRACE();
}
break;
case STR_TASK:
if (tc->pid && (bt->flags & BT_THREAD_GROUP)) {
tgid = task_tgid(value);
DO_THREAD_GROUP_BACKTRACE();
} else
DO_TASK_BACKTRACE();
break;
case STR_INVALID:
error(INFO, "%sinvalid task or pid value: %s\n",
subsequent++ ? "\n" : "", args[optind]);
break;
}
optind++;
}
}
void
print_stack_text_syms(struct bt_info *bt, ulong esp, ulong eip)
{
ulong next_sp, next_pc;
int i;
ulong *up;
struct load_module *lm;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
if (bt->flags & BT_TEXT_SYMBOLS) {
if (!(bt->flags & BT_TEXT_SYMBOLS_ALL))
fprintf(fp, "%sSTART: %s at %lx\n",
space(VADDR_PRLEN > 8 ? 14 : 6),
bt->flags & BT_SYMBOL_OFFSET ?
value_to_symstr(eip, buf2, bt->radix) :
closest_symbol(eip), eip);
}
if (bt->hp)
bt->hp->eip = bt->hp->esp = 0;
next_pc = next_sp = 0;
for (i = (esp - bt->stackbase)/sizeof(ulong);
i < LONGS_PER_STACK; i++) {
up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]);
if (is_kernel_text_offset(*up)) {
if (!next_pc)
next_pc = *up;
else if (!next_sp)
next_sp = bt->stackbase + (i * sizeof(long));
}
if (is_kernel_text(*up) && (bt->flags &
(BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_PRINT))) {
if (bt->flags & (BT_ERROR_MASK|BT_TEXT_SYMBOLS)) {
fprintf(fp, " %s[%s] %s at %lx",
bt->flags & BT_ERROR_MASK ?
" " : "",
mkstring(buf1, VADDR_PRLEN,
RJUST|LONG_HEX,
MKSTR(bt->stackbase +
(i * sizeof(long)))),
bt->flags & BT_SYMBOL_OFFSET ?
value_to_symstr(*up, buf2, bt->radix) :
closest_symbol(*up), *up);
if (module_symbol(*up, NULL, &lm, NULL, 0))
fprintf(fp, " [%s]", lm->mod_name);
fprintf(fp, "\n");
} else
fprintf(fp, "%lx: %s\n",
bt->stackbase +
(i * sizeof(long)),
value_to_symstr(*up, buf1, 0));
}
}
if (bt->hp) {
bt->hp->eip = next_pc;
bt->hp->esp = next_sp;
}
}
int
in_alternate_stack(int cpu, ulong address)
{
if (cpu >= NR_CPUS)
return FALSE;
if (machdep->in_alternate_stack)
if (machdep->in_alternate_stack(cpu, address))
return TRUE;
if (tt->flags & IRQSTACKS) {
if (in_irq_ctx(BT_SOFTIRQ, cpu, address) ||
in_irq_ctx(BT_HARDIRQ, cpu, address))
return TRUE;
}
return FALSE;
}
/*
* Gather the EIP, ESP and stack address for the target task, and passing
* them on to the machine-specific back trace command.
*/
void
back_trace(struct bt_info *bt)
{
int i;
ulong *up;
char buf[BUFSIZE];
ulong eip, esp;
struct bt_info btsave = { 0 };
if (bt->flags & BT_RAW) {
if (bt->hp && bt->hp->esp)
esp = bt->hp->esp;
else
esp = GET_STACKBASE(bt->task);
raw_stack_dump(esp, STACKSIZE());
return;
}
if (LIVE() && !(bt->flags & BT_EFRAME_SEARCH) &&
((bt->task == tt->this_task) || is_task_active(bt->task))) {
if (BT_REFERENCE_CHECK(bt) ||
bt->flags & (BT_TEXT_SYMBOLS_PRINT|BT_TEXT_SYMBOLS_NOPRINT))
return;
if (!(bt->flags &
(BT_KSTACKP|BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_ALL)))
fprintf(fp, "(active)\n");
if (!(bt->flags & (BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_ALL) || REMOTE_PAUSED()))
return;
}
fill_stackbuf(bt);
if (CRASHDEBUG(4)) {
for (i = 0, up = (ulong *)bt->stackbuf;
i < LONGS_PER_STACK; i++, up++) {
if (is_kernel_text(*up))
fprintf(fp, "%lx: %s\n",
tt->flags & THREAD_INFO ?
bt->tc->thread_info +
(i * sizeof(long)) :
bt->task + (i * sizeof(long)),
value_to_symstr(*up, buf, 0));
}
}
if (BT_REFERENCE_CHECK(bt)) {
if (can_eval(bt->ref->str)) {
bt->ref->hexval = eval(bt->ref->str,
FAULT_ON_ERROR, NULL);
bt->ref->cmdflags |= BT_REF_HEXVAL;
} else if (hexadecimal(bt->ref->str, 0)) {
bt->ref->hexval = htol(bt->ref->str,
FAULT_ON_ERROR, NULL);
bt->ref->cmdflags |= BT_REF_HEXVAL;
} else
bt->ref->cmdflags |= BT_REF_SYMBOL;
}
if (bt->flags & BT_EFRAME_SEARCH) {
machdep->eframe_search(bt);
return;
}
if (bt->hp) {
if (bt->hp->esp && !INSTACK(bt->hp->esp, bt) &&
!in_alternate_stack(bt->tc->processor, bt->hp->esp))
error(FATAL,
"non-process stack address for this task: %lx\n"
" (valid range: %lx - %lx)\n",
bt->hp->esp, bt->stackbase, bt->stacktop);
eip = bt->hp->eip;
esp = bt->hp->esp;
machdep->get_stack_frame(bt, eip ? NULL : &eip,
esp ? NULL : &esp);
if (in_irq_ctx(BT_HARDIRQ, bt->tc->processor, esp)) {
bt->stackbase = tt->hardirq_ctx[bt->tc->processor];
bt->stacktop = bt->stackbase + STACKSIZE();
alter_stackbuf(bt);
bt->flags |= BT_HARDIRQ;
} else if (in_irq_ctx(BT_SOFTIRQ, bt->tc->processor, esp)) {
bt->stackbase = tt->softirq_ctx[bt->tc->processor];
bt->stacktop = bt->stackbase + STACKSIZE();
alter_stackbuf(bt);
bt->flags |= BT_SOFTIRQ;
}
} else if (XEN_HYPER_MODE())
machdep->get_stack_frame(bt, &eip, &esp);
else if (NETDUMP_DUMPFILE())
get_netdump_regs(bt, &eip, &esp);
else if (KDUMP_DUMPFILE())
get_kdump_regs(bt, &eip, &esp);
else if (DISKDUMP_DUMPFILE())
get_diskdump_regs(bt, &eip, &esp);
else if (KVMDUMP_DUMPFILE())
get_kvmdump_regs(bt, &eip, &esp);
else if (LKCD_DUMPFILE())
get_lkcd_regs(bt, &eip, &esp);
else if (XENDUMP_DUMPFILE())
get_xendump_regs(bt, &eip, &esp);
else if (SADUMP_DUMPFILE())
get_sadump_regs(bt, &eip, &esp);
else if (REMOTE_PAUSED()) {
if (!is_task_active(bt->task) || !get_remote_regs(bt, &eip, &esp))
machdep->get_stack_frame(bt, &eip, &esp);
} else
machdep->get_stack_frame(bt, &eip, &esp);
if (bt->flags & BT_KSTACKP) {
bt->stkptr = esp;
return;
}
if (ACTIVE() && !INSTACK(esp, bt)) {
sprintf(buf, "/proc/%ld", bt->tc->pid);
if (!file_exists(buf, NULL))
error(INFO, "task no longer exists\n");
else
error(INFO,
"invalid/stale stack pointer for this task: %lx\n",
esp);
return;
}
if (bt->flags &
(BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_PRINT|BT_TEXT_SYMBOLS_NOPRINT)) {
if (bt->flags & BT_TEXT_SYMBOLS_ALL) {
esp = bt->stackbase +
((tt->flags & THREAD_INFO) ?
SIZE(thread_info) : SIZE(task_struct));
eip = 0;
}
if (machdep->flags & MACHDEP_BT_TEXT) {
bt->instptr = eip;
bt->stkptr = esp;
machdep->back_trace(bt);
} else
print_stack_text_syms(bt, esp, eip);
if (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)) {
struct bt_info btloc;
struct stack_hook stack_hook;
BZERO(&btloc, sizeof(struct bt_info));
BZERO(&stack_hook, sizeof(struct stack_hook));
btloc.flags = bt->flags & ~(BT_HARDIRQ|BT_SOFTIRQ);
btloc.hp = &stack_hook;
btloc.tc = bt->tc;
btloc.task = bt->task;
btloc.stackbase = GET_STACKBASE(bt->task);
btloc.stacktop = GET_STACKTOP(bt->task);
switch (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ))
{
case BT_HARDIRQ:
btloc.hp->eip = symbol_value("do_IRQ");
if (symbol_exists("__do_IRQ"))
btloc.hp->esp = ULONG(bt->stackbuf +
OFFSET(thread_info_previous_esp));
else
btloc.hp->esp = ULONG(bt->stackbuf +
SIZE(irq_ctx) -
(sizeof(char *)*2));
fprintf(fp, "--- <hard IRQ> ---\n");
if (in_irq_ctx(BT_SOFTIRQ, bt->tc->processor, btloc.hp->esp)) {
btloc.flags |= BT_SOFTIRQ;
btloc.stackbase = tt->softirq_ctx[bt->tc->processor];
btloc.stacktop = btloc.stackbase + STACKSIZE();
}
break;
case BT_SOFTIRQ:
btloc.hp->eip = symbol_value("do_softirq");
btloc.hp->esp = ULONG(bt->stackbuf +
OFFSET(thread_info_previous_esp));
fprintf(fp, "--- <soft IRQ> ---\n");
break;
}
back_trace(&btloc);
}
return;
}
bt->instptr = eip;
bt->stkptr = esp;
complete_trace:
if (BT_REFERENCE_CHECK(bt))
BCOPY(bt, &btsave, sizeof(struct bt_info));
if (CRASHDEBUG(4))
dump_bt_info(bt, "back_trace");
machdep->back_trace(bt);
if ((bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)) && restore_stack(bt))
goto complete_trace;
if (BT_REFERENCE_FOUND(bt)) {
#ifdef XEN_HYPERVISOR_ARCH
if (XEN_HYPER_MODE())
xen_hyper_print_bt_header(fp, bt->task, 0);
else
print_task_header(fp, task_to_context(bt->task), 0);
#else
print_task_header(fp, task_to_context(bt->task), 0);
#endif /* XEN_HYPERVISOR_ARCH */
BCOPY(&btsave, bt, sizeof(struct bt_info));
bt->ref = NULL;
machdep->back_trace(bt);
fprintf(fp, "\n");
}
}
/*
* Restore a bt_info to make the jump from an IRQ stack to the task's
* normal stack.
*/
static int
restore_stack(struct bt_info *bt)
{
ulonglong type;
struct syment *sp;
ulong retvaddr;
bt->instptr = bt->stkptr = 0;
type = 0;
switch (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ))
{
case BT_HARDIRQ:
retvaddr = ULONG(bt->stackbuf +
SIZE(irq_ctx) - sizeof(char *));
if ((sp = value_search(retvaddr, NULL)) &&
STREQ(sp->name, "do_IRQ"))
bt->instptr = retvaddr;
else
bt->instptr = symbol_value("do_IRQ");
if (symbol_exists("__do_IRQ"))
bt->stkptr = ULONG(bt->stackbuf +
OFFSET(thread_info_previous_esp));
else
bt->stkptr = ULONG(bt->stackbuf +
SIZE(irq_ctx) - (sizeof(char *)*2));
type = BT_HARDIRQ;
break;
case BT_SOFTIRQ:
retvaddr = ULONG(bt->stackbuf +
SIZE(irq_ctx) - sizeof(char *));
if ((sp = value_search(retvaddr, NULL)) &&
STREQ(sp->name, "do_softirq"))
bt->instptr = retvaddr;
else
bt->instptr = symbol_value("do_softirq");
bt->stkptr = ULONG(bt->stackbuf +
OFFSET(thread_info_previous_esp));
type = BT_SOFTIRQ;
break;
}
if ((type == BT_HARDIRQ) && bt->instptr &&
in_irq_ctx(BT_SOFTIRQ, bt->tc->processor, bt->stkptr)) {
bt->flags &= ~BT_HARDIRQ;
bt->flags |= BT_SOFTIRQ;
bt->stackbase = tt->softirq_ctx[bt->tc->processor];
bt->stacktop = bt->stackbase + STACKSIZE();
if (!readmem(bt->stackbase, KVADDR, bt->stackbuf,
bt->stacktop - bt->stackbase,
"restore softirq_ctx stack", RETURN_ON_ERROR)) {
error(INFO,
"read of softirq stack at %lx failed\n",
bt->stackbase);
type = 0;
}
} else {
bt->flags &= ~(BT_HARDIRQ|BT_SOFTIRQ);
bt->stackbase = GET_STACKBASE(bt->tc->task);
bt->stacktop = GET_STACKTOP(bt->tc->task);
if (!readmem(bt->stackbase, KVADDR, bt->stackbuf,
bt->stacktop - bt->stackbase,
"restore_stack contents", RETURN_ON_ERROR)) {
error(INFO, "restore_stack of stack at %lx failed\n",
bt->stackbase);
type = 0;
}
if (!(bt->instptr && INSTACK(bt->stkptr, bt)))
type = 0;
}
if (type) {
if (!BT_REFERENCE_CHECK(bt))
fprintf(fp, "--- %s ---\n", type == BT_HARDIRQ ?
"<hard IRQ>" : "<soft IRQ>");
return TRUE;
}
return FALSE;
}
#define MAXHOOKS (100)
struct stack_hook *
gather_text_list(struct bt_info *bt)
{
int cnt;
struct bt_info btloc;
char buf[BUFSIZE], *p1;
struct stack_hook *hooks;
ulong esp, eip;
FILE *savedfp;
BCOPY(bt, &btloc, sizeof(struct bt_info));
hooks = (struct stack_hook *)GETBUF(sizeof(struct stack_hook)*MAXHOOKS);
cnt = 0;
savedfp = fp;
open_tmpfile2();
fp = pc->tmpfile2;
btloc.flags = BT_TEXT_SYMBOLS_PRINT;
back_trace(&btloc);
rewind(pc->tmpfile2);
while (fgets(buf, BUFSIZE, pc->tmpfile2)) {
if ((p1 = strstr(buf, ":"))) {
esp = eip = 0;
*p1 = NULLCHAR;
if (((esp = htol(buf, RETURN_ON_ERROR, NULL)) != BADADDR)
&& INSTACK(esp, bt))
eip = GET_STACK_ULONG(esp);
if (esp && eip) {
hooks[cnt].esp = esp;
hooks[cnt].eip = eip;
if (++cnt == MAXHOOKS)
break;
}
}
}
close_tmpfile2();
fp = savedfp;
if (cnt)
return (bt->textlist = hooks);
else {
FREEBUF(hooks);
return (bt->textlist = NULL);
}
}
/*
* Debug routine most likely useful from above in back_trace()
*/
void
dump_bt_info(struct bt_info *bt, char *where)
{
fprintf(fp, "[%lx] %s:\n", (ulong)bt, where);
fprintf(fp, " task: %lx\n", bt->task);
fprintf(fp, " flags: %llx\n", bt->flags);
fprintf(fp, " instptr: %lx\n", bt->instptr);
fprintf(fp, " stkptr: %lx\n", bt->stkptr);
fprintf(fp, " bptr: %lx\n", bt->bptr);
fprintf(fp, " stackbase: %lx\n", bt->stackbase);
fprintf(fp, " stacktop: %lx\n", bt->stacktop);
fprintf(fp, " tc: %lx ", (ulong)bt->tc);
if (bt->tc)
fprintf(fp, "(%ld, %lx)\n", bt->tc->pid, bt->tc->task);
else
fprintf(fp, "(unknown context)\n");
fprintf(fp, " hp: %lx\n", (ulong)bt->hp);
fprintf(fp, " ref: %lx\n", (ulong)bt->ref);
fprintf(fp, " stackbuf: %lx\n", (ulong)bt->stackbuf);
fprintf(fp, " textlist: %lx\n", (ulong)bt->textlist);
fprintf(fp, " frameptr: %lx\n", (ulong)bt->frameptr);
fprintf(fp, " call_target: %s\n", bt->call_target ?
bt->call_target : "none");
fprintf(fp, " eframe_ip: %lx\n", bt->eframe_ip);
fprintf(fp, " debug: %lx\n", bt->debug);
fprintf(fp, " radix: %ld\n", bt->radix);
fprintf(fp, " cpumask: %lx\n", (ulong)bt->cpumask);
}
/*
* LKCD doesn't save state of the active tasks in the TSS, so poke around
* the raw stack for some reasonable hooks.
*/
static void
get_lkcd_regs(struct bt_info *bt, ulong *eip, ulong *esp)
{
int i;
char *sym;
ulong *up;
ulong sysrq_eip, sysrq_esp;
if (!is_task_active(bt->task)) {
machdep->get_stack_frame(bt, eip, esp);
return;
}
/* try to get it from the header */
if (get_lkcd_regs_for_cpu(bt, eip, esp) == 0)
return;
/* if that fails: do guessing */
sysrq_eip = sysrq_esp = 0;
for (i = 0, up = (ulong *)bt->stackbuf; i < LONGS_PER_STACK; i++, up++){
sym = closest_symbol(*up);
if (STREQ(sym, "dump_execute") && INSTACK(*(up-1), bt)) {
*eip = *up;
*esp = *(up-1);
return;
}
/* Begin 3PAR change -- required for our panic path */
if (STREQ(sym, "dump_ipi") && INSTACK(*(up-1), bt)) {
*eip = *up;
*esp = *(up-1);
return;
}
/* End 3PAR change */
if (STREQ(sym, "panic") && INSTACK(*(up-1), bt)) {
*eip = *up;
*esp = *(up-1);
return;
}
/* Egenera */
if (STREQ(sym, "netdump_ipi")) {
*eip = *up;
*esp = bt->task +
((char *)(up-1) - bt->stackbuf);
return;
}
if (STREQ(sym, "dump_execute")) {
*eip = *up;
*esp = bt->stackbase +
((char *)(up) - bt->stackbuf);
return;
}
if (STREQ(sym, "vmdump_nmi_callback")) {
*eip = *up;
*esp = bt->stackbase +
((char *)(up) - bt->stackbuf);
return;
}
if (STREQ(sym, "smp_stop_cpu_interrupt")) {
*eip = *up;
*esp = bt->task +
((char *)(up-1) - bt->stackbuf);
return;
}
if (STREQ(sym, "stop_this_cpu")) {
*eip = *up;
*esp = bt->task +
((char *)(up-1) - bt->stackbuf);
return;
}
if (SYSRQ_TASK(bt->task) &&
STREQ(sym, "smp_call_function_interrupt")) {
sysrq_eip = *up;
sysrq_esp = bt->task +
((char *)(up-1) - bt->stackbuf);
}
}
if (sysrq_eip) {
*eip = sysrq_eip;
*esp = sysrq_esp;
return;
}
machdep->get_stack_frame(bt, eip, esp);
}
/*
* Store the head of the kernel module list for future use.
* Count the number of symbols defined by all modules in the system,
* and pass it on to store_module_symbols() to deal with.
*/
void
module_init(void)
{
int i, c;
ulong size, mod, mod_next;
uint nsyms;
ulong total, numksyms;
char *modbuf, *kallsymsbuf;
ulong kallsyms_header;
struct syment *sp, *sp_array[10];
struct kernel_list_head list;
int modules_found;
if (kernel_symbol_exists("module_list"))
kt->flags |= KMOD_V1;
else if (kernel_symbol_exists("modules"))
kt->flags |= KMOD_V2;
else
error(WARNING, "cannot determine how modules are linked\n");
if (kt->flags & NO_MODULE_ACCESS || !(kt->flags & (KMOD_V1|KMOD_V2))) {
error(WARNING, "no kernel module access\n\n");
kt->module_list = 0;
kt->mods_installed = 0;
return;
}
STRUCT_SIZE_INIT(module, "module");
MEMBER_OFFSET_INIT(module_name, "module", "name");
MEMBER_OFFSET_INIT(module_syms, "module", "syms");
mod_next = nsyms = 0;
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V1:
MEMBER_OFFSET_INIT(module_size_of_struct, "module",
"size_of_struct");
MEMBER_OFFSET_INIT(module_next, "module", "next");
MEMBER_OFFSET_INIT(module_nsyms, "module", "nsyms");
MEMBER_OFFSET_INIT(module_size, "module", "size");
MEMBER_OFFSET_INIT(module_flags, "module", "flags");
get_symbol_data("module_list", sizeof(ulong), &kt->module_list);
kt->kernel_module = symbol_value("kernel_module");
break;
case KMOD_V2:
MEMBER_OFFSET_INIT(module_num_syms, "module", "num_syms");
MEMBER_OFFSET_INIT(module_list, "module", "list");
MEMBER_OFFSET_INIT(module_gpl_syms, "module", "gpl_syms");
MEMBER_OFFSET_INIT(module_num_gpl_syms, "module",
"num_gpl_syms");
MEMBER_OFFSET_INIT(module_module_core, "module",
"module_core");
MEMBER_OFFSET_INIT(module_core_size, "module",
"core_size");
MEMBER_OFFSET_INIT(module_core_text_size, "module",
"core_text_size");
MEMBER_OFFSET_INIT(module_module_init, "module", "module_init");
MEMBER_OFFSET_INIT(module_init_size, "module", "init_size");
MEMBER_OFFSET_INIT(module_init_text_size, "module",
"init_text_size");
MEMBER_OFFSET_INIT(module_percpu, "module", "percpu");
/*
* Make sure to pick the kernel "modules" list_head symbol,
* not to be confused with the ia64/sn "modules[]" array.
* The kernel modules list_head will either point to itself
* (empty) or contain vmalloc'd module addresses; the ia64/sn
* modules array contains a list of kmalloc'd addresses.
*/
if ((c = get_syment_array("modules", sp_array, 10)) > 1) {
modules_found = FALSE;
for (i = 0; i < c; i++) {
sp = sp_array[i];
if (!readmem(sp->value, KVADDR,
&list, sizeof(struct kernel_list_head),
"modules list_head test",
RETURN_ON_ERROR|QUIET))
continue;
if ((ulong)list.next == symbol_value("modules")) {
kt->mods_installed = 0;
return;
}
if (IS_VMALLOC_ADDR((ulong)list.next) &&
IS_VMALLOC_ADDR((ulong)list.prev)) {
kt->kernel_module = sp->value;
kt->module_list = (ulong)list.next;
modules_found = TRUE;
break;
}
}
if (!modules_found) {
error(WARNING,
"cannot determine which of %d \"modules\" symbols is appropriate\n\n",
c);
kt->mods_installed = 0;
kt->flags |= NO_MODULE_ACCESS;
return;
}
} else {
get_symbol_data("modules", sizeof(ulong),
&kt->module_list);
if (kt->module_list == symbol_value("modules")) {
kt->mods_installed = 0;
return;
}
kt->kernel_module = symbol_value("modules");
}
kt->module_list -= OFFSET(module_list);
break;
}
total = kt->mods_installed = 0;
modbuf = GETBUF(SIZE(module));
kallsymsbuf = kt->flags & KALLSYMS_V1 ?
GETBUF(SIZE(kallsyms_header)) : NULL;
please_wait("gathering module symbol data");
for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) {
if (CRASHDEBUG(3))
fprintf(fp, "module: %lx\n", mod);
if (!readmem(mod, KVADDR, modbuf, SIZE(module),
"module struct", RETURN_ON_ERROR|QUIET)) {
error(WARNING,
"%scannot access vmalloc'd module memory\n\n",
DUMPFILE() ? "\n" : "");
kt->mods_installed = 0;
kt->flags |= NO_MODULE_ACCESS;
FREEBUF(modbuf);
return;
}
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V1:
nsyms = UINT(modbuf + OFFSET(module_nsyms));
break;
case KMOD_V2:
nsyms = UINT(modbuf + OFFSET(module_num_syms)) +
UINT(modbuf + OFFSET(module_num_gpl_syms));
break;
}
total += nsyms;
total += 2; /* store the module's start/ending addresses */
/*
* If the module has kallsyms, set up to grab them as well.
*/
switch (kt->flags & (KALLSYMS_V1|KALLSYMS_V2))
{
case KALLSYMS_V1:
kallsyms_header = ULONG(modbuf +
OFFSET(module_kallsyms_start));
if (kallsyms_header) {
if (!readmem(kallsyms_header, KVADDR,
kallsymsbuf, SIZE(kallsyms_header),
"kallsyms_header", RETURN_ON_ERROR|QUIET)) {
error(WARNING,
"%scannot access module kallsyms_header\n",
DUMPFILE() ? "\n" : "");
} else {
nsyms = UINT(kallsymsbuf +
OFFSET(kallsyms_header_symbols));
total += nsyms;
}
}
break;
case KALLSYMS_V2:
if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
numksyms = UINT(modbuf + OFFSET(module_num_symtab));
size = UINT(modbuf + OFFSET(module_core_size));
} else {
numksyms = ULONG(modbuf + OFFSET(module_num_symtab));
size = ULONG(modbuf + OFFSET(module_core_size));
}
if (!size) {
/*
* Bail out here instead of a crashing with a
* getbuf(0) failure during storage later on.
*/
error(WARNING,
"invalid kernel module size: 0\n");
kt->mods_installed = 0;
kt->flags |= NO_MODULE_ACCESS;
FREEBUF(modbuf);
return;
}
total += numksyms;
break;
}
kt->mods_installed++;
NEXT_MODULE(mod_next, modbuf);
}
FREEBUF(modbuf);
if (kallsymsbuf)
FREEBUF(kallsymsbuf);
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V1:
store_module_symbols_v1(total, kt->mods_installed);
break;
case KMOD_V2:
store_module_symbols_v2(total, kt->mods_installed);
break;
}
please_wait_done();
}
/*
* Verify that the current set of modules jives with what's stored.
*/
static int
verify_modules(void)
{
int i;
int found, irregularities;
ulong mod, mod_next, mod_base;
long mod_size;
char *modbuf, *module_name;
ulong module_list, mod_name;
physaddr_t paddr;
int mods_installed;
struct load_module *lm;
char buf[BUFSIZE];
if (DUMPFILE() || !kt->module_list || (kt->flags & NO_MODULE_ACCESS))
return TRUE;
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V1:
get_symbol_data("module_list", sizeof(ulong), &module_list);
break;
case KMOD_V2:
if (kt->module_list == symbol_value("modules")) {
if (!kt->mods_installed)
return TRUE;
}
get_symbol_data("modules", sizeof(ulong), &module_list);
module_list -= OFFSET(module_list);
break;
}
mods_installed = irregularities = 0;
mod_base = mod_next = 0;
modbuf = GETBUF(SIZE(module));
for (mod = module_list; mod != kt->kernel_module; mod = mod_next) {
if (!readmem(mod, KVADDR, modbuf, SIZE(module),
"module struct", RETURN_ON_ERROR|QUIET)) {
error(WARNING,
"cannot access vmalloc'd module memory\n");
FREEBUF(modbuf);
return FALSE;
}
for (i = 0, found = FALSE; i < kt->mods_installed; i++) {
lm = &st->load_modules[i];
if (!kvtop(NULL, lm->mod_base, &paddr, 0)) {
irregularities++;
break;
}
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V1:
mod_base = mod;
break;
case KMOD_V2:
mod_base = ULONG(modbuf +
OFFSET(module_module_core));
break;
}
if (lm->mod_base == mod_base) {
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V1:
mod_name = ULONG(modbuf +
OFFSET(module_name));
mod_size = LONG(modbuf +
OFFSET(module_size));
if (!read_string(mod_name, buf,
BUFSIZE-1) || !STREQ(lm->mod_name,
buf) || (mod_size != lm->mod_size)){
irregularities++;
goto irregularity;
}
break;
case KMOD_V2:
module_name = modbuf +
OFFSET(module_name);
if (THIS_KERNEL_VERSION >= LINUX(2,6,27))
mod_size = UINT(modbuf +
OFFSET(module_core_size));
else
mod_size = ULONG(modbuf +
OFFSET(module_core_size));
if (strlen(module_name) < MAX_MOD_NAME)
strcpy(buf, module_name);
else
strncpy(buf, module_name,
MAX_MOD_NAME-1);
if (!STREQ(lm->mod_name, buf) ||
(mod_size != lm->mod_size)) {
irregularities++;
goto irregularity;
}
break;
}
found = TRUE;
irregularity:
break;
}
}
if (!found || irregularities)
return FALSE;
mods_installed++;
NEXT_MODULE(mod_next, modbuf);
}
FREEBUF(modbuf);
if (mods_installed != kt->mods_installed)
return FALSE;
return TRUE;
}
/*
* With no arguments, just dump basic data concerning each of the
* currently-loaded modules. The -s and -S arguments dynamically
* loads module symbols from its object file.
*/
#define LIST_MODULE_HDR (0)
#define LIST_MODULE (1)
#define LOAD_ALL_MODULE_SYMBOLS (2)
#define LOAD_SPECIFIED_MODULE_SYMBOLS (3)
#define DELETE_MODULE_SYMBOLS (4)
#define DELETE_ALL_MODULE_SYMBOLS (5)
#define REMOTE_MODULE_SAVE_MSG (6)
#define REINIT_MODULES (7)
#define LIST_ALL_MODULE_TAINT (8)
void
cmd_mod(void)
{
int c, ctmp;
char *p, *objfile, *modref, *tree, *symlink;
ulong flag, address;
char buf[BUFSIZE];
if (kt->flags & NO_MODULE_ACCESS)
error(FATAL, "cannot access vmalloc'd module memory\n");
if (!verify_modules()) {
error(NOTE,
"modules have changed on this system -- reinitializing\n");
reinit_modules();
}
if (!kt->mods_installed) {
fprintf(fp, "no modules installed\n");
return;
}
for (c = 1, p = NULL; c < argcnt; c++) {
if (args[c][0] != '-')
continue;
if (STREQ(args[c], "-g")) {
ctmp = c;
pc->curcmd_flags |= MOD_SECTIONS;
while (ctmp < argcnt) {
args[ctmp] = args[ctmp+1];
ctmp++;
}
argcnt--;
c--;
} else if (STREQ(args[c], "-r")) {
ctmp = c;
pc->curcmd_flags |= MOD_READNOW;
while (ctmp < argcnt) {
args[ctmp] = args[ctmp+1];
ctmp++;
}
argcnt--;
c--;
} else {
if ((p = strstr(args[c], "g"))) {
pc->curcmd_flags |= MOD_SECTIONS;
shift_string_left(p, 1);
}
if ((p = strstr(args[c], "r"))) {
pc->curcmd_flags |= MOD_READNOW;
shift_string_left(p, 1);
}
/* if I've removed everything but the '-', toss it */
if (STREQ(args[c], "-")) {
ctmp = c;
while (ctmp < argcnt) {
args[ctmp] = args[ctmp+1];
ctmp++;
}
argcnt--;
c--;
}
}
}
if (pc->flags & READNOW)
pc->curcmd_flags |= MOD_READNOW;
modref = objfile = tree = symlink = NULL;
address = 0;
flag = LIST_MODULE_HDR;
while ((c = getopt(argcnt, args, "Rd:Ds:Sot")) != EOF) {
switch(c)
{
case 'R':
if (flag)
cmd_usage(pc->curcmd, SYNOPSIS);
flag = REINIT_MODULES;
break;
case 'D':
if (flag)
cmd_usage(pc->curcmd, SYNOPSIS);
flag = DELETE_ALL_MODULE_SYMBOLS;
break;
case 'd':
if (flag)
cmd_usage(pc->curcmd, SYNOPSIS);
else
flag = DELETE_MODULE_SYMBOLS;
if (hexadecimal(optarg, 0) &&
(strlen(optarg) == VADDR_PRLEN)) {
address = htol(optarg, FAULT_ON_ERROR, NULL);
if (!is_module_address(address, buf))
cmd_usage(pc->curcmd, SYNOPSIS);
modref = buf;
} else if (is_module_name(optarg, &address, NULL))
modref = optarg;
else
cmd_usage(pc->curcmd, SYNOPSIS);
break;
/*
* Revert to using old-style add-symbol-file command
* for KMOD_V2 kernels.
*/
case 'o':
if (flag)
cmd_usage(pc->curcmd, SYNOPSIS);
if (kt->flags & KMOD_V1)
error(INFO,
"-o option is not applicable to this kernel version\n");
st->flags |= USE_OLD_ADD_SYM;
return;
case 'S':
if (flag)
cmd_usage(pc->curcmd, SYNOPSIS);
else
flag = LOAD_ALL_MODULE_SYMBOLS;
break;
case 's':
if (flag)
cmd_usage(pc->curcmd, SYNOPSIS);
else
flag = LOAD_SPECIFIED_MODULE_SYMBOLS;
if (hexadecimal(optarg, 0) &&
(strlen(optarg) == VADDR_PRLEN)) {
address = htol(optarg, FAULT_ON_ERROR, NULL);
if (!is_module_address(address, buf))
cmd_usage(pc->curcmd, SYNOPSIS);
modref = buf;
} else if (is_module_name(optarg, &address, NULL))
modref = optarg;
else
cmd_usage(pc->curcmd, SYNOPSIS);
break;
case 't':
if (flag)
cmd_usage(pc->curcmd, SYNOPSIS);
else
flag = LIST_ALL_MODULE_TAINT;
break;
default:
argerrs++;
break;
}
}
if (tree && (flag != LOAD_ALL_MODULE_SYMBOLS))
argerrs++;
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (NO_MODULES()) {
error(INFO, "no modules loaded in this kernel\n");
if (flag != LIST_MODULE_HDR)
cmd_usage(pc->curcmd, SYNOPSIS);
return;
}
switch (flag)
{
case LOAD_ALL_MODULE_SYMBOLS:
switch (argcnt)
{
case 3:
if (is_directory(args[2]))
tree = args[2];
else {
error(INFO, "%s is not a directory\n", args[2]);
cmd_usage(pc->curcmd, SYNOPSIS);
}
break;
case 2:
break;
default:
cmd_usage(pc->curcmd, SYNOPSIS);
}
break;
case LOAD_SPECIFIED_MODULE_SYMBOLS:
switch (argcnt)
{
case 4:
objfile = args[3];
if (!file_exists(objfile, NULL)) {
if (!(objfile =
find_module_objfile(modref, objfile, tree)))
error(FATAL,
"%s: cannot find or load object file: %s\n",
modref, args[3]);
}
break;
case 3:
if (!(objfile = find_module_objfile(modref,NULL,tree)))
error(FATAL,
"cannot find or load object file for %s module\n",
modref);
break;
default:
cmd_usage(pc->curcmd, SYNOPSIS);
}
if (!is_elf_file(objfile)) {
error(INFO, "%s: not an ELF format object file\n",
objfile);
cmd_usage(pc->curcmd, SYNOPSIS);
}
break;
default:
break;
}
if ((flag == LOAD_ALL_MODULE_SYMBOLS) &&
(tree || kt->module_tree)) {
if (!tree)
tree = kt->module_tree;
}
do_module_cmd(flag, modref, address, objfile, tree);
if (symlink)
FREEBUF(symlink);
}
int
check_specified_module_tree(char *module, char *gdb_buffer)
{
char *p1, *treebuf;
int retval;
retval = FALSE;
/*
* Search for "/lib/modules" in the module name string
* and insert "/usr/lib/debug" there.
*/
if (strstr(module, "/lib/modules")) {
treebuf = GETBUF(strlen(module) + strlen("/usr/lib/debug") +
strlen(".debug") + 1);
strcpy(treebuf, module);
p1 = strstr(treebuf, "/lib/modules");
shift_string_right(p1, strlen("/usr/lib/debug"));
BCOPY("/usr/lib/debug", p1, strlen("/usr/lib/debug"));
strcat(treebuf, ".debug");
if (file_exists(treebuf, NULL)) {
strcpy(gdb_buffer, treebuf);
retval = TRUE;
}
FREEBUF(treebuf);
}
return retval;
}
static void
show_module_taint(void)
{
int i, j, bx;
struct load_module *lm;
int maxnamelen;
int found;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
int gpgsig_ok, license_gplok;
struct syment *sp;
uint *taintsp, taints;
uint8_t tnt_bit;
char tnt_true, tnt_false;
int tnts_exists, tnts_len;
ulong tnts_addr;
char *modbuf;
if (INVALID_MEMBER(module_taints) &&
INVALID_MEMBER(module_license_gplok)) {
MEMBER_OFFSET_INIT(module_taints, "module", "taints");
MEMBER_OFFSET_INIT(module_license_gplok,
"module", "license_gplok");
MEMBER_OFFSET_INIT(module_gpgsig_ok, "module", "gpgsig_ok");
STRUCT_SIZE_INIT(tnt, "tnt");
MEMBER_OFFSET_INIT(tnt_bit, "tnt", "bit");
MEMBER_OFFSET_INIT(tnt_true, "tnt", "true");
MEMBER_OFFSET_INIT(tnt_false, "tnt", "false");
}
if (INVALID_MEMBER(module_taints) &&
INVALID_MEMBER(module_license_gplok))
option_not_supported('t');
modbuf = GETBUF(SIZE(module));
for (i = found = maxnamelen = 0; i < kt->mods_installed; i++) {
lm = &st->load_modules[i];
readmem(lm->module_struct, KVADDR, modbuf, SIZE(module),
"module struct", FAULT_ON_ERROR);
taints = VALID_MEMBER(module_taints) ?
UINT(modbuf + OFFSET(module_taints)) : 0;
license_gplok = VALID_MEMBER(module_license_gplok) ?
INT(modbuf + OFFSET(module_license_gplok)) : 0;
gpgsig_ok = VALID_MEMBER(module_gpgsig_ok) ?
INT(modbuf + OFFSET(module_gpgsig_ok)) : 1;
if (VALID_MEMBER(module_license_gplok) || taints || !gpgsig_ok) {
found++;
maxnamelen = strlen(lm->mod_name) > maxnamelen ?
strlen(lm->mod_name) : maxnamelen;
}
}
if (!found) {
fprintf(fp, "no tainted modules\n");
FREEBUF(modbuf);
return;
}
if (VALID_STRUCT(tnt) && (sp = symbol_search("tnts"))) {
tnts_exists = TRUE;
tnts_len = get_array_length("tnts", NULL, 0);
tnts_addr = sp->value;
} else {
tnts_exists = FALSE;
tnts_len = 0;
tnts_addr = 0;
}
fprintf(fp, "%s %s\n",
mkstring(buf2, maxnamelen, LJUST, "NAME"),
VALID_MEMBER(module_taints) ? "TAINTS" : "LICENSE_GPLOK");
for (i = 0; i < st->mods_installed; i++) {
lm = &st->load_modules[i];
bx = 0;
buf1[0] = '\0';
readmem(lm->module_struct, KVADDR, modbuf, SIZE(module),
"module struct", FAULT_ON_ERROR);
taints = VALID_MEMBER(module_taints) ?
UINT(modbuf + OFFSET(module_taints)) : 0;
license_gplok = VALID_MEMBER(module_license_gplok) ?
INT(modbuf + OFFSET(module_license_gplok)) : 0;
gpgsig_ok = VALID_MEMBER(module_gpgsig_ok) ?
INT(modbuf + OFFSET(module_gpgsig_ok)) : 1;
if (INVALID_MEMBER(module_license_gplok)) {
if (!taints && gpgsig_ok)
continue;
}
if (tnts_exists && taints) {
taintsp = &taints;
for (j = 0; j < (tnts_len * SIZE(tnt)); j += SIZE(tnt)) {
readmem((tnts_addr + j) + OFFSET(tnt_bit),
KVADDR, &tnt_bit, sizeof(uint8_t),
"tnt bit", FAULT_ON_ERROR);
if (NUM_IN_BITMAP(taintsp, tnt_bit)) {
readmem((tnts_addr + j) + OFFSET(tnt_true),
KVADDR, &tnt_true, sizeof(char),
"tnt true", FAULT_ON_ERROR);
buf1[bx++] = tnt_true;
} else {
readmem((tnts_addr + j) + OFFSET(tnt_false),
KVADDR, &tnt_false, sizeof(char),
"tnt false", FAULT_ON_ERROR);
if (tnt_false != ' ' && tnt_false != '-' &&
tnt_false != 'G')
buf1[bx++] = tnt_false;
}
}
}
if (VALID_MEMBER(module_gpgsig_ok) && !gpgsig_ok) {
buf1[bx++] = '(';
buf1[bx++] = 'U';
buf1[bx++] = ')';
}
buf1[bx++] = '\0';
if (tnts_exists)
fprintf(fp, "%s %s\n", mkstring(buf2, maxnamelen,
LJUST, lm->mod_name), buf1);
else
fprintf(fp, "%s %x%s\n", mkstring(buf2, maxnamelen,
LJUST, lm->mod_name),
VALID_MEMBER(module_taints) ?
taints : license_gplok, buf1);
}
FREEBUF(modbuf);
}
/*
* Do the simple list work for cmd_mod().
*/
static void
do_module_cmd(ulong flag, char *modref, ulong address,
char *objfile, char *tree)
{
int i, j;
struct load_module *lm, *lmp;
int maxnamelen;
int maxsizelen;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
if (NO_MODULES())
return;
switch (flag)
{
case LIST_MODULE:
case LIST_MODULE_HDR:
maxnamelen = maxsizelen = 0;
for (i = 0; i < kt->mods_installed; i++) {
lm = &st->load_modules[i];
maxnamelen = strlen(lm->mod_name) > maxnamelen ?
strlen(lm->mod_name) : maxnamelen;
sprintf(buf1, "%ld", lm->mod_size);
maxsizelen = strlen(buf1) > maxsizelen ?
strlen(buf1) : maxsizelen;
}
if (flag == LIST_MODULE_HDR) {
fprintf(fp, "%s %s %s OBJECT FILE\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST,
"MODULE"),
mkstring(buf2, maxnamelen, LJUST, "NAME"),
mkstring(buf3, maxsizelen, RJUST, "SIZE"));
}
for (i = 0; i < kt->mods_installed; i++) {
lm = &st->load_modules[i];
if (!address || (lm->module_struct == address) ||
(lm->mod_base == address)) {
fprintf(fp, "%s ", mkstring(buf1, VADDR_PRLEN,
LONG_HEX|RJUST, MKSTR(lm->module_struct)));
fprintf(fp, "%s ", mkstring(buf2, maxnamelen,
LJUST, lm->mod_name));
fprintf(fp, "%s ", mkstring(buf3, maxsizelen,
RJUST|LONG_DEC, MKSTR(lm->mod_size)));
// fprintf(fp, "%6ld ", lm->mod_size);
if (strlen(lm->mod_namelist))
fprintf(fp, "%s %s",
lm->mod_namelist,
lm->mod_flags & MOD_REMOTE ?
" (temporary)" : "");
else {
fprintf(fp, "(not loaded)");
if (lm->mod_flags & MOD_KALLSYMS)
fprintf(fp,
" [CONFIG_KALLSYMS]");
}
fprintf(fp, "\n");
}
}
break;
case REMOTE_MODULE_SAVE_MSG:
if (!REMOTE())
return;
for (i = j = 0, lmp = NULL; i < kt->mods_installed; i++) {
lm = &st->load_modules[i];
if (lm->mod_flags & MOD_REMOTE) {
j++;
lmp = lm;
}
}
switch (j)
{
case 0:
return;
case 1:
error(NOTE,
"\nTo save the %s module object locally,\n enter: \"save %s\"\n",
lmp->mod_name, lmp->mod_name);
break;
default:
error(NOTE,
"\nTo save all temporary remote module objects locally,\n enter: \"save modules\"\n");
fprintf(fp,
" To save a single remote module object locally,\n enter: \"save NAME\",\n"
" where \"NAME\" is one of the module names shown in the list above.\n");
break;
}
break;
case LOAD_SPECIFIED_MODULE_SYMBOLS:
if (!load_module_symbols(modref, objfile, address))
error(FATAL, "cannot load symbols from: %s\n", objfile);
do_module_cmd(LIST_MODULE_HDR, 0, address, 0, NULL);
do_module_cmd(REMOTE_MODULE_SAVE_MSG, 0, 0, 0, NULL);
break;
case LOAD_ALL_MODULE_SYMBOLS:
for (i = j = 0; i < kt->mods_installed; i++) {
lm = &st->load_modules[i];
if (STREQ(lm->mod_name, "(unknown module)")) {
error(INFO,
"cannot find object file for unknown module at %lx\n",
lm->mod_base);
continue;
}
modref = lm->mod_name;
address = lm->mod_base;
if ((objfile = find_module_objfile(modref,NULL,tree))) {
if (!is_elf_file(objfile)) {
error(INFO,
"%s: not an ELF format object file\n",
objfile);
} else if (!load_module_symbols(modref,
objfile, address))
error(INFO,
"cannot load symbols from: %s\n",
objfile);
do_module_cmd(j++ ?
LIST_MODULE : LIST_MODULE_HDR,
0, address, 0, tree);
FREEBUF(objfile);
} else if ((lm->mod_flags & MOD_LOAD_SYMS) ||
strlen(lm->mod_namelist)) {
if (CRASHDEBUG(1))
fprintf(fp,
"%s: module symbols are already loaded\n",
modref);
do_module_cmd(j++ ?
LIST_MODULE : LIST_MODULE_HDR,
0, address, 0, tree);
} else
error(INFO,
"cannot find or load object file for %s module\n",
modref);
}
do_module_cmd(REMOTE_MODULE_SAVE_MSG, 0, 0, 0, tree);
break;
case DELETE_ALL_MODULE_SYMBOLS:
delete_load_module(ALL_MODULES);
break;
case DELETE_MODULE_SYMBOLS:
delete_load_module(address);
break;
case REINIT_MODULES:
reinit_modules();
do_module_cmd(LIST_MODULE_HDR, NULL, 0, NULL, NULL);
break;
case LIST_ALL_MODULE_TAINT:
show_module_taint();
break;
}
}
/*
* Reinitialize the current set of modules:
*
* 1. first clear out all references to the current set.
* 2. call module_init() again.
* 3. display the new set.
*/
static void
reinit_modules(void)
{
delete_load_module(ALL_MODULES);
st->mods_installed = 0;
st->flags &= ~MODULE_SYMS;
free(st->ext_module_symtable);
free(st->load_modules);
st->ext_module_symtable = NULL;
st->load_modules = NULL;
kt->mods_installed = 0;
clear_text_value_cache();
module_init();
}
static char *
module_objfile_search(char *modref, char *filename, char *tree)
{
char buf[BUFSIZE];
char file[BUFSIZE];
char dir[BUFSIZE];
struct load_module *lm;
char *retbuf;
int initrd;
struct syment *sp;
char *p1, *p2;
char *env;
char *namelist;
retbuf = NULL;
initrd = FALSE;
if (filename)
strcpy(file, filename);
#ifdef MODULES_IN_CWD
else {
char *fileext[] = { "ko", "o"};
int i;
for (i = 0; i < 2; i++) {
sprintf(file, "%s.%s", modref, fileext[i]);
if (access(file, R_OK) == 0) {
retbuf = GETBUF(strlen(file)+1);
strcpy(retbuf, file);
if (CRASHDEBUG(1))
fprintf(fp,
"find_module_objfile: [%s] file in cwd\n",
retbuf);
return retbuf;
}
}
}
#else
else
sprintf(file, "%s.o", modref);
#endif
/*
* Later versions of insmod create a symbol at the module's base
* address. Examples:
*
* __insmod_sunrpc_O/lib/modules/2.2.17/misc/sunrpc.o_M3A7EE300_V131601
* __insmod_lockd_O/lib/modules/2.2.17/fs/lockd.o_M3A7EE300_V131601
* __insmod_nfsd_O/lib/modules/2.2.17/fs/nfsd.o_M3A7EE300_V131601
* __insmod_nfs_O/lib/modules/2.2.17/fs/nfs.o_M3A7EE300_V131601
*/
if ((st->flags & INSMOD_BUILTIN) && !filename) {
sprintf(buf, "__insmod_%s_O/", modref);
if (symbol_query(buf, NULL, &sp) == 1) {
if (CRASHDEBUG(1))
fprintf(fp, "search: INSMOD_BUILTIN %s\n", sp->name);
BZERO(buf, BUFSIZE);
p1 = strstr(sp->name, "/");
if ((p2 = strstr(sp->name, file)))
p2 += strlen(file);
if (p2) {
strncpy(buf, p1, p2-p1);
if (!strstr(buf, "/lib/modules/")) {
sprintf(dir, "/lib/%s.o", modref);
if (STREQ(dir, buf))
initrd = TRUE;
} else if (REMOTE())
strcpy(file, buf);
else {
retbuf = GETBUF(strlen(buf)+1);
strcpy(retbuf, buf);
if (CRASHDEBUG(1))
fprintf(fp,
"find_module_objfile: [%s]\n",
retbuf);
return retbuf;
}
}
}
if (is_module_name(modref, NULL, &lm) &&
(lm->mod_flags & MOD_INITRD)) {
sprintf(dir, "/lib/%s.o", modref);
initrd = TRUE;
}
}
if (initrd)
error(NOTE, "%s: installed from initrd image\n", dir);
if (REMOTE()) {
retbuf = GETBUF(MAX_MOD_NAMELIST*2);
if (!is_module_name(modref, NULL, &lm)) {
error(INFO, "%s is not a module reference\n", modref);
return NULL;
}
if ((lm->mod_flags & MOD_LOAD_SYMS) &&
strlen(lm->mod_namelist)) {
if (CRASHDEBUG(1))
fprintf(fp, "redundant mod call: %s\n",
lm->mod_namelist);
strcpy(retbuf, lm->mod_namelist);
return retbuf;
}
if (find_remote_module_objfile(lm, file, retbuf))
return retbuf;
return NULL;
}
if (tree) {
if (!(retbuf = search_directory_tree(tree, file, 1))) {
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V2:
sprintf(file, "%s.ko", modref);
retbuf = search_directory_tree(tree, file, 1);
if (!retbuf) {
sprintf(file, "%s.ko.debug", modref);
retbuf = search_directory_tree(tree, file, 1);
}
}
}
return retbuf;
}
sprintf(dir, "%s/%s", DEFAULT_REDHAT_DEBUG_LOCATION,
kt->utsname.release);
retbuf = search_directory_tree(dir, file, 0);
if (!retbuf && (env = getenv("CRASH_MODULE_PATH"))) {
sprintf(dir, "%s", env);
if (!(retbuf = search_directory_tree(dir, file, 0))) {
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V2:
sprintf(file, "%s.ko", modref);
retbuf = search_directory_tree(dir, file, 0);
if (!retbuf) {
sprintf(file, "%s.ko.debug", modref);
retbuf = search_directory_tree(dir, file, 0);
}
}
}
}
if (!retbuf) {
sprintf(dir, "/lib/modules/%s/updates", kt->utsname.release);
if (!(retbuf = search_directory_tree(dir, file, 0))) {
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V2:
sprintf(file, "%s.ko", modref);
retbuf = search_directory_tree(dir, file, 0);
}
}
}
if (!retbuf) {
sprintf(dir, "/lib/modules/%s", kt->utsname.release);
if (!(retbuf = search_directory_tree(dir, file, 0))) {
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V2:
sprintf(file, "%s.ko", modref);
retbuf = search_directory_tree(dir, file, 0);
}
}
}
if (!retbuf && !filename && !tree && kt->module_tree) {
sprintf(dir, "%s", kt->module_tree);
if (!(retbuf = search_directory_tree(dir, file, 0))) {
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V2:
sprintf(file, "%s.ko", modref);
retbuf = search_directory_tree(dir, file, 0);
if (!retbuf) {
sprintf(file, "%s.ko.debug", modref);
retbuf = search_directory_tree(dir, file, 0);
}
}
}
}
/*
* Check the directory tree where the vmlinux file is located.
*/
if (!retbuf &&
(namelist = realpath(pc->namelist_orig ?
pc->namelist_orig : pc->namelist, NULL))) {
sprintf(dir, "%s", dirname(namelist));
if (!(retbuf = search_directory_tree(dir, file, 0))) {
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
case KMOD_V2:
sprintf(file, "%s.ko", modref);
retbuf = search_directory_tree(dir, file, 0);
if (!retbuf) {
sprintf(file, "%s.ko.debug", modref);
retbuf = search_directory_tree(dir, file, 0);
}
}
}
free(namelist);
}
if (!retbuf && is_livepatch()) {
sprintf(file, "%s.ko", modref);
sprintf(dir, "/usr/lib/kpatch/%s", kt->utsname.release);
if (!(retbuf = search_directory_tree(dir, file, 0))) {
sprintf(file, "%s.ko.debug", modref);
sprintf(dir, "/usr/lib/debug/usr/lib/kpatch/%s",
kt->utsname.release);
retbuf = search_directory_tree(dir, file, 0);
}
}
return retbuf;
}
/*
* First look for a module based upon its reference name.
* If that fails, try replacing any underscores in the
* reference name with a dash.
* If that fails, because of intermingled dashes and underscores,
* try a regex expression.
*
* Example: module name "dm_mod" comes from "dm-mod.ko" objfile
* module name "dm_region_hash" comes from "dm-region_hash.ko" objfile
*/
static char *
find_module_objfile(char *modref, char *filename, char *tree)
{
char * retbuf;
char tmpref[BUFSIZE];
int i, c;
retbuf = module_objfile_search(modref, filename, tree);
if (!retbuf) {
strncpy(tmpref, modref, BUFSIZE);
for (c = 0; c < BUFSIZE && tmpref[c]; c++)
if (tmpref[c] == '_')
tmpref[c] = '-';
retbuf = module_objfile_search(tmpref, filename, tree);
}
if (!retbuf && (count_chars(modref, '_') > 1)) {
for (i = c = 0; modref[i]; i++) {
if (modref[i] == '_') {
tmpref[c++] = '[';
tmpref[c++] = '_';
tmpref[c++] = '-';
tmpref[c++] = ']';
} else
tmpref[c++] = modref[i];
}
tmpref[c] = NULLCHAR;
retbuf = module_objfile_search(tmpref, filename, tree);
}
return retbuf;
}
/*
* Try to load module symbols with name.
*/
int
load_module_symbols_helper(char *name)
{
char *objfile;
ulong address;
if (is_module_name(name, &address, NULL) &&
(objfile = find_module_objfile(name, NULL, NULL))) {
do_module_cmd(LOAD_SPECIFIED_MODULE_SYMBOLS, name, address,
objfile, NULL);
return TRUE;
}
return FALSE;
}
/*
* Unlink any temporary remote module object files.
*/
void
unlink_module(struct load_module *load_module)
{
int i;
struct load_module *lm;
if (load_module) {
if (load_module->mod_flags & MOD_REMOTE)
unlink(load_module->mod_namelist);
return;
}
for (i = 0; i < kt->mods_installed; i++) {
lm = &st->load_modules[i];
if (lm->mod_flags & MOD_REMOTE)
unlink(lm->mod_namelist);
}
}
/*
* Dump the kernel log_buf in chronological order.
*/
void
cmd_log(void)
{
int c;
int msg_flags;
msg_flags = 0;
while ((c = getopt(argcnt, args, "tdm")) != EOF) {
switch(c)
{
case 't':
msg_flags |= SHOW_LOG_TEXT;
break;
case 'd':
msg_flags |= SHOW_LOG_DICT;
break;
case 'm':
msg_flags |= SHOW_LOG_LEVEL;
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
dump_log(msg_flags);
}
void
dump_log(int msg_flags)
{
int i, len, tmp, show_level;
ulong log_buf, log_end;
char *buf;
char last;
ulong index;
struct syment *nsp;
int log_wrap, loglevel, log_buf_len;
if (kernel_symbol_exists("log_first_idx") &&
kernel_symbol_exists("log_next_idx")) {
dump_variable_length_record_log(msg_flags);
return;
}
if (msg_flags & SHOW_LOG_DICT)
option_not_supported('d');
if ((msg_flags & SHOW_LOG_TEXT) && STREQ(pc->curcmd, "log"))
option_not_supported('t');
show_level = msg_flags & SHOW_LOG_LEVEL ? TRUE : FALSE;
if (symbol_exists("log_buf_len")) {
get_symbol_data("log_buf_len", sizeof(int), &log_buf_len);
get_symbol_data("log_buf", sizeof(ulong), &log_buf);
} else {
if ((ARRAY_LENGTH(log_buf) == 0) &&
(get_array_length("log_buf", NULL, 0) == 0)) {
if ((nsp = next_symbol("log_buf", NULL)) == NULL)
error(FATAL,
"cannot determine length of log_buf\n");
builtin_array_length("log_buf",
(int)(nsp->value - symbol_value("log_buf")),
NULL);
}
log_buf_len = ARRAY_LENGTH(log_buf);
log_buf = symbol_value("log_buf");
}
buf = GETBUF(log_buf_len);
log_wrap = FALSE;
last = 0;
if ((len = get_symbol_length("log_end")) == sizeof(int)) {
get_symbol_data("log_end", len, &tmp);
log_end = (ulong)tmp;
} else
get_symbol_data("log_end", len, &log_end);
if (!readmem(log_buf, KVADDR, buf,
log_buf_len, "log_buf contents", RETURN_ON_ERROR|QUIET)) {
error(WARNING, "\ncannot read log_buf contents\n");
return;
}
if (log_end < log_buf_len)
index = 0;
else
index = log_end & (log_buf_len - 1);
if ((log_end < log_buf_len) && (index == 0) && (buf[index] == '<'))
loglevel = TRUE;
else
loglevel = FALSE;
if (index != 0)
log_wrap = TRUE;
wrap_around:
for (i = index; i < log_buf_len; i++) {
if (loglevel && !show_level) {
switch (buf[i])
{
case '>':
loglevel = FALSE;
/* FALLTHROUGH */
case '<':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
continue;
default:
loglevel = FALSE;
break;
}
}
if (buf[i]) {
fputc(ascii(buf[i]) ? buf[i] : '.', fp);
loglevel = buf[i] == '\n' ? TRUE : FALSE;
last = buf[i];
}
}
if (log_wrap) {
log_buf_len = index;
index = 0;
log_wrap = FALSE;
goto wrap_around;
}
if (last != '\n')
fprintf(fp, "\n");
FREEBUF(buf);
}
/*
* get log record by index; idx must point to valid message.
*/
static char *
log_from_idx(uint32_t idx, char *logbuf)
{
char *logptr;
uint16_t msglen;
logptr = logbuf + idx;
/*
* A length == 0 record is the end of buffer marker.
* Wrap around and return the message at the start of
* the buffer.
*/
msglen = USHORT(logptr + OFFSET(log_len));
if (!msglen)
logptr = logbuf;
return logptr;
}
/*
* get next record index; idx must point to valid message.
*/
static uint32_t
log_next(uint32_t idx, char *logbuf)
{
char *logptr;
uint16_t msglen;
logptr = logbuf + idx;
/*
* A length == 0 record is the end of buffer marker. Wrap around and
* read the message at the start of the buffer as *this* one, and
* return the one after that.
*/
msglen = USHORT(logptr + OFFSET(log_len));
if (!msglen) {
msglen = USHORT(logbuf + OFFSET(log_len));
return msglen;
}
return idx + msglen;
}
static void
dump_log_entry(char *logptr, int msg_flags)
{
int indent;
char *msg, *p;
uint16_t i, text_len, dict_len, level;
uint64_t ts_nsec;
ulonglong nanos;
ulong rem;
char buf[BUFSIZE];
int ilen;
ilen = level = 0;
text_len = USHORT(logptr + OFFSET(log_text_len));
dict_len = USHORT(logptr + OFFSET(log_dict_len));
if (VALID_MEMBER(log_level)) {
/*
* Initially a "u16 level", then a "u8 level:3"
*/
if (SIZE(log_level) == sizeof(short))
level = USHORT(logptr + OFFSET(log_level));
else
level = UCHAR(logptr + OFFSET(log_level));
} else {
if (VALID_MEMBER(log_flags_level))
level = UCHAR(logptr + OFFSET(log_flags_level));
else if (msg_flags & SHOW_LOG_LEVEL)
msg_flags &= ~SHOW_LOG_LEVEL;
}
ts_nsec = ULONGLONG(logptr + OFFSET(log_ts_nsec));
msg = logptr + SIZE(log);
if (CRASHDEBUG(1))
fprintf(fp,
"\nlog %lx -> msg: %lx ts_nsec: %lld flags/level: %x"
" text_len: %d dict_len: %d\n",
(ulong)logptr, (ulong)msg, (ulonglong)ts_nsec,
level, text_len, dict_len);
if ((msg_flags & SHOW_LOG_TEXT) == 0) {
nanos = (ulonglong)ts_nsec / (ulonglong)1000000000;
rem = (ulonglong)ts_nsec % (ulonglong)1000000000;
sprintf(buf, "[%5lld.%06ld] ", nanos, rem/1000);
ilen = strlen(buf);
fprintf(fp, "%s", buf);
}
if (msg_flags & SHOW_LOG_LEVEL) {
sprintf(buf, "<%x>", level);
ilen += strlen(buf);
fprintf(fp, "%s", buf);
}
for (i = 0, p = msg; i < text_len; i++, p++) {
if (*p == '\n')
fprintf(fp, "\n%s", space(ilen));
else if (isprint(*p) || isspace(*p))
fputc(*p, fp);
else
fputc('.', fp);
}
if (dict_len & (msg_flags & SHOW_LOG_DICT)) {
fprintf(fp, "\n");
indent = TRUE;
for (i = 0; i < dict_len; i++, p++) {
if (indent) {
fprintf(fp, "%s", space(ilen));
indent = FALSE;
}
if (isprint(*p))
fputc(*p, fp);
else if (*p == NULLCHAR) {
fputc('\n', fp);
indent = TRUE;
} else
fputc('.', fp);
}
}
fprintf(fp, "\n");
}
/*
* Handle the new variable-length-record log_buf.
*/
static void
dump_variable_length_record_log(int msg_flags)
{
uint32_t idx, log_first_idx, log_next_idx, log_buf_len;
ulong log_buf;
char *logptr, *logbuf, *log_struct_name;
if (INVALID_SIZE(log)) {
if (STRUCT_EXISTS("printk_log")) {
/*
* In kernel 3.11 the log structure name was renamed
* from log to printk_log. See 62e32ac3505a0cab.
*/
log_struct_name = "printk_log";
} else
log_struct_name = "log";
STRUCT_SIZE_INIT(log, log_struct_name);
MEMBER_OFFSET_INIT(log_ts_nsec, log_struct_name, "ts_nsec");
MEMBER_OFFSET_INIT(log_len, log_struct_name, "len");
MEMBER_OFFSET_INIT(log_text_len, log_struct_name, "text_len");
MEMBER_OFFSET_INIT(log_dict_len, log_struct_name, "dict_len");
MEMBER_OFFSET_INIT(log_level, log_struct_name, "level");
MEMBER_SIZE_INIT(log_level, log_struct_name, "level");
MEMBER_OFFSET_INIT(log_flags_level, log_struct_name, "flags_level");
/*
* If things change, don't kill a dumpfile session
* searching for a panic message.
*/
if (INVALID_SIZE(log) ||
INVALID_MEMBER(log_ts_nsec) ||
INVALID_MEMBER(log_len) ||
INVALID_MEMBER(log_text_len) ||
INVALID_MEMBER(log_dict_len) ||
(INVALID_MEMBER(log_level) && INVALID_MEMBER(log_flags_level)) ||
!kernel_symbol_exists("log_buf_len") ||
!kernel_symbol_exists("log_buf")) {
error(WARNING, "\nlog buf data structure(s) have changed\n");
return;
}
}
get_symbol_data("log_first_idx", sizeof(uint32_t), &log_first_idx);
get_symbol_data("log_next_idx", sizeof(uint32_t), &log_next_idx);
get_symbol_data("log_buf_len", sizeof(uint32_t), &log_buf_len);
get_symbol_data("log_buf", sizeof(char *), &log_buf);
if (CRASHDEBUG(1)) {
fprintf(fp, "log_buf: %lx\n", (ulong)log_buf);
fprintf(fp, "log_buf_len: %d\n", log_buf_len);
fprintf(fp, "log_first_idx: %d\n", log_first_idx);
fprintf(fp, "log_next_idx: %d\n", log_next_idx);
}
logbuf = GETBUF(log_buf_len);
if (!readmem(log_buf, KVADDR, logbuf,
log_buf_len, "log_buf contents", RETURN_ON_ERROR|QUIET)) {
error(WARNING, "\ncannot read log_buf contents\n");
FREEBUF(logbuf);
return;
}
hq_open();
idx = log_first_idx;
while (idx != log_next_idx) {
logptr = log_from_idx(idx, logbuf);
dump_log_entry(logptr, msg_flags);
if (!hq_enter((ulong)logptr)) {
error(INFO, "\nduplicate log_buf message pointer\n");
break;
}
idx = log_next(idx, logbuf);
if (idx >= log_buf_len) {
error(INFO, "\ninvalid log_buf entry encountered\n");
break;
}
if (CRASHDEBUG(1) && (idx == log_next_idx))
fprintf(fp, "\nfound log_next_idx OK\n");
}
hq_close();
FREEBUF(logbuf);
}
/*
* Display general system info.
*/
void
cmd_sys(void)
{
int c, cnt;
ulong sflag;
char buf[BUFSIZE];
sflag = FALSE;
while ((c = getopt(argcnt, args, "ctp:")) != EOF) {
switch(c)
{
case 'p':
if (STREQ(optarg, "anic"))
panic_this_kernel();
else
argerrs++;
break;
case 'c':
sflag = TRUE;
break;
case 't':
show_kernel_taints(buf, VERBOSE);
return;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (!args[optind]) {
if (sflag)
dump_sys_call_table(NULL, 0);
else
display_sys_stats();
return;
}
cnt = 0;
do {
if (sflag)
dump_sys_call_table(args[optind], cnt++);
else if (STREQ(args[optind], "config"))
read_in_kernel_config(IKCFG_READ);
else
cmd_usage(args[optind], COMPLETE_HELP);
optind++;
} while (args[optind]);
}
static int
is_livepatch(void)
{
int i;
struct load_module *lm;
char buf[BUFSIZE];
show_kernel_taints(buf, !VERBOSE);
if (strstr(buf, "K")) /* TAINT_LIVEPATCH */
return TRUE;
for (i = 0; i < st->mods_installed; i++) {
lm = &st->load_modules[i];
if (STREQ("kpatch", lm->mod_name))
return TRUE;
}
return FALSE;
}
/*
* Display system stats at init-time or for the sys command.
*/
void
display_sys_stats(void)
{
struct new_utsname *uts;
char buf[BUFSIZE];
ulong mhz;
uts = &kt->utsname;
// if (!(pc->flags & RUNTIME) && !DUMPFILE() && !GDB_PATCHED())
// fprintf(fp, "\n");
/*
* It's now safe to unlink the remote namelist.
*/
if (pc->flags & UNLINK_NAMELIST) {
unlink(pc->namelist);
pc->flags &= ~UNLINK_NAMELIST;
pc->flags |= NAMELIST_UNLINKED;
}
if (REMOTE()) {
switch (pc->flags &
(NAMELIST_LOCAL|NAMELIST_UNLINKED|NAMELIST_SAVED))
{
case NAMELIST_UNLINKED:
fprintf(fp, " KERNEL: %s (temporary)\n",
pc->namelist);
break;
case (NAMELIST_UNLINKED|NAMELIST_SAVED):
fprintf(fp, " KERNEL: %s\n", pc->namelist);
break;
case NAMELIST_LOCAL:
fprintf(fp, " KERNEL: %s\n", pc->namelist);
break;
}
} else {
if (pc->system_map) {
fprintf(fp, " SYSTEM MAP: %s%s\n", pc->system_map,
is_livepatch() ? " [LIVEPATCH]" : "");
fprintf(fp, "DEBUG KERNEL: %s %s\n",
pc->namelist_orig ?
pc->namelist_orig : pc->namelist,
debug_kernel_version(pc->namelist));
} else
fprintf(fp, " KERNEL: %s%s\n", pc->namelist_orig ?
pc->namelist_orig : pc->namelist,
is_livepatch() ? " [LIVEPATCH]" : "");
}
if (pc->debuginfo_file) {
if (STREQ(pc->debuginfo_file, pc->namelist_debug) &&
pc->namelist_debug_orig)
fprintf(fp, " DEBUGINFO: %s\n",
pc->namelist_debug_orig);
else
fprintf(fp, " DEBUGINFO: %s\n", pc->debuginfo_file);
} else if (pc->namelist_debug)
fprintf(fp, "DEBUG KERNEL: %s %s\n", pc->namelist_debug_orig ?
pc->namelist_debug_orig : pc->namelist_debug,
debug_kernel_version(pc->namelist_debug));
/*
* After the initial banner display, we no longer need the
* temporary namelist file(s).
*/
if (!(pc->flags & RUNTIME)) {
if (pc->namelist_orig)
unlink(pc->namelist);
if (pc->namelist_debug_orig)
unlink(pc->namelist_debug);
}
if (dumpfile_is_split() || sadump_is_diskset() || is_ramdump_image())
fprintf(fp, " DUMPFILES: ");
else
fprintf(fp, " DUMPFILE: ");
if (ACTIVE()) {
if (REMOTE_ACTIVE())
fprintf(fp, "%s@%s (remote live system)\n",
pc->server_memsrc, pc->server);
else
fprintf(fp, "%s\n", pc->live_memsrc);
} else {
if (REMOTE_DUMPFILE())
fprintf(fp, "%s@%s (remote dumpfile)",
pc->server_memsrc, pc->server);
else if (REMOTE_PAUSED())
fprintf(fp, "%s %s (remote paused system)\n",
pc->server_memsrc, pc->server);
else {
if (dumpfile_is_split())
show_split_dumpfiles();
else if (sadump_is_diskset())
sadump_show_diskset();
else if (is_ramdump_image())
show_ramdump_files();
else
fprintf(fp, "%s", pc->dumpfile);
}
if (LIVE())
fprintf(fp, " [LIVE DUMP]");
if (NETDUMP_DUMPFILE() && is_partial_netdump())
fprintf(fp, " [PARTIAL DUMP]");
if (KDUMP_DUMPFILE() && is_incomplete_dump())
fprintf(fp, " [INCOMPLETE]");
if (DISKDUMP_DUMPFILE() && !dumpfile_is_split() &&
(is_partial_diskdump() || is_incomplete_dump())) {
fprintf(fp, " %s%s",
is_partial_diskdump() ?
" [PARTIAL DUMP]" : "",
is_incomplete_dump() ?
" [INCOMPLETE]" : "");
}
fprintf(fp, "\n");
if (KVMDUMP_DUMPFILE() && pc->kvmdump_mapfile)
fprintf(fp, " MAPFILE: %s\n",
pc->kvmdump_mapfile);
}
if (machine_type("PPC64"))
fprintf(fp, " CPUS: %d\n", get_cpus_to_display());
else {
fprintf(fp, " CPUS: %d", kt->cpus);
if (kt->cpus - get_cpus_to_display())
fprintf(fp, " [OFFLINE: %d]",
kt->cpus - get_cpus_to_display());
fprintf(fp, "\n");
}
if (ACTIVE())
get_xtime(&kt->date);
fprintf(fp, " DATE: %s\n",
strip_linefeeds(ctime(&kt->date.tv_sec)));
fprintf(fp, " UPTIME: %s\n", get_uptime(buf, NULL));
fprintf(fp, "LOAD AVERAGE: %s\n", get_loadavg(buf));
fprintf(fp, " TASKS: %ld\n", RUNNING_TASKS());
fprintf(fp, " NODENAME: %s\n", uts->nodename);
fprintf(fp, " RELEASE: %s\n", uts->release);
fprintf(fp, " VERSION: %s\n", uts->version);
fprintf(fp, " MACHINE: %s ", uts->machine);
if ((mhz = machdep->processor_speed()))
fprintf(fp, "(%ld Mhz)\n", mhz);
else
fprintf(fp, "(unknown Mhz)\n");
fprintf(fp, " MEMORY: %s\n", get_memory_size(buf));
#ifdef WHO_CARES
fprintf(fp, " DOMAINNAME: %s\n", uts->domainname);
#endif
if (XENDUMP_DUMPFILE() && (kt->xen_flags & XEN_SUSPEND))
return;
if (DUMPFILE()) {
fprintf(fp, " PANIC: ");
if (machdep->flags & HWRESET)
fprintf(fp, "(HARDWARE RESET)\n");
else if (machdep->flags & INIT)
fprintf(fp, "(INIT)\n");
else if (machdep->flags & MCA)
fprintf(fp, "(MCA)\n");
else {
strip_linefeeds(get_panicmsg(buf));
fprintf(fp, "\"%s\"%s\n", buf,
strstr(buf, "Oops: ") ?
" (check log for details)" : "");
}
}
}
/*
* Get the kernel version from the debug kernel and store it here.
*/
static char *debug_kernel_version_string = NULL;
static char *
debug_kernel_version(char *namelist)
{
FILE *pipe;
int argc;
char buf[BUFSIZE];
char command[BUFSIZE];
char *arglist[MAXARGS];
if (debug_kernel_version_string)
return debug_kernel_version_string;
sprintf(command, "/usr/bin/strings %s", namelist);
if ((pipe = popen(command, "r")) == NULL) {
debug_kernel_version_string = " ";
return debug_kernel_version_string;
}
argc = 0;
while (fgets(buf, BUFSIZE-1, pipe)) {
if (!strstr(buf, "Linux version 2.") &&
!strstr(buf, "Linux version 3.") &&
!strstr(buf, "Linux version 4.") &&
!strstr(buf, "Linux version 5."))
continue;
argc = parse_line(buf, arglist);
break;
}
pclose(pipe);
if ((argc >= 3) && (debug_kernel_version_string = (char *)
malloc(strlen(arglist[2])+3)))
sprintf(debug_kernel_version_string, "(%s)", arglist[2]);
else
debug_kernel_version_string = " ";
return debug_kernel_version_string;
}
/*
* Calculate and return the uptime.
*/
char *
get_uptime(char *buf, ulonglong *j64p)
{
ulong jiffies, tmp1, tmp2;
ulonglong jiffies_64, wrapped;
if (symbol_exists("jiffies_64")) {
get_symbol_data("jiffies_64", sizeof(ulonglong), &jiffies_64);
if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) {
wrapped = (jiffies_64 & 0xffffffff00000000ULL);
if (wrapped) {
wrapped -= 0x100000000ULL;
jiffies_64 &= 0x00000000ffffffffULL;
jiffies_64 |= wrapped;
jiffies_64 += (ulonglong)(300*machdep->hz);
} else {
tmp1 = (ulong)(uint)(-300*machdep->hz);
tmp2 = (ulong)jiffies_64;
jiffies_64 = (ulonglong)(tmp2 - tmp1);
}
}
if (buf)
convert_time(jiffies_64, buf);
if (j64p)
*j64p = jiffies_64;
} else {
get_symbol_data("jiffies", sizeof(long), &jiffies);
if (buf)
convert_time((ulonglong)jiffies, buf);
if (j64p)
*j64p = (ulonglong)jiffies;
}
return buf;
}
#define FSHIFT 11 /* nr of bits of precision */
#define FIXED_1 (1<<FSHIFT)
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
static char *
get_loadavg(char *buf)
{
int a, b, c;
long avenrun[3];
readmem(symbol_value("avenrun"), KVADDR, &avenrun[0],
sizeof(long)*3, "avenrun array", FAULT_ON_ERROR);
a = avenrun[0] + (FIXED_1/200);
b = avenrun[1] + (FIXED_1/200);
c = avenrun[2] + (FIXED_1/200);
sprintf(buf, "%d.%02d, %d.%02d, %d.%02d",
LOAD_INT(a), LOAD_FRAC(a),
LOAD_INT(b), LOAD_FRAC(b),
LOAD_INT(c), LOAD_FRAC(c));
return buf;
}
/*
* Determine whether a string or value equates to a system call name or value.
*/
int
is_system_call(char *name, ulong value)
{
int i;
ulong *sys_call_table, *sct;
char *sp;
long size;
int NR_syscalls;
NR_syscalls = get_NR_syscalls(NULL);
size = sizeof(void *) * NR_syscalls;
sys_call_table = (ulong *)GETBUF(size);
readmem(symbol_value("sys_call_table"), KVADDR, sys_call_table,
size, "sys_call_table", FAULT_ON_ERROR);
for (i = 0, sct = sys_call_table; i < NR_syscalls; i++, sct++) {
if (name && (sp = value_symbol(*sct))) {
if (STREQ(name, sp))
return TRUE;
} else if (value) {
if (value == *sct)
return TRUE;
}
}
return FALSE;
}
char *sys_call_hdr = "NUM SYSTEM CALL FILE AND LINE NUMBER\n";
static void
dump_sys_call_table(char *spec, int cnt)
{
int i, confirmed;
char buf1[BUFSIZE], *scp;
char buf2[BUFSIZE], *p;
char buf3[BUFSIZE];
char *arglist[MAXARGS];
int argc, NR_syscalls;
int number, printit, hdr_printed;
struct syment *sp, *spn;
long size;
#ifdef S390X
unsigned int *sct, *sys_call_table, sys_ni_syscall, addr;
#else
ulong *sys_call_table, *sct, sys_ni_syscall, addr;
#endif
if (NO_LINE_NUMBERS())
error(INFO, "line numbers are not available\n");
NR_syscalls = get_NR_syscalls(&confirmed);
if (CRASHDEBUG(1))
fprintf(fp, "NR_syscalls: %d (%sconfirmed)\n",
NR_syscalls, confirmed ? "" : "not ");
size = sizeof(addr) * NR_syscalls;
#ifdef S390X
sys_call_table = (unsigned int *)GETBUF(size);
#else
sys_call_table = (ulong *)GETBUF(size);
#endif
readmem(symbol_value("sys_call_table"), KVADDR, sys_call_table,
size, "sys_call_table", FAULT_ON_ERROR);
sys_ni_syscall = symbol_value("sys_ni_syscall");
if (spec)
open_tmpfile();
fprintf(fp, "%s", sys_call_hdr);
for (i = 0, sct = sys_call_table; i < NR_syscalls; i++, sct++) {
if (!(scp = value_symbol(*sct))) {
if (confirmed || CRASHDEBUG(1)) {
fprintf(fp, (*gdb_output_radix == 16) ?
"%3x " : "%3d ", i);
fprintf(fp,
"invalid sys_call_table entry: %lx ",
(unsigned long)*sct);
if (strlen(value_to_symstr(*sct, buf1, 0)))
fprintf(fp, "(%s)\n", buf1);
else
fprintf(fp, "\n");
}
continue;
}
fprintf(fp, (*gdb_output_radix == 16) ? "%3x " : "%3d ", i);
if (sys_ni_syscall && *sct == sys_ni_syscall)
fprintf(fp, "%-26s ", "sys_ni_syscall");
else
fprintf(fp, "%-26s ", scp);
/*
* For system call symbols whose first instruction is
* an inline from a header file, the file/line-number is
* confusing. For this command only, look for the first
* instruction address in the system call that shows the
* the actual source file containing the system call.
*/
sp = value_search(*sct, NULL);
spn = next_symbol(NULL, sp);
get_build_directory(buf2);
for (addr = *sct; sp && spn && (addr < spn->value); addr++) {
BZERO(buf1, BUFSIZE);
get_line_number(addr, buf1, FALSE);
if (strstr(buf1, ".h: ") && strstr(buf1, "include/"))
continue;
if (strstr(buf1, buf2)) {
p = buf1 + strlen(buf2);
fprintf(fp, "%s%s",
strlen(buf1) ? ".." : "", p);
break;
}
}
fprintf(fp, "\n");
}
if (spec) {
rewind(pc->tmpfile);
hdr_printed = cnt;
if ((number = IS_A_NUMBER(spec)))
sprintf(buf3, (*gdb_output_radix == 16) ? "%lx" : "%ld",
stol(spec, FAULT_ON_ERROR, NULL));
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
printit = FALSE;
strcpy(buf2, buf1);
argc = parse_line(buf2, arglist);
if (argc < 2)
continue;
if (number && STREQ(arglist[0], buf3))
printit = TRUE;
else if (!number && strstr(arglist[1], spec))
printit = TRUE;
if (printit) {
fprintf(pc->saved_fp, "%s%s", hdr_printed++ ?
"" : sys_call_hdr, buf1);
if (number)
break;
}
}
close_tmpfile();
}
}
/*
* Get the number of system calls in the sys_call_table, confirming
* the number only if the debuginfo data shows sys_call_table as an
* array. Otherwise base it upon next symbol after it.
*/
static int
get_NR_syscalls(int *confirmed)
{
ulong sys_call_table;
struct syment *sp;
int type, cnt;
type = get_symbol_type("sys_call_table", NULL, NULL);
if ((type == TYPE_CODE_ARRAY) &&
(cnt = get_array_length("sys_call_table", NULL, 0))) {
*confirmed = TRUE;
return cnt;
}
*confirmed = FALSE;
sys_call_table = symbol_value("sys_call_table");
if (!(sp = next_symbol("sys_call_table", NULL)))
return 256;
while (sp->value == sys_call_table) {
if (!(sp = next_symbol(sp->name, NULL)))
return 256;
}
if (machine_type("S390X"))
cnt = (sp->value - sys_call_table)/sizeof(int);
else
cnt = (sp->value - sys_call_table)/sizeof(void *);
return cnt;
}
/*
* "help -k" output
*/
void
dump_kernel_table(int verbose)
{
int i, j, more, nr_cpus;
struct new_utsname *uts;
int others;
others = 0;
more = FALSE;
uts = &kt->utsname;
fprintf(fp, " flags: %lx\n (", kt->flags);
if (kt->flags & NO_MODULE_ACCESS)
fprintf(fp, "%sNO_MODULE_ACCESS", others++ ? "|" : "");
if (kt->flags & TVEC_BASES_V1)
fprintf(fp, "%sTVEC_BASES_V1", others++ ? "|" : "");
if (kt->flags & TVEC_BASES_V2)
fprintf(fp, "%sTVEC_BASES_V2", others++ ? "|" : "");
if (kt->flags & GCC_2_96)
fprintf(fp, "%sGCC_2_96", others++ ? "|" : "");
if (kt->flags & GCC_3_2)
fprintf(fp, "%sGCC_3_2", others++ ? "|" : "");
if (kt->flags & GCC_3_2_3)
fprintf(fp, "%sGCC_3_2_3", others++ ? "|" : "");
if (kt->flags & GCC_3_3_2)
fprintf(fp, "%sGCC_3_3_2", others++ ? "|" : "");
if (kt->flags & GCC_3_3_3)
fprintf(fp, "%sGCC_3_3_3", others++ ? "|" : "");
if (kt->flags & RA_SEEK)
fprintf(fp, "%sRA_SEEK", others++ ? "|" : "");
if (kt->flags & NO_RA_SEEK)
fprintf(fp, "%sNO_RA_SEEK", others++ ? "|" : "");
if (kt->flags & KALLSYMS_V1)
fprintf(fp, "%sKALLSYMS_V1", others++ ? "|" : "");
if (kt->flags & NO_KALLSYMS)
fprintf(fp, "%sNO_KALLSYMS", others++ ? "|" : "");
if (kt->flags & PER_CPU_OFF)
fprintf(fp, "%sPER_CPU_OFF", others++ ? "|" : "");
if (kt->flags & SMP)
fprintf(fp, "%sSMP", others++ ? "|" : "");
if (kt->flags & KMOD_V1)
fprintf(fp, "%sKMOD_V1", others++ ? "|" : "");
if (kt->flags & KMOD_V2)
fprintf(fp, "%sKMOD_V2", others++ ? "|" : "");
if (kt->flags & KALLSYMS_V2)
fprintf(fp, "%sKALLSYMS_V2", others++ ? "|" : "");
if (kt->flags & USE_OLD_BT)
fprintf(fp, "%sUSE_OLD_BT", others++ ? "|" : "");
if (kt->flags & ARCH_XEN)
fprintf(fp, "%sARCH_XEN", others++ ? "|" : "");
if (kt->flags & ARCH_PVOPS_XEN)
fprintf(fp, "%sARCH_PVOPS_XEN", others++ ? "|" : "");
if (kt->flags & ARCH_OPENVZ)
fprintf(fp, "%sARCH_OPENVZ", others++ ? "|" : "");
if (kt->flags & ARCH_PVOPS)
fprintf(fp, "%sARCH_PVOPS", others++ ? "|" : "");
if (kt->flags & NO_IKCONFIG)
fprintf(fp, "%sNO_IKCONFIG", others++ ? "|" : "");
if (kt->flags & DWARF_UNWIND)
fprintf(fp, "%sDWARF_UNWIND", others++ ? "|" : "");
if (kt->flags & NO_DWARF_UNWIND)
fprintf(fp, "%sNO_DWARF_UNWIND", others++ ? "|" : "");
if (kt->flags & DWARF_UNWIND_MEMORY)
fprintf(fp, "%sDWARF_UNWIND_MEMORY", others++ ? "|" : "");
if (kt->flags & DWARF_UNWIND_EH_FRAME)
fprintf(fp, "%sDWARF_UNWIND_EH_FRAME", others++ ? "|" : "");
if (kt->flags & DWARF_UNWIND_MODULES)
fprintf(fp, "%sDWARF_UNWIND_MODULES", others++ ? "|" : "");
if (kt->flags & BUGVERBOSE_OFF)
fprintf(fp, "%sBUGVERBOSE_OFF", others++ ? "|" : "");
if (kt->flags & RELOC_SET)
fprintf(fp, "%sRELOC_SET", others++ ? "|" : "");
if (kt->flags & RELOC_FORCE)
fprintf(fp, "%sRELOC_FORCE", others++ ? "|" : "");
if (kt->flags & PRE_KERNEL_INIT)
fprintf(fp, "%sPRE_KERNEL_INIT", others++ ? "|" : "");
if (kt->flags & IRQ_DESC_TREE)
fprintf(fp, "%sIRQ_DESC_TREE", others++ ? "|" : "");
fprintf(fp, ")\n");
others = 0;
fprintf(fp, " flags2: %llx %s", kt->flags2,
kt->flags2 ? " \n (" : " (unused");
if (kt->flags2 & RELOC_AUTO)
fprintf(fp, "%sRELOC_AUTO", others++ ? "|" : "");
if (kt->flags2 & KASLR)
fprintf(fp, "%sKASLR", others++ ? "|" : "");
if (kt->flags2 & KASLR_CHECK)
fprintf(fp, "%sKASLR_CHECK", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " stext: %lx\n", kt->stext);
fprintf(fp, " etext: %lx\n", kt->etext);
fprintf(fp, " stext_init: %lx\n", kt->stext_init);
fprintf(fp, " etext_init: %lx\n", kt->etext_init);
fprintf(fp, " init_begin: %lx\n", kt->init_begin);
fprintf(fp, " init_end: %lx\n", kt->init_end);
fprintf(fp, " end: %lx\n", kt->end);
fprintf(fp, " cpus: %d\n", kt->cpus);
fprintf(fp, " cpus_override: %s\n", kt->cpus_override);
fprintf(fp, " NR_CPUS: %d (compiled-in to this version of %s)\n",
NR_CPUS, pc->program_name);
fprintf(fp, "kernel_NR_CPUS: %d\n", kt->kernel_NR_CPUS);
others = 0;
fprintf(fp, "ikconfig_flags: %x (", kt->ikconfig_flags);
if (kt->ikconfig_flags & IKCONFIG_AVAIL)
fprintf(fp, "%sIKCONFIG_AVAIL", others++ ? "|" : "");
if (kt->ikconfig_flags & IKCONFIG_LOADED)
fprintf(fp, "%sIKCONFIG_LOADED", others++ ? "|" : "");
if (!kt->ikconfig_flags)
fprintf(fp, "unavailable");
fprintf(fp, ")\n");
fprintf(fp, " ikconfig_ents: %d\n", kt->ikconfig_ents);
if (kt->display_bh == display_bh_1)
fprintf(fp, " display_bh: display_bh_1()\n");
else if (kt->display_bh == display_bh_2)
fprintf(fp, " display_bh: display_bh_2()\n");
else if (kt->display_bh == display_bh_3)
fprintf(fp, " display_bh: display_bh_3()\n");
else
fprintf(fp, " display_bh: %lx\n", (ulong)kt->display_bh);
fprintf(fp, " highest_irq: ");
if (kt->highest_irq)
fprintf(fp, "%d\n", kt->highest_irq);
else
fprintf(fp, "(unused/undetermined)\n");
fprintf(fp, " module_list: %lx\n", kt->module_list);
fprintf(fp, " kernel_module: %lx\n", kt->kernel_module);
fprintf(fp, "mods_installed: %d\n", kt->mods_installed);
fprintf(fp, " module_tree: %s\n", kt->module_tree ?
kt->module_tree : "(not used)");
if (!(pc->flags & KERNEL_DEBUG_QUERY) && ACTIVE())
get_xtime(&kt->date);
fprintf(fp, " date: %s\n",
strip_linefeeds(ctime(&kt->date.tv_sec)));
fprintf(fp, " proc_version: %s\n", strip_linefeeds(kt->proc_version));
fprintf(fp, " new_utsname: \n");
fprintf(fp, " .sysname: %s\n", uts->sysname);
fprintf(fp, " .nodename: %s\n", uts->nodename);
fprintf(fp, " .release: %s\n", uts->release);
fprintf(fp, " .version: %s\n", uts->version);
fprintf(fp, " .machine: %s\n", uts->machine);
fprintf(fp, " .domainname: %s\n", uts->domainname);
fprintf(fp, "kernel_version: %d.%d.%d\n", kt->kernel_version[0],
kt->kernel_version[1], kt->kernel_version[2]);
fprintf(fp, " gcc_version: %d.%d.%d\n", kt->gcc_version[0],
kt->gcc_version[1], kt->gcc_version[2]);
fprintf(fp, " BUG_bytes: %d\n", kt->BUG_bytes);
fprintf(fp, " relocate: %lx", kt->relocate);
if (kt->flags2 & KASLR)
fprintf(fp, " (KASLR offset: %lx / %ldMB)",
kt->relocate * -1,
(kt->relocate * -1) >> 20);
fprintf(fp, "\n runq_siblings: %d\n", kt->runq_siblings);
fprintf(fp, " __rq_idx[NR_CPUS]: ");
nr_cpus = kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS;
for (i = 0; i < nr_cpus; i++) {
if (!(kt->__rq_idx)) {
fprintf(fp, "(unused)");
break;
}
fprintf(fp, "%ld ", kt->__rq_idx[i]);
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (kt->__rq_idx[j])
more = TRUE;
}
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n __cpu_idx[NR_CPUS]: ");
for (i = 0; i < nr_cpus; i++) {
if (!(kt->__cpu_idx)) {
fprintf(fp, "(unused)");
break;
}
fprintf(fp, "%ld ", kt->__cpu_idx[i]);
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (kt->__cpu_idx[j])
more = TRUE;
}
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n __per_cpu_offset[NR_CPUS]:");
for (i = 0; i < nr_cpus; i++) {
fprintf(fp, "%s%.*lx ", (i % 4) == 0 ? "\n " : "",
LONG_PRLEN, kt->__per_cpu_offset[i]);
if ((i % 4) == 0) {
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (kt->__per_cpu_offset[j] &&
(kt->__per_cpu_offset[j] != kt->__per_cpu_offset[i]))
more = TRUE;
}
}
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n cpu_flags[NR_CPUS]: ");
for (i = 0; i < nr_cpus; i++) {
if (!(kt->cpu_flags)) {
fprintf(fp, "(unused)\n");
goto no_cpu_flags;
}
fprintf(fp, "%lx ", kt->cpu_flags[i]);
for (j = i, more = FALSE; j < nr_cpus; j++) {
if (kt->cpu_flags[j])
more = TRUE;
}
if (!more) {
fprintf(fp, "...");
break;
}
}
fprintf(fp, "\n");
fprintf(fp, " cpu_possible_map: ");
if (cpu_map_addr("possible")) {
for (i = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & POSSIBLE_MAP)
fprintf(fp, "%d ", i);
}
fprintf(fp, "\n");
} else
fprintf(fp, "(does not exist)\n");
fprintf(fp, " cpu_present_map: ");
if (cpu_map_addr("present")) {
for (i = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & PRESENT_MAP)
fprintf(fp, "%d ", i);
}
fprintf(fp, "\n");
} else
fprintf(fp, "(does not exist)\n");
fprintf(fp, " cpu_online_map: ");
if (cpu_map_addr("online")) {
for (i = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & ONLINE_MAP)
fprintf(fp, "%d ", i);
}
fprintf(fp, "\n");
} else
fprintf(fp, "(does not exist)\n");
fprintf(fp, " cpu_active_map: ");
if (cpu_map_addr("active")) {
for (i = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & ACTIVE_MAP)
fprintf(fp, "%d ", i);
}
fprintf(fp, "\n");
} else
fprintf(fp, "(does not exist)\n");
no_cpu_flags:
fprintf(fp, " vmcoreinfo: \n");
fprintf(fp, " log_buf_SYMBOL: %lx\n", kt->vmcoreinfo.log_buf_SYMBOL);
fprintf(fp, " log_end_SYMBOL: %ld\n", kt->vmcoreinfo.log_end_SYMBOL);
fprintf(fp, " log_buf_len_SYMBOL: %ld\n", kt->vmcoreinfo.log_buf_len_SYMBOL);
fprintf(fp, " logged_chars_SYMBOL: %ld\n", kt->vmcoreinfo.logged_chars_SYMBOL);
fprintf(fp, "log_first_idx_SYMBOL: %ld\n", kt->vmcoreinfo.log_first_idx_SYMBOL);
fprintf(fp, " log_next_idx_SYMBOL: %ld\n", kt->vmcoreinfo.log_next_idx_SYMBOL);
fprintf(fp, " log_SIZE: %ld\n", kt->vmcoreinfo.log_SIZE);
fprintf(fp, " log_ts_nsec_OFFSET: %ld\n", kt->vmcoreinfo.log_ts_nsec_OFFSET);
fprintf(fp, " log_len_OFFSET: %ld\n", kt->vmcoreinfo.log_len_OFFSET);
fprintf(fp, " log_text_len_OFFSET: %ld\n", kt->vmcoreinfo.log_text_len_OFFSET);
fprintf(fp, " log_dict_len_OFFSET: %ld\n", kt->vmcoreinfo.log_dict_len_OFFSET);
fprintf(fp, " phys_base_SYMBOL: %lx\n", kt->vmcoreinfo.phys_base_SYMBOL);
fprintf(fp, " _stext_SYMBOL: %lx\n", kt->vmcoreinfo._stext_SYMBOL);
fprintf(fp, " hypervisor: %s\n", kt->hypervisor);
others = 0;
fprintf(fp, " xen_flags: %lx (", kt->xen_flags);
if (kt->xen_flags & WRITABLE_PAGE_TABLES)
fprintf(fp, "%sWRITABLE_PAGE_TABLES", others++ ? "|" : "");
if (kt->xen_flags & SHADOW_PAGE_TABLES)
fprintf(fp, "%sSHADOW_PAGE_TABLES", others++ ? "|" : "");
if (kt->xen_flags & CANONICAL_PAGE_TABLES)
fprintf(fp, "%sCANONICAL_PAGE_TABLES", others++ ? "|" : "");
if (kt->xen_flags & XEN_SUSPEND)
fprintf(fp, "%sXEN_SUSPEND", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " m2p_page: %lx\n", (ulong)kt->m2p_page);
fprintf(fp, "phys_to_machine_mapping: %lx\n", kt->phys_to_machine_mapping);
fprintf(fp, " p2m_table_size: %ld\n", kt->p2m_table_size);
fprintf(fp, " p2m_mapping_cache[%d]: %s\n", P2M_MAPPING_CACHE,
verbose ? "" : "(use \"help -K\" to view cache contents)");
for (i = 0; verbose && (i < P2M_MAPPING_CACHE); i++) {
if (!kt->p2m_mapping_cache[i].mapping)
continue;
fprintf(fp, " [%d] mapping: %lx pfn: ", i, kt->p2m_mapping_cache[i].mapping);
if (PVOPS_XEN())
fprintf(fp, "%lx ", kt->p2m_mapping_cache[i].pfn);
else
fprintf(fp, "n/a ");
fprintf(fp, "start: %lx end: %lx (%ld mfns)\n",
kt->p2m_mapping_cache[i].start,
kt->p2m_mapping_cache[i].end,
kt->p2m_mapping_cache[i].end - kt->p2m_mapping_cache[i].start + 1);
}
fprintf(fp, " last_mapping_read: %lx\n", kt->last_mapping_read);
fprintf(fp, " p2m_cache_index: %ld\n", kt->p2m_cache_index);
fprintf(fp, " p2m_pages_searched: %ld\n", kt->p2m_pages_searched);
fprintf(fp, " p2m_mfn_cache_hits: %ld ", kt->p2m_mfn_cache_hits);
if (kt->p2m_pages_searched)
fprintf(fp, "(%ld%%)\n", kt->p2m_mfn_cache_hits * 100 / kt->p2m_pages_searched);
else
fprintf(fp, "\n");
fprintf(fp, " p2m_page_cache_hits: %ld ", kt->p2m_page_cache_hits);
if (kt->p2m_pages_searched)
fprintf(fp, "(%ld%%)\n", kt->p2m_page_cache_hits * 100 / kt->p2m_pages_searched);
else
fprintf(fp, "\n");
fprintf(fp, " pvops_xen:\n");
fprintf(fp, " p2m_top: %lx\n", kt->pvops_xen.p2m_top);
fprintf(fp, " p2m_top_entries: %d\n", kt->pvops_xen.p2m_top_entries);
if (symbol_exists("p2m_mid_missing"))
fprintf(fp, " p2m_mid_missing: %lx\n", kt->pvops_xen.p2m_mid_missing);
fprintf(fp, " p2m_missing: %lx\n", kt->pvops_xen.p2m_missing);
}
/*
* Set the context to the active task on a given cpu -- dumpfiles only.
*/
void
set_cpu(int cpu)
{
ulong task;
if (cpu >= kt->cpus)
error(FATAL, "invalid cpu number: system has only %d cpu%s\n",
kt->cpus, kt->cpus > 1 ? "s" : "");
if (hide_offline_cpu(cpu))
error(FATAL, "invalid cpu number: cpu %d is OFFLINE\n", cpu);
if ((task = get_active_task(cpu)))
set_context(task, NO_PID);
else
error(FATAL, "cannot determine active task on cpu %ld\n", cpu);
show_context(CURRENT_CONTEXT());
}
/*
* Collect the irq_desc[] entry along with its associated handler and
* action structures.
*/
void
cmd_irq(void)
{
int i, c;
int nr_irqs;
ulong *cpus;
int show_intr, choose_cpu;
char buf[10];
char arg_buf[BUFSIZE];
cpus = NULL;
show_intr = 0;
choose_cpu = 0;
while ((c = getopt(argcnt, args, "dbuasc:")) != EOF) {
switch(c)
{
case 'd':
display_idt_table();
return;
case 'b':
if (!kt->display_bh) {
if (symbol_exists("bh_base") &&
symbol_exists("bh_mask") &&
symbol_exists("bh_active"))
kt->display_bh = display_bh_1;
else if (symbol_exists("bh_base") &&
symbol_exists("softirq_state") &&
symbol_exists("softirq_vec"))
kt->display_bh = display_bh_2;
else if (symbol_exists("bh_base") &&
symbol_exists("irq_stat") &&
symbol_exists("softirq_vec") &&
VALID_MEMBER(irq_cpustat_t___softirq_active)
&& VALID_MEMBER(irq_cpustat_t___softirq_mask))
kt->display_bh = display_bh_3;
else if (get_symbol_type("softirq_vec", NULL, NULL) ==
TYPE_CODE_ARRAY)
kt->display_bh = display_bh_4;
else
error(FATAL,
"bottom-half option not supported\n");
}
kt->display_bh();
return;
case 'u':
pc->curcmd_flags |= IRQ_IN_USE;
if (kernel_symbol_exists("no_irq_chip"))
pc->curcmd_private = (ulonglong)symbol_value("no_irq_chip");
else if (kernel_symbol_exists("no_irq_type"))
pc->curcmd_private = (ulonglong)symbol_value("no_irq_type");
else
error(WARNING,
"irq: -u option ignored: \"no_irq_chip\" or \"no_irq_type\" symbols do not exist\n");
break;
case 'a':
if (!machdep->get_irq_affinity)
option_not_supported(c);
if (VALID_STRUCT(irq_data)) {
if (INVALID_MEMBER(irq_data_affinity))
option_not_supported(c);
} else if (INVALID_MEMBER(irq_desc_t_affinity))
option_not_supported(c);
if ((nr_irqs = machdep->nr_irqs) == 0)
error(FATAL, "cannot determine number of IRQs\n");
fprintf(fp, "IRQ NAME AFFINITY\n");
for (i = 0; i < nr_irqs; i++)
machdep->get_irq_affinity(i);
return;
case 's':
if (!machdep->show_interrupts)
option_not_supported(c);
show_intr = 1;
break;
case 'c':
if (choose_cpu) {
error(INFO, "only one -c option allowed\n");
argerrs++;
} else {
choose_cpu = 1;
BZERO(arg_buf, BUFSIZE);
strncpy(arg_buf, optarg, strlen(optarg));
}
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if ((nr_irqs = machdep->nr_irqs) == 0)
error(FATAL, "cannot determine number of IRQs\n");
if (show_intr) {
cpus = get_cpumask_buf();
if (choose_cpu) {
make_cpumask(arg_buf, cpus, FAULT_ON_ERROR, NULL);
} else {
for (i = 0; i < kt->cpus; i++)
SET_BIT(cpus, i);
}
for (i = 0; i < kt->cpus; i++) {
if (NUM_IN_BITMAP(cpus, i) && hide_offline_cpu(i))
error(INFO, "CPU%d is OFFLINE.\n", i);
}
fprintf(fp, " ");
BZERO(buf, 10);
for (i = 0; i < kt->cpus; i++) {
if (hide_offline_cpu(i))
continue;
if (NUM_IN_BITMAP(cpus, i)) {
sprintf(buf, "CPU%d", i);
fprintf(fp, "%10s ", buf);
}
}
fprintf(fp, "\n");
for (i = 0; i < nr_irqs; i++)
machdep->show_interrupts(i, cpus);
if (choose_cpu)
FREEBUF(cpus);
return;
}
pc->curcmd_flags &= ~HEADER_PRINTED;
if (!args[optind]) {
for (i = 0; i < nr_irqs; i++)
machdep->dump_irq(i);
return;
}
pc->curcmd_flags &= ~IRQ_IN_USE;
while (args[optind]) {
i = dtoi(args[optind], FAULT_ON_ERROR, NULL);
if (i >= nr_irqs)
error(FATAL, "invalid IRQ value: %d (%d max)\n",
i, nr_irqs-1);
machdep->dump_irq(i);
optind++;
}
}
static ulong
get_irq_desc_addr(int irq)
{
int c;
ulong cnt, addr, ptr;
long len;
struct radix_tree_pair *rtp;
addr = 0;
rtp = NULL;
if (!VALID_STRUCT(irq_desc_t))
error(FATAL, "cannot determine size of irq_desc_t\n");
len = SIZE(irq_desc_t);
if (symbol_exists("irq_desc"))
addr = symbol_value("irq_desc") + (len * irq);
else if (symbol_exists("_irq_desc"))
addr = symbol_value("_irq_desc") + (len * irq);
else if (symbol_exists("irq_desc_ptrs")) {
if (get_symbol_type("irq_desc_ptrs", NULL, NULL) == TYPE_CODE_PTR)
get_symbol_data("irq_desc_ptrs", sizeof(void *), &ptr);
else
ptr = symbol_value("irq_desc_ptrs");
ptr += (irq * sizeof(void *));
readmem(ptr, KVADDR, &addr,
sizeof(void *), "irq_desc_ptrs entry",
FAULT_ON_ERROR);
} else if (kt->flags & IRQ_DESC_TREE) {
if (kt->highest_irq && (irq > kt->highest_irq))
return addr;
cnt = do_radix_tree(symbol_value("irq_desc_tree"),
RADIX_TREE_COUNT, NULL);
len = sizeof(struct radix_tree_pair) * (cnt+1);
rtp = (struct radix_tree_pair *)GETBUF(len);
rtp[0].index = cnt;
cnt = do_radix_tree(symbol_value("irq_desc_tree"),
RADIX_TREE_GATHER, rtp);
if (kt->highest_irq == 0)
kt->highest_irq = rtp[cnt-1].index;
for (c = 0; c < cnt; c++) {
if (rtp[c].index == irq) {
if (CRASHDEBUG(1))
fprintf(fp, "index: %ld value: %lx\n",
rtp[c].index, (ulong)rtp[c].value);
addr = (ulong)rtp[c].value;
break;
}
}
FREEBUF(rtp);
} else {
error(FATAL,
"neither irq_desc, _irq_desc, irq_desc_ptrs "
"or irq_desc_tree symbols exist\n");
}
return addr;
}
static void
display_cpu_affinity(ulong *mask)
{
int cpu, seq, start, count;
seq = FALSE;
start = 0;
count = 0;
for (cpu = 0; cpu < kt->cpus; ++cpu) {
if (NUM_IN_BITMAP(mask, cpu)) {
if (seq)
continue;
start = cpu;
seq = TRUE;
} else if (seq) {
if (count)
fprintf(fp, ",");
if (start == cpu - 1)
fprintf(fp, "%d", cpu - 1);
else
fprintf(fp, "%d-%d", start, cpu - 1);
count++;
seq = FALSE;
}
}
if (seq) {
if (count)
fprintf(fp, ",");
if (start == kt->cpus - 1)
fprintf(fp, "%d", kt->cpus - 1);
else
fprintf(fp, "%d-%d", start, kt->cpus - 1);
}
}
/*
* Do the work for cmd_irq().
*/
void
generic_dump_irq(int irq)
{
ulong irq_desc_addr;
char buf[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
int status, depth, others;
ulong handler, action, value;
ulong tmp1, tmp2;
handler = UNINITIALIZED;
action = 0;
irq_desc_addr = get_irq_desc_addr(irq);
if (!irq_desc_addr && symbol_exists("irq_desc_ptrs")) {
if (!(pc->curcmd_flags & IRQ_IN_USE))
fprintf(fp, " IRQ: %d (unused)\n\n", irq);
return;
}
if (irq_desc_addr) {
if (VALID_MEMBER(irq_desc_t_status))
readmem(irq_desc_addr + OFFSET(irq_desc_t_status),
KVADDR, &status, sizeof(int), "irq_desc status",
FAULT_ON_ERROR);
if (VALID_MEMBER(irq_desc_t_handler))
readmem(irq_desc_addr + OFFSET(irq_desc_t_handler),
KVADDR, &handler, sizeof(long), "irq_desc handler",
FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_desc_t_chip))
readmem(irq_desc_addr + OFFSET(irq_desc_t_chip), KVADDR,
&handler, sizeof(long), "irq_desc chip",
FAULT_ON_ERROR);
readmem(irq_desc_addr + OFFSET(irq_desc_t_action), KVADDR,
&action, sizeof(long), "irq_desc action", FAULT_ON_ERROR);
readmem(irq_desc_addr + OFFSET(irq_desc_t_depth), KVADDR, &depth,
sizeof(int), "irq_desc depth", FAULT_ON_ERROR);
}
if (!action && (handler == (ulong)pc->curcmd_private))
return;
if ((handler == UNINITIALIZED) && VALID_STRUCT(irq_data))
goto irq_desc_format_v2;
if (!irq_desc_addr) {
if (!(pc->curcmd_flags & IRQ_IN_USE))
fprintf(fp, " IRQ: %d (unused)\n\n", irq);
return;
}
fprintf(fp, " IRQ: %d\n", irq);
fprintf(fp, " STATUS: %x %s", status, status ? "(" : "");
others = 0;
if (status & IRQ_INPROGRESS) {
fprintf(fp, "IRQ_INPROGRESS");
others++;
}
if (status & IRQ_DISABLED)
fprintf(fp, "%sIRQ_DISABLED", others++ ? "|" : "");
if (status & IRQ_PENDING)
fprintf(fp, "%sIRQ_PENDING", others++ ? "|" : "");
if (status & IRQ_REPLAY)
fprintf(fp, "%sIRQ_REPLAY", others++ ? "|" : "");
if (status & IRQ_AUTODETECT)
fprintf(fp, "%sIRQ_AUTODETECT", others++ ? "|" : "");
if (status & IRQ_WAITING)
fprintf(fp, "%sIRQ_WAITING", others++ ? "|" : "");
if (status & IRQ_LEVEL)
fprintf(fp, "%sIRQ_LEVEL", others++ ? "|" : "");
if (status & IRQ_MASKED)
fprintf(fp, "%sIRQ_MASKED", others++ ? "|" : "");
fprintf(fp, "%s\n", status ? ")" : "");
fprintf(fp, "HANDLER: ");
if (value_symbol(handler)) {
fprintf(fp, "%lx ", handler);
pad_line(fp, VADDR_PRLEN == 8 ?
VADDR_PRLEN+2 : VADDR_PRLEN-6, ' ');
fprintf(fp, "<%s>\n", value_symbol(handler));
} else
fprintf(fp, "%lx\n", handler);
if (handler) {
if (VALID_MEMBER(hw_interrupt_type_typename))
readmem(handler+OFFSET(hw_interrupt_type_typename),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type typename", FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_chip_typename))
readmem(handler+OFFSET(irq_chip_typename),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type typename", FAULT_ON_ERROR);
fprintf(fp, " typename: %lx ", tmp1);
BZERO(buf, BUFSIZE);
if (read_string(tmp1, buf, BUFSIZE-1))
fprintf(fp, "\"%s\"", buf);
fprintf(fp, "\n");
if (VALID_MEMBER(hw_interrupt_type_startup))
readmem(handler+OFFSET(hw_interrupt_type_startup),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type startup", FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_chip_startup))
readmem(handler+OFFSET(irq_chip_startup),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type startup", FAULT_ON_ERROR);
fprintf(fp, " startup: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>", value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "startup indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
if (VALID_MEMBER(hw_interrupt_type_shutdown))
readmem(handler+OFFSET(hw_interrupt_type_shutdown),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type shutdown", FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_chip_shutdown))
readmem(handler+OFFSET(irq_chip_shutdown),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type shutdown", FAULT_ON_ERROR);
fprintf(fp, " shutdown: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>", value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "shutdown indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
if (VALID_MEMBER(hw_interrupt_type_handle)) {
readmem(handler+OFFSET(hw_interrupt_type_handle),
KVADDR,
&tmp1, sizeof(void *),
"hw_interrupt_type handle", FAULT_ON_ERROR);
fprintf(fp, " handle: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "handle indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(hw_interrupt_type_enable))
readmem(handler+OFFSET(hw_interrupt_type_enable),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type enable", FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_chip_enable))
readmem(handler+OFFSET(irq_chip_enable),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type enable", FAULT_ON_ERROR);
fprintf(fp, " enable: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>", value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "enable indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
if (VALID_MEMBER(hw_interrupt_type_disable))
readmem(handler+OFFSET(hw_interrupt_type_disable),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type disable", FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_chip_disable))
readmem(handler+OFFSET(irq_chip_disable),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type disable", FAULT_ON_ERROR);
fprintf(fp, " disable: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>", value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "disable indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
if (VALID_MEMBER(hw_interrupt_type_ack)) {
readmem(handler+OFFSET(hw_interrupt_type_ack), KVADDR,
&tmp1, sizeof(void *),
"hw_interrupt_type ack", FAULT_ON_ERROR);
fprintf(fp, " ack: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "ack indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
} else if (VALID_MEMBER(irq_chip_ack)) {
readmem(handler+OFFSET(irq_chip_ack), KVADDR,
&tmp1, sizeof(void *),
"irq_chip ack", FAULT_ON_ERROR);
fprintf(fp, " ack: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "ack indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(irq_chip_mask)) {
readmem(handler+OFFSET(irq_chip_mask), KVADDR,
&tmp1, sizeof(void *),
"irq_chip mask", FAULT_ON_ERROR);
fprintf(fp, " mask: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "mask indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(irq_chip_mask_ack)) {
readmem(handler+OFFSET(irq_chip_mask_ack), KVADDR,
&tmp1, sizeof(void *),
"irq_chip mask_ack", FAULT_ON_ERROR);
fprintf(fp, " mask_ack: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "mask_ack indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(irq_chip_unmask)) {
readmem(handler+OFFSET(irq_chip_unmask), KVADDR,
&tmp1, sizeof(void *),
"irq_chip unmask", FAULT_ON_ERROR);
fprintf(fp, " unmask: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "unmask indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(irq_chip_eoi)) {
readmem(handler+OFFSET(irq_chip_eoi), KVADDR,
&tmp1, sizeof(void *),
"irq_chip eoi", FAULT_ON_ERROR);
fprintf(fp, " eoi: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "eoi indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(hw_interrupt_type_end)) {
readmem(handler+OFFSET(hw_interrupt_type_end), KVADDR,
&tmp1, sizeof(void *),
"hw_interrupt_type end", FAULT_ON_ERROR);
fprintf(fp, " end: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "end indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
} else if (VALID_MEMBER(irq_chip_end)) {
readmem(handler+OFFSET(irq_chip_end), KVADDR,
&tmp1, sizeof(void *),
"irq_chip end", FAULT_ON_ERROR);
fprintf(fp, " end: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "end indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(hw_interrupt_type_set_affinity)) {
readmem(handler+OFFSET(hw_interrupt_type_set_affinity),
KVADDR, &tmp1, sizeof(void *),
"hw_interrupt_type set_affinity",
FAULT_ON_ERROR);
fprintf(fp, " set_affinity: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "set_affinity indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
} else if (VALID_MEMBER(irq_chip_set_affinity)) {
readmem(handler+OFFSET(irq_chip_set_affinity),
KVADDR, &tmp1, sizeof(void *),
"irq_chip set_affinity",
FAULT_ON_ERROR);
fprintf(fp, " set_affinity: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "set_affinity indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(irq_chip_retrigger)) {
readmem(handler+OFFSET(irq_chip_retrigger), KVADDR,
&tmp1, sizeof(void *),
"irq_chip retrigger", FAULT_ON_ERROR);
fprintf(fp, " retrigger: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "retrigger indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(irq_chip_set_type)) {
readmem(handler+OFFSET(irq_chip_set_type), KVADDR,
&tmp1, sizeof(void *),
"irq_chip set_type", FAULT_ON_ERROR);
fprintf(fp, " set_type: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "set_type indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
if (VALID_MEMBER(irq_chip_set_wake)) {
readmem(handler+OFFSET(irq_chip_set_wake), KVADDR,
&tmp1, sizeof(void *),
"irq_chip set wake", FAULT_ON_ERROR);
fprintf(fp, " set_wake: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>",
value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "set_wake indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
}
}
do_linked_action:
fprintf(fp, " ACTION: ");
if (value_symbol(action)) {
fprintf(fp, "%lx ", action);
pad_line(fp, VADDR_PRLEN == 8 ?
VADDR_PRLEN+2 : VADDR_PRLEN-6, ' ');
fprintf(fp, "<%s>\n", value_symbol(action));
} else if (action)
fprintf(fp, "%lx\n", action);
else
fprintf(fp, "(none)\n");
if (action) {
readmem(action+OFFSET(irqaction_handler), KVADDR,
&tmp1, sizeof(void *),
"irqaction handler", FAULT_ON_ERROR);
fprintf(fp, " handler: %lx ", tmp1);
if (is_kernel_text(tmp1))
fprintf(fp, "<%s>", value_to_symstr(tmp1, buf, 0));
else if (readmem(tmp1, KVADDR, &tmp2,
sizeof(ulong), "handler indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(tmp2))
fprintf(fp, "<%s>",
value_to_symstr(tmp2, buf, 0));
fprintf(fp, "\n");
readmem(action+OFFSET(irqaction_flags), KVADDR,
&value, sizeof(void *),
"irqaction flags", FAULT_ON_ERROR);
fprintf(fp, " flags: %lx\n", value);
if (VALID_MEMBER(irqaction_mask)) {
readmem(action+OFFSET(irqaction_mask), KVADDR,
&tmp1, sizeof(void *),
"irqaction mask", FAULT_ON_ERROR);
fprintf(fp, " mask: %lx\n", tmp1);
}
readmem(action+OFFSET(irqaction_name), KVADDR,
&tmp1, sizeof(void *),
"irqaction name", FAULT_ON_ERROR);
fprintf(fp, " name: %lx ", tmp1);
BZERO(buf, BUFSIZE);
if (read_string(tmp1, buf, BUFSIZE-1))
fprintf(fp, "\"%s\"", buf);
fprintf(fp, "\n");
readmem(action+OFFSET(irqaction_dev_id), KVADDR,
&tmp1, sizeof(void *),
"irqaction dev_id", FAULT_ON_ERROR);
fprintf(fp, " dev_id: %lx\n", tmp1);
readmem(action+OFFSET(irqaction_next), KVADDR,
&action, sizeof(void *),
"irqaction dev_id", FAULT_ON_ERROR);
fprintf(fp, " next: %lx\n", action);
}
if (action)
goto do_linked_action;
fprintf(fp, " DEPTH: %d\n\n", depth);
return;
irq_desc_format_v2:
if (!(pc->curcmd_flags & HEADER_PRINTED)) {
fprintf(fp, " IRQ %s %s NAME\n",
mkstring(buf1, VADDR_PRLEN, CENTER,
"IRQ_DESC/_DATA"),
mkstring(buf2, VADDR_PRLEN, CENTER,
"IRQACTION"));
pc->curcmd_flags |= HEADER_PRINTED;
}
if (!irq_desc_addr) {
if (pc->curcmd_flags & IRQ_IN_USE)
return;
}
fprintf(fp, "%s %s ",
mkstring(buf1, 4, CENTER|RJUST|INT_DEC, MKSTR((ulong)irq)),
irq_desc_addr ?
mkstring(buf2, MAX(VADDR_PRLEN, strlen("IRQ_DESC/_DATA")),
CENTER|LONG_HEX, MKSTR(irq_desc_addr)) :
mkstring(buf3,
MAX(VADDR_PRLEN, strlen("IRQ_DESC/_DATA")),
CENTER, "(unused)"));
do_linked_action_v2:
fprintf(fp, "%s ", action ?
mkstring(buf1, MAX(VADDR_PRLEN, strlen("IRQACTION")),
CENTER|LONG_HEX, MKSTR(action)) :
mkstring(buf2, MAX(VADDR_PRLEN, strlen("IRQACTION")),
CENTER, "(unused)"));
if (action) {
readmem(action+OFFSET(irqaction_name), KVADDR,
&tmp1, sizeof(void *),
"irqaction name", FAULT_ON_ERROR);
if (read_string(tmp1, buf, BUFSIZE-1))
fprintf(fp, "\"%s\"", buf);
readmem(action+OFFSET(irqaction_next), KVADDR,
&action, sizeof(void *),
"irqaction next", FAULT_ON_ERROR);
if (action) {
fprintf(fp, "\n%s",
space(4 + 2 + MAX(VADDR_PRLEN,
strlen("IRQ_DESC/_DATA")) + 2));
goto do_linked_action_v2;
}
}
fprintf(fp, "\n");
}
void
generic_get_irq_affinity(int irq)
{
ulong irq_desc_addr;
long len;
ulong affinity_ptr;
ulong *affinity;
ulong tmp_addr;
ulong action, name;
char buf[BUFSIZE];
char name_buf[BUFSIZE];
affinity = NULL;
irq_desc_addr = get_irq_desc_addr(irq);
if (!irq_desc_addr)
return;
readmem(irq_desc_addr + OFFSET(irq_desc_t_action), KVADDR,
&action, sizeof(long), "irq_desc action", FAULT_ON_ERROR);
if (!action)
return;
if ((len = STRUCT_SIZE("cpumask_t")) < 0)
len = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong);
affinity = (ulong *)GETBUF(len);
if (VALID_STRUCT(irq_data))
tmp_addr = irq_desc_addr + \
OFFSET(irq_data_affinity);
else
tmp_addr = irq_desc_addr + \
OFFSET(irq_desc_t_affinity);
if (symbol_exists("alloc_cpumask_var")) /* pointer member */
readmem(tmp_addr,KVADDR, &affinity_ptr, sizeof(ulong),
"irq_desc affinity", FAULT_ON_ERROR);
else /* array member */
affinity_ptr = tmp_addr;
readmem(affinity_ptr, KVADDR, affinity, len,
"irq_desc affinity", FAULT_ON_ERROR);
fprintf(fp, "%3d ", irq);
BZERO(name_buf, BUFSIZE);
while (action) {
readmem(action+OFFSET(irqaction_name), KVADDR,
&name, sizeof(void *),
"irqaction name", FAULT_ON_ERROR);
BZERO(buf, BUFSIZE);
if (read_string(name, buf, BUFSIZE-1)) {
if (strlen(name_buf) != 0)
strncat(name_buf, ",", 2);
strncat(name_buf, buf, strlen(buf));
}
readmem(action+OFFSET(irqaction_next), KVADDR,
&action, sizeof(void *),
"irqaction dev_id", FAULT_ON_ERROR);
}
fprintf(fp, "%-20s ", name_buf);
display_cpu_affinity(affinity);
fprintf(fp, "\n");
FREEBUF(affinity);
}
void
generic_show_interrupts(int irq, ulong *cpus)
{
int i;
ulong irq_desc_addr;
ulong handler, action, name;
uint kstat_irq;
uint kstat_irqs[kt->cpus];
ulong kstat_irqs_ptr;
struct syment *percpu_sp;
ulong tmp, tmp1;
char buf[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char name_buf[BUFSIZE];
handler = UNINITIALIZED;
irq_desc_addr = get_irq_desc_addr(irq);
if (!irq_desc_addr)
return;
readmem(irq_desc_addr + OFFSET(irq_desc_t_action), KVADDR,
&action, sizeof(long), "irq_desc action", FAULT_ON_ERROR);
if (!action)
return;
if (!symbol_exists("kstat_irqs_cpu")) { /* for RHEL5 or earlier */
if (!(percpu_sp = per_cpu_symbol_search("kstat")))
return;
for (i = 0; i < kt->cpus; i++) {
if (!(NUM_IN_BITMAP(cpus, i)))
continue;
tmp = percpu_sp->value + kt->__per_cpu_offset[i];
readmem(tmp + OFFSET(kernel_stat_irqs) + sizeof(uint) * irq,
KVADDR, &kstat_irq, sizeof(uint),
"kernel_stat irqs", FAULT_ON_ERROR);
kstat_irqs[i] = kstat_irq;
}
} else {
readmem(irq_desc_addr + OFFSET(irq_desc_t_kstat_irqs),
KVADDR, &kstat_irqs_ptr, sizeof(long),
"irq_desc kstat_irqs", FAULT_ON_ERROR);
if (THIS_KERNEL_VERSION > LINUX(2,6,37)) {
for (i = 0; i < kt->cpus; i++) {
if (!(NUM_IN_BITMAP(cpus, i)))
continue;
tmp = kstat_irqs_ptr + kt->__per_cpu_offset[i];
readmem(tmp, KVADDR, &kstat_irq, sizeof(uint),
"kernel_stat irqs", FAULT_ON_ERROR);
kstat_irqs[i] = kstat_irq;
}
} else
readmem(kstat_irqs_ptr, KVADDR, kstat_irqs,
sizeof(kstat_irqs), "kstat_irqs",
FAULT_ON_ERROR);
}
if (VALID_MEMBER(irq_desc_t_handler))
readmem(irq_desc_addr + OFFSET(irq_desc_t_handler),
KVADDR, &handler, sizeof(long), "irq_desc handler",
FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_desc_t_chip))
readmem(irq_desc_addr + OFFSET(irq_desc_t_chip), KVADDR,
&handler, sizeof(long), "irq_desc chip",
FAULT_ON_ERROR);
else if (VALID_MEMBER(irq_data_chip))
readmem(irq_desc_addr + OFFSET(irq_data_chip), KVADDR,
&handler, sizeof(long), "irq_data chip",
FAULT_ON_ERROR);
fprintf(fp, "%3d: ", irq);
for (i = 0; i < kt->cpus; i++) {
if (hide_offline_cpu(i))
continue;
if (NUM_IN_BITMAP(cpus, i))
fprintf(fp, "%10u ", kstat_irqs[i]);
}
if (handler != UNINITIALIZED) {
if (VALID_MEMBER(hw_interrupt_type_typename)) {
readmem(handler+OFFSET(hw_interrupt_type_typename),
KVADDR, &tmp, sizeof(void *),
"hw_interrupt_type typename", FAULT_ON_ERROR);
BZERO(buf, BUFSIZE);
if (read_string(tmp, buf, BUFSIZE-1))
fprintf(fp, "%14s", buf);
}
else if (VALID_MEMBER(irq_chip_typename)) {
readmem(handler+OFFSET(irq_chip_typename),
KVADDR, &tmp, sizeof(void *),
"hw_interrupt_type typename", FAULT_ON_ERROR);
BZERO(buf, BUFSIZE);
if (read_string(tmp, buf, BUFSIZE-1))
fprintf(fp, "%8s", buf);
BZERO(buf1, BUFSIZE);
if (VALID_MEMBER(irq_desc_t_name))
readmem(irq_desc_addr+OFFSET(irq_desc_t_name),
KVADDR, &tmp1, sizeof(void *),
"irq_desc name", FAULT_ON_ERROR);
if (read_string(tmp1, buf1, BUFSIZE-1))
fprintf(fp, "-%-8s", buf1);
}
}
BZERO(name_buf, BUFSIZE);
while (action) {
readmem(action+OFFSET(irqaction_name), KVADDR,
&name, sizeof(void *),
"irqaction name", FAULT_ON_ERROR);
BZERO(buf2, BUFSIZE);
if (read_string(name, buf2, BUFSIZE-1)) {
if (strlen(name_buf) != 0)
strncat(name_buf, ",", 2);
strncat(name_buf, buf2, strlen(buf2));
}
readmem(action+OFFSET(irqaction_next), KVADDR,
&action, sizeof(void *),
"irqaction dev_id", FAULT_ON_ERROR);
}
fprintf(fp, " %s\n", name_buf);
}
/*
* Dump the earlier 2.2 Linux version's bottom-half essentials.
*/
static void
display_bh_1(void)
{
int i;
ulong bh_mask, bh_active;
ulong bh_base[32];
char buf[BUFSIZE];
get_symbol_data("bh_mask", sizeof(ulong), &bh_mask);
get_symbol_data("bh_active", sizeof(ulong), &bh_active);
readmem(symbol_value("bh_base"), KVADDR, bh_base, sizeof(void *) * 32,
"bh_base[32]", FAULT_ON_ERROR);
fprintf(fp, "BH_MASK BH_ACTIVE\n");
fprintf(fp, "%08lx %08lx\n", bh_mask, bh_active);
fprintf(fp, "\nBH_BASE %s\n",
mkstring(buf, VADDR_PRLEN, CENTER|LJUST, "FUNCTION"));
for (i = 0; i < 32; i++) {
if (!bh_base[i])
continue;
fprintf(fp, " %2d %lx <%s>\n", i, bh_base[i],
value_to_symstr(bh_base[i], buf, 0));
}
}
/*
* Dump the 2.3-ish Linux version's bottom half essentials.
*/
static void
display_bh_2(void)
{
int i;
ulong bh_base[32];
struct softirq_state {
uint32_t active;
uint32_t mask;
} softirq_state;
struct softirq_action {
void *action;
void *data;
} softirq_vec[32];
char buf[BUFSIZE];
readmem(symbol_value("bh_base"), KVADDR, bh_base, sizeof(void *) * 32,
"bh_base[32]", FAULT_ON_ERROR);
readmem(symbol_value("softirq_vec"), KVADDR, softirq_vec,
sizeof(struct softirq_action) * 32,
"softirq_vec[32]", FAULT_ON_ERROR);
fprintf(fp, "CPU MASK ACTIVE\n");
for (i = 0; i < kt->cpus; i++) {
readmem(symbol_value("softirq_state") +
(i * SIZE(softirq_state)), KVADDR,
&softirq_state, sizeof(struct softirq_state),
"softirq_state", FAULT_ON_ERROR);
fprintf(fp, " %-2d %08x %08x\n",
i, softirq_state.mask,
softirq_state.active);
}
fprintf(fp, "\nVEC %s\n",
mkstring(buf, VADDR_PRLEN, CENTER|LJUST, "ACTION"));
for (i = 0; i < 32; i++) {
if (!softirq_vec[i].action)
continue;
fprintf(fp, " %-2d %lx <%s>\n", i,
(ulong)softirq_vec[i].action,
value_to_symstr((ulong)softirq_vec[i].action, buf, 0));
}
fprintf(fp, "\nBH_BASE %s\n",
mkstring(buf, VADDR_PRLEN, CENTER|LJUST, "FUNCTION"));
for (i = 0; i < 32; i++) {
if (!bh_base[i])
continue;
fprintf(fp, " %2d %lx <%s>\n", i, bh_base[i],
value_to_symstr(bh_base[i], buf, 0));
}
}
/*
* Dump the 2.4 Linux version's bottom half essentials.
*/
static void
display_bh_3(void)
{
int i;
ulong bh_base[32];
struct softirq_action {
void *action;
void *data;
} softirq_vec[32];
char buf[BUFSIZE];
uint active, mask;
ulong function;
readmem(symbol_value("bh_base"), KVADDR, bh_base, sizeof(void *) * 32,
"bh_base[32]", FAULT_ON_ERROR);
readmem(symbol_value("softirq_vec"), KVADDR, softirq_vec,
sizeof(struct softirq_action) * 32,
"softirq_vec[32]", FAULT_ON_ERROR);
fprintf(fp, "CPU MASK ACTIVE\n");
for (i = 0; i < kt->cpus; i++) {
readmem(symbol_value("irq_stat") +
(i * SIZE(irq_cpustat_t)) +
OFFSET(irq_cpustat_t___softirq_active), KVADDR,
&active, sizeof(uint),
"__softirq_active", FAULT_ON_ERROR);
readmem(symbol_value("irq_stat") +
(i * SIZE(irq_cpustat_t)) +
OFFSET(irq_cpustat_t___softirq_mask), KVADDR,
&mask, sizeof(uint),
"__softirq_mask", FAULT_ON_ERROR);
fprintf(fp, " %-2d %08x %08x\n", i, mask, active);
}
fprintf(fp, "\nVEC %s\n",
mkstring(buf, VADDR_PRLEN, CENTER|LJUST, "ACTION"));
for (i = 0; i < 32; i++) {
if (!softirq_vec[i].action)
continue;
fprintf(fp, " %-2d %lx ", i, (ulong)softirq_vec[i].action);
if (is_kernel_text((ulong)softirq_vec[i].action))
fprintf(fp, "<%s>",
value_to_symstr((ulong)softirq_vec[i].action,
buf, 0));
else if (readmem((ulong)softirq_vec[i].action, KVADDR,
&function, sizeof(ulong), "action indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(function))
fprintf(fp, "<%s>",
value_to_symstr(function, buf, 0));
fprintf(fp, "\n");
}
fprintf(fp, "\nBH_BASE %s\n",
mkstring(buf, VADDR_PRLEN, CENTER|LJUST, "FUNCTION"));
for (i = 0; i < 32; i++) {
if (!bh_base[i])
continue;
fprintf(fp, " %2d %lx ", i, bh_base[i]);
if (is_kernel_text(bh_base[i]))
fprintf(fp, "<%s>",
value_to_symstr(bh_base[i], buf, 0));
else if (readmem(bh_base[i], KVADDR, &function,
sizeof(ulong), "bh_base indirection",
RETURN_ON_ERROR|QUIET) && is_kernel_text(function))
fprintf(fp, "<%s>",
value_to_symstr(function, buf, 0));
fprintf(fp, "\n");
}
}
/*
* Dump the 2.6 Linux version's bottom half essentials.
*/
static void
display_bh_4(void)
{
int i, len;
char buf[BUFSIZE];
char *array;
ulong *p;
struct load_module *lm;
if (!(len = get_array_length("softirq_vec", NULL, 0)))
error(FATAL, "cannot determine softirq_vec array length\n");
fprintf(fp, "SOFTIRQ_VEC %s\n",
mkstring(buf, VADDR_PRLEN, CENTER|RJUST, "ACTION"));
array = GETBUF(SIZE(softirq_action) * (len+1));
readmem(symbol_value("softirq_vec"), KVADDR,
array, SIZE(softirq_action) * len,
"softirq_vec", FAULT_ON_ERROR);
for (i = 0, p = (ulong *)array; i < len; i++, p++) {
if (*p) {
fprintf(fp, " [%d]%s %s <%s>",
i, i < 10 ? space(4) : space(3),
mkstring(buf, VADDR_PRLEN,
LONG_HEX|CENTER|RJUST, MKSTR(*p)),
value_symbol(*p));
if (module_symbol(*p, NULL, &lm, NULL, 0))
fprintf(fp, " [%s]", lm->mod_name);
fprintf(fp, "\n");
}
if (SIZE(softirq_action) == (sizeof(void *)*2))
p++;
}
FREEBUF(array);
}
/*
* Dump the entries in the old- and new-style timer queues in
* chronological order.
*/
void
cmd_timer(void)
{
int c;
int rflag;
rflag = 0;
while ((c = getopt(argcnt, args, "r")) != EOF) {
switch(c)
{
case 'r':
rflag = 1;
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (rflag)
dump_hrtimer_data();
else
dump_timer_data();
}
static void
dump_hrtimer_data(void)
{
int i, j;
int hrtimer_max_clock_bases, max_hrtimer_bases;
struct syment * hrtimer_bases;
hrtimer_max_clock_bases = 0;
max_hrtimer_bases = 0;
/*
* deside whether hrtimer is available and
* set hrtimer_max_clock_bases or max_hrtimer_bases.
* if both are not available, hrtimer is not available.
*/
if (VALID_STRUCT(hrtimer_clock_base)) {
hrtimer_max_clock_bases = 2;
if (symbol_exists("ktime_get_boottime"))
hrtimer_max_clock_bases = 3;
} else if (VALID_STRUCT(hrtimer_base)) {
max_hrtimer_bases = 2;
} else
option_not_supported('r');
hrtimer_bases = per_cpu_symbol_search("hrtimer_bases");
for (i = 0; i < kt->cpus; i++) {
if (i)
fprintf(fp, "\n");
if (hide_offline_cpu(i)) {
fprintf(fp, "CPU: %d [OFFLINE]\n", i);
continue;
}
fprintf(fp, "CPU: %d ", i);
if (VALID_STRUCT(hrtimer_clock_base)) {
fprintf(fp, "HRTIMER_CPU_BASE: %lx\n",
(ulong)(hrtimer_bases->value +
kt->__per_cpu_offset[i]));
for (j = 0; j < hrtimer_max_clock_bases; j++) {
if (j)
fprintf(fp, "\n");
dump_hrtimer_clock_base(
(void *)(hrtimer_bases->value) +
kt->__per_cpu_offset[i], j);
}
} else {
fprintf(fp, "\n");
for (j = 0; j < max_hrtimer_bases; j++) {
if (j)
fprintf(fp, "\n");
dump_hrtimer_base(
(void *)(hrtimer_bases->value) +
kt->__per_cpu_offset[i], j);
}
}
}
}
static int expires_len = -1;
static int softexpires_len = -1;
static void
dump_hrtimer_clock_base(const void *hrtimer_bases, const int num)
{
void *base;
ulonglong current_time, now;
ulonglong offset;
ulong get_time;
char buf[BUFSIZE];
base = (void *)hrtimer_bases + OFFSET(hrtimer_cpu_base_clock_base) +
SIZE(hrtimer_clock_base) * num;
readmem((ulong)(base + OFFSET(hrtimer_clock_base_get_time)), KVADDR,
&get_time, sizeof(get_time), "hrtimer_clock_base get_time",
FAULT_ON_ERROR);
fprintf(fp, " CLOCK: %d HRTIMER_CLOCK_BASE: %lx [%s]\n", num,
(ulong)base, value_to_symstr(get_time, buf, 0));
/* get current time(uptime) */
get_uptime(NULL, &current_time);
offset = 0;
if (VALID_MEMBER(hrtimer_clock_base_offset))
offset = ktime_to_ns(base + OFFSET(hrtimer_clock_base_offset));
now = current_time * 1000000000LL / machdep->hz + offset;
dump_active_timers(base, now);
}
static void
dump_hrtimer_base(const void *hrtimer_bases, const int num)
{
void *base;
ulonglong current_time, now;
ulong get_time;
char buf[BUFSIZE];
base = (void *)hrtimer_bases + SIZE(hrtimer_base) * num;
readmem((ulong)(base + OFFSET(hrtimer_base_get_time)), KVADDR,
&get_time, sizeof(get_time), "hrtimer_base get_time",
FAULT_ON_ERROR);
fprintf(fp, " CLOCK: %d HRTIMER_BASE: %lx [%s]\n", num,
(ulong)base, value_to_symstr(get_time, buf, 0));
/* get current time(uptime) */
get_uptime(NULL, &current_time);
now = current_time * 1000000000LL / machdep->hz;
dump_active_timers(base, now);
}
static void
dump_active_timers(const void *base, ulonglong now)
{
int next, i, t;
struct rb_node *curr;
int timer_cnt;
ulong *timer_list;
void *timer;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char buf4[BUFSIZE];
next = 0;
timer_list = 0;
/* search hrtimers */
hq_open();
timer_cnt = 0;
next_one:
i = 0;
/* get the first node */
if (VALID_MEMBER(hrtimer_base_pending))
readmem((ulong)(base + OFFSET(hrtimer_base_pending) -
OFFSET(hrtimer_list) + OFFSET(hrtimer_node)),
KVADDR, &curr, sizeof(curr), "hrtimer_base pending",
FAULT_ON_ERROR);
else if (VALID_MEMBER(hrtimer_base_first))
readmem((ulong)(base + OFFSET(hrtimer_base_first)),
KVADDR, &curr, sizeof(curr), "hrtimer_base first",
FAULT_ON_ERROR);
else if (VALID_MEMBER(hrtimer_clock_base_first))
readmem((ulong)(base + OFFSET(hrtimer_clock_base_first)),
KVADDR, &curr, sizeof(curr), "hrtimer_clock_base first",
FAULT_ON_ERROR);
else
readmem((ulong)(base + OFFSET(hrtimer_clock_base_active) +
OFFSET(timerqueue_head_next)),
KVADDR, &curr, sizeof(curr), "hrtimer_clock base",
FAULT_ON_ERROR);
while (curr && i < next) {
curr = rb_next(curr);
i++;
}
if (curr) {
if (!hq_enter((ulong)curr)) {
error(INFO, "duplicate rb_node: %lx\n", curr);
return;
}
timer_cnt++;
next++;
goto next_one;
}
if (timer_cnt) {
timer_list = (ulong *)GETBUF(timer_cnt * sizeof(long));
timer_cnt = retrieve_list(timer_list, timer_cnt);
}
hq_close();
if (!timer_cnt) {
fprintf(fp, " (empty)\n");
return;
}
/* dump hrtimers */
/* print header */
expires_len = get_expires_len(timer_cnt, timer_list, 0);
if (expires_len < 7)
expires_len = 7;
softexpires_len = get_expires_len(timer_cnt, timer_list, 1);
if (softexpires_len > -1) {
if (softexpires_len < 11)
softexpires_len = 11;
fprintf(fp, " %s\n", mkstring(buf1, softexpires_len, CENTER|RJUST,
"CURRENT"));
sprintf(buf1, "%lld", now);
fprintf(fp, " %s\n", mkstring(buf1, softexpires_len,
CENTER|RJUST, NULL));
fprintf(fp, " %s %s %s %s\n",
mkstring(buf1, softexpires_len, CENTER|RJUST, "SOFTEXPIRES"),
mkstring(buf2, expires_len, CENTER|RJUST, "EXPIRES"),
mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "HRTIMER"),
mkstring(buf4, VADDR_PRLEN, CENTER|LJUST, "FUNCTION"));
} else {
fprintf(fp, " %s\n", mkstring(buf1, expires_len, CENTER|RJUST,
"CURRENT"));
sprintf(buf1, "%lld", now);
fprintf(fp, " %s\n", mkstring(buf1, expires_len, CENTER|RJUST, NULL));
fprintf(fp, " %s %s %s\n",
mkstring(buf1, expires_len, CENTER|RJUST, "EXPIRES"),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "HRTIMER"),
mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "FUNCTION"));
}
/* print timers */
for (t = 0; t < timer_cnt; t++) {
if (VALID_MEMBER(timerqueue_node_node))
timer = (void *)(timer_list[t] -
OFFSET(timerqueue_node_node) -
OFFSET(hrtimer_node));
else
timer = (void *)(timer_list[t] - OFFSET(hrtimer_node));
print_timer(timer);
}
}
static int
get_expires_len(const int timer_cnt, const ulong *timer_list, const int getsoft)
{
void *last_timer;
char buf[BUFSIZE];
ulonglong softexpires, expires;
int len;
len = -1;
if (!timer_cnt)
return len;
if (VALID_MEMBER(timerqueue_node_node))
last_timer = (void *)(timer_list[timer_cnt - 1] -
OFFSET(timerqueue_node_node) -
OFFSET(hrtimer_node));
else
last_timer = (void *)(timer_list[timer_cnt -1] -
OFFSET(hrtimer_node));
if (getsoft) {
/* soft expires exist*/
if (VALID_MEMBER(hrtimer_softexpires)) {
softexpires = ktime_to_ns(last_timer +
OFFSET(hrtimer_softexpires));
sprintf(buf, "%lld", softexpires);
len = strlen(buf);
}
} else {
if (VALID_MEMBER(hrtimer_expires))
expires = ktime_to_ns(last_timer + OFFSET(hrtimer_expires));
else
expires = ktime_to_ns(last_timer + OFFSET(hrtimer_node) +
OFFSET(timerqueue_node_expires));
sprintf(buf, "%lld", expires);
len = strlen(buf);
}
return len;
}
/*
* print hrtimer and its related information
*/
static void
print_timer(const void *timer)
{
ulonglong softexpires, expires;
ulong function;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
/* align information */
fprintf(fp, " ");
if (!accessible((ulong)timer)) {
fprintf(fp, "(destroyed timer)\n");
return;
}
if (VALID_MEMBER(hrtimer_expires))
expires = ktime_to_ns(timer + OFFSET(hrtimer_expires));
else
expires = ktime_to_ns(timer + OFFSET(hrtimer_node) +
OFFSET(timerqueue_node_expires));
if (VALID_MEMBER(hrtimer_softexpires)) {
softexpires = ktime_to_ns(timer + OFFSET(hrtimer_softexpires));
sprintf(buf1, "%lld-%lld", softexpires, expires);
}
if (VALID_MEMBER(hrtimer_softexpires)) {
softexpires = ktime_to_ns(timer + OFFSET(hrtimer_softexpires));
sprintf(buf1, "%lld", softexpires);
fprintf(fp, "%s ",
mkstring(buf2, softexpires_len, CENTER|RJUST, buf1));
}
sprintf(buf1, "%lld", expires);
fprintf(fp, "%s ", mkstring(buf2, expires_len, CENTER|RJUST, buf1));
fprintf(fp, "%lx ", (ulong)timer);
if (readmem((ulong)(timer + OFFSET(hrtimer_function)), KVADDR, &function,
sizeof(function), "hrtimer function", QUIET|RETURN_ON_ERROR)) {
fprintf(fp, "%lx ", function);
fprintf(fp ,"<%s>", value_to_symstr(function, buf3, 0));
}
fprintf(fp, "\n");
}
/*
* convert ktime to ns, only need the address of ktime
*/
static ulonglong
ktime_to_ns(const void *ktime)
{
ulonglong ns;
ns = 0;
if (!accessible((ulong)ktime))
return ns;
if (VALID_MEMBER(ktime_t_tv64)) {
readmem((ulong)ktime + OFFSET(ktime_t_tv64), KVADDR, &ns,
sizeof(ns), "ktime_t tv64", QUIET|RETURN_ON_ERROR);
} else {
uint32_t sec, nsec;
sec = 0;
nsec = 0;
readmem((ulong)ktime + OFFSET(ktime_t_sec), KVADDR, &sec,
sizeof(sec), "ktime_t sec", QUIET|RETURN_ON_ERROR);
readmem((ulong)ktime + OFFSET(ktime_t_nsec), KVADDR, &nsec,
sizeof(nsec), "ktime_t nsec", QUIET|RETURN_ON_ERROR);
ns = sec * 1000000000L + nsec;
}
return ns;
}
/*
* Display the pending timer queue entries, both the old and new-style.
*/
struct timer_data {
ulong address;
ulong expires;
ulong function;
};
struct tv_range {
ulong base;
ulong end;
};
#define TVN (6)
static void
dump_timer_data(void)
{
int i;
ulong timer_active;
struct timer_struct {
unsigned long expires;
void *fn;
} timer_table[32];
char buf[BUFSIZE];
char buf1[BUFSIZE];
struct timer_struct *tp;
ulong mask, highest, function;
ulong jiffies, timer_jiffies;
ulong *vec;
long count;
int vec_root_size, vec_size;
struct timer_data *td;
int flen, tdx, old_timers_exist;
struct tv_range tv[TVN];
if (per_cpu_symbol_search("per_cpu__tvec_bases")) {
dump_timer_data_tvec_bases_v2();
return;
} else if (symbol_exists("tvec_bases")) {
dump_timer_data_tvec_bases_v1();
return;
}
BZERO(tv, sizeof(struct tv_range) * TVN);
vec_root_size = (i = ARRAY_LENGTH(timer_vec_root_vec)) ?
i : get_array_length("timer_vec_root.vec",
NULL, SIZE(list_head));
vec_size = (i = ARRAY_LENGTH(timer_vec_vec)) ?
i : get_array_length("timer_vec.vec", NULL, SIZE(list_head));
vec = (ulong *)GETBUF(SIZE(list_head) * MAX(vec_root_size, vec_size));
if (symbol_exists("timer_active") && symbol_exists("timer_table")) {
get_symbol_data("timer_active", sizeof(ulong), &timer_active);
readmem(symbol_value("timer_table"), KVADDR, &timer_table,
sizeof(struct timer_struct) * 32, "timer_table[32]",
FAULT_ON_ERROR);
old_timers_exist = TRUE;
} else
old_timers_exist = FALSE;
/*
* Get rough count first, and then gather a bunch of timer_data
* structs to stuff in a sortable array.
*/
count = 0;
for (mask = 1, tp = timer_table+0; old_timers_exist && mask;
tp++, mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
count++;
}
init_tv_ranges(tv, vec_root_size, vec_size, 0);
count += do_timer_list(symbol_value("tv1") + OFFSET(timer_vec_root_vec),
vec_root_size, vec, NULL, NULL, tv);
count += do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec),
vec_size, vec, NULL, NULL, tv);
td = (struct timer_data *)
GETBUF((count*2) * sizeof(struct timer_data));
tdx = 0;
get_symbol_data("jiffies", sizeof(ulong), &jiffies);
get_symbol_data("timer_jiffies", sizeof(ulong), &timer_jiffies);
if (old_timers_exist)
get_symbol_data("timer_active", sizeof(ulong), &timer_active);
highest = 0;
for (i = 0, mask = 1, tp = timer_table+0; old_timers_exist && mask;
i++, tp++, mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
td[tdx].address = i;
td[tdx].expires = tp->expires;
td[tdx].function = (ulong)tp->fn;
if (td[tdx].expires > highest)
highest = td[tdx].expires;
tdx++;
}
do_timer_list(symbol_value("tv1") + OFFSET(timer_vec_root_vec),
vec_root_size, vec, (void *)td, &highest, tv);
do_timer_list(symbol_value("tv2") + OFFSET(timer_vec_vec),
vec_size, vec, (void *)td, &highest, tv);
do_timer_list(symbol_value("tv3") + OFFSET(timer_vec_vec),
vec_size, vec, (void *)td, &highest, tv);
do_timer_list(symbol_value("tv4") + OFFSET(timer_vec_vec),
vec_size, vec, (void *)td, &highest, tv);
tdx = do_timer_list(symbol_value("tv5") + OFFSET(timer_vec_vec),
vec_size, vec, (void *)td, &highest, tv);
qsort(td, tdx, sizeof(struct timer_data), compare_timer_data);
/*
* Because the jiffies values can fluctuate wildly from dump to
* dump, try to use the appropriate amount of space...
*/
sprintf(buf, "%ld", highest);
flen = MAX(strlen(buf), strlen("JIFFIES"));
fprintf(fp, "%s\n", mkstring(buf, flen, CENTER|LJUST, "JIFFIES"));
fprintf(fp, "%s\n", mkstring(buf, flen, RJUST|LONG_DEC,MKSTR(jiffies)));
fprintf(fp, "%s TIMER_LIST/TABLE FUNCTION\n",
mkstring(buf, flen, CENTER|LJUST, "EXPIRES"));
for (i = 0; i < tdx; i++) {
fprintf(fp, "%s",
mkstring(buf, flen, RJUST|LONG_DEC, MKSTR(td[i].expires)));
if (td[i].address < 32) {
sprintf(buf, "timer_table[%ld]", td[i].address);
fprintf(fp, " %s ",
mkstring(buf, 16, CENTER|LJUST, NULL));
} else {
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(td[i].address));
fprintf(fp, " %s ", mkstring(buf, 16, CENTER, buf1));
}
if (is_kernel_text(td[i].function))
fprintf(fp, "%s <%s>\n",
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(td[i].function)),
value_to_symstr(td[i].function, buf, 0));
else {
fprintf(fp, "%s ",
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(td[i].function)));
if (readmem(td[i].function, KVADDR, &function,
sizeof(ulong), "timer function",
RETURN_ON_ERROR|QUIET)) {
if (is_kernel_text(function))
fprintf(fp, "<%s>",
value_to_symstr(function, buf, 0));
}
fprintf(fp, "\n");
}
}
}
/*
* Newer per-cpu timers, using "tvec_bases".
*/
static void
dump_timer_data_tvec_bases_v1(void)
{
int i, cpu, tdx, flen;
struct timer_data *td;
int vec_root_size, vec_size;
struct tv_range tv[TVN];
ulong *vec, jiffies, highest, function;
long count;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
kt->flags |= TVEC_BASES_V1;
/*
*/
vec_root_size = (i = ARRAY_LENGTH(tvec_root_s_vec)) ?
i : get_array_length("tvec_root_s.vec", NULL, SIZE(list_head));
vec_size = (i = ARRAY_LENGTH(tvec_s_vec)) ?
i : get_array_length("tvec_s.vec", NULL, SIZE(list_head));
vec = (ulong *)GETBUF(SIZE(list_head) * MAX(vec_root_size, vec_size));
cpu = 0;
next_cpu:
count = 0;
td = (struct timer_data *)NULL;
BZERO(tv, sizeof(struct tv_range) * TVN);
init_tv_ranges(tv, vec_root_size, vec_size, cpu);
count += do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec),
vec_root_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
if (count)
td = (struct timer_data *)
GETBUF((count*2) * sizeof(struct timer_data));
tdx = 0;
highest = 0;
get_symbol_data("jiffies", sizeof(ulong), &jiffies);
do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec),
vec_root_size, vec, (void *)td, &highest, tv);
do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
qsort(td, tdx, sizeof(struct timer_data), compare_timer_data);
fprintf(fp, "TVEC_BASES[%d]: %lx\n", cpu,
symbol_value("tvec_bases") + (SIZE(tvec_t_base_s) * cpu));
sprintf(buf1, "%ld", highest);
flen = MAX(strlen(buf1), strlen("JIFFIES"));
fprintf(fp, "%s\n", mkstring(buf1,flen, CENTER|RJUST, "JIFFIES"));
fprintf(fp, "%s\n", mkstring(buf1,flen,
RJUST|LONG_DEC,MKSTR(jiffies)));
fprintf(fp, "%s %s %s\n",
mkstring(buf1, flen, CENTER|RJUST, "EXPIRES"),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "TIMER_LIST"),
mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "FUNCTION"));
for (i = 0; i < tdx; i++) {
fprintf(fp, "%s",
mkstring(buf1, flen, RJUST|LONG_DEC, MKSTR(td[i].expires)));
fprintf(fp, " %s ", mkstring(buf1,
MAX(VADDR_PRLEN, strlen("TIMER_LIST")),
RJUST|CENTER|LONG_HEX, MKSTR(td[i].address)));
if (is_kernel_text(td[i].function)) {
fprintf(fp, "%s <%s>\n",
mkstring(buf2, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(td[i].function)),
value_to_symstr(td[i].function, buf1, 0));
} else {
fprintf(fp, "%s ", mkstring(buf1, VADDR_PRLEN,
RJUST|LONG_HEX, MKSTR(td[i].function)));
if (readmem(td[i].function, KVADDR, &function,
sizeof(ulong), "timer function",
RETURN_ON_ERROR|QUIET)) {
if (is_kernel_text(function))
fprintf(fp, "<%s>",
value_to_symstr(function, buf1, 0));
}
fprintf(fp, "\n");
}
}
if (td)
FREEBUF(td);
if (++cpu < kt->cpus)
goto next_cpu;
}
/*
* 2.6 per-cpu timers, using "per_cpu__tvec_bases".
*/
static void
dump_timer_data_tvec_bases_v2(void)
{
int i, cpu, tdx, flen;
struct timer_data *td;
int vec_root_size, vec_size;
struct tv_range tv[TVN];
ulong *vec, jiffies, highest, function;
ulong tvec_bases;
long count;
struct syment *sp;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
kt->flags |= TVEC_BASES_V2;
/*
*/
vec_root_size = (i = ARRAY_LENGTH(tvec_root_s_vec)) ?
i : get_array_length("tvec_root_s.vec", NULL, SIZE(list_head));
if (!vec_root_size &&
(i = get_array_length("tvec_root.vec", NULL, SIZE(list_head))))
vec_root_size = i;
if (!vec_root_size)
error(FATAL, "cannot determine tvec_root.vec[] array size\n");
vec_size = (i = ARRAY_LENGTH(tvec_s_vec)) ?
i : get_array_length("tvec_s.vec", NULL, SIZE(list_head));
if (!vec_size &&
(i = get_array_length("tvec.vec", NULL, SIZE(list_head))))
vec_size = i;
if (!vec_size)
error(FATAL, "cannot determine tvec.vec[] array size\n");
vec = (ulong *)GETBUF(SIZE(list_head) * MAX(vec_root_size, vec_size));
cpu = 0;
next_cpu:
/*
* hide data of offline cpu and goto next cpu
*/
if (hide_offline_cpu(cpu)) {
fprintf(fp, "TVEC_BASES[%d]: [OFFLINE]\n", cpu);
if (++cpu < kt->cpus)
goto next_cpu;
}
count = 0;
td = (struct timer_data *)NULL;
BZERO(tv, sizeof(struct tv_range) * TVN);
init_tv_ranges(tv, vec_root_size, vec_size, cpu);
count += do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec),
vec_root_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
count += do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL, tv);
if (count)
td = (struct timer_data *)
GETBUF((count*2) * sizeof(struct timer_data));
tdx = 0;
highest = 0;
get_symbol_data("jiffies", sizeof(ulong), &jiffies);
do_timer_list(tv[1].base + OFFSET(tvec_root_s_vec),
vec_root_size, vec, (void *)td, &highest, tv);
do_timer_list(tv[2].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
do_timer_list(tv[3].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
do_timer_list(tv[4].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
tdx = do_timer_list(tv[5].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest, tv);
qsort(td, tdx, sizeof(struct timer_data), compare_timer_data);
sp = per_cpu_symbol_search("per_cpu__tvec_bases");
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
tvec_bases = sp->value + kt->__per_cpu_offset[cpu];
else
tvec_bases = sp->value;
if (symbol_exists("boot_tvec_bases")) {
readmem(tvec_bases, KVADDR, &tvec_bases, sizeof(void *),
"per-cpu tvec_bases", FAULT_ON_ERROR);
}
fprintf(fp, "TVEC_BASES[%d]: %lx\n", cpu, tvec_bases);
sprintf(buf1, "%ld", highest);
flen = MAX(strlen(buf1), strlen("JIFFIES"));
fprintf(fp, "%s\n", mkstring(buf1,flen, CENTER|RJUST, "JIFFIES"));
fprintf(fp, "%s\n", mkstring(buf1,flen,
RJUST|LONG_DEC,MKSTR(jiffies)));
fprintf(fp, "%s %s %s\n",
mkstring(buf1, flen, CENTER|RJUST, "EXPIRES"),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "TIMER_LIST"),
mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "FUNCTION"));
for (i = 0; i < tdx; i++) {
fprintf(fp, "%s",
mkstring(buf1, flen, RJUST|LONG_DEC, MKSTR(td[i].expires)));
fprintf(fp, " %s ", mkstring(buf1,
MAX(VADDR_PRLEN, strlen("TIMER_LIST")),
RJUST|CENTER|LONG_HEX, MKSTR(td[i].address)));
if (is_kernel_text(td[i].function)) {
fprintf(fp, "%s <%s>\n",
mkstring(buf2, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(td[i].function)),
value_to_symstr(td[i].function, buf1, 0));
} else {
fprintf(fp, "%s ", mkstring(buf1, VADDR_PRLEN,
RJUST|LONG_HEX, MKSTR(td[i].function)));
if (readmem(td[i].function, KVADDR, &function,
sizeof(ulong), "timer function",
RETURN_ON_ERROR|QUIET)) {
if (is_kernel_text(function))
fprintf(fp, "<%s>",
value_to_symstr(function, buf1, 0));
}
fprintf(fp, "\n");
}
}
if (td)
FREEBUF(td);
if (++cpu < kt->cpus)
goto next_cpu;
}
/*
* The comparison function must return an integer less than,
* equal to, or greater than zero if the first argument is
* considered to be respectively less than, equal to, or
* greater than the second. If two members compare as equal,
* their order in the sorted array is undefined.
*/
static int
compare_timer_data(const void *v1, const void *v2)
{
struct timer_data *t1, *t2;
t1 = (struct timer_data *)v1;
t2 = (struct timer_data *)v2;
return (t1->expires < t2->expires ? -1 :
t1->expires == t2->expires ? 0 : 1);
}
/*
* Create the address range for each of the timer vectors.
*/
static void
init_tv_ranges(struct tv_range *tv, int vec_root_size, int vec_size, int cpu)
{
ulong tvec_bases;
struct syment *sp;
if (kt->flags & TVEC_BASES_V1) {
tv[1].base = symbol_value("tvec_bases") +
(SIZE(tvec_t_base_s) * cpu) +
OFFSET(tvec_t_base_s_tv1);
tv[1].end = tv[1].base + SIZE(tvec_root_s);
tv[2].base = tv[1].end;
tv[2].end = tv[2].base + SIZE(tvec_s);
tv[3].base = tv[2].end;
tv[3].end = tv[3].base + SIZE(tvec_s);
tv[4].base = tv[3].end;
tv[4].end = tv[4].base + SIZE(tvec_s);
tv[5].base = tv[4].end;
tv[5].end = tv[5].base + SIZE(tvec_s);
} else if (kt->flags & TVEC_BASES_V2) {
sp = per_cpu_symbol_search("per_cpu__tvec_bases");
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
tvec_bases = sp->value + kt->__per_cpu_offset[cpu];
else
tvec_bases = sp->value;
if (symbol_exists("boot_tvec_bases")) {
readmem(tvec_bases, KVADDR, &tvec_bases, sizeof(void *),
"per-cpu tvec_bases", FAULT_ON_ERROR);
}
tv[1].base = tvec_bases +
OFFSET(tvec_t_base_s_tv1);
tv[1].end = tv[1].base + SIZE(tvec_root_s);
tv[2].base = tv[1].end;
tv[2].end = tv[2].base + SIZE(tvec_s);
tv[3].base = tv[2].end;
tv[3].end = tv[3].base + SIZE(tvec_s);
tv[4].base = tv[3].end;
tv[4].end = tv[4].base + SIZE(tvec_s);
tv[5].base = tv[4].end;
tv[5].end = tv[5].base + SIZE(tvec_s);
} else {
tv[1].base = symbol_value("tv1");
tv[1].end = tv[1].base + SIZE(timer_vec_root);
tv[2].base = symbol_value("tv2");
tv[2].end = tv[2].base + SIZE(timer_vec);
tv[3].base = symbol_value("tv3");
tv[3].end = tv[3].base + SIZE(timer_vec);
tv[4].base = symbol_value("tv4");
tv[4].end = tv[4].base + SIZE(timer_vec);
tv[5].base = symbol_value("tv5");
tv[5].end = tv[5].base + SIZE(timer_vec);
}
}
#define IN_TV_RANGE(vaddr) \
((((vaddr) >= tv[1].base) && ((vaddr) < tv[1].end)) || \
(((vaddr) >= tv[2].base) && ((vaddr) < tv[2].end)) || \
(((vaddr) >= tv[3].base) && ((vaddr) < tv[3].end)) || \
(((vaddr) >= tv[4].base) && ((vaddr) < tv[4].end)) || \
(((vaddr) >= tv[5].base) && ((vaddr) < tv[5].end)))
/*
* Count, or stash, the entries of a linked timer_list -- depending
* upon the option value.
*/
static int
do_timer_list(ulong vec_kvaddr,
int size,
ulong *vec,
void *option,
ulong *highest,
struct tv_range *tv)
{
int i, t;
int count, tdx;
ulong expires, function;
struct timer_data *td;
char *timer_list_buf;
ulong *timer_list;
int timer_cnt;
struct list_data list_data, *ld;
long sz;
ulong offset;
tdx = 0;
td = option ? (struct timer_data *)option : NULL;
if (td) {
while (td[tdx].function)
tdx++;
}
if (VALID_MEMBER(timer_list_list))
sz = SIZE(list_head) * size;
else if (VALID_MEMBER(timer_list_entry))
sz = SIZE(list_head) * size;
else
sz = sizeof(ulong) * size;
readmem(vec_kvaddr, KVADDR, vec, sz, "timer_list vec array",
FAULT_ON_ERROR);
if (VALID_MEMBER(timer_list_list)) {
offset = OFFSET(timer_list_list);
goto new_timer_list_format;
}
if (VALID_MEMBER(timer_list_entry)) {
offset = OFFSET(timer_list_entry);
goto new_timer_list_format;
}
if (VALID_MEMBER(timer_list_next) >= 0)
offset = OFFSET(timer_list_next);
else
error(FATAL, "no timer_list next, list, or entry members?\n");
ld = &list_data;
timer_list_buf = GETBUF(SIZE(timer_list));
for (i = count = 0; i < size; i++) {
if (vec[i]) {
BZERO(ld, sizeof(struct list_data));
ld->start = vec[i];
ld->member_offset = offset;
hq_open();
timer_cnt = do_list(ld);
if (!timer_cnt)
continue;
timer_list = (ulong *)GETBUF(timer_cnt * sizeof(ulong));
timer_cnt = retrieve_list(timer_list, timer_cnt);
hq_close();
for (t = 0; t < timer_cnt; t++) {
readmem(timer_list[t], KVADDR, timer_list_buf,
SIZE(timer_list), "timer_list buffer",
FAULT_ON_ERROR);
expires = ULONG(timer_list_buf +
OFFSET(timer_list_expires));
function = ULONG(timer_list_buf +
OFFSET(timer_list_function));
if (td) {
td[tdx].address = timer_list[t];
td[tdx].expires = expires;
td[tdx].function = function;
if (highest && (expires > *highest))
*highest = expires;
tdx++;
}
}
FREEBUF(timer_list);
count += timer_cnt;
}
}
FREEBUF(timer_list_buf);
return(td ? tdx : count);
new_timer_list_format:
ld = &list_data;
timer_list_buf = GETBUF(SIZE(timer_list));
for (i = count = 0; i < (size*2); i += 2,
vec_kvaddr += SIZE(list_head)) {
if (vec[i] == vec_kvaddr)
continue;
BZERO(ld, sizeof(struct list_data));
ld->start = vec[i];
ld->list_head_offset = offset;
ld->end = vec_kvaddr;
ld->flags = RETURN_ON_LIST_ERROR;
hq_open();
if ((timer_cnt = do_list(ld)) == -1) {
/* Ignore chains with errors */
error(INFO,
"ignoring faulty timer list at index %d of timer array\n",
i/2);
continue;
}
if (!timer_cnt)
continue;
timer_list = (ulong *)GETBUF(timer_cnt * sizeof(ulong));
timer_cnt = retrieve_list(timer_list, timer_cnt);
hq_close();
for (t = 0; t < timer_cnt; t++) {
if (IN_TV_RANGE(timer_list[t]))
break;
count++;
readmem(timer_list[t], KVADDR, timer_list_buf,
SIZE(timer_list), "timer_list buffer",
FAULT_ON_ERROR);
expires = ULONG(timer_list_buf +
OFFSET(timer_list_expires));
function = ULONG(timer_list_buf +
OFFSET(timer_list_function));
if (td) {
td[tdx].address = timer_list[t];
td[tdx].expires = expires;
td[tdx].function = function;
if (highest && (expires > *highest))
*highest = expires;
tdx++;
}
}
}
FREEBUF(timer_list_buf);
return(td ? tdx : count);
}
/*
* Panic a live system by exploiting this code in do_exit():
*
* if (!tsk->pid)
* panic("Attempted to kill the idle task!");
*
* by writing a zero to this task's pid number. If the write
* succeeds, the subsequent exit() call will invoke the panic.
*/
static void
panic_this_kernel(void)
{
pid_t zero_pid = 0;
if (DUMPFILE())
error(FATAL, "cannot panic a dumpfile!\n");
if (!(pc->flags & MFD_RDWR) || (pc->flags & MEMMOD))
error(FATAL, "cannot write to %s\n", pc->live_memsrc);
writemem(pid_to_task(pc->program_pid) + OFFSET(task_struct_pid), KVADDR,
&zero_pid, sizeof(pid_t), "zero pid", FAULT_ON_ERROR);
clean_exit(0);
}
/*
* Dump the list of entries on a wait queue, taking into account the two
* different definitions: wait_queue vs. __wait_queue (wait_queue_t).
*/
void
cmd_waitq(void)
{
ulong q = 0;
char *wq_name = NULL; /* name of symbol which is a waitq */
char *wq_struct = NULL; /* struct containing the waitq */
char *wq_member = NULL; /* member of struct which is a waitq */
int recd_address = 0;
if (argcnt < 2 || argcnt > 3) {
cmd_usage(pc->curcmd, SYNOPSIS);
}
if (IS_A_NUMBER(args[1])) {
q = htol(args[1], FAULT_ON_ERROR, NULL);
recd_address = 1;
} else {
/*
* We weren't given a number... see if it is the name of
* a symbol or and struct.member format.
*/
char *dot;
dot = strstr(args[1], ".");
if (dot == NULL) {
wq_name = args[1];
q = symbol_value(wq_name);
} else {
wq_struct = args[1];
wq_member = dot+1;
*dot = '\0';
if (argcnt != 3) {
fprintf(fp, "must supply an address for %s\n",
wq_struct);
return;
}
q = htol(args[2], FAULT_ON_ERROR, NULL);
if (MEMBER_OFFSET(wq_struct, wq_member) == -1) {
fprintf(fp, "%s is not a member of %s\n",
wq_member, wq_struct);
return;
}
q += MEMBER_OFFSET(wq_struct, wq_member);
}
}
if (q != 0 && IS_KVADDR(q)) {
/*
* If we weren't passed in an address and we're dealing
* with old style wait_queue, we must dereference the pointer
* and pass in the addr of the first elem on the queue.
* If we were supplied an address, assume the user knows
* what should be provided.
*/
if (!recd_address && VALID_STRUCT(wait_queue)) {
ulong first_elem;
readmem(q, KVADDR, &first_elem, sizeof(q),
"wait queue pointer", FAULT_ON_ERROR);
if (first_elem == 0) {
fprintf(fp, "wait queue %lx is empty\n", q);
return;
} else {
q = first_elem;
}
}
dump_waitq(q, wq_name);
}
}
static void
dump_waitq(ulong wq, char *wq_name)
{
struct list_data list_data, *ld;
ulong *wq_list; /* addr of wait queue element */
ulong next_offset; /* next pointer of wq element */
ulong task_offset; /* offset of task in wq element */
int cnt; /* # elems on Queue */
int start_index; /* where to start in wq array */
int i;
ld = &list_data;
BZERO(ld, sizeof(*ld));
/*
* setup list depending on how the wait queues are organized.
*/
if (VALID_STRUCT(wait_queue)) {
task_offset = OFFSET(wait_queue_task);
next_offset = OFFSET(wait_queue_next);
ld->end = wq;
ld->start = wq;
ld->member_offset = next_offset;
ld->list_head_offset = task_offset;
start_index = 0;
} else if (VALID_STRUCT(__wait_queue)) {
ulong task_list_offset;
next_offset = OFFSET(list_head_next);
task_offset = OFFSET(__wait_queue_task);
task_list_offset = OFFSET(__wait_queue_head_task_list);
ld->end = ld->start = wq + task_list_offset + next_offset;
ld->list_head_offset = OFFSET(__wait_queue_task_list);
ld->member_offset = next_offset;
start_index = 1;
} else {
return;
}
hq_open();
cnt = do_list(ld);
if (cnt <= 1) {
/*
* Due to the queueing of wait queues, list count returns
* an extra number of list entries:
* - in the case of a wait_queue_head_t, there is the
* the list_entry in that structure;
* - in the case of a simple wait_queue, we have the
* pointer back to the wait_queue head (see the
* WAIT_QUEUE_HEAD macro in 2.2 systems).
*/
if (wq_name)
fprintf(fp, "wait queue \"%s\" (%lx) is empty\n",
wq_name, wq);
else
fprintf(fp, "wait queue %lx is empty\n", wq);
hq_close();
return;
}
wq_list = (ulong *) GETBUF(cnt * sizeof(ulong));
cnt = retrieve_list(wq_list, cnt);
for (i = start_index; i < cnt; i++) {
struct task_context *tc;
ulong task;
readmem(wq_list[i] + task_offset, KVADDR, &task,
sizeof(void *), "wait_queue_t.task", FAULT_ON_ERROR);
if ((tc = task_to_context(task)) ||
(tc = task_to_context(stkptr_to_task(task)))) {
print_task_header(fp, tc, 0);
} else {
break;
}
}
hq_close();
}
/*
* If active, clear the references to the last page tables read.
*/
void
clear_machdep_cache(void)
{
if (ACTIVE()) {
machdep->last_pgd_read = 0;
machdep->last_pmd_read = 0;
machdep->last_ptbl_read = 0;
if (machdep->clear_machdep_cache)
machdep->clear_machdep_cache();
}
}
/*
* If it exists, return the number of cpus in the cpu_online_map.
*/
int
get_cpus_online()
{
int i, len, online;
char *buf;
ulong *maskptr, addr;
if (!(addr = cpu_map_addr("online")))
return 0;
len = cpu_map_size("online");
buf = GETBUF(len);
online = 0;
if (readmem(addr, KVADDR, buf, len,
"cpu_online_map", RETURN_ON_ERROR)) {
maskptr = (ulong *)buf;
for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++)
online += count_bits_long(*maskptr);
if (CRASHDEBUG(1))
error(INFO, "get_cpus_online: online: %d\n", online);
}
FREEBUF(buf);
return online;
}
/*
* Check whether a cpu is offline
*/
int
check_offline_cpu(int cpu)
{
if (!cpu_map_addr("online"))
return FALSE;
if (in_cpu_map(ONLINE_MAP, cpu))
return FALSE;
return TRUE;
}
/*
* Check whether the data related to the specified cpu should be hidden.
*/
int
hide_offline_cpu(int cpu)
{
if (!(pc->flags2 & OFFLINE_HIDE))
return FALSE;
return check_offline_cpu(cpu);
}
/*
* If it exists, return the highest cpu number in the cpu_online_map.
*/
int
get_highest_cpu_online()
{
int i, len;
char *buf;
ulong *maskptr, addr;
int high, highest;
if (!(addr = cpu_map_addr("online")))
return -1;
len = cpu_map_size("online");
buf = GETBUF(len);
highest = -1;
if (readmem(addr, KVADDR, buf, len,
"cpu_online_map", RETURN_ON_ERROR)) {
maskptr = (ulong *)buf;
for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++) {
if ((high = highest_bit_long(*maskptr)) < 0)
continue;
highest = high + (i * (sizeof(ulong)*8));
}
if (CRASHDEBUG(1))
error(INFO, "get_highest_cpu_online: %d\n", highest);
}
FREEBUF(buf);
return highest;
}
/*
* If it exists, return the number of cpus in the cpu_active_map.
*/
int
get_cpus_active()
{
int i, len, active;
char *buf;
ulong *maskptr, addr;
if (!(addr = cpu_map_addr("active")))
return 0;
len = cpu_map_size("active");
buf = GETBUF(len);
active = 0;
if (readmem(addr, KVADDR, buf, len,
"cpu_active_map", RETURN_ON_ERROR)) {
maskptr = (ulong *)buf;
for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++)
active += count_bits_long(*maskptr);
if (CRASHDEBUG(1))
error(INFO, "get_cpus_active: active: %d\n", active);
}
FREEBUF(buf);
return active;
}
/*
* If it exists, return the number of cpus in the cpu_present_map.
*/
int
get_cpus_present()
{
int i, len, present;
char *buf;
ulong *maskptr, addr;
if (!(addr = cpu_map_addr("present")))
return 0;
len = cpu_map_size("present");
buf = GETBUF(len);
present = 0;
if (readmem(addr, KVADDR, buf, len,
"cpu_present_map", RETURN_ON_ERROR)) {
maskptr = (ulong *)buf;
for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++)
present += count_bits_long(*maskptr);
if (CRASHDEBUG(1))
error(INFO, "get_cpus_present: present: %d\n", present);
}
FREEBUF(buf);
return present;
}
/*
* If it exists, return the highest cpu number in the cpu_present_map.
*/
int
get_highest_cpu_present()
{
int i, len;
char *buf;
ulong *maskptr, addr;
int high, highest;
if (!(addr = cpu_map_addr("present")))
return -1;
len = cpu_map_size("present");
buf = GETBUF(len);
highest = -1;
if (readmem(addr, KVADDR, buf, len,
"cpu_present_map", RETURN_ON_ERROR)) {
maskptr = (ulong *)buf;
for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++) {
if ((high = highest_bit_long(*maskptr)) < 0)
continue;
highest = high + (i * (sizeof(ulong)*8));
}
if (CRASHDEBUG(1))
error(INFO, "get_highest_cpu_present: %d\n", highest);
}
FREEBUF(buf);
return highest;
}
/*
* If it exists, return the number of cpus in the cpu_possible_map.
*/
int
get_cpus_possible()
{
int i, len, possible;
char *buf;
ulong *maskptr, addr;
if (!(addr = cpu_map_addr("possible")))
return 0;
len = cpu_map_size("possible");
buf = GETBUF(len);
possible = 0;
if (readmem(addr, KVADDR, buf, len,
"cpu_possible_map", RETURN_ON_ERROR)) {
maskptr = (ulong *)buf;
for (i = 0; i < (len/sizeof(ulong)); i++, maskptr++)
possible += count_bits_long(*maskptr);
if (CRASHDEBUG(1))
error(INFO, "get_cpus_possible: possible: %d\n",
possible);
}
FREEBUF(buf);
return possible;
}
/*
* When displaying cpus, return the number of cpus online if possible,
* otherwise kt->cpus.
*/
int
get_cpus_to_display(void)
{
int online = get_cpus_online();
return (online ? online : kt->cpus);
}
/*
* Xen machine-address to pseudo-physical-page translator.
*/
ulonglong
xen_m2p(ulonglong machine)
{
ulong mfn, pfn;
mfn = XEN_MACHINE_TO_MFN(machine);
pfn = __xen_m2p(machine, mfn);
if (pfn == XEN_MFN_NOT_FOUND) {
if (CRASHDEBUG(1) && !STREQ(pc->curcmd, "search"))
error(INFO,
"xen_m2p: machine address %lx not found\n",
machine);
return XEN_MACHADDR_NOT_FOUND;
}
return XEN_PFN_TO_PSEUDO(pfn);
}
static ulong
__xen_m2p(ulonglong machine, ulong mfn)
{
ulong c, i, kmfn, mapping, p, pfn;
ulong start, end;
ulong *mp = (ulong *)kt->m2p_page;
/*
* Check the FIFO cache first.
*/
for (c = 0; c < P2M_MAPPING_CACHE; c++) {
if (kt->p2m_mapping_cache[c].mapping &&
((mfn >= kt->p2m_mapping_cache[c].start) &&
(mfn <= kt->p2m_mapping_cache[c].end))) {
if (kt->p2m_mapping_cache[c].mapping != kt->last_mapping_read) {
if (!readmem(kt->p2m_mapping_cache[c].mapping, KVADDR,
mp, PAGESIZE(), "phys_to_machine_mapping page (cached)",
RETURN_ON_ERROR))
error(FATAL, "cannot access "
"phys_to_machine_mapping page\n");
else
kt->last_mapping_read = kt->p2m_mapping_cache[c].mapping;
} else
kt->p2m_page_cache_hits++;
for (i = 0; i < XEN_PFNS_PER_PAGE; i++) {
kmfn = (*(mp+i)) & ~XEN_FOREIGN_FRAME;
if (kmfn == mfn) {
p = P2M_MAPPING_PAGE_PFN(c);
pfn = p + i;
if (CRASHDEBUG(1))
console("(cached) mfn: %lx (%llx) p: %ld"
" i: %ld pfn: %lx (%llx)\n",
mfn, machine, p,
i, pfn, XEN_PFN_TO_PSEUDO(pfn));
kt->p2m_mfn_cache_hits++;
return pfn;
}
}
/*
* Stale entry -- clear it out.
*/
kt->p2m_mapping_cache[c].mapping = 0;
}
}
if (PVOPS_XEN()) {
/*
* The machine address was not cached, so search from the
* beginning of the p2m_top array, caching the contiguous
* range containing the found machine address.
*/
if (symbol_exists("p2m_mid_missing"))
pfn = __xen_pvops_m2p_l3(machine, mfn);
else
pfn = __xen_pvops_m2p_l2(machine, mfn);
if (pfn != XEN_MFN_NOT_FOUND)
return pfn;
} else {
/*
* The machine address was not cached, so search from the
* beginning of the phys_to_machine_mapping array, caching
* the contiguous range containing the found machine address.
*/
mapping = kt->phys_to_machine_mapping;
for (p = 0; p < kt->p2m_table_size; p += XEN_PFNS_PER_PAGE)
{
if (mapping != kt->last_mapping_read) {
if (!readmem(mapping, KVADDR, mp, PAGESIZE(),
"phys_to_machine_mapping page",
RETURN_ON_ERROR))
error(FATAL,
"cannot access"
" phys_to_machine_mapping page\n");
else
kt->last_mapping_read = mapping;
}
kt->p2m_pages_searched++;
if (search_mapping_page(mfn, &i, &start, &end)) {
pfn = p + i;
if (CRASHDEBUG(1))
console("pages: %d mfn: %lx (%llx) p: %ld"
" i: %ld pfn: %lx (%llx)\n",
(p/XEN_PFNS_PER_PAGE)+1, mfn, machine,
p, i, pfn, XEN_PFN_TO_PSEUDO(pfn));
c = kt->p2m_cache_index;
kt->p2m_mapping_cache[c].start = start;
kt->p2m_mapping_cache[c].end = end;
kt->p2m_mapping_cache[c].mapping = mapping;
kt->p2m_cache_index = (c+1) % P2M_MAPPING_CACHE;
return pfn;
}
mapping += PAGESIZE();
}
}
if (CRASHDEBUG(1))
console("machine address %llx not found\n", machine);
return (XEN_MFN_NOT_FOUND);
}
static ulong
__xen_pvops_m2p_l2(ulonglong machine, ulong mfn)
{
ulong c, e, end, i, mapping, p, p2m, pfn, start;
for (e = p = 0, p2m = kt->pvops_xen.p2m_top;
e < kt->pvops_xen.p2m_top_entries;
e++, p += XEN_PFNS_PER_PAGE, p2m += sizeof(void *)) {
if (!readmem(p2m, KVADDR, &mapping, sizeof(void *),
"p2m_top", RETURN_ON_ERROR))
error(FATAL, "cannot access p2m_top[] entry\n");
if (mapping == kt->pvops_xen.p2m_missing)
continue;
if (mapping != kt->last_mapping_read) {
if (!readmem(mapping, KVADDR, (void *)kt->m2p_page,
PAGESIZE(), "p2m_top page", RETURN_ON_ERROR))
error(FATAL, "cannot access p2m_top[] page\n");
kt->last_mapping_read = mapping;
}
kt->p2m_pages_searched++;
if (search_mapping_page(mfn, &i, &start, &end)) {
pfn = p + i;
if (CRASHDEBUG(1))
console("pages: %d mfn: %lx (%llx) p: %ld"
" i: %ld pfn: %lx (%llx)\n",
(p/XEN_PFNS_PER_PAGE)+1, mfn, machine,
p, i, pfn, XEN_PFN_TO_PSEUDO(pfn));
c = kt->p2m_cache_index;
kt->p2m_mapping_cache[c].start = start;
kt->p2m_mapping_cache[c].end = end;
kt->p2m_mapping_cache[c].mapping = mapping;
kt->p2m_mapping_cache[c].pfn = p;
kt->p2m_cache_index = (c+1) % P2M_MAPPING_CACHE;
return pfn;
}
}
return XEN_MFN_NOT_FOUND;
}
static ulong
__xen_pvops_m2p_l3(ulonglong machine, ulong mfn)
{
ulong c, end, i, j, k, mapping, p;
ulong p2m_mid, p2m_top, pfn, start;
p2m_top = kt->pvops_xen.p2m_top;
for (i = 0; i < XEN_P2M_TOP_PER_PAGE; ++i, p2m_top += sizeof(void *)) {
if (!readmem(p2m_top, KVADDR, &mapping,
sizeof(void *), "p2m_top", RETURN_ON_ERROR))
error(FATAL, "cannot access p2m_top[] entry\n");
if (mapping == kt->pvops_xen.p2m_mid_missing)
continue;
p2m_mid = mapping;
for (j = 0; j < XEN_P2M_MID_PER_PAGE; ++j, p2m_mid += sizeof(void *)) {
if (!readmem(p2m_mid, KVADDR, &mapping,
sizeof(void *), "p2m_mid", RETURN_ON_ERROR))
error(FATAL, "cannot access p2m_mid[] entry\n");
if (mapping == kt->pvops_xen.p2m_missing)
continue;
if (mapping != kt->last_mapping_read) {
if (!readmem(mapping, KVADDR, (void *)kt->m2p_page,
PAGESIZE(), "p2m_mid page", RETURN_ON_ERROR))
error(FATAL, "cannot access p2m_mid[] page\n");
kt->last_mapping_read = mapping;
}
if (!search_mapping_page(mfn, &k, &start, &end))
continue;
p = i * XEN_P2M_MID_PER_PAGE * XEN_P2M_PER_PAGE;
p += j * XEN_P2M_PER_PAGE;
pfn = p + k;
if (CRASHDEBUG(1))
console("pages: %d mfn: %lx (%llx) p: %ld"
" i: %ld j: %ld k: %ld pfn: %lx (%llx)\n",
(p / XEN_P2M_PER_PAGE) + 1, mfn, machine,
p, i, j, k, pfn, XEN_PFN_TO_PSEUDO(pfn));
c = kt->p2m_cache_index;
kt->p2m_mapping_cache[c].start = start;
kt->p2m_mapping_cache[c].end = end;
kt->p2m_mapping_cache[c].mapping = mapping;
kt->p2m_mapping_cache[c].pfn = p;
kt->p2m_cache_index = (c + 1) % P2M_MAPPING_CACHE;
return pfn;
}
}
return XEN_MFN_NOT_FOUND;
}
/*
* Search for an mfn in the current mapping page, and if found,
* determine the range of contiguous mfns that it's contained
* within (if any).
*/
#define PREV_UP 0x1
#define NEXT_UP 0x2
#define PREV_DOWN 0x4
#define NEXT_DOWN 0x8
static int
search_mapping_page(ulong mfn, ulong *index, ulong *startptr, ulong *endptr)
{
int n, found;
ulong i, kmfn;
ulong flags, start, end, next, prev, curr;
ulong *mp;
mp = (ulong *)kt->m2p_page;
for (i = 0, found = FALSE; i < XEN_PFNS_PER_PAGE; i++) {
kmfn = (*(mp+i)) & ~XEN_FOREIGN_FRAME;
if (kmfn == mfn) {
found = TRUE;
*index = i;
break;
}
}
if (found) {
flags = 0;
next = prev = XEN_MFN_NOT_FOUND;
start = end = kmfn;
if (i)
prev = (*(mp+(i-1))) & ~XEN_FOREIGN_FRAME;
if ((i+1) != XEN_PFNS_PER_PAGE)
next = (*(mp+(i+1))) & ~XEN_FOREIGN_FRAME;
if (prev == (kmfn-1))
flags |= PREV_UP;
else if (prev == (kmfn+1))
flags |= PREV_DOWN;
if (next == (kmfn+1))
flags |= NEXT_UP;
else if (next == (kmfn-1))
flags |= NEXT_DOWN;
/* Should be impossible, but just in case... */
if ((flags & PREV_UP) && (flags & NEXT_DOWN))
flags &= ~NEXT_DOWN;
else if ((flags & PREV_DOWN) && (flags & NEXT_UP))
flags &= ~NEXT_UP;
if (flags & (PREV_UP|PREV_DOWN)) {
start = prev;
for (n = (i-2); n >= 0; n--) {
curr = (*(mp+n)) & ~XEN_FOREIGN_FRAME;
if (flags & PREV_UP) {
if (curr == (start-1))
start = curr;
} else {
if (curr == (start+1))
start = curr;
}
}
}
if (flags & (NEXT_UP|NEXT_DOWN)) {
end = next;
for (n = (i+2); n < XEN_PFNS_PER_PAGE; n++) {
curr = (*(mp+n)) & ~XEN_FOREIGN_FRAME;
if (flags & NEXT_UP) {
if (curr == (end+1))
end = curr;
} else {
if (curr == (end-1))
end = curr;
}
}
}
if (start > end) {
curr = start;
start = end;
end = curr;
}
*startptr = start;
*endptr = end;
if (CRASHDEBUG(2))
fprintf(fp, "mfn: %lx -> start: %lx end: %lx (%ld mfns)\n",
mfn, start, end, end - start);
}
return found;
}
/*
* IKCONFIG management.
*/
#define IKCONFIG_MAX 5000
static struct ikconfig_list {
char *name;
char *val;
} *ikconfig_all;
static void add_ikconfig_entry(char *line, struct ikconfig_list *ent)
{
char *tokptr, *name, *val;
name = strtok_r(line, "=", &tokptr);
sscanf(name, "CONFIG_%s", name);
val = strtok_r(NULL, "", &tokptr);
ent->name = strdup(name);
ent->val = strdup(val);
}
static int setup_ikconfig(char *config)
{
char *ent, *tokptr;
struct ikconfig_list *new;
ikconfig_all = calloc(1, sizeof(struct ikconfig_list) * IKCONFIG_MAX);
if (!ikconfig_all) {
error(WARNING, "cannot calloc for ikconfig entries.\n");
return 0;
}
ent = strtok_r(config, "\n", &tokptr);
while (ent) {
while (whitespace(*ent))
ent++;
if (ent[0] != '#') {
add_ikconfig_entry(ent,
&ikconfig_all[kt->ikconfig_ents++]);
if (kt->ikconfig_ents == IKCONFIG_MAX) {
error(WARNING, "ikconfig overflow.\n");
return 1;
}
}
ent = strtok_r(NULL, "\n", &tokptr);
}
if (kt->ikconfig_ents == 0) {
free(ikconfig_all);
return 0;
}
if ((new = realloc(ikconfig_all,
sizeof(struct ikconfig_list) * kt->ikconfig_ents)))
ikconfig_all = new;
return 1;
}
static void free_ikconfig(void)
{
int i;
for (i = 0; i < kt->ikconfig_ents; i++) {
free(ikconfig_all[i].name);
free(ikconfig_all[i].val);
}
free(ikconfig_all);
}
int get_kernel_config(char *conf_name, char **str)
{
int i;
int ret = IKCONFIG_N;
char *name;
if (!(kt->ikconfig_flags & IKCONFIG_AVAIL)) {
error(WARNING, "CONFIG_IKCONFIG is not set\n");
return ret;
} else if (!(kt->ikconfig_flags & IKCONFIG_LOADED)) {
read_in_kernel_config(IKCFG_SETUP);
if (!(kt->ikconfig_flags & IKCONFIG_LOADED)) {
error(WARNING, "IKCFG_SETUP failed\n");
return ret;
}
}
name = strdup(conf_name);
if (!strncmp(name, "CONFIG_", strlen("CONFIG_")))
sscanf(name, "CONFIG_%s", name);
for (i = 0; i < kt->ikconfig_ents; i++) {
if (STREQ(name, ikconfig_all[i].name)) {
if (str)
*str = ikconfig_all[i].val;
if (STREQ(ikconfig_all[i].val, "y"))
ret = IKCONFIG_Y;
else if (STREQ(ikconfig_all[i].val, "m"))
ret = IKCONFIG_M;
else
ret = IKCONFIG_STR;
break;
}
}
free(name);
return ret;
}
/*
* Read the relevant IKCONFIG (In Kernel Config) data if available.
*/
static char *ikconfig[] = {
"CONFIG_NR_CPUS",
"CONFIG_PGTABLE_4",
"CONFIG_HZ",
"CONFIG_DEBUG_BUGVERBOSE",
"CONFIG_DEBUG_INFO_REDUCED",
NULL,
};
void
read_in_kernel_config(int command)
{
struct syment *sp;
int ii, jj, ret, end, found=0;
unsigned long size, bufsz;
char *pos, *ln, *buf, *head, *tail, *val, *uncomp;
char line[512];
z_stream stream;
if ((kt->flags & NO_IKCONFIG) && !(pc->flags & RUNTIME))
return;
if ((sp = symbol_search("kernel_config_data")) == NULL) {
if (command == IKCFG_READ)
error(FATAL,
"kernel_config_data does not exist in this kernel\n");
else if (command == IKCFG_SETUP || command == IKCFG_FREE)
error(WARNING,
"kernel_config_data does not exist in this kernel\n");
return;
}
/* We don't know how large IKCONFIG is, so we start with
* 32k, if we can't find MAGIC_END assume we didn't read
* enough, double it and try again.
*/
ii = 32;
again:
size = ii * 1024;
if ((buf = (char *)malloc(size)) == NULL) {
error(WARNING, "cannot malloc IKCONFIG input buffer\n");
return;
}
if (!readmem(sp->value, KVADDR, buf, size,
"kernel_config_data", RETURN_ON_ERROR)) {
error(WARNING, "cannot read kernel_config_data\n");
goto out2;
}
/* Find the start */
if (strstr(buf, MAGIC_START))
head = buf + MAGIC_SIZE + 10; /* skip past MAGIC_START and gzip header */
else {
error(WARNING, "could not find MAGIC_START!\n");
goto out2;
}
tail = head;
end = strlen(MAGIC_END);
/* Find the end*/
while (tail < (buf + (size - 1))) {
if (strncmp(tail, MAGIC_END, end)==0) {
found = 1;
break;
}
tail++;
}
if (found) {
bufsz = tail - head;
size = 10 * bufsz;
if ((uncomp = (char *)malloc(size)) == NULL) {
error(WARNING, "cannot malloc IKCONFIG output buffer\n");
goto out2;
}
} else {
if (ii > 512) {
error(WARNING, "could not find MAGIC_END!\n");
goto out2;
} else {
free(buf);
ii *= 2;
goto again;
}
}
/* initialize zlib */
stream.next_in = (Bytef *)head;
stream.avail_in = (uInt)bufsz;
stream.next_out = (Bytef *)uncomp;
stream.avail_out = (uInt)size;
stream.zalloc = NULL;
stream.zfree = NULL;
stream.opaque = NULL;
ret = inflateInit2(&stream, -MAX_WBITS);
if (ret != Z_OK) {
read_in_kernel_config_err(ret, "initialize");
goto out1;
}
ret = inflate(&stream, Z_FINISH);
if (ret != Z_STREAM_END) {
inflateEnd(&stream);
if (ret == Z_NEED_DICT ||
(ret == Z_BUF_ERROR && stream.avail_in == 0)) {
read_in_kernel_config_err(Z_DATA_ERROR, "uncompress");
goto out1;
}
read_in_kernel_config_err(ret, "uncompress");
goto out1;
}
size = stream.total_out;
ret = inflateEnd(&stream);
pos = uncomp;
if (command == IKCFG_INIT)
kt->ikconfig_flags |= IKCONFIG_AVAIL;
else if (command == IKCFG_SETUP) {
if (!(kt->ikconfig_flags & IKCONFIG_LOADED)) {
if (setup_ikconfig(pos)) {
kt->ikconfig_flags |= IKCONFIG_LOADED;
if (CRASHDEBUG(1))
fprintf(fp,
"ikconfig: %d valid configs.\n",
kt->ikconfig_ents);
} else
error(WARNING, "IKCFG_SETUP failed\n\n");
} else
error(WARNING,
"IKCFG_SETUP: ikconfig data already loaded\n");
goto out1;
} else if (command == IKCFG_FREE) {
if (kt->ikconfig_flags & IKCONFIG_LOADED) {
free_ikconfig();
kt->ikconfig_ents = 0;
kt->ikconfig_flags &= ~IKCONFIG_LOADED;
} else
error(WARNING, "IKCFG_FREE: ikconfig data not loaded\n");
goto out1;
}
do {
ret = sscanf(pos, "%511[^\n]\n%n", line, &ii);
if (ret > 0) {
if ((command == IKCFG_READ) || CRASHDEBUG(8))
fprintf(fp, "%s\n", line);
pos += ii;
ln = line;
/* skip leading whitespace */
while (whitespace(*ln))
ln++;
/* skip comments -- except when looking for "not set" */
if (*ln == '#') {
if (strstr(ln, "CONFIG_DEBUG_BUGVERBOSE") &&
strstr(ln, "not set"))
kt->flags |= BUGVERBOSE_OFF;
if (strstr(ln, "CONFIG_DEBUG_INFO_REDUCED"))
if (CRASHDEBUG(1))
error(INFO, "%s\n", ln);
continue;
}
/* Find '=' */
if ((head = strchr(ln, '=')) != NULL) {
*head = '\0';
val = head + 1;
head--;
/* skip trailing whitespace */
while (whitespace(*head)) {
*head = '\0';
head--;
}
/* skip whitespace */
while (whitespace(*val))
val++;
} else /* Bad line, skip it */
continue;
if (command != IKCFG_INIT)
continue;
for (jj = 0; ikconfig[jj]; jj++) {
if (STREQ(ln, ikconfig[jj])) {
if (STREQ(ln, "CONFIG_NR_CPUS")) {
kt->kernel_NR_CPUS = atoi(val);
if (CRASHDEBUG(1))
error(INFO,
"CONFIG_NR_CPUS: %d\n",
kt->kernel_NR_CPUS);
} else if (STREQ(ln, "CONFIG_PGTABLE_4")) {
machdep->flags |= VM_4_LEVEL;
if (CRASHDEBUG(1))
error(INFO, "CONFIG_PGTABLE_4\n");
} else if (STREQ(ln, "CONFIG_HZ")) {
machdep->hz = atoi(val);
if (CRASHDEBUG(1))
error(INFO,
"CONFIG_HZ: %d\n",
machdep->hz);
} else if (STREQ(ln, "CONFIG_DEBUG_INFO_REDUCED")) {
if (STREQ(val, "y")) {
error(WARNING,
"CONFIG_DEBUG_INFO_REDUCED=y\n");
no_debugging_data(INFO);
}
}
}
}
}
} while (ret > 0);
out1:
free(uncomp);
out2:
free(buf);
return;
}
static void
read_in_kernel_config_err(int e, char *msg)
{
error(WARNING, "zlib could not %s\n", msg);
switch (e) {
case Z_OK:
fprintf(fp, "Z_OK\n");
break;
case Z_STREAM_END:
fprintf(fp, "Z_STREAM_END\n");
break;
case Z_NEED_DICT:
fprintf(fp, "Z_NEED_DICT\n");
break;
case Z_ERRNO:
fprintf(fp, "Z_ERNO\n");
break;
case Z_STREAM_ERROR:
fprintf(fp, "Z_STREAM\n");
break;
case Z_DATA_ERROR:
fprintf(fp, "Z_DATA_ERROR\n");
break;
case Z_MEM_ERROR: /* out of memory */
fprintf(fp, "Z_MEM_ERROR\n");
break;
case Z_BUF_ERROR: /* not enough room in output buf */
fprintf(fp, "Z_BUF_ERROR\n");
break;
case Z_VERSION_ERROR:
fprintf(fp, "Z_VERSION_ERROR\n");
break;
default:
fprintf(fp, "UNKNOWN ERROR: %d\n", e);
break;
}
}
/*
* With the evidence available, attempt to pre-determine whether
* this is a paravirt-capable kernel running as bare-metal, xen,
* kvm, etc.
*
* NOTE: Only bare-metal pv_ops kernels are supported so far.
*/
void
paravirt_init(void)
{
/*
* pv_init_ops appears to be (as of 2.6.27) an arch-common
* symbol. This may have to change.
*/
if (kernel_symbol_exists("pv_init_ops")) {
if (CRASHDEBUG(1))
error(INFO, "pv_init_ops exists: ARCH_PVOPS\n");
kt->flags |= ARCH_PVOPS;
}
}
/*
* Get the kernel's xtime timespec from its relevant location.
*/
static void
get_xtime(struct timespec *date)
{
struct syment *sp;
uint64_t xtime_sec;
if (VALID_MEMBER(timekeeper_xtime) &&
(sp = kernel_symbol_search("timekeeper"))) {
readmem(sp->value + OFFSET(timekeeper_xtime), KVADDR,
date, sizeof(struct timespec),
"timekeeper xtime", RETURN_ON_ERROR);
} else if (VALID_MEMBER(timekeeper_xtime_sec) &&
(sp = kernel_symbol_search("timekeeper"))) {
readmem(sp->value + OFFSET(timekeeper_xtime_sec), KVADDR,
&xtime_sec, sizeof(uint64_t),
"timekeeper xtime_sec", RETURN_ON_ERROR);
date->tv_sec = (__time_t)xtime_sec;
} else if (VALID_MEMBER(timekeeper_xtime_sec) &&
(sp = kernel_symbol_search("shadow_timekeeper"))) {
readmem(sp->value + OFFSET(timekeeper_xtime_sec), KVADDR,
&xtime_sec, sizeof(uint64_t),
"shadow_timekeeper xtime_sec", RETURN_ON_ERROR);
date->tv_sec = (__time_t)xtime_sec;
} else if (kernel_symbol_exists("xtime"))
get_symbol_data("xtime", sizeof(struct timespec), date);
}
static void
hypervisor_init(void)
{
ulong x86_hyper, name, pv_init_ops;
char buf[BUFSIZE], *p1;
kt->hypervisor = "(undetermined)";
BZERO(buf, BUFSIZE);
if (kernel_symbol_exists("pv_info") &&
MEMBER_EXISTS("pv_info", "name") &&
readmem(symbol_value("pv_info") + MEMBER_OFFSET("pv_info", "name"),
KVADDR, &name, sizeof(char *), "pv_info.name",
QUIET|RETURN_ON_ERROR) && read_string(name, buf, BUFSIZE-1))
kt->hypervisor = strdup(buf);
else if (try_get_symbol_data("x86_hyper", sizeof(void *), &x86_hyper)) {
if (!x86_hyper)
kt->hypervisor = "bare hardware";
else if (MEMBER_EXISTS("hypervisor_x86", "name") &&
readmem(x86_hyper + MEMBER_OFFSET("hypervisor_x86", "name"),
KVADDR, &name, sizeof(char *), "x86_hyper->name",
QUIET|RETURN_ON_ERROR) && read_string(name, buf, BUFSIZE-1))
kt->hypervisor = strdup(buf);
} else if (XENDUMP_DUMPFILE() || XEN())
kt->hypervisor = "Xen";
else if (KVMDUMP_DUMPFILE())
kt->hypervisor = "KVM";
else if (PVOPS() && readmem(symbol_value("pv_init_ops"), KVADDR,
&pv_init_ops, sizeof(void *), "pv_init_ops", RETURN_ON_ERROR) &&
(p1 = value_symbol(pv_init_ops)) &&
STREQ(p1, "native_patch"))
kt->hypervisor = "bare hardware";
if (CRASHDEBUG(1))
fprintf(fp, "hypervisor: %s\n", kt->hypervisor);
}
/*
* Get and display the kernel log buffer using the vmcoreinfo
* data alone without the vmlinux file.
*/
void
get_log_from_vmcoreinfo(char *file)
{
char *string;
char buf[BUFSIZE];
char *p1, *p2;
struct vmcoreinfo_data *vmc = &kt->vmcoreinfo;
if (!(pc->flags2 & VMCOREINFO))
error(FATAL, "%s: no VMCOREINFO section\n", file);
vmc->log_SIZE = vmc->log_ts_nsec_OFFSET = vmc->log_len_OFFSET =
vmc->log_text_len_OFFSET = vmc->log_dict_len_OFFSET = -1;
if ((string = pc->read_vmcoreinfo("OSRELEASE"))) {
if (CRASHDEBUG(1))
fprintf(fp, "OSRELEASE: %s\n", string);
strcpy(buf, string);
p1 = p2 = buf;
while (*p2 != '.')
p2++;
*p2 = NULLCHAR;
kt->kernel_version[0] = atoi(p1);
p1 = ++p2;
while (*p2 != '.')
p2++;
*p2 = NULLCHAR;
kt->kernel_version[1] = atoi(p1);
p1 = ++p2;
while ((*p2 >= '0') && (*p2 <= '9'))
p2++;
*p2 = NULLCHAR;
kt->kernel_version[2] = atoi(p1);
if (CRASHDEBUG(1))
fprintf(fp, "base kernel version: %d.%d.%d\n",
kt->kernel_version[0],
kt->kernel_version[1],
kt->kernel_version[2]);
free(string);
} else
error(FATAL, "VMCOREINFO: cannot determine kernel version\n");
if ((string = pc->read_vmcoreinfo("PAGESIZE"))) {
machdep->pagesize = atoi(string);
machdep->pageoffset = machdep->pagesize - 1;
if (CRASHDEBUG(1))
fprintf(fp, "PAGESIZE: %d\n", machdep->pagesize);
free(string);
} else
error(FATAL, "VMCOREINFO: cannot determine page size\n");
if ((string = pc->read_vmcoreinfo("SYMBOL(log_buf)"))) {
vmc->log_buf_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(log_buf): %lx\n",
vmc->log_buf_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("SYMBOL(log_end)"))) {
vmc->log_end_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(log_end): %lx\n",
vmc->log_end_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("SYMBOL(log_buf_len)"))) {
vmc->log_buf_len_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(log_buf_len): %lx\n",
vmc->log_buf_len_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("SYMBOL(logged_chars)"))) {
vmc->logged_chars_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(logged_chars): %lx\n",
vmc->logged_chars_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("SYMBOL(log_first_idx)"))) {
vmc->log_first_idx_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(log_first_idx): %lx\n",
vmc->log_first_idx_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("SYMBOL(log_next_idx)"))) {
vmc->log_next_idx_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(log_next_idx): %lx\n",
vmc->log_next_idx_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("SYMBOL(phys_base)"))) {
vmc->phys_base_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(phys_base): %lx\n",
vmc->phys_base_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("SYMBOL(_stext)"))) {
vmc->_stext_SYMBOL = htol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SYMBOL(_stext): %lx\n",
vmc->_stext_SYMBOL);
free(string);
}
if ((string = pc->read_vmcoreinfo("OFFSET(log.ts_nsec)"))) {
vmc->log_ts_nsec_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(log.ts_nsec): %ld\n",
vmc->log_ts_nsec_OFFSET);
free(string);
} else if ((string = pc->read_vmcoreinfo("OFFSET(printk_log.ts_nsec)"))) {
vmc->log_ts_nsec_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(printk_log.ts_nsec): %ld\n",
vmc->log_ts_nsec_OFFSET);
free(string);
}
if ((string = pc->read_vmcoreinfo("OFFSET(log.len)"))) {
vmc->log_len_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(log.len): %ld\n",
vmc->log_len_OFFSET);
free(string);
} else if ((string = pc->read_vmcoreinfo("OFFSET(printk_log.len)"))) {
vmc->log_len_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(printk_log.len): %ld\n",
vmc->log_len_OFFSET);
free(string);
}
if ((string = pc->read_vmcoreinfo("OFFSET(log.text_len)"))) {
vmc->log_text_len_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(log.text_len): %ld\n",
vmc->log_text_len_OFFSET);
free(string);
} else if ((string = pc->read_vmcoreinfo("OFFSET(printk_log.text_len)"))) {
vmc->log_text_len_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(printk_log.text_len): %ld\n",
vmc->log_text_len_OFFSET);
free(string);
}
if ((string = pc->read_vmcoreinfo("OFFSET(log.dict_len)"))) {
vmc->log_dict_len_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(log.dict_len): %ld\n",
vmc->log_dict_len_OFFSET);
free(string);
} else if ((string = pc->read_vmcoreinfo("OFFSET(printk_log.dict_len)"))) {
vmc->log_dict_len_OFFSET = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "OFFSET(printk_log.dict_len): %ld\n",
vmc->log_dict_len_OFFSET);
free(string);
}
if ((string = pc->read_vmcoreinfo("SIZE(log)"))) {
vmc->log_SIZE = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SIZE(log): %ld\n", vmc->log_SIZE);
free(string);
} else if ((string = pc->read_vmcoreinfo("SIZE(printk_log)"))) {
vmc->log_SIZE = dtol(string, RETURN_ON_ERROR, NULL);
if (CRASHDEBUG(1))
fprintf(fp, "SIZE(printk_log): %ld\n", vmc->log_SIZE);
free(string);
}
/*
* The per-arch VTOP() macro must be functional.
*/
machdep_init(LOG_ONLY);
if (vmc->log_buf_SYMBOL && vmc->log_buf_len_SYMBOL &&
vmc->log_first_idx_SYMBOL && vmc->log_next_idx_SYMBOL &&
(vmc->log_SIZE > 0) &&
(vmc->log_ts_nsec_OFFSET >= 0) &&
(vmc->log_len_OFFSET >= 0) &&
(vmc->log_text_len_OFFSET >= 0) &&
(vmc->log_dict_len_OFFSET >= 0))
dump_variable_length_record();
else if (vmc->log_buf_SYMBOL && vmc->log_end_SYMBOL &&
vmc->log_buf_len_SYMBOL && vmc->logged_chars_SYMBOL)
dump_log_legacy();
else
error(FATAL, "VMCOREINFO: no log buffer data\n");
}
static void
dump_log_legacy(void)
{
int i;
physaddr_t paddr;
ulong long_value;
uint int_value;
ulong log_buf;
uint log_end, log_buf_len, logged_chars, total;
char *buf, *p;
ulong index, bytes;
struct vmcoreinfo_data *vmc;
vmc = &kt->vmcoreinfo;
log_buf = log_end = log_buf_len = logged_chars = 0;
paddr = VTOP(vmc->log_buf_SYMBOL);
if (readmem(paddr, PHYSADDR, &long_value, sizeof(ulong),
"log_buf pointer", RETURN_ON_ERROR))
log_buf = long_value;
else
error(FATAL, "cannot read log_buf value\n");
if (CRASHDEBUG(1))
fprintf(fp, "log_buf vaddr: %lx paddr: %llx => %lx\n",
vmc->log_buf_SYMBOL, (ulonglong)paddr, log_buf);
paddr = VTOP(vmc->log_end_SYMBOL);
if (THIS_KERNEL_VERSION < LINUX(2,6,25)) {
if (readmem(paddr, PHYSADDR, &long_value, sizeof(ulong),
"log_end (long)", RETURN_ON_ERROR))
log_end = (uint)long_value;
else
error(FATAL, "cannot read log_end value\n");
} else {
if (readmem(paddr, PHYSADDR, &int_value, sizeof(uint),
"log_end (int)", RETURN_ON_ERROR))
log_end = int_value;
else
error(FATAL, "cannot read log_end value\n");
}
if (CRASHDEBUG(1))
fprintf(fp, "log_end vaddr: %lx paddr: %llx => %d\n",
vmc->log_end_SYMBOL, (ulonglong)paddr, log_end);
paddr = VTOP(vmc->log_buf_len_SYMBOL);
if (readmem(paddr, PHYSADDR, &int_value, sizeof(uint),
"log_buf_len", RETURN_ON_ERROR))
log_buf_len = int_value;
else
error(FATAL, "cannot read log_buf_len value\n");
if (CRASHDEBUG(1))
fprintf(fp, "log_buf_len vaddr: %lx paddr: %llx => %d\n",
vmc->log_buf_len_SYMBOL, (ulonglong)paddr, log_buf_len);
paddr = VTOP(vmc->logged_chars_SYMBOL);
if (readmem(paddr, PHYSADDR, &int_value, sizeof(uint),
"logged_chars", RETURN_ON_ERROR))
logged_chars = int_value;
else
error(FATAL, "cannot read logged_chars value\n");
if (CRASHDEBUG(1))
fprintf(fp, "logged_chars vaddr: %lx paddr: %llx => %d\n",
vmc->logged_chars_SYMBOL, (ulonglong)paddr, logged_chars);
if ((buf = calloc(sizeof(char), log_buf_len)) == NULL)
error(FATAL, "cannot calloc log_buf_len (%d) bytes\n",
log_buf_len);
paddr = VTOP(log_buf);
if (log_end < log_buf_len) {
bytes = log_end;
if (!readmem(paddr, PHYSADDR, buf, bytes,
"log_buf", RETURN_ON_ERROR))
error(FATAL, "cannot read log_buf\n");
total = bytes;
} else {
index = log_end & (log_buf_len - 1);
bytes = log_buf_len - index;
if (!readmem(paddr + index, PHYSADDR, buf, bytes,
"log_buf + index", RETURN_ON_ERROR))
error(FATAL, "cannot read log_buf\n");
if (!readmem(paddr, PHYSADDR, buf + bytes, index,
"log_buf", RETURN_ON_ERROR))
error(FATAL, "cannot read log_buf\n");
total = log_buf_len;
}
for (i = 0, p = buf; i < total; i++, p++) {
if (*p == NULLCHAR)
fputc('\n', fp);
else if (ascii(*p))
fputc(*p, fp);
else
fputc('.', fp);
}
}
static void
dump_variable_length_record(void)
{
physaddr_t paddr;
ulong long_value;
uint32_t int_value;
struct vmcoreinfo_data *vmc;
ulong log_buf;
uint32_t idx, log_buf_len, log_first_idx, log_next_idx;
char *buf, *logptr;
vmc = &kt->vmcoreinfo;
log_buf = log_buf_len = log_first_idx = log_next_idx = 0;
paddr = VTOP(vmc->log_buf_SYMBOL);
if (readmem(paddr, PHYSADDR, &long_value, sizeof(ulong),
"log_buf pointer", RETURN_ON_ERROR))
log_buf = long_value;
else
error(FATAL, "cannot read log_buf value\n");
if (CRASHDEBUG(1))
fprintf(fp, "log_buf vaddr: %lx paddr: %llx => %lx\n",
vmc->log_buf_SYMBOL, (ulonglong)paddr, log_buf);
paddr = VTOP(vmc->log_buf_len_SYMBOL);
if (readmem(paddr, PHYSADDR, &int_value, sizeof(uint),
"log_buf_len", RETURN_ON_ERROR))
log_buf_len = int_value;
else
error(FATAL, "cannot read log_buf_len value\n");
if (CRASHDEBUG(1))
fprintf(fp, "log_buf_len vaddr: %lx paddr: %llx => %d\n",
vmc->log_buf_len_SYMBOL, (ulonglong)paddr, log_buf_len);
paddr = VTOP(vmc->log_first_idx_SYMBOL);
if (readmem(paddr, PHYSADDR, &int_value, sizeof(uint),
"log_first_idx", RETURN_ON_ERROR))
log_first_idx = int_value;
else
error(FATAL, "cannot read log_first_idx value\n");
if (CRASHDEBUG(1))
fprintf(fp, "log_first_idx vaddr: %lx paddr: %llx => %d\n",
vmc->log_first_idx_SYMBOL, (ulonglong)paddr, log_first_idx);
paddr = VTOP(vmc->log_next_idx_SYMBOL);
if (readmem(paddr, PHYSADDR, &int_value, sizeof(uint),
"log_next_idx", RETURN_ON_ERROR))
log_next_idx = int_value;
else
error(FATAL, "cannot read log_next_idx value\n");
if (CRASHDEBUG(1))
fprintf(fp, "log_next_idx vaddr: %lx paddr: %llx => %d\n",
vmc->log_next_idx_SYMBOL, (ulonglong)paddr, log_next_idx);
ASSIGN_SIZE(log)= vmc->log_SIZE;
ASSIGN_OFFSET(log_ts_nsec) = vmc->log_ts_nsec_OFFSET;
ASSIGN_OFFSET(log_len) = vmc->log_len_OFFSET;
ASSIGN_OFFSET(log_text_len) = vmc->log_text_len_OFFSET;
ASSIGN_OFFSET(log_dict_len) = vmc->log_dict_len_OFFSET;
if ((buf = calloc(sizeof(char), log_buf_len)) == NULL)
error(FATAL, "cannot calloc log_buf_len (%d) bytes\n",
log_buf_len);
paddr = VTOP(log_buf);
if (!readmem(paddr, PHYSADDR, buf, log_buf_len,
"log_buf", RETURN_ON_ERROR))
error(FATAL, "cannot read log_buf\n");
hq_init();
hq_open();
idx = log_first_idx;
while (idx != log_next_idx) {
logptr = log_from_idx(idx, buf);
dump_log_entry(logptr, 0);
if (!hq_enter((ulong)logptr)) {
error(INFO, "\nduplicate log_buf message pointer\n");
break;
}
idx = log_next(idx, buf);
if (idx >= log_buf_len) {
error(INFO, "\ninvalid log_buf entry encountered\n");
break;
}
if (CRASHDEBUG(1) && (idx == log_next_idx))
fprintf(fp, "\nfound log_next_idx OK\n");
}
hq_close();
}
static void
show_kernel_taints(char *buf, int verbose)
{
int i, bx;
uint8_t tnt_bit;
char tnt_true, tnt_false;
int tnts_len;
ulong tnts_addr;
ulong tainted_mask, *tainted_mask_ptr;
int tainted;
struct syment *sp;
if (!VALID_STRUCT(tnt)) {
STRUCT_SIZE_INIT(tnt, "tnt");
MEMBER_OFFSET_INIT(tnt_bit, "tnt", "bit");
MEMBER_OFFSET_INIT(tnt_true, "tnt", "true");
MEMBER_OFFSET_INIT(tnt_false, "tnt", "false");
}
if (VALID_STRUCT(tnt) && (sp = symbol_search("tnts"))) {
tnts_len = get_array_length("tnts", NULL, 0);
tnts_addr = sp->value;
} else
tnts_addr = tnts_len = 0;
bx = 0;
buf[0] = '\0';
tainted_mask = tainted = 0;
if (kernel_symbol_exists("tainted_mask")) {
get_symbol_data("tainted_mask", sizeof(ulong), &tainted_mask);
tainted_mask_ptr = &tainted_mask;
} else if (kernel_symbol_exists("tainted")) {
get_symbol_data("tainted", sizeof(int), &tainted);
if (verbose)
fprintf(fp, "TAINTED: %x\n", tainted);
return;
} else if (verbose)
option_not_supported('t');
for (i = 0; i < (tnts_len * SIZE(tnt)); i += SIZE(tnt)) {
readmem((tnts_addr + i) + OFFSET(tnt_bit),
KVADDR, &tnt_bit, sizeof(uint8_t),
"tnt bit", FAULT_ON_ERROR);
if (NUM_IN_BITMAP(tainted_mask_ptr, tnt_bit)) {
readmem((tnts_addr + i) + OFFSET(tnt_true),
KVADDR, &tnt_true, sizeof(char),
"tnt true", FAULT_ON_ERROR);
buf[bx++] = tnt_true;
} else {
readmem((tnts_addr + i) + OFFSET(tnt_false),
KVADDR, &tnt_false, sizeof(char),
"tnt false", FAULT_ON_ERROR);
if (tnt_false != ' ' && tnt_false != '-' &&
tnt_false != 'G')
buf[bx++] = tnt_false;
}
}
buf[bx++] = '\0';
if (verbose)
fprintf(fp, "TAINTED_MASK: %lx %s\n", tainted_mask, buf);
}