crash/kernel.c
Dave Anderson 46d2121960 Fix for the "timer -r" command on Linux 4.10 and later kernels that
contain commit 2456e855354415bfaeb7badaa14e11b3e02c8466, titled
"ktime: Get rid of the union".  Without the patch, the command fails
with the error message "timer: invalid structure member offset:
ktime_t_sec".
(k-hagio@ab.jp.nec.com)
2018-05-29 14:04:03 -04:00

11119 lines
304 KiB
C

/* kernel.c - core analysis suite
*
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
* Copyright (C) 2002-2018 David Anderson
* Copyright (C) 2002-2018 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "defs.h"
#include "xen_hyper_defs.h"
#include "xen_dom0.h"
#include <elf.h>
#include <libgen.h>
#include <ctype.h>
#include <stdbool.h>
#include "xendump.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(const ulong *cpus);
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(const ulong *cpus);
static void dump_timer_data_tvec_bases_v1(const ulong *cpus);
static void dump_timer_data_tvec_bases_v2(const ulong *cpus);
static void dump_timer_data_tvec_bases_v3(const ulong *cpus);
static void dump_timer_data_timer_bases(const ulong *cpus);
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 do_timer_list_v3(ulong, int, ulong *, void *,ulong *);
struct timer_bases_data;
static int do_timer_list_v4(struct timer_bases_data *);
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 ulong __xen_pvops_m2p_hyper(ulonglong, ulong);
static ulong __xen_pvops_m2p_domU(ulonglong, ulong);
static int read_xc_p2m(ulonglong, void *, long);
static void read_p2m(ulong, int, void *);
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);
static void dump_dmi_info(void);
static void list_source_code(struct gnu_request *, int);
static void source_tree_init(void);
static ulong dump_audit_skb_queue(ulong);
static ulong __dump_audit(char *);
static void dump_audit(void);
/*
* 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");
}
kt->end = highest_bss_symbol();
if ((sp1 = kernel_symbol_search("_end")) && (sp1->value > kt->end))
kt->end = sp1->value;
/*
* 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 if (!symbol_exists("xen_p2m_addr")) {
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 (CRASHDEBUG(1))
fprintf(fp, "xtime timespec.tv_sec: %lx: %s\n",
kt->date.tv_sec, strip_linefeeds(ctime(&kt->date.tv_sec)));
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");
if (CRASHDEBUG(1)) {
fprintf(fp, "utsname:\n");
fprintf(fp, " sysname: %s\n", printable_string(kt->utsname.sysname) ?
kt->utsname.sysname : "(not printable)");
fprintf(fp, " nodename: %s\n", printable_string(kt->utsname.nodename) ?
kt->utsname.nodename : "(not printable)");
fprintf(fp, " release: %s\n", printable_string(kt->utsname.release) ?
kt->utsname.release : "(not printable)");
fprintf(fp, " version: %s\n", printable_string(kt->utsname.version) ?
kt->utsname.version : "(not printable)");
fprintf(fp, " machine: %s\n", printable_string(kt->utsname.machine) ?
kt->utsname.machine : "(not printable)");
fprintf(fp, " domainname: %s\n", printable_string(kt->utsname.domainname) ?
kt->utsname.domainname : "(not printable)");
}
strncpy(buf, kt->utsname.release, 65);
if (buf[64])
buf[64] = NULLCHAR;
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");
MEMBER_OFFSET_INIT(irq_desc_irq_data, "irq_desc", "irq_data");
}
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");
}
}
if (per_cpu_symbol_search("timer_bases")) {
kt->flags2 |= TIMER_BASES;
MEMBER_OFFSET_INIT(timer_base_vectors, "timer_base", "vectors");
STRUCT_SIZE_INIT(timer_base, "timer_base");
} else if (per_cpu_symbol_search("per_cpu__tvec_bases")) {
if (MEMBER_EXISTS("tvec_base", "migration_enabled"))
kt->flags2 |= TVEC_BASES_V3;
else
kt->flags |= TVEC_BASES_V2;
} else if (symbol_exists("tvec_bases"))
kt->flags |= TVEC_BASES_V1;
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 (INVALID_MEMBER(module_num_symtab) &&
MEMBER_EXISTS("module", "core_kallsyms")) {
ASSIGN_OFFSET(module_num_symtab) =
MEMBER_OFFSET("module", "core_kallsyms") +
MEMBER_OFFSET("mod_kallsyms", "num_symtab");
ASSIGN_OFFSET(module_symtab) =
MEMBER_OFFSET("module", "core_kallsyms") +
MEMBER_OFFSET("mod_kallsyms", "symtab");
ASSIGN_OFFSET(module_strtab) =
MEMBER_OFFSET("module", "core_kallsyms") +
MEMBER_OFFSET("mod_kallsyms", "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");
if (kt->source_tree)
source_tree_init();
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;
}
sprintf(map_symbol, "__cpu_%s_mask", type);
if (kernel_symbol_exists(map_symbol))
return symbol_value(map_symbol);
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";
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, "%scpu_%s_%s: cpus: ",
space(strlen("possible")-strlen(mapinfo[m].name)),
mapinfo[m].name, cpu_map_type(mapinfo[m].name));
for (i = c = 0; i < NR_CPUS; i++) {
if (kt->cpu_flags[i] & mapinfo[m].cpu_flag) {
fprintf(fp, "%d ", i);
c++;
}
}
fprintf(fp, "%s\n", c ? "" : "(none)");
}
}
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/2];
char buffer2[BUFSIZE/2];
char buffer3[BUFSIZE/2];
char buffer4[BUFSIZE/2];
char buffer5[BUFSIZE*2];
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/2)-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(buffer5, " %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(buffer5) > 48 ? "\n " : "", buffer5);
program_usage(SHORT_FORM);
}
/*
* Set up the gdb source code path.
*/
static void
source_tree_init(void)
{
FILE *pipe;
char command[BUFSIZE*2];
char buf[BUFSIZE];
if (!is_directory(kt->source_tree)) {
error(INFO, "invalid --src argument: %s\n\n",
kt->source_tree);
kt->source_tree = NULL;
return;
}
sprintf(command, "/usr/bin/ls -d %s/arch/*/include/asm 2>/dev/null",
kt->source_tree);
if ((pipe = popen(command, "r"))) {
if (fgets(buf, BUFSIZE-1, pipe)) {
sprintf(command, "directory %s", buf);
gdb_pass_through(command, NULL, GNU_RETURN_ON_ERROR);
}
pclose(pipe);
} else
error(INFO, "%s: %s\n", command, strerror(errno));
sprintf(command, "directory %s", kt->source_tree);
gdb_pass_through(command, NULL, GNU_RETURN_ON_ERROR);
}
static void
list_source_code(struct gnu_request *req, int count_entered)
{
int argc, line, last, done, assembly;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE*2];
char file[BUFSIZE];
char *argv[MAXARGS];
struct syment *sp;
ulong remaining, offset;
char *p1;
sp = value_search(req->addr, &offset);
if (!sp || !is_symbol_text(sp))
error(FATAL, "%lx: not a kernel text address\n", req->addr);
sprintf(buf1, "list *0x%lx", req->addr);
open_tmpfile();
if (!gdb_pass_through(buf1, pc->tmpfile, GNU_RETURN_ON_ERROR)) {
close_tmpfile();
error(FATAL, "gdb command failed: %s\n", buf1);
}
done = FALSE;
last = line = assembly = file[0] = 0;
remaining = count_entered ? req->count : 0;
rewind(pc->tmpfile);
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
strcpy(buf2, buf1);
argc = parse_line(buf2, argv);
if (!line && hexadecimal(argv[0], 0) &&
STREQ(argv[1], "is") &&
(STREQ(argv[2], "in") || STREQ(argv[2], "at"))) {
/*
* Don't bother continuing beyond the initial
* list command if it's assembly language.
*/
if (STREQ(argv[2], "at"))
assembly = TRUE;
strip_beginning_char(argv[argc-1], '(');
strip_ending_char(argv[argc-1], '.');
strip_ending_char(argv[argc-1], ')');
p1 = strstr_rightmost(argv[argc-1], ":");
*p1 = NULLCHAR;
strcpy(file, argv[argc-1]);
line = atoi(p1+1);
fprintf(pc->saved_fp, "FILE: %s\nLINE: %d\n\n", file, line);
continue;
}
/*
* Check for 2 possible results of unavailable source.
*/
if ((argc == 3) &&
decimal(argv[0], 0) &&
STREQ(argv[1], "in") &&
STREQ(argv[2], file))
error(FATAL,
"%s: source code is not available\n\n", req->buf);
sprintf(buf3, "%s: No such file or directory.", file);
if (decimal(argv[0], 0) && strstr(buf1, buf3))
error(FATAL,
"%s: source code is not available\n\n", req->buf);
if (decimal(argv[0], 0)) {
if (count_entered && (last >= line)) {
if (!remaining--) {
done = TRUE;
break;
}
}
last = atoi(argv[0]);
fprintf(pc->saved_fp, "%s%s",
last == line ? "* " : " ", buf1);
} else
continue;
if (!count_entered && (last > line) &&
STREQ(first_space(buf1), "\t}\n")) {
done = TRUE;
break;
}
}
close_tmpfile();
if (!line) {
fprintf(fp, "FILE: (unknown)\nLINE: (unknown)\n\n");
error(FATAL, "%s: source code is not available\n\n", req->buf);
}
if ((count_entered && !remaining) || (!count_entered && assembly)) {
fprintf(fp, "\n");
return;
}
/*
* If the end of the containing function or a specified count
* has not been reached, continue the listing until it has.
*/
while (!done) {
open_tmpfile();
if (!gdb_pass_through("list", fp, GNU_RETURN_ON_ERROR)) {
close_tmpfile();
return;
}
rewind(pc->tmpfile);
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
strcpy(buf2, buf1);
argc = parse_line(buf2, argv);
if (decimal(argv[0], 0))
line = atoi(argv[0]);
else
continue;
if (count_entered) {
if (!remaining--) {
done = TRUE;
break;
}
}
if (line == last) {
done = TRUE;
break;
}
last = line;
fprintf(pc->saved_fp, " %s", buf1);
if (!count_entered &&
STREQ(first_space(buf1), "\t}\n")) {
done = TRUE;
break;
}
}
close_tmpfile();
}
fprintf(fp, "\n");
}
/*
* From either a syment pointer, or a virtual address evaluated
* from a symbol name plus an offset value, determine whether
* there are multiple symbols with the same name, or if it is
* determined to be an invalid expression of a text address.
*
* If there are multiple text symbols with the same name, then
* display a "duplicate text symbols found" message followed by
* a list of each symbol's information, and return FALSE.
*
* If a symbol name plus and offset value evaluates to an address
* that goes beyond the end of the text function, print an "invalid
* expression" message, and return FALSE;
*
* If there is one text symbol and one or more data symbols with
* the same name, reset the incoming address based upon the
* single text symbol, and return TRUE.
*
* All of the remaining possibilities return TRUE without changing
* the incoming address:
*
* (1) if an evaluated address cannot be resolved to any symbol.
* (2) if an evaluated address argument did not contain a symbol name.
* (3) if there is only one possible symbol resolution.
* (4) if there are multiple data symbols.
*/
static int
resolve_text_symbol(char *arg, struct syment *sp_in, struct gnu_request *req, int radix)
{
int text_symbols;
struct syment *sp, *sp_orig, *first_text_sp, *sp_arg, *sp_addr;
ulong offset, radix_flag;
char buf[BUFSIZE];
char *op;
sp_arg = NULL;
if (!sp_in && !IS_A_NUMBER(arg)) {
strcpy(buf, arg);
strip_beginning_char(buf, '(');
strip_ending_char(buf, ')');
clean_line(buf);
if ((op = strpbrk(buf, "><+-&|*/%^"))) {
*op = NULLCHAR;
clean_line(buf);
if ((sp = symbol_search(buf)) && is_symbol_text(sp)) {
sp_arg = sp;
text_symbols = 1;
while ((sp = symbol_search_next(sp->name, sp))) {
if (is_symbol_text(sp))
text_symbols++;
}
if (text_symbols > 1) {
sp_orig = sp_arg;
goto duplicates;
}
}
}
}
if (sp_in) {
sp_orig = sp_in;
offset = 0;
} else if ((sp_orig = value_search(req->addr, &offset))) {
if (!strstr(arg, sp_orig->name)) {
if (sp_arg && (sp_orig != sp_arg)) {
error(INFO, "invalid expression: %s evaluates to: %s+%lx\n",
arg, sp_orig->name, offset);
return FALSE;
}
return TRUE;
}
} else {
if (CRASHDEBUG(1))
error(INFO, "%s: no text symbol found\n", arg);
return TRUE;
}
if (symbol_name_count(sp_orig->name) <= 1)
return TRUE;
if (sp_arg) {
sp_addr = value_search(req->addr, &offset);
if (sp_arg != sp_addr) {
if (STREQ(sp_arg->name, sp_addr->name)) {
sp_orig = sp_arg;
goto duplicates;
}
error(INFO, "invalid expression: %s evaluates to %s: %s+%lx\n",
arg, sp_addr->name, offset);
return FALSE;
}
}
text_symbols = 0;
first_text_sp = NULL;
sp = sp_orig;
do {
if (is_symbol_text(sp)) {
if (!first_text_sp)
first_text_sp = sp;
text_symbols++;
}
} while ((sp = symbol_search_next(sp->name, sp)));
/*
* If no text symbols for a symbol name exist, let it be...
*/
if (!text_symbols) {
if (CRASHDEBUG(1))
error(INFO, "%s: no text symbol found\n", arg);
return TRUE;
}
/*
* If only one symbol with the specified name is text,
* reset the req->addr as appropriate in case a
* lower-value data symbol was originally selected.
*/
if (text_symbols == 1) {
if (sp_in)
req->addr = first_text_sp->value;
else
req->addr = first_text_sp->value + offset;
return TRUE;
}
duplicates:
/*
* Multiple text symbols with the same name exist.
* Display them all and return FALSE.
*/
error(INFO, "%s: duplicate text symbols found:\n", arg);
radix_flag = radix == 10 ? SHOW_DEC_OFFS : SHOW_HEX_OFFS;
sp = sp_orig;
do {
if (is_symbol_text(sp)) {
if (module_symbol(sp->value, NULL, NULL, NULL, 0))
show_symbol(sp, 0, SHOW_LINENUM|SHOW_MODULE|radix_flag);
else
show_symbol(sp, 0, SHOW_LINENUM|radix_flag);
}
} while ((sp = symbol_search_next(sp->name, sp)));
return FALSE;
}
/*
* 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, forward;
int unfiltered, user_mode, count_entered, bug_bytes_entered, sources;
unsigned int radix;
ulong curaddr;
ulong target;
ulong count;
ulong offset;
ulong low, high;
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 = forward = count_entered = bug_bytes_entered = sources = FALSE;
sp = NULL;
unfiltered = user_mode = do_machdep_filter = do_load_module_filter = 0;
radix = 0;
target = 0;
req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
req->flags |= GNU_FROM_TTY_OFF|GNU_RETURN_ON_ERROR;
req->count = 1;
while ((c = getopt(argcnt, args, "dxhulsrfUb: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':
if (sources)
error(FATAL,
"-s can only be used with kernel addresses\n");
user_mode = TRUE;
break;
case 'r':
if (forward)
error(FATAL,
"-r and -f are mutually exclusive\n");
if (sources)
error(FATAL,
"-r and -s are mutually exclusive\n");
reverse = TRUE;
break;
case 'f':
if (reverse)
error(FATAL,
"-r and -f are mutually exclusive\n");
if (sources)
error(FATAL,
"-f and -s are mutually exclusive\n");
forward = 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 's':
if (reverse)
error(FATAL,
"-r and -s are mutually exclusive\n");
if (forward)
error(FATAL,
"-f and -s are mutually exclusive\n");
if (user_mode)
error(FATAL,
"-s can only be used with kernel addresses\n");
if (NO_LINE_NUMBERS())
error(INFO, "line numbers are not available\n");
sources = TRUE;
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->buf = args[optind];
req->addr = eval(args[optind], FAULT_ON_ERROR, NULL);
if (!user_mode &&
!resolve_text_symbol(args[optind], NULL, req, radix)) {
FREEBUF(req);
return;
}
} else if (hexadecimal(args[optind], 0) && !symbol_exists(args[optind])) {
req->buf = args[optind];
req->addr = htol(args[optind], FAULT_ON_ERROR, NULL);
sp = value_search(req->addr, &offset);
if (!user_mode && !sp) {
error(WARNING,
"%lx: no associated kernel symbol found\n",
req->addr);
unfiltered = TRUE;
}
if (!offset && sp && is_symbol_text(sp))
req->flags |= GNU_FUNCTION_ONLY;
} else if ((sp = symbol_search(args[optind]))) {
req->buf = args[optind];
req->addr = sp->value;
if (!resolve_text_symbol(args[optind], sp, req, radix)) {
FREEBUF(req);
return;
}
if (is_symbol_text(sp))
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);
return;
}
if (args[++optind]) {
if (reverse || forward) {
error(INFO,
"count argument ignored with -%s option\n",
reverse ? "r" : "f");
} else {
req->count = stol(args[optind],
FAULT_ON_ERROR, NULL);
req->flags &= ~GNU_FUNCTION_ONLY;
count_entered++;
}
}
if (sources) {
list_source_code(req, count_entered);
return;
}
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;
}
req->command = GNU_RESOLVE_TEXT_ADDR;
gdb_interface(req);
req->flags &= ~GNU_COMMAND_FAILED;
if (reverse || forward || req->flags & GNU_FUNCTION_ONLY) {
if (get_text_function_range(sp ? sp->value : req->addr,
&low, &high))
req->addr2 = high;
else 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);
}
}
if (reverse || forward) {
target = req->addr;
if ((sp = value_search(target, NULL)) == NULL)
error(FATAL, "cannot resolve address: %lx\n", target);
req->addr = sp->value;
} else
count = 0;
do_load_module_filter = module_symbol(req->addr, NULL, NULL,
NULL, *gdb_output_radix);
do_machdep_filter = machdep->dis_filter(req->addr, NULL, radix);
open_tmpfile();
if (reverse)
sprintf(buf5, "x/%ldi 0x%lx",
(target - req->addr) ? target - req->addr : 1,
req->addr);
else
sprintf(buf5, "x/%ldi 0x%lx",
count_entered && req->count ? req->count :
forward || req->flags & GNU_FUNCTION_ONLY ?
req->addr2 - req->addr : 1,
req->addr);
gdb_pass_through(buf5, NULL, GNU_RETURN_ON_ERROR);
if (req->flags & GNU_COMMAND_FAILED) {
close_tmpfile();
error(FATAL, dis_err, req->addr);
}
rewind(pc->tmpfile);
while (fgets(buf2, BUFSIZE, pc->tmpfile)) {
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 (forward) {
if (curaddr < target)
continue;
else
forward = FALSE;
}
if (!reverse)
if (!count_entered && req->addr2 &&
(curaddr >= req->addr2))
break;
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 (reverse) {
if (curaddr >= target) {
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;
}
}
if (count_entered && LASTCHAR(clean_line(buf2)) != ':')
if (++count == req->count)
break;
}
close_tmpfile();
}
else if (bug_bytes_entered)
return;
else cmd_usage(pc->curcmd, SYNOPSIS);
FREEBUF(req);
return;
}
/*
* 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_OPT_BT)
bt->flags |= BT_OPT_BACK_TRACE;
while ((c = getopt(argcnt, args, "D:fFI:S:c:aAloreEgstTdxR:Ov")) != 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 (!(machine_type("X86") || machine_type("X86_64") || machine_type("ARM64")) ||
XEN_HYPER_MODE())
option_not_supported(c);
bt->flags |= BT_OPT_BACK_TRACE;
break;
case 'O':
if (!(machine_type("X86") || machine_type("X86_64") || machine_type("ARM64")) ||
XEN_HYPER_MODE())
option_not_supported(c);
else if (kt->flags & USE_OPT_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 %s bt method by default (already set)\n",
machine_type("ARM64") ? "optional" : "old");
return;
}
kt->flags &= ~USE_OPT_BT;
error(INFO, "use %s bt method by default\n",
machine_type("ARM64") ? "original" : "new");
} else {
kt->flags |= USE_OPT_BT;
error(INFO, "use %s bt method by default\n",
machine_type("ARM64") ? "optional" : "old");
}
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, "orc"))
hook.esp = (ulong)-5;
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);
strcpy(arg_buf, 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;
case 'v':
if (XEN_HYPER_MODE())
option_not_supported(c);
check_stack_overflow();
return;
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) && 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;
}
if (bt->stackbase == 0) {
fprintf(fp, "(no stack)\n");
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 (VMSS_DUMPFILE())
get_vmware_vmss_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)) {
if (!LOCAL_ACTIVE()) {
error(INFO, "task no longer exists\n");
return;
}
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:
if (kernel_symbol_exists("hardirq_stack") &&
STRUCT_EXISTS("irq_stack")) {
btloc.hp->eip = symbol_value("handle_irq");
btloc.hp->esp = ULONG(bt->stackbuf);
} else {
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");
if (kernel_symbol_exists("softirq_stack") &&
STRUCT_EXISTS("irq_stack")) {
if (kernel_symbol_exists("do_softirq_own_stack"))
btloc.hp->eip = symbol_value("do_softirq_own_stack");
btloc.hp->esp = ULONG(bt->stackbuf);
} else
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:
if (kernel_symbol_exists("hardirq_stack") &&
STRUCT_EXISTS("irq_stack")) {
bt->instptr = symbol_value("handle_irq");
bt->stkptr = ULONG(bt->stackbuf);
} else {
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:
if (kernel_symbol_exists("softirq_stack") &&
STRUCT_EXISTS("irq_stack")) {
if (kernel_symbol_exists("do_softirq_own_stack"))
bt->instptr = symbol_value("do_softirq_own_stack");
else
bt->instptr = symbol_value("do_softirq");
bt->stkptr = ULONG(bt->stackbuf);
} else {
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");
if (MEMBER_EXISTS("module", "module_core")) {
MEMBER_OFFSET_INIT(module_core_size, "module",
"core_size");
MEMBER_OFFSET_INIT(module_init_size, "module",
"init_size");
MEMBER_OFFSET_INIT(module_core_text_size, "module",
"core_text_size");
MEMBER_OFFSET_INIT(module_init_text_size, "module",
"init_text_size");
MEMBER_OFFSET_INIT(module_module_core, "module",
"module_core");
MEMBER_OFFSET_INIT(module_module_init, "module",
"module_init");
} else {
ASSIGN_OFFSET(module_core_size) =
MEMBER_OFFSET("module", "core_layout") +
MEMBER_OFFSET("module_layout", "size");
ASSIGN_OFFSET(module_init_size) =
MEMBER_OFFSET("module", "init_layout") +
MEMBER_OFFSET("module_layout", "size");
ASSIGN_OFFSET(module_core_text_size) =
MEMBER_OFFSET("module", "core_layout") +
MEMBER_OFFSET("module_layout", "text_size");
ASSIGN_OFFSET(module_init_text_size) =
MEMBER_OFFSET("module", "init_layout") +
MEMBER_OFFSET("module_layout", "text_size");
ASSIGN_OFFSET(module_module_core) =
MEMBER_OFFSET("module", "core_layout") +
MEMBER_OFFSET("module_layout", "base");
ASSIGN_OFFSET(module_module_init) =
MEMBER_OFFSET("module", "init_layout") +
MEMBER_OFFSET("module_layout", "base");
}
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 */
total += 2; /* and the init 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_4_10(void)
{
int i, j, bx;
struct load_module *lm;
int maxnamelen;
int found;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
struct syment *sp;
ulong *taintsp, taints;
bool tnt_mod;
char tnt_true;
int tnts_len;
ulong tnts_addr;
char *modbuf;
if (INVALID_MEMBER(module_taints)) {
MEMBER_OFFSET_INIT(module_taints, "module", "taints");
STRUCT_SIZE_INIT(taint_flag, "taint_flag");
MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true");
if (INVALID_MEMBER(tnt_true))
MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "c_true");
MEMBER_OFFSET_INIT(tnt_mod, "taint_flag", "module");
}
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);
if (MEMBER_SIZE("module", "taints") == sizeof(ulong))
taints = ULONG(modbuf + OFFSET(module_taints));
else
taints = UINT(modbuf + OFFSET(module_taints));
if (taints) {
found++;
maxnamelen = strlen(lm->mod_name) > maxnamelen ?
strlen(lm->mod_name) : maxnamelen;
}
}
if (!found) {
fprintf(fp, "no tainted modules\n");
FREEBUF(modbuf);
return;
}
tnts_len = get_array_length("taint_flags", NULL, 0);
sp = symbol_search("taint_flags");
tnts_addr = sp->value;
fprintf(fp, "%s %s\n",
mkstring(buf2, maxnamelen, LJUST, "NAME"), "TAINTS");
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);
if (MEMBER_SIZE("module", "taints") == sizeof(ulong))
taints = ULONG(modbuf + OFFSET(module_taints));
else
taints = UINT(modbuf + OFFSET(module_taints));
if (!taints)
continue;
taintsp = &taints;
for (j = 0; j < tnts_len; j++) {
readmem((tnts_addr + j * SIZE(taint_flag)) +
OFFSET(tnt_mod),
KVADDR, &tnt_mod, sizeof(bool),
"tnt mod", FAULT_ON_ERROR);
if (!tnt_mod)
continue;
if (NUM_IN_BITMAP(taintsp, j)) {
readmem((tnts_addr + j * SIZE(taint_flag)) +
OFFSET(tnt_true),
KVADDR, &tnt_true, sizeof(char),
"tnt true", FAULT_ON_ERROR);
buf1[bx++] = tnt_true;
}
}
buf1[bx++] = '\0';
fprintf(fp, "%s %s\n", mkstring(buf2, maxnamelen,
LJUST, lm->mod_name), buf1);
}
FREEBUF(modbuf);
}
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 (VALID_STRUCT(taint_flag) ||
(kernel_symbol_exists("taint_flags") && STRUCT_EXISTS("taint_flag"))) {
show_module_taint_4_10();
return;
}
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-1);
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, "tdma")) != 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;
case 'a':
msg_flags |= SHOW_LOG_AUDIT;
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (msg_flags & SHOW_LOG_AUDIT) {
dump_audit();
return;
}
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, "ctip:")) != 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;
case 'i':
dump_dmi_info();
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() ||
is_excluded_vmemmap())) {
fprintf(fp, " %s%s%s",
is_partial_diskdump() ?
" [PARTIAL DUMP]" : "",
is_incomplete_dump() ?
" [INCOMPLETE]" : "",
is_excluded_vmemmap() ?
" [EXCLUDED VMEMMAP]" : "");
}
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, c, 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_OPT_BT)
fprintf(fp, "%sUSE_OPT_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++ ? "|" : "");
if (kt->flags2 & TVEC_BASES_V3)
fprintf(fp, "%sTVEC_BASES_V3", others++ ? "|" : "");
if (kt->flags2 & TIMER_BASES)
fprintf(fp, "%sTIMER_BASES", 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)");
fprintf(fp, " source_tree: %s\n", kt->source_tree ?
kt->source_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, " possible cpus: ");
if (cpu_map_addr("possible")) {
for (i = c = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & POSSIBLE_MAP) {
fprintf(fp, "%d ", i);
c++;
}
}
fprintf(fp, "%s\n", c ? "" : "(none)");
} else
fprintf(fp, "(nonexistent)\n");
fprintf(fp, " present cpus: ");
if (cpu_map_addr("present")) {
for (i = c = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & PRESENT_MAP) {
fprintf(fp, "%d ", i);
c++;
}
}
fprintf(fp, "%s\n", c ? "" : "(none)");
} else
fprintf(fp, "(nonexistent)\n");
fprintf(fp, " online cpus: ");
if (cpu_map_addr("online")) {
for (i = c = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & ONLINE_MAP) {
fprintf(fp, "%d ", i);
c++;
}
}
fprintf(fp, "%s\n", c ? "" : "(none)");
} else
fprintf(fp, "(nonexistent)\n");
fprintf(fp, " active cpus: ");
if (cpu_map_addr("active")) {
for (i = c = 0; i < nr_cpus; i++) {
if (kt->cpu_flags[i] & ACTIVE_MAP) {
fprintf(fp, "%d ", i);
c++;
}
}
fprintf(fp, "%s\n", c ? "" : "(none)");
} else
fprintf(fp, "(nonexistent)\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");
if (!symbol_exists("xen_p2m_addr")) {
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[15];
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);
strcpy(arg_buf, 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, 15);
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)
strcat(name_buf, ",");
strcat(name_buf, 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)) {
tmp = irq_desc_addr + OFFSET(irq_data_chip);
if (VALID_MEMBER(irq_desc_irq_data))
tmp += OFFSET(irq_desc_irq_data);
readmem(tmp, 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)
strcat(name_buf, ",");
strcat(name_buf, 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;
char *cpuspec;
ulong *cpus = NULL;
rflag = 0;
while ((c = getopt(argcnt, args, "rC:")) != EOF) {
switch(c)
{
case 'r':
rflag = 1;
break;
case 'C':
cpuspec = optarg;
cpus = get_cpumask_buf();
make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL);
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (rflag)
dump_hrtimer_data(cpus);
else
dump_timer_data(cpus);
if (cpus)
FREEBUF(cpus);
}
static void
dump_hrtimer_data(const ulong *cpus)
{
int i, j, k = 0;
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 (cpus && !NUM_IN_BITMAP(cpus, i))
continue;
if (k++)
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 if (VALID_MEMBER(ktime_t_sec) && VALID_MEMBER(ktime_t_nsec)) {
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;
} else {
readmem((ulong)ktime, KVADDR, &ns,
sizeof(ns), "ktime_t", QUIET|RETURN_ON_ERROR);
}
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(const ulong *cpus)
{
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 (kt->flags2 & TIMER_BASES) {
dump_timer_data_timer_bases(cpus);
return;
} else if (kt->flags2 & TVEC_BASES_V3) {
dump_timer_data_tvec_bases_v3(cpus);
return;
} else if (kt->flags & TVEC_BASES_V2) {
dump_timer_data_tvec_bases_v2(cpus);
return;
} else if (kt->flags & TVEC_BASES_V1) {
dump_timer_data_tvec_bases_v1(cpus);
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(const ulong *cpus)
{
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];
/*
*/
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:
if (cpus && !NUM_IN_BITMAP(cpus, cpu)) {
if (++cpu < kt->cpus)
goto next_cpu;
return;
}
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(const ulong *cpus)
{
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];
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:
if (cpus && !NUM_IN_BITMAP(cpus, cpu)) {
if (++cpu < kt->cpus)
goto next_cpu;
return;
}
/*
* 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;
return;
}
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;
}
/*
* Linux 4.2 timers use new tvec_root, tvec and timer_list structures
*/
static void
dump_timer_data_tvec_bases_v3(const ulong *cpus)
{
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, head_size;
struct syment *sp;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
vec_root_size = vec_size = 0;
head_size = SIZE(hlist_head);
if ((i = get_array_length("tvec_root.vec", NULL, head_size)))
vec_root_size = i;
else
error(FATAL, "cannot determine tvec_root.vec[] array size\n");
if ((i = get_array_length("tvec.vec", NULL, head_size)))
vec_size = i;
else
error(FATAL, "cannot determine tvec.vec[] array size\n");
vec = (ulong *)GETBUF(head_size * MAX(vec_root_size, vec_size));
cpu = 0;
next_cpu:
if (cpus && !NUM_IN_BITMAP(cpus, cpu)) {
if (++cpu < kt->cpus)
goto next_cpu;
return;
}
/*
* 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;
return;
}
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_v3(tv[1].base + OFFSET(tvec_root_s_vec),
vec_root_size, vec, NULL, NULL);
count += do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL);
count += do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL);
count += do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL);
count += do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec),
vec_size, vec, NULL, NULL);
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_v3(tv[1].base + OFFSET(tvec_root_s_vec),
vec_root_size, vec, (void *)td, &highest);
do_timer_list_v3(tv[2].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest);
do_timer_list_v3(tv[3].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest);
do_timer_list_v3(tv[4].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest);
tdx = do_timer_list_v3(tv[5].base + OFFSET(tvec_s_vec),
vec_size, vec, (void *)td, &highest);
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;
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) ||
(kt->flags2 & TVEC_BASES_V3)) {
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 = 0;
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))
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) {
hq_close();
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);
}
FREEBUF(timer_list_buf);
return(td ? tdx : count);
}
static int
do_timer_list_v3(ulong vec_kvaddr,
int size,
ulong *vec,
void *option,
ulong *highest)
{
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;
tdx = 0;
td = option ? (struct timer_data *)option : NULL;
if (td) {
while (td[tdx].function)
tdx++;
}
readmem(vec_kvaddr, KVADDR, vec, SIZE(hlist_head) * size,
"timer_list vec array", FAULT_ON_ERROR);
ld = &list_data;
timer_list_buf = GETBUF(SIZE(timer_list));
for (i = count = 0; i < size; i++, vec_kvaddr += SIZE(hlist_head)) {
if (vec[i] == 0)
continue;
BZERO(ld, sizeof(struct list_data));
ld->start = vec[i];
ld->list_head_offset = OFFSET(timer_list_entry);
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);
continue;
}
if (!timer_cnt) {
hq_close();
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++) {
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);
}
FREEBUF(timer_list_buf);
return(td ? tdx : count);
}
#define TIMERS_CHUNK (100)
struct timer_bases_data {
int total, cnt, num_vectors;
ulong *vectors;
ulong timer_base;
struct timer_data *timers;
};
static int
do_timer_list_v4(struct timer_bases_data *data)
{
int i, t, timer_cnt, found;
struct list_data list_data, *ld;
ulong *timer_list;
ulong expires, function;
long oldsize;
char *timer_list_buf;
timer_list_buf = GETBUF(SIZE(timer_list));
ld = &list_data;
for (i = found = 0; i < data->num_vectors; i++) {
if (data->vectors[i] == 0)
continue;
if (CRASHDEBUG(1))
fprintf(fp, "%lx vectors[%d]: %lx\n",
data->timer_base + OFFSET(timer_base_vectors) + (i * sizeof(void *)),
i, data->vectors[i]);
BZERO(ld, sizeof(struct list_data));
ld->start = data->vectors[i];
ld->list_head_offset = OFFSET(timer_list_entry);
ld->end = 0;
ld->flags = RETURN_ON_LIST_ERROR;
hq_open();
if ((timer_cnt = do_list(ld)) == -1) {
/* Ignore chains with errors */
if (CRASHDEBUG(1))
error(INFO,
"ignoring faulty timer_list in timer_base.vector[%d] list\n",
i);
hq_close();
continue;
}
if (!timer_cnt) {
hq_close();
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 (CRASHDEBUG(1))
fprintf(fp, " %lx\n", timer_list[t]);
if (!readmem(timer_list[t], KVADDR, timer_list_buf,
SIZE(timer_list), "timer_list buffer", QUIET|RETURN_ON_ERROR))
continue;
expires = ULONG(timer_list_buf + OFFSET(timer_list_expires));
function = ULONG(timer_list_buf + OFFSET(timer_list_function));
data->timers[data->cnt].address = timer_list[t];
data->timers[data->cnt].expires = expires;
data->timers[data->cnt].function = function;
data->cnt++;
if (data->cnt == data->total) {
oldsize = data->total * sizeof(struct timer_data);
RESIZEBUF(data->timers, oldsize, oldsize * 2);
data->total *= 2;
}
found++;
}
FREEBUF(timer_list);
}
FREEBUF(timer_list_buf);
return found;
}
/*
* Linux 4.8 timers use new timer_bases[][]
*/
static void
dump_timer_data_timer_bases(const ulong *cpus)
{
int i, cpu, flen, base, nr_bases, found, display, j = 0;
struct syment *sp;
ulong timer_base, jiffies, function;
struct timer_bases_data data;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
if (!(data.num_vectors = get_array_length("timer_base.vectors", NULL, 0)))
error(FATAL, "cannot determine timer_base.vectors[] array size\n");
data.vectors = (ulong *)GETBUF(data.num_vectors * sizeof(void *));
data.timers = (struct timer_data *)GETBUF(sizeof(struct timer_data) * TIMERS_CHUNK);
data.total = TIMERS_CHUNK;
data.cnt = 0;
nr_bases = kernel_symbol_exists("sysctl_timer_migration") ? 2 : 1;
cpu = 0;
get_symbol_data("jiffies", sizeof(ulong), &jiffies);
sprintf(buf1, "%ld", jiffies);
flen = MAX(strlen(buf1), strlen("JIFFIES"));
fprintf(fp, "%s\n", mkstring(buf1, flen, LJUST, "JIFFIES"));
fprintf(fp, "%s\n\n", mkstring(buf1, flen,
RJUST|LONG_DEC,MKSTR(jiffies)));
next_cpu:
if (cpus && !NUM_IN_BITMAP(cpus, cpu)) {
if (++cpu < kt->cpus)
goto next_cpu;
goto done;
}
/*
* hide data of offline cpu and goto next cpu
*/
if (hide_offline_cpu(cpu)) {
fprintf(fp, "TIMER_BASES[%d]: [OFFLINE]\n", cpu);
if (++cpu < kt->cpus)
goto next_cpu;
goto done;
}
base = 0;
sp = per_cpu_symbol_search("per_cpu__timer_bases");
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
timer_base = sp->value + kt->__per_cpu_offset[cpu];
else
timer_base = sp->value;
if (j++)
fprintf(fp, "\n");
next_base:
fprintf(fp, "TIMER_BASES[%d][%s]: %lx\n", cpu,
base == 0 ? "BASE_STD" : "BASE_DEF", timer_base);
readmem(timer_base + OFFSET(timer_base_vectors), KVADDR, data.vectors,
data.num_vectors * sizeof(void *), "timer_base.vectors[]", FAULT_ON_ERROR);
data.cnt = 0;
data.timer_base = timer_base;
found = do_timer_list_v4(&data);
qsort(data.timers, found, sizeof(struct timer_data), compare_timer_data);
fprintf(fp, " %s TIMER_LIST FUNCTION\n",
mkstring(buf1, flen, LJUST, "EXPIRES"));
for (i = 0; i < found; i++) {
display = FALSE;
if (is_kernel_text(data.timers[i].function)) {
display = TRUE;
function = data.timers[i].function;
} else {
if (readmem(data.timers[i].function, KVADDR, &function,
sizeof(ulong), "timer function",
RETURN_ON_ERROR|QUIET) && is_kernel_text(function))
display = TRUE;
else {
if (LIVE()) {
if (CRASHDEBUG(1))
fprintf(fp, "(invalid/stale entry at %lx)\n",
data.timers[i].address);
display = FALSE;
} else {
function = data.timers[i].function;
display = TRUE;
}
}
}
if (display) {
fprintf(fp, " %s",
mkstring(buf1, flen, RJUST|LONG_DEC, MKSTR(data.timers[i].expires)));
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(data.timers[i].address));
fprintf(fp, " %s ", mkstring(buf2, 16, CENTER, buf1));
fprintf(fp, "%s <%s>\n",
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(data.timers[i].function)),
value_to_symstr(function, buf2, 0));
}
}
if (!found)
fprintf(fp, " (none)\n");
if ((nr_bases == 2) && (base == 0)) {
base++;
timer_base += SIZE(timer_base);
goto next_base;
}
if (++cpu < kt->cpus)
goto next_cpu;
done:
FREEBUF(data.vectors);
FREEBUF(data.timers);
}
/*
* 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 (!LOCAL_ACTIVE())
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;
int memtype;
if (XEN_CORE_DUMPFILE() && symbol_exists("xen_p2m_addr"))
memtype = PHYSADDR;
else
memtype = KVADDR;
/*
* 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 (memtype == PHYSADDR)
pc->curcmd_flags |= XEN_MACHINE_ADDR;
read_p2m(c, memtype, mp);
if (memtype == PHYSADDR)
pc->curcmd_flags &= ~XEN_MACHINE_ADDR;
} 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 tree/array, caching the contiguous
* range containing the found machine address.
*/
if (symbol_exists("p2m_mid_missing"))
pfn = __xen_pvops_m2p_l3(machine, mfn);
else if (symbol_exists("xen_p2m_addr")) {
if (XEN_CORE_DUMPFILE())
pfn = __xen_pvops_m2p_hyper(machine, mfn);
else
pfn = __xen_pvops_m2p_domU(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;
}
static ulong
__xen_pvops_m2p_hyper(ulonglong machine, ulong mfn)
{
ulong c, end, i, mapping, p, pfn, start;
for (p = 0;
p < xkd->p2m_frames;
++p) {
mapping = PTOB(xkd->p2m_mfn_frame_list[p]);
if (mapping != kt->last_mapping_read) {
pc->curcmd_flags |= XEN_MACHINE_ADDR;
if (!readmem(mapping, PHYSADDR, (void *)kt->m2p_page,
PAGESIZE(), "p2m_mfn_frame_list page", RETURN_ON_ERROR))
error(FATAL, "cannot access p2m_mfn_frame_list[] page\n");
pc->curcmd_flags &= ~XEN_MACHINE_ADDR;
kt->last_mapping_read = mapping;
}
kt->p2m_pages_searched++;
if (search_mapping_page(mfn, &i, &start, &end)) {
pfn = p * XEN_PFNS_PER_PAGE + i;
if (CRASHDEBUG(1))
console("pages: %d mfn: %lx (%llx) p: %ld"
" i: %ld pfn: %lx (%llx)\n", p + 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 * XEN_PFNS_PER_PAGE;
kt->p2m_cache_index = (c+1) % P2M_MAPPING_CACHE;
return pfn;
}
}
return XEN_MFN_NOT_FOUND;
}
static void read_p2m(ulong cache_index, int memtype, void *buffer)
{
/*
* Use special read function for PV domain p2m reading.
* See the comments of read_xc_p2m().
*/
if (symbol_exists("xen_p2m_addr") && !XEN_CORE_DUMPFILE()) {
if (!read_xc_p2m(kt->p2m_mapping_cache[cache_index].mapping,
buffer, PAGESIZE()))
error(FATAL, "cannot access phys_to_machine_mapping page\n");
} else if (!readmem(kt->p2m_mapping_cache[cache_index].mapping, memtype,
buffer, PAGESIZE(), "phys_to_machine_mapping page (cached)",
RETURN_ON_ERROR))
error(FATAL, "cannot access phys_to_machine_mapping page\n");
kt->last_mapping_read = kt->p2m_mapping_cache[cache_index].mapping;
}
/*
* PV domain p2m mapping info is stored in xd->xfd at xch_index_offset. It
* is organized as struct xen_dumpcore_p2m and the pfns are progressively
* increased by 1 from 0.
*
* This is a special p2m reading function for xen PV domain vmcores after
* kernel commit 054954eb051f35e74b75a566a96fe756015352c8 (xen: switch
* to linear virtual mapped sparse p2m list). It is invoked for reading
* p2m associate stuff by read_p2m().
*/
static int read_xc_p2m(ulonglong addr, void *buffer, long size)
{
ulong i, new_p2m_buf_size;
off_t offset;
struct xen_dumpcore_p2m *new_p2m_buf;
static struct xen_dumpcore_p2m *p2m_buf;
static ulong p2m_buf_size = 0;
if (size <= 0) {
if ((CRASHDEBUG(1) && !STREQ(pc->curcmd, "search")) ||
CRASHDEBUG(2))
error(INFO, "invalid size request: %ld\n", size);
return FALSE;
}
/*
* We extract xen_dumpcore_p2m.gmfn and copy them into the
* buffer. So, we need temporary p2m_buf whose size is
* (size * (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong)))
* to put xen_dumpcore_p2m structures read from xd->xfd.
*/
new_p2m_buf_size = size * (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong));
if (p2m_buf_size != new_p2m_buf_size) {
p2m_buf_size = new_p2m_buf_size;
new_p2m_buf = realloc(p2m_buf, p2m_buf_size);
if (new_p2m_buf == NULL) {
free(p2m_buf);
error(FATAL, "cannot realloc p2m buffer\n");
}
p2m_buf = new_p2m_buf;
}
offset = addr * (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong));
offset += xd->xc_core.header.xch_index_offset;
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
error(FATAL,
"cannot lseek to xch_index_offset offset 0x%lx\n", offset);
if (read(xd->xfd, (void*)p2m_buf, p2m_buf_size) != p2m_buf_size)
error(FATAL,
"cannot read from xch_index_offset offset 0x%lx\n", offset);
for (i = 0; i < size / sizeof(ulong); i++)
*((ulong *)buffer + i) = p2m_buf[i].gmfn;
return TRUE;
}
static ulong
__xen_pvops_m2p_domU(ulonglong machine, ulong mfn)
{
ulong c, end, i, mapping, p, pfn, start;
/*
* xch_nr_pages is the number of pages of p2m mapping. It is composed
* of struct xen_dumpcore_p2m. The stuff we want to copy into the mapping
* page is mfn whose type is unsigned long.
* So actual number of p2m pages should be:
*
* xch_nr_pages / (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong))
*/
for (p = 0;
p < xd->xc_core.header.xch_nr_pages /
(sizeof(struct xen_dumpcore_p2m) / sizeof(ulong));
++p) {
mapping = p * PAGESIZE();
if (mapping != kt->last_mapping_read) {
if (!read_xc_p2m(mapping, (void *)kt->m2p_page, PAGESIZE()))
error(FATAL, "cannot read the last mapping page\n");
kt->last_mapping_read = mapping;
}
kt->p2m_pages_searched++;
if (search_mapping_page(mfn, &i, &start, &end)) {
pfn = p * XEN_PFNS_PER_PAGE + i;
c = kt->p2m_cache_index;
if (CRASHDEBUG (1))
console("mfn: %lx (%llx) i: %ld pfn: %lx (%llx)\n",
mfn, machine, i, pfn, XEN_PFN_TO_PSEUDO(pfn));
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 * XEN_PFNS_PER_PAGE;
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_v4_10(char *buf, int verbose)
{
int i, bx;
char tnt_true, tnt_false;
int tnts_len;
ulong tnts_addr;
ulong tainted_mask, *tainted_mask_ptr;
struct syment *sp;
if (!VALID_STRUCT(taint_flag)) {
STRUCT_SIZE_INIT(taint_flag, "taint_flag");
MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true");
MEMBER_OFFSET_INIT(tnt_false, "taint_flag", "false");
if (INVALID_MEMBER(tnt_true)) {
MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "c_true");
MEMBER_OFFSET_INIT(tnt_false, "taint_flag", "c_false");
}
}
bx = 0;
buf[0] = '\0';
/*
* Make sure that all dependencies are valid to prevent
* a fatal error from killing the session during the
* pre-RUNTIME system banner display.
*/
if (!(pc->flags & RUNTIME)) {
if (INVALID_MEMBER(tnt_true) || INVALID_MEMBER(tnt_false) ||
!kernel_symbol_exists("tainted_mask"))
return;
}
tnts_len = get_array_length("taint_flags", NULL, 0);
sp = symbol_search("taint_flags");
tnts_addr = sp->value;
get_symbol_data("tainted_mask", sizeof(ulong), &tainted_mask);
tainted_mask_ptr = &tainted_mask;
for (i = 0; i < tnts_len; i++) {
if (NUM_IN_BITMAP(tainted_mask_ptr, i)) {
readmem((tnts_addr + i * SIZE(taint_flag)) +
OFFSET(tnt_true),
KVADDR, &tnt_true, sizeof(char),
"tnt true", FAULT_ON_ERROR);
buf[bx++] = tnt_true;
} else {
readmem((tnts_addr + i * SIZE(taint_flag)) +
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);
}
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(taint_flag) ||
(kernel_symbol_exists("taint_flags") && STRUCT_EXISTS("taint_flag"))) {
show_kernel_taints_v4_10(buf, verbose);
return;
}
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);
}
static void
dump_dmi_info(void)
{
int i, array_len, len, maxlen;
ulong dmi_ident_p, vaddr;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char *arglist[MAXARGS];
if (!kernel_symbol_exists("dmi_ident"))
error(FATAL, "dmi_ident does not exist in this kernel\n");
dmi_ident_p = symbol_value("dmi_ident");
array_len = get_array_length("dmi_ident", NULL, 0);
maxlen = 0;
open_tmpfile();
if (dump_enumerator_list("dmi_field")) {
rewind(pc->tmpfile);
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
if (!strstr(buf1, " = "))
continue;
if ((parse_line(buf1, arglist) != 3) ||
(atoi(arglist[2]) >= array_len))
break;
len = strlen(arglist[0]);
if (len > maxlen)
maxlen = len;
}
rewind(pc->tmpfile);
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
if (!strstr(buf1, " = "))
continue;
if ((parse_line(buf1, arglist) != 3) ||
((i = atoi(arglist[2])) >= array_len))
break;
readmem(dmi_ident_p + (sizeof(void *) * i),
KVADDR, &vaddr, sizeof(void *),
"dmi_ident", FAULT_ON_ERROR);
if (!vaddr)
continue;
read_string(vaddr, buf2, BUFSIZE-1);
fprintf(pc->saved_fp, " %s%s: %s\n",
space(maxlen - strlen(arglist[0])), arglist[0], buf2);
}
} else {
for (i = 0; i < array_len; i++) {
readmem(dmi_ident_p + (sizeof(void *) * i),
KVADDR, &vaddr, sizeof(void *),
"dmi_ident", FAULT_ON_ERROR);
if (!vaddr)
continue;
read_string(vaddr, buf1, BUFSIZE-1);
fprintf(pc->saved_fp, " dmi_ident[%d]: %s\n", i, buf1);
}
}
close_tmpfile();
}
#define NLMSG_ALIGNTO 4
#define NLMSG_DATA(nlh) (nlh + roundup(SIZE(nlmsghdr), NLMSG_ALIGNTO))
static ulong
dump_audit_skb_queue(ulong audit_skb_queue)
{
ulong skb_buff_head_next = 0, p;
uint32_t qlen = 0;
if (INVALID_SIZE(nlmsghdr)) {
STRUCT_SIZE_INIT(nlmsghdr, "nlmsghdr");
MEMBER_OFFSET_INIT(nlmsghdr_nlmsg_type, "nlmsghdr", "nlmsg_type");
MEMBER_SIZE_INIT(nlmsghdr_nlmsg_type, "nlmsghdr", "nlmsg_type");
MEMBER_OFFSET_INIT(sk_buff_head_next, "sk_buff_head", "next");
MEMBER_OFFSET_INIT(sk_buff_head_qlen, "sk_buff_head", "qlen");
MEMBER_SIZE_INIT(sk_buff_head_qlen, "sk_buff_head", "qlen");
MEMBER_OFFSET_INIT(sk_buff_data, "sk_buff", "data");
MEMBER_OFFSET_INIT(sk_buff_len, "sk_buff", "len");
MEMBER_OFFSET_INIT(sk_buff_next, "sk_buff", "next");
MEMBER_SIZE_INIT(sk_buff_len, "sk_buff", "len");
}
readmem(audit_skb_queue + OFFSET(sk_buff_head_qlen),
KVADDR,
&qlen,
SIZE(sk_buff_head_qlen),
"audit_skb_queue.qlen",
FAULT_ON_ERROR);
if (!qlen)
return 0;
readmem(audit_skb_queue + OFFSET(sk_buff_head_next),
KVADDR,
&skb_buff_head_next,
sizeof(void *),
"audit_skb_queue.next",
FAULT_ON_ERROR);
if (!skb_buff_head_next)
error(FATAL, "audit_skb_queue.next: NULL\n");
p = skb_buff_head_next;
do {
ulong data, len, data_len;
uint16_t nlmsg_type;
char *buf = NULL;
if (CRASHDEBUG(2))
fprintf(fp, "%#016lx\n", p);
readmem(p + OFFSET(sk_buff_len),
KVADDR,
&len,
SIZE(sk_buff_len),
"sk_buff.data",
FAULT_ON_ERROR);
data_len = len - roundup(SIZE(nlmsghdr), NLMSG_ALIGNTO);
readmem(p + OFFSET(sk_buff_data),
KVADDR,
&data,
sizeof(void *),
"sk_buff.data",
FAULT_ON_ERROR);
if (!data)
error(FATAL, "sk_buff.data: NULL\n");
readmem(data + OFFSET(nlmsghdr_nlmsg_type),
KVADDR,
&nlmsg_type,
SIZE(nlmsghdr_nlmsg_type),
"nlmsghdr.nlmsg_type",
FAULT_ON_ERROR);
buf = GETBUF(data_len + 1);
readmem(NLMSG_DATA(data),
KVADDR,
buf,
data_len,
"sk_buff.data + sizeof(struct nlmsghdr)",
FAULT_ON_ERROR);
buf[data_len] = '\0';
fprintf(fp, "type=%u %s\n", nlmsg_type, buf);
FREEBUF(buf);
readmem(p + OFFSET(sk_buff_next),
KVADDR,
&p,
sizeof(void *),
"skb_buff.next",
FAULT_ON_ERROR);
} while (p != audit_skb_queue);
return qlen;
}
static ulong
__dump_audit(char *symname)
{
if (symbol_exists(symname)) {
if (CRASHDEBUG(1))
fprintf(fp, "# %s:\n", symname);
return dump_audit_skb_queue(symbol_value(symname));
}
return 0;
}
static void
dump_audit(void)
{
ulong qlen = 0;
if (symbol_exists("audit_skb_queue")) {
qlen += __dump_audit("audit_skb_hold_queue");
qlen += __dump_audit("audit_skb_queue");
} else if (symbol_exists("audit_queue")) {
qlen += __dump_audit("audit_hold_queue");
qlen += __dump_audit("audit_retry_queue");
qlen += __dump_audit("audit_queue");
} else
option_not_supported('a');
if (!qlen)
error(INFO, "kernel audit log is empty\n");
}