diff --git a/crash.patch-4.0-7.7 b/crash.patch-4.0-7.7 new file mode 100644 index 0000000..4e631ac --- /dev/null +++ b/crash.patch-4.0-7.7 @@ -0,0 +1,83706 @@ +--- crash/unwind_x86_64.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/unwind_x86_64.h 2006-10-20 14:58:14.000000000 -0400 +@@ -0,0 +1,92 @@ ++#define CONFIG_64BIT 1 ++#define NULL ((void *)0) ++ ++typedef unsigned long size_t; ++typedef unsigned char u8; ++typedef signed short s16; ++typedef unsigned short u16; ++typedef signed int s32; ++typedef unsigned int u32; ++typedef unsigned long long u64; ++ ++struct pt_regs { ++ unsigned long r15; ++ unsigned long r14; ++ unsigned long r13; ++ unsigned long r12; ++ unsigned long rbp; ++ unsigned long rbx; ++/* arguments: non interrupts/non tracing syscalls only save upto here*/ ++ unsigned long r11; ++ unsigned long r10; ++ unsigned long r9; ++ unsigned long r8; ++ unsigned long rax; ++ unsigned long rcx; ++ unsigned long rdx; ++ unsigned long rsi; ++ unsigned long rdi; ++ unsigned long orig_rax; ++/* end of arguments */ ++/* cpu exception frame or undefined */ ++ unsigned long rip; ++ unsigned long cs; ++ unsigned long eflags; ++ unsigned long rsp; ++ unsigned long ss; ++/* top of stack page */ ++}; ++ ++struct unwind_frame_info ++{ ++ struct pt_regs regs; ++}; ++ ++extern int unwind(struct unwind_frame_info *); ++extern void init_unwind_table(void); ++extern void free_unwind_table(void); ++ ++#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) ++#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) ++#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) ++#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1) ++#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) ++#define get_unaligned(ptr) (*(ptr)) ++//#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr))) ++#define THREAD_ORDER 1 ++#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER) ++ ++#define UNW_PC(frame) (frame)->regs.rip ++#define UNW_SP(frame) (frame)->regs.rsp ++#ifdef CONFIG_FRAME_POINTER ++ #define UNW_FP(frame) (frame)->regs.rbp ++ #define FRAME_RETADDR_OFFSET 8 ++ #define FRAME_LINK_OFFSET 0 ++ #define STACK_BOTTOM(tsk) (((tsk)->thread.rsp0 - 1) & ~(THREAD_SIZE - 1)) ++ #define STACK_TOP(tsk) ((tsk)->thread.rsp0) ++#endif ++ ++ ++#define EXTRA_INFO(f) { BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) % FIELD_SIZEOF(struct unwind_frame_info, f)) + offsetof(struct unwind_frame_info, f)/ FIELD_SIZEOF(struct unwind_frame_info, f), FIELD_SIZEOF(struct unwind_frame_info, f) } ++ ++#define PTREGS_INFO(f) EXTRA_INFO(regs.f) ++ ++#define UNW_REGISTER_INFO \ ++ PTREGS_INFO(rax),\ ++ PTREGS_INFO(rdx),\ ++ PTREGS_INFO(rcx),\ ++ PTREGS_INFO(rbx), \ ++ PTREGS_INFO(rsi), \ ++ PTREGS_INFO(rdi), \ ++ PTREGS_INFO(rbp), \ ++ PTREGS_INFO(rsp), \ ++ PTREGS_INFO(r8), \ ++ PTREGS_INFO(r9), \ ++ PTREGS_INFO(r10),\ ++ PTREGS_INFO(r11), \ ++ PTREGS_INFO(r12), \ ++ PTREGS_INFO(r13), \ ++ PTREGS_INFO(r14), \ ++ PTREGS_INFO(r15), \ ++ PTREGS_INFO(rip) ++ +--- crash/s390dbf.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/s390dbf.c 2006-08-14 13:58:56.000000000 -0400 +@@ -0,0 +1,1340 @@ ++/* ++ * s390 debug feature command for crash ++ * ++ * Copyright (C) IBM Corp. 2006 ++ * Author(s): Michael Holzheu ++ */ ++ ++#if defined(S390) || defined(S390X) ++ ++#include "defs.h" ++#include ++#include ++ ++/* ++ * Compat layer to integrate lcrash commands into crash ++ * Maps lcrash API to crash functions ++ */ ++ ++#define KL_NBPW sizeof(long) ++#define KL_ERRORFP stderr ++#define MAX_ARGS 128 ++#define MAX_CMDLINE 256 ++ ++#define C_FALSE 0x00000001 /* Command takes no arguments */ ++#define C_TRUE 0x00000002 /* Command requires arguments */ ++#define C_ALL 0x00000004 /* All elements */ ++#define C_PERM 0x00000008 /* Allocate perminant blocks */ ++#define C_TEMP 0x00000000 /* For completeness */ ++#define C_FULL 0x00000010 /* Full output */ ++#define C_LIST 0x00000020 /* List items */ ++#define C_NEXT 0x00000040 /* Follow links */ ++#define C_WRITE 0x00000080 /* Write output to file */ ++#define C_NO_OPCHECK 0x00000100 /* Don't reject bad cmd line options */ ++#define C_ITER 0x00000200 /* set iteration threshold */ ++ ++#define C_LFLG_SHFT 12 ++ ++#define KL_ARCH_S390 0 ++#define KL_ARCH_S390X 1 ++#ifdef __s390x__ ++#define KL_ARCH KL_ARCH_S390X ++#define FMTPTR "l" ++#define KL_PTRSZ 8 ++#else ++#define KL_ARCH KL_ARCH_S390 ++#define FMTPTR "ll" ++#define KL_PTRSZ 4 ++#endif ++ ++typedef unsigned long uaddr_t; ++typedef unsigned long kaddr_t; ++ ++typedef struct _syment { ++ char *s_name; ++ kaddr_t s_addr; ++} syment_t; ++ ++typedef struct option_s { ++ struct option_s *op_next; ++ char op_char; ++ char *op_arg; ++} option_t; ++ ++typedef struct command_s { ++ int flags; ++ char cmdstr[MAX_CMDLINE]; ++ char *command; ++ char *cmdline; ++ option_t *options; ++ int nargs; ++ char *args[MAX_ARGS]; ++ char *pipe_cmd; ++ FILE *ofp; ++ FILE *efp; ++} command_t; ++ ++static inline syment_t* kl_lkup_symaddr(kaddr_t addr) ++{ ++ static syment_t sym; ++ struct syment *crash_sym; ++ ++ crash_sym = value_search(addr, &sym.s_addr); ++ if (!crash_sym) ++ return NULL; ++ sym.s_name = crash_sym->name; ++ return &sym; ++} ++ ++static inline syment_t* kl_lkup_symname(char* name) ++{ ++ static syment_t sym; ++ sym.s_addr = symbol_value(name); ++ sym.s_name = NULL; ++ if(!sym.s_addr) ++ return NULL; ++ else ++ return &sym; ++} ++ ++static inline void GET_BLOCK(kaddr_t addr, int size, void* ptr) ++{ ++ readmem(addr, KVADDR,ptr,size,"GET_BLOCK",FAULT_ON_ERROR); ++} ++ ++static inline kaddr_t KL_VREAD_PTR(kaddr_t addr) ++{ ++ unsigned long ptr; ++ readmem(addr, KVADDR,&ptr,sizeof(ptr),"GET_BLOCK",FAULT_ON_ERROR); ++ return (kaddr_t)ptr; ++} ++ ++static inline uint32_t KL_GET_UINT32(void* ptr) ++{ ++ return *((uint32_t*)ptr); ++} ++ ++static inline uint64_t KL_GET_UINT64(void* ptr) ++{ ++ return *((uint64_t*)ptr); ++} ++ ++static inline kaddr_t KL_GET_PTR(void* ptr) ++{ ++ return *((kaddr_t*)ptr); ++} ++ ++static inline void* K_PTR(void* addr, char* struct_name, char* member_name) ++{ ++ return addr+MEMBER_OFFSET(struct_name,member_name); ++} ++ ++static inline uint32_t KL_UINT(void* ptr, char* struct_name, char* member_name) ++{ ++ return (uint32_t) ULONG(ptr+MEMBER_OFFSET(struct_name,member_name)); ++} ++ ++static inline uint32_t KL_VREAD_UINT32(kaddr_t addr) ++{ ++ uint32_t rc; ++ readmem(addr, KVADDR,&rc,sizeof(rc),"KL_VREAD_UINT32",FAULT_ON_ERROR); ++ return rc; ++} ++ ++static inline uint32_t KL_INT(void* ptr, char* struct_name, char* member_name) ++{ ++ return UINT(ptr+MEMBER_OFFSET(struct_name,member_name)); ++} ++ ++static inline int set_cmd_flags(command_t *cmd, int flags, char *extraops) ++{ ++ return 0; ++} ++ ++static inline void kl_s390tod_to_timeval(uint64_t todval, struct timeval *xtime) ++{ ++ todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); ++ ++ todval >>= 12; ++ xtime->tv_sec = todval / 1000000; ++ xtime->tv_usec = todval % 1000000; ++} ++ ++static inline int kl_struct_len(char* struct_name) ++{ ++ return STRUCT_SIZE(struct_name); ++} ++ ++static inline kaddr_t kl_funcaddr(kaddr_t addr) ++{ ++ struct syment *crash_sym; ++ ++ crash_sym = value_search(addr, &addr); ++ if (!crash_sym) ++ return -1; ++ else ++ return crash_sym->value; ++} ++ ++#define CMD_USAGE(cmd, s) \ ++ fprintf(cmd->ofp, "Usage: %s %s\n", cmd->command, s); \ ++ fprintf(cmd->ofp, "Enter \"help %s\" for details.\n",cmd->command); ++ ++/* ++ * s390 debug feature implementation ++ */ ++ ++#ifdef DBF_DYNAMIC_VIEWS /* views defined in shared libs */ ++#include ++#endif ++ ++/* Local flags ++ */ ++ ++#define LOAD_FLAG (1 << C_LFLG_SHFT) ++#define VIEWS_FLAG (2 << C_LFLG_SHFT) ++ ++#ifndef MIN ++#define MIN(a,b) (((a)<(b))?(a):(b)) ++#endif ++ ++/* Stuff which has to match with include/asm-s390/debug.h */ ++ ++#define DBF_VERSION_V1 1 ++#define DBF_VERSION_V2 2 ++#define PAGE_SIZE 4096 ++#define DEBUG_MAX_VIEWS 10 /* max number of views in proc fs */ ++#define DEBUG_MAX_PROCF_LEN 16 /* max length for a proc file name */ ++#define DEBUG_SPRINTF_MAX_ARGS 10 ++ ++/* define debug-structures for lcrash */ ++#define DEBUG_DATA(entry) (char*)(entry + 1) ++ ++typedef struct debug_view_s debug_view_t; ++ ++/* struct to hold contents of struct __debug_entry from dump ++ */ ++typedef struct debug_entry_s{ ++ union { ++ struct { ++ unsigned long long clock:52; ++ unsigned long long exception:1; ++ unsigned long long level:3; ++ unsigned long long cpuid:8; ++ } fields; ++ ++ unsigned long long stck; ++ } id; ++ kaddr_t caller; /* changed from void* to kaddr_t */ ++} __attribute__((packed)) debug_entry_t; ++/* typedef struct __debug_entry debug_entry_t; */ ++ ++ ++static unsigned int dbf_version; ++ ++/* struct is used to manage contents of structs debug_info from dump ++ * in lcrash ++ */ ++typedef struct debug_info_s { ++ struct debug_info_s *next; ++ struct debug_info_s *prev; ++ kaddr_t next_dbi; /* store next ptr of struct in dump */ ++ kaddr_t prev_dbi; /* store prev ptr of struct in dump */ ++ int level; ++ int nr_areas; ++ int page_order; ++ int buf_size; ++ int entry_size; ++ void **areas; /* contents of debug areas from dump */ ++ int active_area; ++ int *active_entry; /* change to uint32_t ? */ ++ debug_view_t *views[DEBUG_MAX_VIEWS]; ++ char name[DEBUG_MAX_PROCF_LEN]; ++ kaddr_t addr; ++ int pages_per_area_v2; ++ void ***areas_v2; ++} debug_info_t; ++ ++ ++/* functions to generate dbf output ++ */ ++typedef int (debug_header_proc_t) (debug_info_t* id, debug_view_t* view, ++ int area, debug_entry_t* entry, ++ char* out_buf); ++typedef int (debug_format_proc_t) (debug_info_t* id, debug_view_t* view, ++ char* out_buf, const char* in_buf); ++typedef int (debug_prolog_proc_t) (debug_info_t* id, debug_view_t* view, ++ char* out_buf); ++ ++struct debug_view_s { ++ char name[DEBUG_MAX_PROCF_LEN]; ++ debug_prolog_proc_t* prolog_proc; ++ debug_header_proc_t* header_proc; ++ debug_format_proc_t* format_proc; ++ void* private_data; ++}; ++ ++#define LCRASH_DB_VIEWS 1000 ++ ++static debug_info_t *debug_area_first = NULL; ++static debug_info_t *debug_area_last = NULL; ++static debug_view_t *debug_views[LCRASH_DB_VIEWS]; ++static int initialized = 0; ++static iconv_t ebcdic_ascii_conv = 0; ++ ++void s390dbf_usage(command_t * cmd); ++static int add_lcrash_debug_view(debug_view_t *); ++static int dbe_size = 0; ++ ++static void ++EBCASC(char *inout, size_t len) ++{ ++ iconv(ebcdic_ascii_conv, &inout, &len, &inout, &len); ++} ++ ++/* ++ * prints header for debug entry ++ */ ++static int ++dflt_header_fn(debug_info_t * id, debug_view_t *view, ++ int area, debug_entry_t * entry, char *out_buf) ++{ ++ struct timeval time_val; ++ unsigned long long time; ++ char *except_str; ++ kaddr_t caller; ++ int rc = 0; ++ char *caller_name; ++ int offset; ++ char caller_buf[30]; ++ unsigned int level; ++ syment_t *caller_sym; ++ debug_entry_t lentry; /* store byte swapped values of entry */ ++ ++ lentry.id.stck = KL_GET_UINT64(&entry->id); ++ lentry.caller = KL_GET_PTR(&entry->caller); ++ level = lentry.id.fields.level; ++ time = lentry.id.stck; ++ ++ kl_s390tod_to_timeval(time, &time_val); ++ ++ if (lentry.id.fields.exception) ++ except_str = "*"; ++ else ++ except_str = "-"; ++ caller = lentry.caller; ++ if(KL_ARCH == KL_ARCH_S390){ ++ caller &= 0x7fffffff; ++ } ++ caller_sym = kl_lkup_symaddr(caller); ++ if(caller_sym){ ++ caller_name = caller_sym->s_name; ++ offset = caller - kl_funcaddr(caller); ++ } ++ else { ++ sprintf(caller_buf, "%llx", (unsigned long long)caller); ++ caller_name = caller_buf; ++ offset = 0; ++ } ++ ++ if(KL_ARCH == KL_ARCH_S390X){ ++ rc += sprintf(out_buf, ++ "%02i %011lu:%06lu %1u %1s %02i <%20s+%04i> ", ++ area, time_val.tv_sec, time_val.tv_usec, level, ++ except_str, entry->id.fields.cpuid, caller_name, ++ offset); ++ } else { ++ rc += sprintf(out_buf, ++ "%02i %011lu:%06lu %1u %1s %02i <%-20s+%04i> ", ++ area, time_val.tv_sec, time_val.tv_usec, level, ++ except_str, lentry.id.fields.cpuid, caller_name, ++ offset); ++ } ++ return rc; ++} ++ ++/* ++ * prints debug header in raw format ++ */ ++static int ++raw_header_fn(debug_info_t * id, debug_view_t *view, ++ int area, debug_entry_t * entry, char *out_buf) ++{ ++ int rc; ++ ++ rc = sizeof(debug_entry_t); ++ if (out_buf == NULL) ++ goto out; ++ memcpy(out_buf,entry,sizeof(debug_entry_t)); ++ out: ++ return rc; ++} ++ ++/* ++ * prints debug data in raw format ++ */ ++static int ++raw_format_fn(debug_info_t * id, debug_view_t *view, ++ char *out_buf, const char *in_buf) ++{ ++ int rc; ++ ++ rc = id->buf_size; ++ if (out_buf == NULL || in_buf == NULL) ++ goto out; ++ memcpy(out_buf, in_buf, id->buf_size); ++ out: ++ return rc; ++} ++ ++/* ++ * prints debug data in hex/ascii format ++ */ ++static int ++hex_ascii_format_fn(debug_info_t * id, debug_view_t *view, ++ char *out_buf, const char *in_buf) ++{ ++ int i, rc = 0; ++ ++ if (out_buf == NULL || in_buf == NULL) { ++ rc = id->buf_size * 4 + 3; ++ goto out; ++ } ++ for (i = 0; i < id->buf_size; i++) { ++ rc += sprintf(out_buf + rc, "%02x ", ++ ((unsigned char *) in_buf)[i]); ++ } ++ rc += sprintf(out_buf + rc, "| "); ++ for (i = 0; i < id->buf_size; i++) { ++ unsigned char c = in_buf[i]; ++ if (!isprint(c)) ++ rc += sprintf(out_buf + rc, "."); ++ else ++ rc += sprintf(out_buf + rc, "%c", c); ++ } ++ rc += sprintf(out_buf + rc, "\n"); ++ out: ++ return rc; ++} ++ ++/* ++ * prints debug data in sprintf format ++ */ ++static int ++sprintf_format_fn(debug_info_t * id, debug_view_t *view, ++ char *out_buf, const char *in_buf) ++{ ++#define _BUFSIZE 1024 ++ char buf[_BUFSIZE]; ++ int i, k, rc = 0, num_longs = 0, num_used_args = 0, num_strings = 0; ++ /* use kaddr_t to store long values of 32bit and 64bit archs here */ ++ kaddr_t inbuf_cpy[DEBUG_SPRINTF_MAX_ARGS]; ++ /* store ptrs to strings to be deallocated at end of this function */ ++ uaddr_t to_dealloc[DEBUG_SPRINTF_MAX_ARGS]; ++ kaddr_t addr; ++ ++ memset(buf, 0, sizeof(buf)); ++ memset(inbuf_cpy, 0, sizeof(inbuf_cpy)); ++ memset(to_dealloc, 0, sizeof(to_dealloc)); ++ ++ if (out_buf == NULL || in_buf == NULL) { ++ rc = id->buf_size * 4 + 3; ++ goto out; ++ } ++ ++ /* get the format string into buf */ ++ addr = KL_GET_PTR((void*)in_buf); ++ GET_BLOCK(addr, _BUFSIZE, buf); ++ ++ k = 0; ++ for (i = 0; buf[i] && (buf[i] != '\n'); i++) { ++ if (buf[i] != '%') ++ continue; ++ if (k == DEBUG_SPRINTF_MAX_ARGS) { ++ fprintf(KL_ERRORFP, ++ "\nToo much parameters in sprinf view (%i)\n" ++ ,k + 1); ++ fprintf(KL_ERRORFP, "Format String: %s)\n", buf); ++ break; ++ } ++ /* for sprintf we have only unsigned long values ... */ ++ if (buf[i+1] != 's'){ ++ /* we use KL_GET_PTR here to read ulong value */ ++ addr = KL_GET_PTR((void*) in_buf + ((k + 1)* KL_NBPW)); ++ inbuf_cpy[k] = addr; ++ } else { /* ... or ptrs to strings in debug areas */ ++ inbuf_cpy[k] = (uaddr_t) malloc(_BUFSIZE); ++ to_dealloc[num_strings++] = inbuf_cpy[k]; ++ addr = KL_GET_PTR((void*) in_buf + ((k + 1)* KL_NBPW)); ++ GET_BLOCK(addr, _BUFSIZE, ++ (void*)(uaddr_t)(inbuf_cpy[k])); ++ } ++ k++; ++ } ++ ++ /* count of longs fit into one entry */ ++ num_longs = id->buf_size / KL_NBPW; /* sizeof(long); */ ++ if(num_longs < 1) /* bufsize of entry too small */ ++ goto out; ++ if(num_longs == 1) { /* no args, just print the format string */ ++ rc = sprintf(out_buf + rc, "%s", buf); ++ goto out; ++ } ++ ++ /* number of arguments used for sprintf (without the format string) */ ++ num_used_args = MIN(DEBUG_SPRINTF_MAX_ARGS, (num_longs - 1)); ++ ++ rc = sprintf(out_buf + rc, buf, (uaddr_t)(inbuf_cpy[0]), ++ (uaddr_t)(inbuf_cpy[1]), (uaddr_t)(inbuf_cpy[2]), ++ (uaddr_t)(inbuf_cpy[3]), (uaddr_t)(inbuf_cpy[4]), ++ (uaddr_t)(inbuf_cpy[5]), (uaddr_t)(inbuf_cpy[6]), ++ (uaddr_t)(inbuf_cpy[7]), (uaddr_t)(inbuf_cpy[8]), ++ (uaddr_t)(inbuf_cpy[9])); ++ out: ++ while (num_strings--){ ++ free((char*)(to_dealloc[num_strings])); ++ } ++ return rc; ++} ++ ++ ++/*********************************** ++ * functions for debug-views ++ ***********************************/ ++ ++/* ++ * prints out actual debug level ++ */ ++static int ++prolog_level_fn(debug_info_t * id, ++ debug_view_t *view, char *out_buf) ++{ ++ int rc = 0; ++ ++ if (out_buf == NULL) { ++ rc = 2; ++ goto out; ++ } ++ rc = sprintf(out_buf, "%i\n", id->level); ++ out: ++ return rc; ++} ++ ++/* ++ * prints out actual pages_per_area ++ */ ++static int ++prolog_pages_fn(debug_info_t * id, ++ debug_view_t *view, char *out_buf) ++{ ++ int rc = 0; ++ ++ if (out_buf == NULL) { ++ rc = 2; ++ goto out; ++ } ++ rc = sprintf(out_buf, "%i\n", id->pages_per_area_v2); ++ out: ++ return rc; ++} ++ ++/* ++ * prints out prolog ++ */ ++static int ++prolog_fn(debug_info_t * id, ++ debug_view_t *view, char *out_buf) ++{ ++ int rc = 0; ++ ++ rc = sprintf(out_buf, "AREA TIME LEVEL EXCEPTION CP CALLING FUNCTION" ++ " + OFFSET DATA\n===================================" ++ "=======================================\n"); ++ return rc; ++} ++ ++/* ++ * prints debug data in hex format ++ */ ++static int ++hex_format_fn(debug_info_t * id, debug_view_t *view, ++ char *out_buf, const char *in_buf) ++{ ++ int i, rc = 0; ++ ++ for (i = 0; i < id->buf_size; i++) { ++ rc += sprintf(out_buf + rc, "%02x ", ++ ((unsigned char *) in_buf)[i]); ++ } ++ rc += sprintf(out_buf + rc, "\n"); ++ return rc; ++} ++ ++/* ++ * prints debug data in ascii format ++ */ ++static int ++ascii_format_fn(debug_info_t * id, debug_view_t *view, ++ char *out_buf, const char *in_buf) ++{ ++ int i, rc = 0; ++ ++ if (out_buf == NULL || in_buf == NULL) { ++ rc = id->buf_size + 1; ++ goto out; ++ } ++ for (i = 0; i < id->buf_size; i++) { ++ unsigned char c = in_buf[i]; ++ if (!isprint(c)) ++ rc += sprintf(out_buf + rc, "."); ++ else ++ rc += sprintf(out_buf + rc, "%c", c); ++ } ++ rc += sprintf(out_buf + rc, "\n"); ++ out: ++ return rc; ++} ++ ++/* ++ * prints debug data in ebcdic format ++ */ ++static int ++ebcdic_format_fn(debug_info_t * id, debug_view_t *view, ++ char *out_buf, const char *in_buf) ++{ ++ int i, rc = 0; ++ ++ if (out_buf == NULL || in_buf == NULL) { ++ rc = id->buf_size + 1; ++ goto out; ++ } ++ for (i = 0; i < id->buf_size; i++) { ++ char c = in_buf[i]; ++ EBCASC(&c, 1); ++ if (!isprint(c)) ++ rc += sprintf(out_buf + rc, "."); ++ else ++ rc += sprintf(out_buf + rc, "%c", c); ++ } ++ rc += sprintf(out_buf + rc, "\n"); ++ out: ++ return rc; ++} ++ ++debug_view_t ascii_view = { ++ "ascii", ++ &prolog_fn, ++ &dflt_header_fn, ++ &ascii_format_fn, ++}; ++ ++debug_view_t ebcdic_view = { ++ "ebcdic", ++ &prolog_fn, ++ &dflt_header_fn, ++ &ebcdic_format_fn, ++}; ++ ++debug_view_t hex_view = { ++ "hex", ++ &prolog_fn, ++ &dflt_header_fn, ++ &hex_format_fn, ++}; ++ ++debug_view_t level_view = { ++ "level", ++ &prolog_level_fn, ++ NULL, ++ NULL, ++}; ++ ++debug_view_t pages_view = { ++ "pages", ++ &prolog_pages_fn, ++ NULL, ++ NULL, ++}; ++ ++debug_view_t raw_view = { ++ "raw", ++ NULL, ++ &raw_header_fn, ++ &raw_format_fn, ++}; ++ ++debug_view_t hex_ascii_view = { ++ "hex_ascii", ++ &prolog_fn, ++ &dflt_header_fn, ++ &hex_ascii_format_fn, ++}; ++ ++debug_view_t sprintf_view = { ++ "sprintf", ++ &prolog_fn, ++ &dflt_header_fn, ++ &sprintf_format_fn, ++}; ++ ++ ++static debug_entry_t * ++debug_find_oldest_entry(debug_entry_t *entries, int num, int entry_size) ++{ ++ debug_entry_t *result, *current; ++ int i; ++ uint64_t clock1, clock2; ++ ++ result = entries; ++ current = entries; ++ for (i=0; i < num; i++) { ++ if (current->id.stck == 0) ++ break; ++ clock1 = current->id.fields.clock; ++ clock2 = result->id.fields.clock; ++ clock1 = KL_GET_UINT64(&clock1); ++ clock2 = KL_GET_UINT64(&clock2); ++ if (clock1 < clock2) ++ result = current; ++ current = (debug_entry_t *) ((char *) current + entry_size); ++ } ++ return result; ++} ++ ++ ++/* ++ * debug_format_output: ++ * - calls prolog, header and format functions of view to format output ++ */ ++static int ++debug_format_output_v1(debug_info_t * debug_area, debug_view_t *view, ++ FILE * ofp) ++{ ++ int i, j, len; ++ int nr_of_entries; ++ debug_entry_t *act_entry, *last_entry; ++ char *act_entry_data; ++ char buf[2048]; ++ ++ /* print prolog */ ++ if (view->prolog_proc) { ++ len = view->prolog_proc(debug_area, view, buf); ++ fwrite(buf,len, 1, ofp); ++ memset(buf, 0, 2048); ++ } ++ /* print debug records */ ++ if (!(view->format_proc) && !(view->header_proc)) ++ goto out; ++ if(debug_area->entry_size <= 0){ ++ fprintf(ofp, "Invalid entry_size: %i\n",debug_area->entry_size); ++ goto out; ++ } ++ nr_of_entries = (PAGE_SIZE << debug_area->page_order) / debug_area->entry_size; ++ for (i = 0; i < debug_area->nr_areas; i++) { ++ act_entry = debug_find_oldest_entry(debug_area->areas[i], ++ nr_of_entries, ++ debug_area->entry_size); ++ last_entry = (debug_entry_t *) ((char *) debug_area->areas[i] + ++ (PAGE_SIZE << debug_area->page_order) - ++ debug_area->entry_size); ++ for (j = 0; j < nr_of_entries; j++) { ++ act_entry_data = (char*)act_entry + dbe_size; ++ if (act_entry->id.stck == 0) ++ break; /* empty entry */ ++ if (view->header_proc) { ++ len = view->header_proc(debug_area, view, i, ++ act_entry, buf); ++ fwrite(buf,len, 1, ofp); ++ memset(buf, 0, 2048); ++ } ++ if (view->format_proc) { ++ len = view->format_proc(debug_area, view, ++ buf, act_entry_data); ++ fwrite(buf,len, 1, ofp); ++ memset(buf, 0, 2048); ++ } ++ act_entry = ++ (debug_entry_t *) (((char *) act_entry) + ++ debug_area->entry_size); ++ if (act_entry > last_entry) ++ act_entry = debug_area->areas[i]; ++ } ++ } ++ out: ++ return 1; ++} ++ ++/* ++ * debug_format_output_v2: ++ * - calls prolog, header and format functions of view to format output ++ */ ++static int ++debug_format_output_v2(debug_info_t * debug_area, ++ debug_view_t *view, FILE * ofp) ++{ ++ int i, j, k, len; ++ debug_entry_t *act_entry; ++ char *act_entry_data; ++ char buf[2048]; ++ ++ /* print prolog */ ++ if (view->prolog_proc) { ++ len = view->prolog_proc(debug_area, view, buf); ++ fwrite(buf,len, 1, ofp); ++ memset(buf, 0, 2048); ++ } ++ /* print debug records */ ++ if (!(view->format_proc) && !(view->header_proc)) ++ goto out; ++ if(debug_area->entry_size <= 0){ ++ fprintf(ofp, "Invalid entry_size: %i\n",debug_area->entry_size); ++ goto out; ++ } ++ for (i = 0; i < debug_area->nr_areas; i++) { ++ int nr_entries_per_page = PAGE_SIZE/debug_area->entry_size; ++ for (j = 0; j < debug_area->pages_per_area_v2; j++) { ++ act_entry = debug_area->areas_v2[i][j]; ++ for (k = 0; k < nr_entries_per_page; k++) { ++ act_entry_data = (char*)act_entry + dbe_size; ++ if (act_entry->id.stck == 0) ++ break; /* empty entry */ ++ if (view->header_proc) { ++ len = view->header_proc(debug_area, ++ view, i, act_entry, buf); ++ fwrite(buf,len, 1, ofp); ++ memset(buf, 0, 2048); ++ } ++ if (view->format_proc) { ++ len = view->format_proc(debug_area, ++ view, buf, act_entry_data); ++ fwrite(buf,len, 1, ofp); ++ memset(buf, 0, 2048); ++ } ++ act_entry = (debug_entry_t *) (((char *) ++ act_entry) + debug_area->entry_size); ++ } ++ } ++ } ++out: ++ return 1; ++} ++ ++static debug_info_t * ++find_debug_area(const char *area_name) ++{ ++ debug_info_t* act_debug_info = debug_area_first; ++ while(act_debug_info != NULL){ ++ if (strcmp(act_debug_info->name, area_name) == 0) ++ return act_debug_info; ++ act_debug_info = act_debug_info->next; ++ } ++ return NULL; ++} ++ ++static void ++dbf_init(void) ++{ ++ if (!initialized) { ++ if(dbf_version >= DBF_VERSION_V2) ++ add_lcrash_debug_view(&pages_view); ++ add_lcrash_debug_view(&ascii_view); ++ add_lcrash_debug_view(&level_view); ++ add_lcrash_debug_view(&ebcdic_view); ++ add_lcrash_debug_view(&hex_view); ++ add_lcrash_debug_view(&hex_ascii_view); ++ add_lcrash_debug_view(&sprintf_view); ++ add_lcrash_debug_view(&raw_view); ++ ebcdic_ascii_conv = iconv_open("ISO-8859-1", "EBCDIC-US"); ++ initialized = 1; ++ } ++} ++ ++static debug_view_t* ++get_debug_view(kaddr_t addr) ++{ ++ void* k_debug_view; ++ int k_debug_view_size; ++ debug_view_t* rc; ++ ++ rc = (debug_view_t*)malloc(sizeof(debug_view_t)); ++ memset(rc, 0, sizeof(debug_view_t)); ++ ++ k_debug_view_size = kl_struct_len("debug_view"); ++ k_debug_view = malloc(k_debug_view_size); ++ GET_BLOCK(addr, k_debug_view_size, k_debug_view); ++ strncpy(rc->name,K_PTR(k_debug_view,"debug_view","name"), ++ DEBUG_MAX_PROCF_LEN); ++ ++ free(k_debug_view); ++ return rc; ++} ++ ++static void ++free_debug_view(debug_view_t* view) ++{ ++ if(view) ++ free(view); ++} ++ ++static void ++debug_get_areas_v1(debug_info_t* db_info, void* k_dbi) ++{ ++ kaddr_t mem_pos; ++ kaddr_t dbe_addr; ++ int area_size, i; ++ ++ /* get areas */ ++ /* place to hold ptrs to debug areas in lcrash */ ++ area_size = PAGE_SIZE << db_info->page_order; ++ db_info->areas = (void**)malloc(db_info->nr_areas * sizeof(void *)); ++ memset(db_info->areas, 0, db_info->nr_areas * sizeof(void *)); ++ mem_pos = (kaddr_t) KL_UINT(k_dbi,"debug_info","areas"); ++ for (i = 0; i < db_info->nr_areas; i++) { ++ dbe_addr = KL_VREAD_PTR(mem_pos); ++ db_info->areas[i] = (debug_entry_t *) malloc(area_size); ++ /* read raw data for debug area */ ++ GET_BLOCK(dbe_addr, area_size, db_info->areas[i]); ++ mem_pos += KL_NBPW; ++ } ++} ++ ++static void ++debug_get_areas_v2(debug_info_t* db_info, void* k_dbi) ++{ ++ kaddr_t area_ptr; ++ kaddr_t page_array_ptr; ++ kaddr_t page_ptr; ++ int i,j; ++ db_info->areas_v2=(void***)malloc(db_info->nr_areas * sizeof(void **)); ++ area_ptr = (kaddr_t) KL_UINT(k_dbi,"debug_info","areas"); ++ for (i = 0; i < db_info->nr_areas; i++) { ++ db_info->areas_v2[i] = (void**)malloc(db_info->pages_per_area_v2 ++ * sizeof(void*)); ++ page_array_ptr = KL_VREAD_PTR(area_ptr); ++ for(j=0; j < db_info->pages_per_area_v2; j++) { ++ page_ptr = KL_VREAD_PTR(page_array_ptr); ++ db_info->areas_v2[i][j] = (void*)malloc(PAGE_SIZE); ++ /* read raw data for debug area */ ++ GET_BLOCK(page_ptr, PAGE_SIZE, db_info->areas_v2[i][j]); ++ page_array_ptr += KL_NBPW; ++ } ++ area_ptr += KL_NBPW; ++ } ++} ++ ++static debug_info_t* ++get_debug_info(kaddr_t addr,int get_areas) ++{ ++ void *k_dbi; ++ kaddr_t mem_pos; ++ kaddr_t view_addr; ++ debug_info_t* db_info; ++ int i; ++ int dbi_size; ++ ++ /* get sizes of kernel structures */ ++ if(!(dbi_size = kl_struct_len("debug_info"))){ ++ fprintf (KL_ERRORFP, ++ "Could not determine sizeof(struct debug_info)\n"); ++ return(NULL); ++ } ++ if(!(dbe_size = kl_struct_len("__debug_entry"))){ ++ fprintf(KL_ERRORFP, ++ "Could not determine sizeof(struct __debug_entry)\n"); ++ return(NULL); ++ } ++ ++ /* get kernel debug_info structure */ ++ k_dbi = malloc(dbi_size); ++ GET_BLOCK(addr, dbi_size, k_dbi); ++ ++ db_info = (debug_info_t*)malloc(sizeof(debug_info_t)); ++ memset(db_info, 0, sizeof(debug_info_t)); ++ ++ /* copy members */ ++ db_info->level = KL_INT(k_dbi,"debug_info","level"); ++ db_info->nr_areas = KL_INT(k_dbi,"debug_info","nr_areas"); ++ db_info->pages_per_area_v2= KL_INT(k_dbi,"debug_info","pages_per_area"); ++ db_info->page_order = KL_INT(k_dbi,"debug_info","page_order"); ++ db_info->buf_size = KL_INT(k_dbi,"debug_info","buf_size"); ++ db_info->entry_size = KL_INT(k_dbi,"debug_info","entry_size"); ++ db_info->next_dbi = KL_UINT(k_dbi,"debug_info","next"); ++ db_info->prev_dbi = KL_UINT(k_dbi,"debug_info","prev"); ++ db_info->addr = addr; ++ strncpy(db_info->name,K_PTR(k_dbi,"debug_info","name"), ++ DEBUG_MAX_PROCF_LEN); ++ ++ ++ if(get_areas){ ++ if(dbf_version == DBF_VERSION_V1) ++ debug_get_areas_v1(db_info,k_dbi); ++ else ++ debug_get_areas_v2(db_info,k_dbi); ++ } else { ++ db_info->areas = NULL; ++ } ++ ++ /* get views */ ++ mem_pos = (uaddr_t) K_PTR(k_dbi,"debug_info","views"); ++ memset(&db_info->views, 0, DEBUG_MAX_VIEWS * sizeof(void*)); ++ for (i = 0; i < DEBUG_MAX_VIEWS; i++) { ++ view_addr = KL_GET_PTR((void*)(uaddr_t)mem_pos); ++ if(view_addr == 0){ ++ break; ++ } else { ++ db_info->views[i] = get_debug_view(view_addr); ++ } ++ mem_pos += KL_NBPW; ++ } ++ free(k_dbi); ++ return db_info; ++} ++ ++static void ++free_debug_info_v1(debug_info_t * db_info) ++{ ++ int i; ++ if(db_info->areas){ ++ for (i = 0; i < db_info->nr_areas; i++) { ++ free(db_info->areas[i]); ++ } ++ } ++ for (i = 0; i < DEBUG_MAX_VIEWS; i++) { ++ free_debug_view(db_info->views[i]); ++ } ++ free(db_info->areas); ++ free(db_info); ++} ++ ++static void ++free_debug_info_v2(debug_info_t * db_info) ++{ ++ int i,j; ++ if(db_info->areas) { ++ for (i = 0; i < db_info->nr_areas; i++) { ++ for(j = 0; j < db_info->pages_per_area_v2; j++) { ++ free(db_info->areas_v2[i][j]); ++ } ++ free(db_info->areas[i]); ++ } ++ free(db_info->areas); ++ db_info->areas = NULL; ++ } ++ for (i = 0; i < DEBUG_MAX_VIEWS; i++) { ++ free_debug_view(db_info->views[i]); ++ } ++ free(db_info); ++} ++ ++static int ++get_debug_areas(void) ++{ ++ kaddr_t act_debug_area; ++ syment_t *debug_sym; ++ debug_info_t *act_debug_area_cpy; ++ ++ if(!(debug_sym = kl_lkup_symname("debug_area_first"))){ ++ printf("Did not find debug_areas"); ++ return -1; ++ } ++ act_debug_area = KL_VREAD_PTR(debug_sym->s_addr); ++ while(act_debug_area != 0){ ++ act_debug_area_cpy = get_debug_info(act_debug_area,0); ++ act_debug_area = act_debug_area_cpy->next_dbi; ++ if(debug_area_first == NULL){ ++ debug_area_first = act_debug_area_cpy; ++ } else { ++ debug_area_last->next = act_debug_area_cpy; ++ } ++ debug_area_last = act_debug_area_cpy; ++ } ++ return 0; ++} ++ ++static void ++free_debug_areas(void) ++{ ++ debug_info_t* next; ++ debug_info_t* act_debug_info = debug_area_first; ++ ++ while(act_debug_info != NULL){ ++ next = act_debug_info->next; ++ if(dbf_version == DBF_VERSION_V1) ++ free_debug_info_v1(act_debug_info); ++ else ++ free_debug_info_v2(act_debug_info); ++ act_debug_info = next; ++ } ++ ++ debug_area_first = NULL; ++ debug_area_last = NULL; ++} ++ ++static debug_view_t * ++find_lcrash_debug_view(const char *name) ++{ ++ int i; ++ for (i = 0; (i < LCRASH_DB_VIEWS) && (debug_views[i] != NULL); i++) { ++ if (strcmp(debug_views[i]->name, name) == 0) ++ return debug_views[i]; ++ } ++ return NULL; ++} ++ ++static void ++print_lcrash_debug_views(FILE * ofp) ++{ ++ int i; ++ fprintf(ofp, "REGISTERED VIEWS\n"); ++ fprintf(ofp, "=====================\n"); ++ for (i = 0; i < LCRASH_DB_VIEWS; i++) { ++ if (debug_views[i] == NULL) { ++ return; ++ } ++ fprintf(ofp, " - %s\n", debug_views[i]->name); ++ } ++} ++ ++static int ++add_lcrash_debug_view(debug_view_t *view) ++{ ++ int i; ++ for (i = 0; i < LCRASH_DB_VIEWS; i++) { ++ if (debug_views[i] == NULL) { ++ debug_views[i] = view; ++ return 0; ++ } ++ if (strcmp(debug_views[i]->name, view->name) == 0) ++ return -1; ++ } ++ return -1; ++} ++ ++static int ++list_one_view(char *area_name, char *view_name, command_t * cmd) ++{ ++ debug_info_t *db_info; ++ debug_view_t *db_view; ++ ++ if ((db_info = find_debug_area(area_name)) == NULL) { ++ fprintf(cmd->efp, "Debug log '%s' not found!\n", area_name); ++ return -1; ++ } ++ ++ db_info = get_debug_info(db_info->addr,1); ++ ++ if ((db_view = find_lcrash_debug_view(view_name)) == NULL) { ++ fprintf(cmd->efp, "View '%s' not registered!\n", view_name); ++ return -1; ++ } ++ if(dbf_version == DBF_VERSION_V1){ ++ debug_format_output_v1(db_info, db_view, cmd->ofp); ++ free_debug_info_v1(db_info); ++ } else { ++ debug_format_output_v2(db_info, db_view, cmd->ofp); ++ free_debug_info_v2(db_info); ++ } ++ return 0; ++} ++ ++static int ++list_areas(FILE * ofp) ++{ ++ debug_info_t* act_debug_info = debug_area_first; ++ fprintf(ofp, "Debug Logs:\n"); ++ fprintf(ofp, "==================\n"); ++ while(act_debug_info != NULL){ ++ fprintf(ofp, " - %s\n", act_debug_info->name); ++ act_debug_info = act_debug_info->next; ++ } ++ return 0; ++} ++ ++static int ++list_one_area(const char *area_name, command_t * cmd) ++{ ++ debug_info_t *db_info; ++ int i; ++ if ((db_info = find_debug_area(area_name)) == NULL) { ++ fprintf(cmd->efp, "Debug log '%s' not found!\n", area_name); ++ return -1; ++ } ++ fprintf(cmd->ofp, "INSTALLED VIEWS FOR '%s':\n", area_name); ++ fprintf(cmd->ofp, "================================================" ++ "==============================\n"); ++ for (i = 0; i < DEBUG_MAX_VIEWS; i++) { ++ if (db_info->views[i] != NULL) { ++ fprintf(cmd->ofp, " - %s ", db_info->views[i]->name); ++ if (find_lcrash_debug_view(db_info->views[i]->name)) ++ fprintf(cmd->ofp, "(available)\n"); ++ else ++ fprintf(cmd->ofp, "(not available)\n"); ++ } ++ } ++ fprintf(cmd->ofp, "=================================================" ++ "=============================\n"); ++ return 0; ++} ++ ++#ifdef DBF_DYNAMIC_VIEWS ++static int ++load_debug_view(const char *path, command_t * cmd) ++{ ++ void *library; ++ const char *error; ++ debug_view_t *(*view_init_func) (void); ++ ++ library = dlopen(path, RTLD_LAZY); ++ if (library == NULL) { ++ fprintf(cmd->efp, "Could not open %s: %s\n", path, dlerror()); ++ return (1); ++ } ++ ++ dlerror(); ++ ++ view_init_func = dlsym(library, "debug_view_init"); ++ error = dlerror(); ++ ++ if (error) { ++ fprintf(stderr, "could not find debug_view_init(): %s\n", ++ error); ++ exit(1); ++ } ++ ++ add_lcrash_debug_view((*view_init_func) ()); ++ ++ fprintf(cmd->ofp, "view %s loaded\n", path); ++ fflush(stdout); ++ return 0; ++} ++#endif ++ ++/* ++ * s390dbf_cmd() -- Run the 's390dbf' command. ++ */ ++static int ++s390dbf_cmd(command_t * cmd) ++{ ++ syment_t *dbf_version_sym; ++ int rc = 0; ++ ++ /* check version */ ++ ++ if(!(dbf_version_sym = kl_lkup_symname("debug_feature_version"))){ ++ fprintf(KL_ERRORFP, ++ "Could not determine debug_feature_version\n"); ++ return -1; ++ } ++ ++ dbf_version = KL_VREAD_UINT32(dbf_version_sym->s_addr); ++ ++ if ((dbf_version != DBF_VERSION_V1) && (dbf_version != DBF_VERSION_V2)){ ++ fprintf(cmd->efp,"lcrash does not support the" ++ " debug feature version of the dump kernel:\n"); ++ fprintf(cmd->efp,"DUMP: %i SUPPORTED: %i and %i\n", ++ dbf_version, DBF_VERSION_V1, DBF_VERSION_V2); ++ return -1; ++ } ++ ++ dbf_init(); ++ ++ if (cmd->flags & C_ALL) { ++ return (0); ++ } ++#ifdef DBF_DYNAMIC_VIEWS ++ if (cmd->flags & LOAD_FLAG) { ++ printf("loading: %s\n", cmd->args[0]); ++ return (load_debug_view(cmd->args[0], cmd)); ++ } ++#endif ++ if (cmd->flags & VIEWS_FLAG) { ++ print_lcrash_debug_views(cmd->ofp); ++ return (0); ++ } ++ if (cmd->nargs > 2) { ++ s390dbf_usage(cmd); ++ return (1); ++ } ++ ++ if(get_debug_areas() == -1) ++ return -1; ++ ++ switch (cmd->nargs) { ++ case 0: ++ rc = list_areas(cmd->ofp); ++ break; ++ case 1: ++ rc = list_one_area(cmd->args[0], cmd); ++ break; ++ case 2: ++ rc = list_one_view(cmd->args[0], cmd->args[1], cmd); ++ break; ++ } ++ ++ free_debug_areas(); ++ ++ return rc; ++} ++ ++#define _S390DBF_USAGE " [-v] [debug log] [debug view]" ++ ++/* ++ * s390dbf_usage() -- Print the usage string for the 's390dbf' command. ++ */ ++void ++s390dbf_usage(command_t * cmd) ++{ ++ CMD_USAGE(cmd, _S390DBF_USAGE); ++} ++ ++/* ++ * s390 debug feature command for crash ++ */ ++ ++char *help_s390dbf[] = { ++ "s390dbf", ++ "s390dbf prints out debug feature logs", ++ "[-v] [debug_log] [debug_log view]", ++ "", ++ "Display Debug logs:", ++ " + If called without parameters, all active debug logs are listed.", ++ " + If called with '-v', all debug views which are available to", ++ " 'crash' are listed", ++ " + If called with the name of a debug log, all debug-views for which", ++ " the debug-log has registered are listed. It is possible thatsome", ++ " of the debug views are not available to 'crash'.", ++ " + If called with the name of a debug-log and an available viewname,", ++ " the specified view is printed.", ++ NULL ++}; ++ ++void cmd_s390dbf() ++{ ++ int i,c; ++ ++ command_t cmd = { ++ .ofp = stdout, ++ .efp = stderr, ++ .cmdstr = "s390dbf", ++ .command = "s390dbf", ++ }; ++ ++ cmd.nargs=argcnt - 1; ++ for (i=1; i < argcnt; i++) ++ cmd.args[i-1] = args[i]; ++ ++ while ((c = getopt(argcnt, args, "v")) != EOF) { ++ switch(c) { ++ case 'v': ++ cmd.flags |= VIEWS_FLAG; ++ break; ++ default: ++ s390dbf_usage(&cmd); ++ return; ++ } ++ } ++ s390dbf_cmd(&cmd); ++} ++ ++#endif ++ +--- crash/vas_crash.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/vas_crash.h 2006-10-11 09:14:36.000000000 -0400 +@@ -1,8 +1,8 @@ + /* vas_crash.h - kernel crash dump file format (on swap) + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 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 +@@ -19,7 +19,7 @@ + */ + + #include +-#include ++//#include + + void save_core(void); + +--- crash/global_data.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/global_data.c 2006-11-21 11:32:04.000000000 -0500 +@@ -68,7 +68,7 @@ + * To add a new command, declare it in defs.h and enter it in this table. + */ + +-struct command_table_entry base_command_table[] = { ++struct command_table_entry linux_command_table[] = { + {"*", cmd_pointer, help_pointer, 0}, + {"alias", cmd_alias, help_alias, 0}, + {"ascii", cmd_ascii, help_ascii, 0}, +@@ -117,6 +117,9 @@ + {"waitq", cmd_waitq, help_waitq, REFRESH_TASK_TABLE}, + {"whatis", cmd_whatis, help_whatis, 0}, + {"wr", cmd_wr, help_wr, 0}, ++#if defined(S390) || defined(S390X) ++ {"s390dbf", cmd_s390dbf, help_s390dbf, 0}, ++#endif + {(char *)NULL} + }; + +--- crash/crash.8.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/crash.8 2008-04-16 10:21:08.000000000 -0400 +@@ -5,7 +5,7 @@ + .TH CRASH 8 + .SH NAME + crash \- Analyze Linux crash data or a live system +-.SH SYNAPSIS ++.SH SYNOPSIS + .B crash + [ + .B -h +@@ -42,9 +42,13 @@ + is a tool for interactively analyzing the state of the Linux system + while it is running, or after a kernel crash has occurred and a + core dump has been created by the Red Hat +-.I netdump +-facility. It is loosely based on the SVR4 UNIX crash +-command, but has been signficantly enhanced ++.I netdump, ++.I diskdump, ++.I kdump, ++or ++.I xendump ++facilities. It is loosely based on the SVR4 UNIX crash ++command, but has been significantly enhanced + by completely merging it with the + .I gdb + debugger. The marriage of the two effectively combines the +@@ -207,15 +211,15 @@ + .I dis + disassembles memory, either entire kernel functions, from a + location for a specified number of instructions, or from the start of a +-fuction up to a specified memory location. ++function up to a specified memory location. + .TP + .I eval + evalues an expression or numeric type and displays the result +-in hexidecimal, decimal, octal and binary. ++in hexadecimal, decimal, octal and binary. + .TP + .I exit + causes +-.I crash ++.B crash + to exit. + .TP + .I extend +@@ -230,7 +234,7 @@ + in the system. + .TP + .I fuser +-displays the tasks using the specifed file or socket. ++displays the tasks using the specified file or socket. + .TP + .I gdb + passes its argument to the underlying +@@ -274,7 +278,7 @@ + display various network related data. + .TP + .I p +-passes its argumnts to the ++passes its arguments to the + .I gdb + "print" command for evaluation and display. + .TP +@@ -358,14 +362,89 @@ + .I whatis + displays the definition of structures, unions, typedefs or + text/data symbols. ++.TP + .I wr + modifies the contents of memory. When writing to memory on + a live system, this command should obviously be used with great care. ++.SH FILES ++.TP ++.I .crashrc ++Initialization commands. The file can be located in the user's ++.B HOME ++directory and/or the current directory. Commands found in the ++.I .crashrc ++file in the ++.B HOME ++directory are executed before those in the current directory's ++.I .crashrc ++file. ++.SH ENVIRONMENT ++.TP ++.B EDITOR ++Command input is read using ++.BR readline(3). ++If ++.B EDITOR ++is set to ++.I emacs ++or ++.I vi ++then suitable keybindings are used. If ++.B EDITOR ++is not set, then ++.I vi ++is used. This can be overridden by ++.B set vi ++or ++.B set emacs ++commands located in a ++.IR .crashrc ++file, or by entering ++.B -e emacs ++on the ++.B crash ++command line. ++.TP ++.B CRASHPAGER ++If ++.B CRASHPAGER ++is set, its value is used as the name of the program to which command output will be sent. ++If not, then command output is sent to ++.B /usr/bin/less -E -X ++by default. ++.SH NOTES ++.PP ++If ++.B crash ++does not work, look for a newer version: kernel evolution frequently makes ++.B crash ++updates necessary. ++.PP ++The command ++.B set scroll off ++will cause output to be sent directly to ++the terminal rather than through a paging program. This is useful, ++for example, if you are running ++.B crash ++in a window of ++.BR emacs . + .SH AUTHOR + Dave Anderson wrote +-.B Crash ++.B crash + .TP + Jay Fenlason wrote this man page. + .SH "SEE ALSO" +-netdump(8) +-gdb(1) ++.PP ++The ++.I help ++command within ++.B crash ++provides more complete and accurate documentation than this man page. ++.PP ++.I http://people.redhat.com/anderson ++- the home page of the ++.B crash ++utility. ++.PP ++.BR netdump (8), ++.BR gdb (1) +--- crash/va_server.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/va_server.c 2006-10-11 09:14:36.000000000 -0400 +@@ -1,8 +1,8 @@ + /* va_server.c - kernel crash dump file translation library + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 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 +@@ -57,13 +57,15 @@ + + extern int monitor_memory(long *, long *, long *, long *); + +-int Page_Size = PAGE_SIZE; /* temporary setting until disk header is read */ ++int Page_Size; + ulong vas_debug = 0; + + extern void *malloc(size_t); + + int va_server_init(char *crash_file, u_long *start, u_long *end, u_long *stride) + { ++ Page_Size = getpagesize(); /* temporary setting until disk header is read */ ++ + if(read_map(crash_file)) { + if(va_server_init_v1(crash_file, start, end, stride)) + return -1; +--- crash/symbols.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/symbols.c 2009-01-29 15:33:13.000000000 -0500 +@@ -1,8 +1,8 @@ + /* symbols.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -21,6 +21,8 @@ + + static void store_symbols(bfd *, int, void *, long, unsigned int); + static void store_sysmap_symbols(void); ++static ulong relocate(ulong, char *, int); ++static int relocate_force(ulong, char *); + static void strip_module_symbol_end(char *s); + static int compare_syms(const void *, const void *); + static int compare_mods(const void *, const void *); +@@ -36,7 +38,9 @@ + static int load_module_index(struct syment *); + static void section_header_info(bfd *, asection *, void *); + static void store_section_data(struct load_module *, bfd *, asection *); +-static void calculate_load_order(struct load_module *, bfd *); ++static void calculate_load_order_v1(struct load_module *, bfd *); ++static void calculate_load_order_v2(struct load_module *, bfd *, int, ++ void *, long, unsigned int); + static void check_insmod_builtin(struct load_module *, int, ulong *); + static int is_insmod_builtin(struct load_module *, struct syment *); + struct load_module; +@@ -45,6 +49,7 @@ + static long rodata_search(ulong *, ulong); + static int ascii_long(ulong word); + static int is_bfd_format(char *); ++static int is_binary_stripped(char *); + static int namespace_ctl(int, struct namespace *, void *, void *); + static void symval_hash_init(void); + static struct syment *symval_hash_search(ulong); +@@ -61,12 +66,16 @@ + struct elf_common; + static void Elf32_Sym_to_common(Elf32_Sym *, struct elf_common *); + static void Elf64_Sym_to_common(Elf64_Sym *, struct elf_common *); ++static void cmd_datatype_common(ulong); ++static int display_per_cpu_info(struct syment *); + + + #define KERNEL_SECTIONS (void *)(1) + #define MODULE_SECTIONS (void *)(2) + #define VERIFY_SECTIONS (void *)(3) + ++#define EV_DWARFEXTRACT 101010101 ++ + #define PARSE_FOR_DATA (1) + #define PARSE_FOR_DECLARATION (2) + static void parse_for_member(struct datatype_member *, ulong); +@@ -96,6 +105,7 @@ + #define SHOW_OFFSET (0x10000) + #define IN_UNION (0x20000) + #define IN_STRUCT (0x40000) ++#define DATATYPE_QUERY (0x80000) + + #define INTEGER_TYPE (UINT8|INT8|UINT16|INT16|UINT32|INT32|UINT64|INT64) + +@@ -110,6 +120,7 @@ + static void dump_datatype_member(FILE *, struct datatype_member *); + static void dump_datatype_flags(ulong, FILE *); + static void dump_enumerator_list(char *); ++static long anon_member_offset(char *, char *); + static int gdb_whatis(char *); + static void do_datatype_declaration(struct datatype_member *, ulong); + +@@ -139,6 +150,12 @@ + if (!bfd_check_format_matches(st->bfd, bfd_object, &matching)) + error(FATAL, "cannot determine object file format: %s\n", + pc->namelist); ++ /* ++ * Check whether the namelist is a kerntypes file built by ++ * dwarfextract, which places a magic number in e_version. ++ */ ++ if (file_elf_version(pc->namelist) == EV_DWARFEXTRACT) ++ pc->flags |= KERNTYPES; + + if (pc->flags & SYSMAP) { + bfd_map_over_sections(st->bfd, section_header_info, +@@ -153,13 +170,16 @@ + } + store_sysmap_symbols(); + return; +- } ++ } else if (LKCD_KERNTYPES()) ++ error(FATAL, "%s: use of kerntypes requires a system map\n", ++ pc->namelist); + + /* + * Pull a bait-and-switch on st->bfd if we've got a separate +- * .gnu_debuglink file that matches the CRC. ++ * .gnu_debuglink file that matches the CRC. Not done for kerntypes. + */ +- if (!(bfd_get_file_flags(st->bfd) & HAS_SYMS)) { ++ if (!(LKCD_KERNTYPES()) && ++ !(bfd_get_file_flags(st->bfd) & HAS_SYMS)) { + if (!check_gnu_debuglink(st->bfd)) + no_debugging_data(FATAL); + } +@@ -471,6 +491,11 @@ + kt->stext_init = (ulong)bfd_get_section_vma(st->bfd, section); + kt->etext_init = kt->stext_init + + (ulong)bfd_section_size(st->bfd, section); ++ ++ if (kt->relocate) { ++ kt->stext_init -= kt->relocate; ++ kt->etext_init -= kt->relocate; ++ } + } + + /* +@@ -486,6 +511,7 @@ + bfd_byte *from, *fromend; + symbol_info syminfo; + struct syment *sp; ++ int first; + + if ((store = bfd_make_empty_symbol(abfd)) == NULL) + error(FATAL, "bfd_make_empty_symbol() failed\n"); +@@ -505,6 +531,13 @@ + st->symcnt = 0; + sp = st->symtable; + ++ if (machine_type("X86")) { ++ if (!(kt->flags & RELOC_SET)) ++ kt->flags |= RELOC_FORCE; ++ } else ++ kt->flags &= ~RELOC_SET; ++ ++ first = 0; + from = (bfd_byte *) minisyms; + fromend = from + symcount * size; + for (; from < fromend; from += size) +@@ -516,7 +549,11 @@ + bfd_get_symbol_info(abfd, sym, &syminfo); + if (machdep->verify_symbol(syminfo.name, syminfo.value, + syminfo.type)) { +- sp->value = syminfo.value; ++ if (kt->flags & (RELOC_SET|RELOC_FORCE)) ++ sp->value = relocate(syminfo.value, ++ (char *)syminfo.name, !(first++)); ++ else ++ sp->value = syminfo.value; + sp->type = syminfo.type; + namespace_ctl(NAMESPACE_INSTALL, &st->namespace, + sp, (char *)syminfo.name); +@@ -540,7 +577,7 @@ + static void + store_sysmap_symbols(void) + { +- int c; ++ int c, first; + long symcount; + char buf[BUFSIZE]; + FILE *map; +@@ -564,6 +601,10 @@ + error(FATAL, "symbol table namespace malloc: %s\n", + strerror(errno)); + ++ if (!machine_type("X86")) ++ kt->flags &= ~RELOC_SET; ++ ++ first = 0; + st->syment_size = symcount * sizeof(struct syment); + st->symcnt = 0; + sp = st->symtable; +@@ -580,7 +621,11 @@ + + if (machdep->verify_symbol(syment.name, syment.value, + syment.type)) { +- sp->value = syment.value; ++ if (kt->flags & RELOC_SET) ++ sp->value = relocate(syment.value, ++ syment.name, !(first++)); ++ else ++ sp->value = syment.value; + sp->type = syment.type; + namespace_ctl(NAMESPACE_INSTALL, &st->namespace, + sp, syment.name); +@@ -603,6 +648,96 @@ + } + + /* ++ * Handle x86 kernels configured such that the vmlinux symbols ++ * are not as loaded into the kernel (not unity-mapped). ++ */ ++static ulong ++relocate(ulong symval, char *symname, int first_symbol) ++{ ++ switch (kt->flags & (RELOC_SET|RELOC_FORCE)) ++ { ++ case RELOC_SET: ++ break; ++ ++ case RELOC_FORCE: ++ if (first_symbol && !relocate_force(symval, symname)) ++ kt->flags &= ~RELOC_FORCE; ++ break; ++ } ++ ++ return (symval - kt->relocate); ++} ++ ++/* ++ * If no --reloc argument was passed, try to figure it out ++ * by comparing the first vmlinux kernel symbol with the ++ * first /proc/kallsyms symbol. (should be "_text") ++ * ++ * Live system only (at least for now). ++ */ ++static int ++relocate_force(ulong symval, char *symname) ++{ ++ FILE *kp; ++ char buf[BUFSIZE]; ++ char *kallsyms[MAXARGS]; ++ ulong first; ++ ++ if (!ACTIVE() || !file_exists("/proc/kallsyms", NULL)) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "cannot determine relocation value: %s\n", ++ !ACTIVE() ? "not a live system" : ++ "/proc/kallsyms does not exist"); ++ return FALSE; ++ } ++ ++ if ((kp = fopen("/proc/kallsyms", "r")) == NULL) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "cannot open /proc/kallsyms to determine relocation\n"); ++ return FALSE; ++ } ++ ++ if (!fgets(buf, BUFSIZE, kp) || ++ (parse_line(buf, kallsyms) != 3) || ++ !hexadecimal(kallsyms[0], 0)) { ++ fclose(kp); ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "malformed /proc/kallsyms: cannot determine relocation value\n"); ++ return FALSE; ++ } ++ fclose(kp); ++ ++ first = htol(kallsyms[0], RETURN_ON_ERROR, NULL); ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "RELOCATE: %s @ %lx %s\n" ++ " %s @ %lx /proc/kallsyms\n", ++ symname, symval, pc->namelist, ++ kallsyms[2], first); ++ ++ /* ++ * If the symbols match and have different values, ++ * force the relocation. ++ */ ++ if (STREQ(symname, kallsyms[2])) { ++ if (symval > first) { ++ kt->relocate = symval - first; ++ return TRUE; ++ } ++ } ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "cannot determine relocation value from first symbol\n"); ++ ++ return FALSE; ++} ++ ++/* + * Install all static kernel symbol values into the symval_hash. + */ + static void +@@ -1142,8 +1277,15 @@ + gpl_syms = ULONG(modbuf + OFFSET(module_gpl_syms)); + nsyms = UINT(modbuf + OFFSET(module_num_syms)); + ngplsyms = UINT(modbuf + OFFSET(module_num_gpl_syms)); +- nksyms = ULONG(modbuf + OFFSET(module_num_symtab)); +- size = ULONG(modbuf + OFFSET(module_core_size)); ++ ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) { ++ nksyms = UINT(modbuf + OFFSET(module_num_symtab)); ++ size = UINT(modbuf + OFFSET(module_core_size)); ++ } else { ++ nksyms = ULONG(modbuf + OFFSET(module_num_symtab)); ++ size = ULONG(modbuf + OFFSET(module_core_size)); ++ } ++ + mod_name = modbuf + OFFSET(module_name); + + lm = &st->load_modules[m++]; +@@ -1159,15 +1301,19 @@ + mod_name); + strncpy(lm->mod_name, mod_name, MAX_MOD_NAME-1); + } +- if (CRASHDEBUG(1)) ++ if (CRASHDEBUG(3)) + fprintf(fp, + "%lx (%lx): %s syms: %d gplsyms: %d ksyms: %ld\n", + mod, lm->mod_base, lm->mod_name, nsyms, + ngplsyms, nksyms); + lm->mod_flags = MOD_EXT_SYMS; + lm->mod_ext_symcnt = mcnt; +- lm->mod_etext_guess = lm->mod_base + +- ULONG(modbuf + OFFSET(module_core_text_size)); ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) ++ lm->mod_etext_guess = lm->mod_base + ++ UINT(modbuf + OFFSET(module_core_text_size)); ++ else ++ lm->mod_etext_guess = lm->mod_base + ++ ULONG(modbuf + OFFSET(module_core_text_size)); + lm->mod_text_start = lm->mod_base; + + st->ext_module_symtable[mcnt].value = lm->mod_base; +@@ -1178,6 +1324,14 @@ + lm_mcnt = mcnt; + mcnt++; + ++ if (nsyms && !IN_MODULE(syms, lm)) { ++ error(WARNING, ++ "[%s] module.syms outside of module " ++ "address space (%lx)\n\n", ++ lm->mod_name, syms); ++ nsyms = 0; ++ } ++ + if (nsyms) { + modsymbuf = GETBUF(sizeof(struct kernel_symbol)*nsyms); + readmem((ulong)syms, KVADDR, modsymbuf, +@@ -1597,7 +1751,10 @@ + return 0; + } + +- nksyms = ULONG(modbuf + OFFSET(module_num_symtab)); ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) ++ nksyms = UINT(modbuf + OFFSET(module_num_symtab)); ++ else ++ nksyms = ULONG(modbuf + OFFSET(module_num_symtab)); + ksymtab = ULONG(modbuf + OFFSET(module_symtab)); + locsymtab = module_buf + (ksymtab - lm->mod_base); + kstrtab = ULONG(modbuf + OFFSET(module_strtab)); +@@ -1623,6 +1780,19 @@ + if (ec->st_shndx == SHN_UNDEF) + continue; + ++ if (!IN_MODULE(kstrtab + ec->st_name, lm)) { ++ if (CRASHDEBUG(3)) { ++ error(WARNING, ++ "%s: bad st_name index: %lx -> %lx\n " ++ " st_value: %lx st_shndx: %ld st_info: %c\n", ++ lm->mod_name, ++ ec->st_name, (kstrtab + ec->st_name), ++ ec->st_value, ec->st_shndx, ++ ec->st_info); ++ } ++ continue; ++ } ++ + nameptr = locstrtab + ec->st_name; + if (*nameptr == '\0') + continue; +@@ -2121,22 +2291,13 @@ + fprintf(fp, "%sFORCE_DEBUGINFO", others++ ? "|" : ""); + if (st->flags & CRC_MATCHES) + fprintf(fp, "%sCRC_MATCHES", others++ ? "|" : ""); ++ if (st->flags & ADD_SYMBOL_FILE) ++ fprintf(fp, "%sADD_SYMBOL_FILE", others++ ? "|" : ""); ++ if (st->flags & USE_OLD_ADD_SYM) ++ fprintf(fp, "%sUSE_OLD_ADD_SYM", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " bfd: %lx\n", (ulong)st->bfd); +- +- sec = (asection **)st->sections; +- fprintf(fp, " sections: %s\n", sec ? "" : "(not in use)"); +- for (i = 0; sec && (i < st->bfd->section_count); i++, sec++) { +- asection *section; +- +- section = *sec; +- fprintf(fp, "%25s vma: %.*lx size: %ld\n", +- section->name, VADDR_PRLEN, +- (ulong)bfd_get_section_vma(st->bfd, section), +- (ulong)bfd_section_size(st->bfd, section)); +- } +- + fprintf(fp, " symtable: %lx\n", (ulong)st->symtable); + fprintf(fp, " symend: %lx\n", (ulong)st->symend); + fprintf(fp, " symcnt: %ld\n", st->symcnt); +@@ -2320,6 +2481,24 @@ + } + } + } ++ ++ fprintf(fp, "\n"); ++ fprintf(fp, "dwarf_eh_frame_file_offset: %llx\n", ++ (unsigned long long)st->dwarf_eh_frame_file_offset); ++ fprintf(fp, " dwarf_eh_frame_size: %ld\n", st->dwarf_eh_frame_size); ++ fprintf(fp, "\n"); ++ ++ sec = (asection **)st->sections; ++ fprintf(fp, " sections: %s\n", sec ? "" : "(not in use)"); ++ for (i = 0; sec && (i < st->bfd->section_count); i++, sec++) { ++ asection *section; ++ ++ section = *sec; ++ fprintf(fp, "%25s vma: %.*lx size: %ld\n", ++ section->name, VADDR_PRLEN, ++ (ulong)bfd_get_section_vma(st->bfd, section), ++ (ulong)bfd_section_size(st->bfd, section)); ++ } + } + + +@@ -2354,6 +2533,106 @@ + } + + /* ++ * Verify a vmlinux file, issuing a warning for processor and endianness ++ * mismatches. ++ */ ++int ++is_kernel(char *file) ++{ ++ int fd, swap; ++ char eheader[BUFSIZE]; ++ Elf32_Ehdr *elf32; ++ Elf64_Ehdr *elf64; ++ ++ if ((fd = open(file, O_RDONLY)) < 0) { ++ error(INFO, "%s: %s\n", file, strerror(errno)); ++ return FALSE; ++ } ++ if (read(fd, eheader, BUFSIZE) != BUFSIZE) { ++ /* error(INFO, "%s: %s\n", file, strerror(errno)); */ ++ close(fd); ++ return FALSE; ++ } ++ close(fd); ++ ++ if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT) ++ return FALSE; ++ ++ elf32 = (Elf32_Ehdr *)&eheader[0]; ++ elf64 = (Elf64_Ehdr *)&eheader[0]; ++ ++ swap = (((eheader[EI_DATA] == ELFDATA2LSB) && ++ (__BYTE_ORDER == __BIG_ENDIAN)) || ++ ((eheader[EI_DATA] == ELFDATA2MSB) && ++ (__BYTE_ORDER == __LITTLE_ENDIAN))); ++ ++ if ((elf32->e_ident[EI_CLASS] == ELFCLASS32) && ++ (swap16(elf32->e_type, swap) == ET_EXEC) && ++ (swap32(elf32->e_version, swap) == EV_CURRENT)) { ++ switch (swap16(elf32->e_machine, swap)) ++ { ++ case EM_386: ++ if (machine_type_mismatch(file, "X86", NULL, 0)) ++ goto bailout; ++ break; ++ ++ case EM_S390: ++ if (machine_type_mismatch(file, "S390", NULL, 0)) ++ goto bailout; ++ break; ++ ++ default: ++ if (machine_type_mismatch(file, "(unknown)", NULL, 0)) ++ goto bailout; ++ } ++ ++ if (endian_mismatch(file, elf32->e_ident[EI_DATA], 0)) ++ goto bailout; ++ ++ } else if ((elf64->e_ident[EI_CLASS] == ELFCLASS64) && ++ (swap16(elf64->e_type, swap) == ET_EXEC) && ++ (swap32(elf64->e_version, swap) == EV_CURRENT)) { ++ switch (swap16(elf64->e_machine, swap)) ++ { ++ case EM_IA_64: ++ if (machine_type_mismatch(file, "IA64", NULL, 0)) ++ goto bailout; ++ break; ++ ++ case EM_PPC64: ++ if (machine_type_mismatch(file, "PPC64", NULL, 0)) ++ goto bailout; ++ break; ++ ++ case EM_X86_64: ++ if (machine_type_mismatch(file, "X86_64", NULL, 0)) ++ goto bailout; ++ break; ++ ++ case EM_386: ++ if (machine_type_mismatch(file, "X86", NULL, 0)) ++ goto bailout; ++ break; ++ ++ case EM_S390: ++ if (machine_type_mismatch(file, "S390X", NULL, 0)) ++ goto bailout; ++ break; ++ ++ default: ++ if (machine_type_mismatch(file, "(unknown)", NULL, 0)) ++ goto bailout; ++ } ++ ++ if (endian_mismatch(file, elf64->e_ident[EI_DATA], 0)) ++ goto bailout; ++ } ++ ++bailout: ++ return(is_bfd_format(file)); ++} ++ ++/* + * Given a choice between two namelists, pick the one for gdb to use. + * For now, just check get their stats and check their sizes; the larger + * one presumably has debug data. +@@ -2427,7 +2706,7 @@ + goto not_system_map; + if (parse_line(buf, mapitems) != 3) + goto not_system_map; +- if ((strlen(mapitems[0]) != MAX_HEXADDR_STRLEN) || ++ if ((strlen(mapitems[0]) > MAX_HEXADDR_STRLEN) || + !hexadecimal(mapitems[0], 0) || (strlen(mapitems[1]) > 1)) + goto not_system_map; + } +@@ -2468,6 +2747,33 @@ + return TRUE; + } + ++static int ++is_binary_stripped(char *filename) ++{ ++#if defined(GDB_6_0) || defined(GDB_6_1) ++ struct bfd *bfd; ++#else ++ struct _bfd *bfd; ++#endif ++ int number_of_symbols; ++ ++ if ((bfd = bfd_openr(filename, NULL)) == NULL) { ++ error(INFO, "cannot open ELF file: %s\n", filename); ++ return FALSE; ++ } ++ ++ if (!bfd_check_format(bfd, bfd_object)) { ++ error(INFO, "invalid ELF file: %s\n", filename); ++ bfd_close(bfd); ++ return FALSE; ++ } ++ ++ number_of_symbols = bfd_canonicalize_symtab(bfd, NULL); ++ ++ bfd_close(bfd); ++ ++ return (number_of_symbols == 0); ++} + + /* + * This command may be used to: +@@ -3463,6 +3769,22 @@ + } + + /* ++ * Same as above, but allow for failure. ++ */ ++int ++try_get_symbol_data(char *symbol, long size, void *local) ++{ ++ struct syment *sp; ++ ++ if ((sp = symbol_search(symbol)) && ++ readmem(sp->value, KVADDR, local, ++ size, symbol, RETURN_ON_ERROR|QUIET)) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++/* + * Return the value of a given symbol. + */ + ulong +@@ -3477,6 +3799,34 @@ + } + + /* ++ * Return the value of a symbol from a specific module. ++ */ ++ulong ++symbol_value_module(char *symbol, char *module) ++{ ++ int i; ++ struct syment *sp, *sp_end; ++ struct load_module *lm; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ ++ if (!STREQ(module, lm->mod_name)) ++ continue; ++ ++ sp = lm->mod_symtable; ++ sp_end = lm->mod_symend; ++ ++ for ( ; sp < sp_end; sp++) { ++ if (STREQ(symbol, sp->name)) ++ return(sp->value); ++ } ++ } ++ ++ return 0; ++} ++ ++/* + * Return the symbol name of a given value, with no allowance for offsets. + * Returns NULL on failure to allow for testing of a value. + */ +@@ -3608,6 +3958,8 @@ + * #define STRUCT_EXISTS(X) (datatype_info((X), NULL, NULL) >= 0) + * #define MEMBER_EXISTS(X,Y) (datatype_info((X), (Y), NULL) >= 0) + * #define MEMBER_SIZE(X,Y) datatype_info((X), (Y), MEMBER_SIZE_REQUEST) ++ * #define MEMBER_TYPE(X,Y) datatype_info((X), (Y), MEMBER_TYPE_REQUEST) ++ * #define ANON_MEMBER_OFFSET(X,Y) datatype_info((X), (Y), ANON_MEMBER_OFFSET_REQUEST) + * + * to determine structure or union sizes, or member offsets. + */ +@@ -3620,6 +3972,9 @@ + ulong type_found; + char buf[BUFSIZE]; + ++ if (dm == ANON_MEMBER_OFFSET_REQUEST) ++ return anon_member_offset(name, member); ++ + strcpy(buf, name); + + req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); +@@ -3743,11 +4098,12 @@ + + FREEBUF(req); + +- if (dm && (dm != MEMBER_SIZE_REQUEST)) { ++ if (dm && (dm != MEMBER_SIZE_REQUEST) && (dm != MEMBER_TYPE_REQUEST)) { + dm->type = type_found; + dm->size = size; + dm->member_size = member_size; + dm->member_typecode = member_typecode; ++ dm->member_offset = offset; + if (req->is_typedef) { + dm->flags |= TYPEDEF; + } +@@ -3762,13 +4118,42 @@ + + if (dm == MEMBER_SIZE_REQUEST) + return member_size; +- else if (member) ++ else if (dm == MEMBER_TYPE_REQUEST) ++ return member_typecode; ++ else if (member) + return offset; + else + return size; + } + + /* ++ * Determine the offset of a member in an anonymous union ++ * in a structure. ++ */ ++static long ++anon_member_offset(char *name, char *member) ++{ ++ int c; ++ char buf[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ ulong value; ++ ++ value = -1; ++ sprintf(buf, "print &((struct %s *)0x0)->%s", name, member); ++ ++ open_tmpfile(); ++ if (gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { ++ rewind(pc->tmpfile); ++ if (fgets(buf, BUFSIZE, pc->tmpfile) && ++ (c = parse_line(strip_linefeeds(buf), arglist))) ++ value = stol(arglist[c-1], RETURN_ON_ERROR|QUIET, NULL); ++ } ++ close_tmpfile(); ++ ++ return value; ++} ++ ++/* + * Get the basic type info for a symbol. Let the caller pass in the + * gnu_request structure to have access to the full response; in either + * case, return the type code. The member field can be used for structures +@@ -3906,276 +4291,31 @@ + restore_radix = output_radix; + output_radix = radix; + output_format = (output_radix == 10) ? 0 : 'x'; +- } +- +- print_union(s, addr); +- +- if (radix) { +- output_radix = restore_radix; +- output_format = (output_radix == 10) ? 0 : 'x'; +- } +-} +- +-/* +- * This command displays either a structure definition, or a formatted display +- * of the contents of a structure at a specified address. If no address is +- * specified, the structure size and the file in which the structure is defined +- * are also displayed. A structure member may be appended to the structure +- * name (in a "struct.member" format) in order to limit the scope of the data +- * displayed to that particular member. Structure data is shown in hexadecimal +- * format. The raw data in a structure may be dumped with the -r flag. +- */ +-void +-cmd_struct(void) +-{ +- int c; +- ulong addr, aflag; +- struct syment *sp; +- int rawdata; +- long len; +- ulong flags; +- ulong list_head_offset; +- int count; +- struct datatype_member struct_member, *sm; +- +- sm = &struct_member; +- count = 1; +- rawdata = 0; +- aflag = 0; +- list_head_offset = 0; +- flags = STRUCT_REQUEST; +- +- while ((c = getopt(argcnt, args, "c:rvol:")) != EOF) { +- switch(c) +- { +- case 'c': +- count = atoi(optarg); +- break; +- +- case 'r': +- rawdata = 1; +- break; +- +- case 'v': +- flags |= STRUCT_VERBOSE; +- break; +- +- case 'o': +- flags |= SHOW_OFFSET; +- break; +- +- case 'l': +- if (IS_A_NUMBER(optarg)) +- list_head_offset = stol(optarg, +- FAULT_ON_ERROR, NULL); +- else if (arg_to_datatype(optarg, +- sm, RETURN_ON_ERROR) > 1) +- list_head_offset = sm->member_offset; +- break; +- +- default: +- argerrs++; +- break; +- } +- } +- +- if (argerrs || !args[optind]) +- cmd_usage(pc->curcmd, SYNOPSIS); +- +- if ((arg_to_datatype(args[optind++], sm, FAULT_ON_ERROR) > 1) && +- rawdata) +- error(FATAL, "member-specific output not allowed with -r\n"); +- +- if ((len = sm->size) < 0) { +- error(INFO, "structure not found: %s\n", sm->name); +- cmd_usage(pc->curcmd, SYNOPSIS); +- } +- +- if (!args[optind]) { +- do_datatype_declaration(sm, flags | (sm->flags & TYPEDEF)); +- return; +- } +- +- while (args[optind]) { +- if (clean_arg() && IS_A_NUMBER(args[optind])) { +- if (aflag) +- count = stol(args[optind], +- FAULT_ON_ERROR, NULL); +- else { +- if (!IS_KVADDR(addr = htol(args[optind], +- FAULT_ON_ERROR, NULL))) +- error(FATAL, +- "invalid kernel virtual address: %s\n", +- args[optind]); +- aflag++; +- } +- } +- else if ((sp = symbol_search(args[optind]))) { +- addr = sp->value; +- aflag++; +- } else { +- fprintf(fp, "symbol not found: %s\n", args[optind]); +- fprintf(fp, "possible aternatives:\n"); +- if (!symbol_query(args[optind], " ", NULL)) +- fprintf(fp, " (none found)\n"); +- return; +- } +- optind++; +- } +- +- if (!aflag) +- error(FATAL, "no kernel virtual address argument entered\n"); +- +- if (list_head_offset) +- addr -= list_head_offset; +- +- if (count < 0) { +- addr -= len * abs(count); +- addr += len; +- } +- +- for (c = 0; c < abs(count); c++, addr += len) { +- if (rawdata) +- raw_data_dump(addr, len, flags & STRUCT_VERBOSE); +- else { +- if (sm->member) +- open_tmpfile(); +- +- print_struct(sm->name, addr); +- +- if (sm->member) { +- parse_for_member(sm, PARSE_FOR_DATA); +- close_tmpfile(); +- } +- } +- } +-} +- +-/* +- * After determining what type of data type follows the *, this routine +- * has the identical functionality as cmd_struct() or cmd_union(). +- */ +-void +-cmd_pointer(void) +-{ +- int c; +- ulong addr, aflag; +- struct syment *sp; +- int rawdata; +- long len; +- ulong flags; +- int count; +- struct datatype_member datatype_member, *dm; +- +- dm = &datatype_member; +- rawdata = 0; +- flags = 0; +- aflag = 0; +- count = 1; +- +- while ((c = getopt(argcnt, args, "c:rvo")) != EOF) { +- switch(c) +- { +- case 'c': +- count = atoi(optarg); +- break; +- +- case 'r': +- rawdata = 1; +- break; +- +- case 'v': +- flags |= STRUCT_VERBOSE; +- break; +- +- case 'o': +- flags |= SHOW_OFFSET; +- break; +- +- default: +- argerrs++; +- break; +- } +- } +- +- if (argerrs || !args[optind]) +- cmd_usage(pc->curcmd, SYNOPSIS); +- +- if ((arg_to_datatype(args[optind++], dm, FAULT_ON_ERROR) > 1) && +- rawdata) +- error(FATAL, "member-specific output not allowed with -r\n"); +- +- if ((len = dm->size) < 0) { +- error(INFO, "structure or union not found: %s\n", dm->name); +- cmd_usage(pc->curcmd, SYNOPSIS); +- } +- +- flags |= dm->type; +- +- if (!args[optind]) { +- do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF)); +- return; +- } +- +- while (args[optind]) { +- if (clean_arg() && IS_A_NUMBER(args[optind])) { +- if (aflag) +- count = stol(args[optind], +- FAULT_ON_ERROR, NULL); +- else { +- if (!IS_KVADDR(addr = htol(args[optind], +- FAULT_ON_ERROR, NULL))) +- error(FATAL, +- "invalid kernel virtual address: %s\n", +- args[optind]); +- aflag++; +- } +- } +- else if ((sp = symbol_search(args[optind]))) { +- addr = sp->value; +- aflag++; +- } else { +- fprintf(fp, "symbol not found: %s\n", args[optind]); +- fprintf(fp, "possible aternatives:\n"); +- if (!symbol_query(args[optind], " ", NULL)) +- fprintf(fp, " (none found)\n"); +- return; +- } +- optind++; +- } +- +- if (!(flags & (UNION_REQUEST|STRUCT_REQUEST))) +- error(FATAL, "invalid argument!"); ++ } + +- if (!aflag) +- error(FATAL, "no kernel virtual address argument entered\n"); ++ print_union(s, addr); + +- if (count < 0) { +- addr -= len * abs(count); +- addr += len; ++ if (radix) { ++ output_radix = restore_radix; ++ output_format = (output_radix == 10) ? 0 : 'x'; + } +- +- for (c = 0; c < abs(count); c++, addr += len) { +- if (rawdata) +- raw_data_dump(addr, len, flags & STRUCT_VERBOSE); +- else { +- if (dm->member) +- open_tmpfile(); +- +- if (flags & UNION_REQUEST) +- print_union(dm->name, addr); +- else if (flags & STRUCT_REQUEST) +- print_struct(dm->name, addr); +- +- if (dm->member) { +- parse_for_member(dm, PARSE_FOR_DATA); +- close_tmpfile(); +- } +- } +- } + } + + /* ++ * This command displays either a structure definition, or a formatted display ++ * of the contents of a structure at a specified address. If no address is ++ * specified, the structure size and the file in which the structure is defined ++ * are also displayed. A structure member may be appended to the structure ++ * name (in a "struct.member" format) in order to limit the scope of the data ++ * displayed to that particular member. Structure data is shown in hexadecimal ++ * format. The raw data in a structure may be dumped with the -r flag. ++ */ ++void ++cmd_struct(void) ++{ ++ cmd_datatype_common(STRUCT_REQUEST); ++} ++/* + * This command displays either a union definition, or a formatted display + * of the contents of a union at a specified address. If no address is + * specified, the union size and the file in which the union is defined +@@ -4187,25 +4327,45 @@ + void + cmd_union(void) + { +- int c; ++ cmd_datatype_common(UNION_REQUEST); ++} ++ ++/* ++ * After determining what type of data type follows the *, this routine ++ * has the identical functionality as cmd_struct() or cmd_union(). ++ */ ++void ++cmd_pointer(void) ++{ ++ cmd_datatype_common(0); ++} ++ ++static void ++cmd_datatype_common(ulong flags) ++{ ++ int i, c; + ulong addr, aflag; + struct syment *sp; + int rawdata; + long len; +- ulong flags; +- int count; +- struct datatype_member union_member, *um; + ulong list_head_offset; ++ int count; ++ int argc_members; ++ int optind_save; ++ struct datatype_member datatype_member, *dm; ++ char *separator; ++ char *structname, *members; ++ char *memberlist[MAXARGS]; + +- um = &union_member; +- count = 1; ++ dm = &datatype_member; ++ count = 0xdeadbeef; + rawdata = 0; + aflag = 0; +- list_head_offset = 0; +- flags = UNION_REQUEST; ++ list_head_offset = 0; ++ argc_members = 0; + +- while ((c = getopt(argcnt, args, "c:rvol:")) != EOF) { +- switch(c) ++ while ((c = getopt(argcnt, args, "fuc:rvol:")) != EOF) { ++ switch (c) + { + case 'c': + count = atoi(optarg); +@@ -4223,14 +4383,28 @@ + flags |= SHOW_OFFSET; + break; + +- case 'l': ++ case 'l': + if (IS_A_NUMBER(optarg)) + list_head_offset = stol(optarg, + FAULT_ON_ERROR, NULL); + else if (arg_to_datatype(optarg, +- um, RETURN_ON_ERROR) > 1) +- list_head_offset = um->member_offset; +- break; ++ dm, RETURN_ON_ERROR) > 1) ++ list_head_offset = dm->member_offset; ++ else ++ error(FATAL, "invalid -l option: %s\n", ++ optarg); ++ break; ++ ++ case 'f': ++ if (!pc->dumpfile) ++ error(FATAL, ++ "-f option requires a dumpfile\n"); ++ pc->curcmd_flags |= MEMTYPE_FILEADDR; ++ break; ++ ++ case 'u': ++ pc->curcmd_flags |= MEMTYPE_UVADDR; ++ break; + + default: + argerrs++; +@@ -4241,75 +4415,177 @@ + if (argerrs || !args[optind]) + cmd_usage(pc->curcmd, SYNOPSIS); + +- if ((arg_to_datatype(args[optind++], um, FAULT_ON_ERROR) > 1) && +- rawdata) +- error(FATAL, "member-specific output not allowed with -r\n"); ++ if ((count_chars(args[optind], ',')+1) > MAXARGS) ++ error(FATAL, "too many members in comma-separated list!\n"); + +- if ((len = um->size) < 0) { +- error(INFO, "union not found: %s\n", um->name); +- cmd_usage(pc->curcmd, SYNOPSIS); +- } +- +- if (!args[optind]) { +- do_datatype_declaration(um, flags | (um->flags & TYPEDEF)); +- return; +- } ++ if ((count_chars(args[optind], '.') > 1) || ++ (LASTCHAR(args[optind]) == ',') || ++ (LASTCHAR(args[optind]) == '.')) ++ error(FATAL, "invalid format: %s\n", args[optind]); ++ ++ optind_save = optind; ++ ++ /* ++ * Take care of address and count (array). ++ */ ++ while (args[++optind]) { ++ if (aflag && (count != 0xdeadbeef)) ++ error(FATAL, "too many arguments!\n"); + +- while (args[optind]) { + if (clean_arg() && IS_A_NUMBER(args[optind])) { +- if (aflag) +- count = stol(args[optind], +- FAULT_ON_ERROR, NULL); +- else { +- if (!IS_KVADDR(addr = htol(args[optind], ++ if (aflag) ++ count = stol(args[optind], ++ FAULT_ON_ERROR, NULL); ++ else { ++ if (pc->curcmd_flags & MEMTYPE_FILEADDR) ++ pc->curcmd_private = stoll(args[optind], ++ FAULT_ON_ERROR, NULL); ++ else if (pc->curcmd_flags & MEMTYPE_UVADDR) { ++ addr = htol(args[optind], FAULT_ON_ERROR, ++ NULL); ++ } else if (!IS_KVADDR(addr = htol(args[optind], + FAULT_ON_ERROR, NULL))) +- error(FATAL, +- "invalid kernel virtual address: %s\n", +- args[optind]); +- aflag++; +- } +- } +- else if ((sp = symbol_search(args[optind]))) { ++ error(FATAL, ++ "invalid kernel virtual address: %s\n", ++ args[optind]); ++ aflag++; ++ } ++ } else if ((sp = symbol_search(args[optind]))) { + addr = sp->value; + aflag++; +- } else { ++ } else { + fprintf(fp, "symbol not found: %s\n", args[optind]); + fprintf(fp, "possible aternatives:\n"); + if (!symbol_query(args[optind], " ", NULL)) + fprintf(fp, " (none found)\n"); +- return; ++ goto freebuf; + } +- optind++; + } + +- if (!aflag) +- error(FATAL, "no kernel virtual address argument entered\n"); ++ optind = optind_save; ++ ++ if (count == 0xdeadbeef) ++ count = 1; ++ else if (!aflag) ++ error(FATAL, "no kernel virtual address argument entered\n"); ++ ++ if ((flags & SHOW_OFFSET) && aflag) { ++ error(INFO, "-o option not valid with an address argument\n"); ++ flags &= ~SHOW_OFFSET; ++ } + + if (list_head_offset) + addr -= list_head_offset; + ++ /* ++ * Handle struct.member[,member] argument format. ++ */ ++ if (strstr(args[optind], ".")) { ++ structname = GETBUF(strlen(args[optind])+1); ++ strcpy(structname, args[optind]); ++ separator = strstr(structname, "."); ++ ++ members = GETBUF(strlen(args[optind])+1); ++ strcpy(members, separator+1); ++ replace_string(members, ",", ' '); ++ argc_members = parse_line(members, memberlist); ++ } else ++ structname = args[optind]; ++ ++ if ((arg_to_datatype(structname, dm, DATATYPE_QUERY|RETURN_ON_ERROR) < 1)) ++ error(FATAL, "invalid data structure reference: %s\n", structname); ++ ++ if ((argc_members > 1) && !aflag) { ++ error(INFO, flags & SHOW_OFFSET ? ++ "-o option not valid with multiple member format\n" : ++ "multiple member format not supported in this syntax\n"); ++ *separator = NULLCHAR; ++ argc_members = 0; ++ flags |= SHOW_OFFSET; ++ } ++ ++ len = dm->size; ++ + if (count < 0) { + addr -= len * abs(count); + addr += len; + } + +- for (c = 0; c < abs(count); c++, addr += len) { +- if (rawdata) +- raw_data_dump(addr, len, flags & STRUCT_VERBOSE); +- else { +- if (um->member) +- open_tmpfile(); +- +- print_union(um->name, addr); +- +- if (um->member) { +- parse_for_member(um, PARSE_FOR_DATA); +- close_tmpfile(); ++ if (pc->curcmd_flags & MEMTYPE_FILEADDR) ++ addr = 0; /* unused, but parsed by gdb */ ++ ++ for (c = 0; c < abs(count); c++, addr += len, pc->curcmd_private += len) { ++ if (c) ++ fprintf(fp,"\n"); ++ ++ i = 0; ++ do { ++ if (argc_members) { ++ *separator = '.'; ++ strcpy(separator+1, memberlist[i]); + } +- } ++ ++ switch (arg_to_datatype(structname, dm, RETURN_ON_ERROR)) ++ { ++ case 0: error(FATAL, "invalid data structure reference: %s\n", ++ structname); ++ break; ++ case 1: break; ++ case 2: if (rawdata) ++ error(FATAL, ++ "member-specific output not allowed with -r\n"); ++ break; ++ } ++ ++ if (!(dm->flags & TYPEDEF)) { ++ if (flags &(STRUCT_REQUEST|UNION_REQUEST) ) { ++ if ((flags & (STRUCT_REQUEST|UNION_REQUEST)) != dm->type) ++ goto freebuf; ++ } else ++ flags |= dm->type; ++ } ++ ++ /* ++ * No address was passed -- dump the structure/member declaration. ++ */ ++ if (!aflag) { ++ do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF)); ++ goto freebuf; ++ } ++ ++ if (!(flags & (UNION_REQUEST|STRUCT_REQUEST))) ++ error(FATAL, "invalid argument"); ++ ++ /* ++ * Display data. ++ */ ++ if (rawdata) ++ raw_data_dump(addr, len, flags & STRUCT_VERBOSE); ++ else { ++ if (dm->member) ++ open_tmpfile(); ++ ++ if (flags & UNION_REQUEST) ++ print_union(dm->name, addr); ++ else if (flags & STRUCT_REQUEST) ++ print_struct(dm->name, addr); ++ ++ if (dm->member) { ++ parse_for_member(dm, PARSE_FOR_DATA); ++ close_tmpfile(); ++ } ++ } ++ } while (++i < argc_members); ++ } ++ ++freebuf: ++ if (argc_members) { ++ FREEBUF(structname); ++ FREEBUF(members); + } + } + ++ + /* + * Generic function for dumping data structure declarations, with a small + * fixup for typedefs, sizes and member offsets. +@@ -4405,7 +4681,10 @@ + + if (!(p1 = strstr(s, "."))) + both = FALSE; +- else { ++ else if (flags & DATATYPE_QUERY) { ++ *p1 = NULLCHAR; ++ both = FALSE; ++ } else { + if ((p1 == s) || !strlen(p1+1)) + goto datatype_member_fatal; + *p1 = NULLCHAR; +@@ -4634,6 +4913,27 @@ + } + + /* ++ * Given the name of an enum, return its value. ++ */ ++int ++enumerator_value(char *e, long *value) ++{ ++ struct datatype_member datatype_member, *dm; ++ ++ dm = &datatype_member; ++ ++ if (arg_to_datatype(e, dm, RETURN_ON_ERROR)) { ++ if ((dm->size >= 0) && ++ (dm->type == ENUM) && dm->tagname) { ++ *value = dm->value; ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++} ++ ++/* + * Verify that a datatype exists, but return on error. + */ + int +@@ -4679,7 +4979,7 @@ + + leader = do_load_module_filter = restore_radix = 0; + +- while ((c = getopt(argcnt, args, "dhx")) != EOF) { ++ while ((c = getopt(argcnt, args, "dhxu")) != EOF) { + switch(c) + { + case 'd': +@@ -4695,6 +4995,10 @@ + output_format = (output_radix == 10) ? 0 : 'x'; + break; + ++ case 'u': ++ pc->curcmd_flags |= MEMTYPE_UVADDR; ++ break; ++ + default: + argerrs++; + break; +@@ -4705,6 +5009,8 @@ + cmd_usage(pc->curcmd, SYNOPSIS); + + if ((sp = symbol_search(args[optind])) && !args[optind+1]) { ++ if (STRNEQ(sp->name, "per_cpu__") && display_per_cpu_info(sp)) ++ return; + sprintf(buf2, "%s = ", args[optind]); + leader = strlen(buf2); + if (module_symbol(sp->value, NULL, NULL, NULL, output_radix)) +@@ -4758,6 +5064,39 @@ + } + + /* ++ * Display the datatype of the per_cpu__xxx symbol and ++ * the addresses of each its per-cpu instances. ++ */ ++static int ++display_per_cpu_info(struct syment *sp) ++{ ++ int c; ++ ulong addr; ++ char buf[BUFSIZE]; ++ ++ if (((kt->flags & (SMP|PER_CPU_OFF)) != (SMP|PER_CPU_OFF)) || ++ (sp->value < symbol_value("__per_cpu_start")) || ++ (sp->value >= symbol_value("__per_cpu_end")) || ++ !((sp->type == 'd') || (sp->type == 'D'))) ++ return FALSE; ++ ++ fprintf(fp, "PER-CPU DATA TYPE:\n "); ++ sprintf(buf, "whatis %s", sp->name); ++ if (!gdb_pass_through(buf, pc->nullfp, GNU_RETURN_ON_ERROR)) ++ fprintf(fp, "[undetermined type] %s;\n", sp->name); ++ else ++ whatis_variable(sp); ++ ++ fprintf(fp, "PER-CPU ADDRESSES:\n"); ++ for (c = 0; c < kt->cpus; c++) { ++ addr = sp->value + kt->__per_cpu_offset[c]; ++ fprintf(fp, " [%d]: %lx\n", c, addr); ++ } ++ ++ return TRUE; ++} ++ ++/* + * As a latch ditch effort before a command is thrown away by exec_command(), + * args[0] is checked to see whether it's the name of a variable, structure, + * union, or typedef. If so, args[0] is changed to the appropriate command, +@@ -4793,9 +5132,9 @@ + command = "whatis"; + else if (!datatype_exists(args[0])) + return FALSE; +- else if (!arg_to_datatype(buf, dm, RETURN_ON_ERROR)) { ++ else if (!arg_to_datatype(buf, dm, RETURN_ON_ERROR|DATATYPE_QUERY)) + return FALSE; +- } else { ++ else { + if (is_gdb_command(FALSE, RETURN_ON_ERROR)) { + pc->curcmd = pc->program_name; + error(FATAL, +@@ -5056,6 +5395,8 @@ + fprintf(ofp, "%sSTRUCT_VERBOSE", others++ ? "|" : ""); + if (flags & SHOW_OFFSET) + fprintf(ofp, "%sSHOW_OFFSET", others++ ? "|" : ""); ++ if (flags & DATATYPE_QUERY) ++ fprintf(ofp, "%sDATATYPE_QUERY", others++ ? "|" : ""); + fprintf(ofp, ")\n"); + } + +@@ -5079,7 +5420,8 @@ + + s = dm->member; + indent = 0; +- on = array = FALSE; ++ array = FALSE; ++ on = 0; + rewind(pc->tmpfile); + + switch (flag) +@@ -5090,7 +5432,7 @@ + next_item: + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (STRNEQ(buf, lookfor1) || STRNEQ(buf, lookfor2)) { +- on = TRUE; ++ on++; + if (strstr(buf, "= {")) + indent = count_leading_spaces(buf); + if (strstr(buf, "[")) +@@ -5098,16 +5440,22 @@ + } + + if (on) { ++ if ((indent && (on > 1) && (count_leading_spaces(buf) == indent) && ++ !strstr(buf, "}")) || (buf[0] == '}')) { ++ break; ++ } + fprintf(pc->saved_fp, buf); + if (!indent) + break; + if (strstr(buf, "}") && + (count_leading_spaces(buf) == indent)) + break; ++ on++; + } + } + if (array) { + on = array = FALSE; ++ on = 0; + goto next_item; + } + break; +@@ -5174,7 +5522,7 @@ + { + int i, c, len; + long offset; +- char *target; ++ char *t1, *target; + char *arglist[MAXARGS]; + char buf1[BUFSIZE]; + char fmt[BUFSIZE]; +@@ -5186,6 +5534,9 @@ + return FALSE; + } + ++ if (STRNEQ(inbuf, " ")) ++ goto do_empty_offset; ++ + if (STRNEQ(inbuf, " union {")) + dm->flags |= IN_UNION; + if (STRNEQ(inbuf, " struct {")) +@@ -5215,9 +5566,20 @@ + } + } + } else if (c) { +- target = arglist[c-1]; +- if (!strstr(target, ";")) +- target = NULL; ++ for (i = 0; i < c; i++) { ++ if (STRNEQ(arglist[i], "(*")) { ++ target = arglist[i]+2; ++ if (!(t1 = strstr(target, ")"))) ++ continue; ++ *t1 = NULLCHAR; ++ break; ++ } ++ } ++ if (i == c) { ++ target = arglist[c-1]; ++ if (!strstr(target, ";")) ++ target = NULL; ++ } + } + + if (!target) +@@ -5307,7 +5669,8 @@ + if ((retval = builtin_array_length(s, 0, two_dim))) + return retval; + +- if (symbol_search(s)) { ++ /* symbol_search cannot be done with just kernel type information */ ++ if (!(LKCD_KERNTYPES()) && symbol_search(s)) { + if (!two_dim) { + req = &gnu_request; + if ((get_symbol_type(copy, NULL, req) == +@@ -5417,6 +5780,23 @@ + } + + /* ++ * Get and store the size of a "known" array. ++ * A wrapper for get_array_length(), for cases in which ++ * the name of the result to be stored is different from the ++ * structure.member to be evaluated. ++ */ ++int ++get_array_length_alt(char *name, char *s, int *two_dim, long entry_size) ++{ ++ int retval; ++ ++ retval = get_array_length(s, two_dim, entry_size); ++ if (retval) ++ retval = builtin_array_length(name, retval, two_dim); ++ return retval; ++} ++ ++/* + * Designed for use by non-debug kernels, but used by all. + */ + int +@@ -5433,6 +5813,8 @@ + lenptr = &array_table.kmem_cache_s_c_name; + else if (STREQ(s, "kmem_cache_s.array")) + lenptr = &array_table.kmem_cache_s_array; ++ else if (STREQ(s, "kmem_cache.array")) ++ lenptr = &array_table.kmem_cache_s_array; + else if (STREQ(s, "kmem_cache_s.cpudata")) + lenptr = &array_table.kmem_cache_s_cpudata; + else if (STREQ(s, "log_buf")) +@@ -5469,11 +5851,16 @@ + lenptr = &array_table.prio_array_queue; + else if (STREQ(s, "height_to_maxindex")) + lenptr = &array_table.height_to_maxindex; ++ else if (STREQ(s, "pid_hash")) ++ lenptr = &array_table.pid_hash; + else if (STREQ(s, "free_area")) { + lenptr = &array_table.free_area; + if (two_dim) + dimptr = &array_table.free_area_DIMENSION; +- } ++ } else if (STREQ(s, "kmem_cache.node")) ++ lenptr = &array_table.kmem_cache_node; ++ else if (STREQ(s, "kmem_cache.cpu_slab")) ++ lenptr = &array_table.kmem_cache_cpu_slab; + + if (!lenptr) /* not stored */ + return(len); +@@ -5606,8 +5993,16 @@ + OFFSET(task_struct_last_run)); + fprintf(fp, " task_struct_timestamp: %ld\n", + OFFSET(task_struct_timestamp)); ++ fprintf(fp, " task_struct_sched_info: %ld\n", ++ OFFSET(task_struct_sched_info)); ++ fprintf(fp, " sched_info_last_arrival: %ld\n", ++ OFFSET(sched_info_last_arrival)); + fprintf(fp, " task_struct_thread_info: %ld\n", + OFFSET(task_struct_thread_info)); ++ fprintf(fp, " task_struct_nsproxy: %ld\n", ++ OFFSET(task_struct_nsproxy)); ++ fprintf(fp, " task_struct_rlim: %ld\n", ++ OFFSET(task_struct_rlim)); + + fprintf(fp, " thread_info_task: %ld\n", + OFFSET(thread_info_task)); +@@ -5618,11 +6013,31 @@ + fprintf(fp, " thread_info_previous_esp: %ld\n", + OFFSET(thread_info_previous_esp)); + ++ fprintf(fp, " nsproxy_mnt_ns: %ld\n", ++ OFFSET(nsproxy_mnt_ns)); ++ fprintf(fp, " mnt_namespace_root: %ld\n", ++ OFFSET(mnt_namespace_root)); ++ fprintf(fp, " mnt_namespace_list: %ld\n", ++ OFFSET(mnt_namespace_list)); ++ + fprintf(fp, " pid_link_pid: %ld\n", + OFFSET(pid_link_pid)); + fprintf(fp, " pid_hash_chain: %ld\n", + OFFSET(pid_hash_chain)); + ++ fprintf(fp, " pid_numbers: %ld\n", ++ OFFSET(pid_numbers)); ++ ++ fprintf(fp, " upid_nr: %ld\n", ++ OFFSET(upid_nr)); ++ fprintf(fp, " upid_ns: %ld\n", ++ OFFSET(upid_ns)); ++ fprintf(fp, " upid_pid_chain: %ld\n", ++ OFFSET(upid_pid_chain)); ++ ++ fprintf(fp, " pid_tasks: %ld\n", ++ OFFSET(pid_tasks)); ++ + fprintf(fp, " hlist_node_next: %ld\n", + OFFSET(hlist_node_next)); + fprintf(fp, " hlist_node_pprev: %ld\n", +@@ -5647,6 +6062,11 @@ + OFFSET(signal_struct_count)); + fprintf(fp, " signal_struct_action: %ld\n", + OFFSET(signal_struct_action)); ++ fprintf(fp, " signal_struct_shared_pending: %ld\n", ++ OFFSET(signal_struct_shared_pending)); ++ fprintf(fp, " signal_struct_rlim: %ld\n", ++ OFFSET(signal_struct_rlim)); ++ + fprintf(fp, " task_struct_start_time: %ld\n", + OFFSET(task_struct_start_time)); + fprintf(fp, " task_struct_times: %ld\n", +@@ -5766,10 +6186,22 @@ + OFFSET(mm_struct_pgd)); + fprintf(fp, " mm_struct_rss: %ld\n", + OFFSET(mm_struct_rss)); ++ fprintf(fp, " mm_struct_anon_rss: %ld\n", ++ OFFSET(mm_struct_anon_rss)); ++ fprintf(fp, " mm_struct_file_rss: %ld\n", ++ OFFSET(mm_struct_file_rss)); + fprintf(fp, " mm_struct_total_vm: %ld\n", + OFFSET(mm_struct_total_vm)); + fprintf(fp, " mm_struct_start_code: %ld\n", + OFFSET(mm_struct_start_code)); ++ fprintf(fp, " mm_struct_arg_start: %ld\n", ++ OFFSET(mm_struct_arg_start)); ++ fprintf(fp, " mm_struct_arg_end: %ld\n", ++ OFFSET(mm_struct_arg_end)); ++ fprintf(fp, " mm_struct_env_start: %ld\n", ++ OFFSET(mm_struct_env_start)); ++ fprintf(fp, " mm_struct_env_end: %ld\n", ++ OFFSET(mm_struct_env_end)); + + fprintf(fp, " vm_area_struct_vm_mm: %ld\n", + OFFSET(vm_area_struct_vm_mm)); +@@ -5885,6 +6317,17 @@ + fprintf(fp, " page_pte: %ld\n", + OFFSET(page_pte)); + ++ fprintf(fp, " page_inuse: %ld\n", ++ OFFSET(page_inuse)); ++ fprintf(fp, " page_objects: %ld\n", ++ OFFSET(page_objects)); ++ fprintf(fp, " page_slab: %ld\n", ++ OFFSET(page_slab)); ++ fprintf(fp, " page_first_page: %ld\n", ++ OFFSET(page_first_page)); ++ fprintf(fp, " page_freelist: %ld\n", ++ OFFSET(page_freelist)); ++ + fprintf(fp, " swap_info_struct_swap_file: %ld\n", + OFFSET(swap_info_struct_swap_file)); + fprintf(fp, " swap_info_struct_swap_vfsmnt: %ld\n", +@@ -5922,6 +6365,8 @@ + OFFSET(irq_desc_t_status)); + fprintf(fp, " irq_desc_t_handler: %ld\n", + OFFSET(irq_desc_t_handler)); ++ fprintf(fp, " irq_desc_t_chip: %ld\n", ++ OFFSET(irq_desc_t_chip)); + fprintf(fp, " irq_desc_t_action: %ld\n", + OFFSET(irq_desc_t_action)); + fprintf(fp, " irq_desc_t_depth: %ld\n", +@@ -5967,11 +6412,52 @@ + fprintf(fp, "hw_interrupt_type_set_affinity: %ld\n", + OFFSET(hw_interrupt_type_set_affinity)); + ++ fprintf(fp, " irq_chip_typename: %ld\n", ++ OFFSET(irq_chip_typename)); ++ fprintf(fp, " irq_chip_startup: %ld\n", ++ OFFSET(irq_chip_startup)); ++ fprintf(fp, " irq_chip_shutdown: %ld\n", ++ OFFSET(irq_chip_shutdown)); ++ fprintf(fp, " irq_chip_enable: %ld\n", ++ OFFSET(irq_chip_enable)); ++ fprintf(fp, " irq_chip_disable: %ld\n", ++ OFFSET(irq_chip_disable)); ++ fprintf(fp, " irq_chip_ack: %ld\n", ++ OFFSET(irq_chip_ack)); ++ fprintf(fp, " irq_chip_mask: %ld\n", ++ OFFSET(irq_chip_mask)); ++ fprintf(fp, " irq_chip_mask_ack: %ld\n", ++ OFFSET(irq_chip_mask_ack)); ++ fprintf(fp, " irq_chip_unmask: %ld\n", ++ OFFSET(irq_chip_unmask)); ++ fprintf(fp, " irq_chip_eoi: %ld\n", ++ OFFSET(irq_chip_eoi)); ++ fprintf(fp, " irq_chip_end: %ld\n", ++ OFFSET(irq_chip_end)); ++ fprintf(fp, " irq_chip_set_affinity: %ld\n", ++ OFFSET(irq_chip_set_affinity)); ++ fprintf(fp, " irq_chip_retrigger: %ld\n", ++ OFFSET(irq_chip_retrigger)); ++ fprintf(fp, " irq_chip_set_type: %ld\n", ++ OFFSET(irq_chip_set_type)); ++ fprintf(fp, " irq_chip_set_wake: %ld\n", ++ OFFSET(irq_chip_set_wake)); ++ + fprintf(fp, "irq_cpustat_t___softirq_active: %ld\n", + OFFSET(irq_cpustat_t___softirq_active)); + fprintf(fp, " irq_cpustat_t___softirq_mask: %ld\n", + OFFSET(irq_cpustat_t___softirq_mask)); + ++ fprintf(fp, " files_struct_fdt: %ld\n", ++ OFFSET(files_struct_fdt)); ++ fprintf(fp, " fdtable_max_fds: %ld\n", ++ OFFSET(fdtable_max_fds)); ++ fprintf(fp, " fdtable_max_fdset: %ld\n", ++ OFFSET(fdtable_max_fdset)); ++ fprintf(fp, " fdtable_open_fds: %ld\n", ++ OFFSET(fdtable_open_fds)); ++ fprintf(fp, " fdtable_fd: %ld\n", ++ OFFSET(fdtable_fd)); + fprintf(fp, " files_struct_max_fds: %ld\n", + OFFSET(files_struct_max_fds)); + fprintf(fp, " files_struct_max_fdset: %ld\n", +@@ -5988,6 +6474,12 @@ + OFFSET(file_f_vfsmnt)); + fprintf(fp, " file_f_count: %ld\n", + OFFSET(file_f_count)); ++ fprintf(fp, " file_f_path: %ld\n", ++ OFFSET(file_f_path)); ++ fprintf(fp, " path_mnt: %ld\n", ++ OFFSET(path_mnt)); ++ fprintf(fp, " path_dentry: %ld\n", ++ OFFSET(path_dentry)); + fprintf(fp, " fs_struct_root: %ld\n", + OFFSET(fs_struct_root)); + fprintf(fp, " fs_struct_pwd: %ld\n", +@@ -6165,6 +6657,51 @@ + fprintf(fp, " slab_free: %ld\n", + OFFSET(slab_free)); + ++ fprintf(fp, " kmem_cache_size: %ld\n", ++ OFFSET(kmem_cache_size)); ++ fprintf(fp, " kmem_cache_objsize: %ld\n", ++ OFFSET(kmem_cache_objsize)); ++ fprintf(fp, " kmem_cache_offset: %ld\n", ++ OFFSET(kmem_cache_offset)); ++ fprintf(fp, " kmem_cache_order: %ld\n", ++ OFFSET(kmem_cache_order)); ++ fprintf(fp, " kmem_cache_local_node: %ld\n", ++ OFFSET(kmem_cache_local_node)); ++ fprintf(fp, " kmem_cache_objects: %ld\n", ++ OFFSET(kmem_cache_objects)); ++ fprintf(fp, " kmem_cache_inuse: %ld\n", ++ OFFSET(kmem_cache_inuse)); ++ fprintf(fp, " kmem_cache_align: %ld\n", ++ OFFSET(kmem_cache_align)); ++ fprintf(fp, " kmem_cache_name: %ld\n", ++ OFFSET(kmem_cache_name)); ++ fprintf(fp, " kmem_cache_list: %ld\n", ++ OFFSET(kmem_cache_list)); ++ fprintf(fp, " kmem_cache_node: %ld\n", ++ OFFSET(kmem_cache_node)); ++ fprintf(fp, " kmem_cache_cpu_slab: %ld\n", ++ OFFSET(kmem_cache_cpu_slab)); ++ fprintf(fp, " kmem_cache_oo: %ld\n", ++ OFFSET(kmem_cache_oo)); ++ ++ fprintf(fp, " kmem_cache_node_nr_partial: %ld\n", ++ OFFSET(kmem_cache_node_nr_partial)); ++ fprintf(fp, " kmem_cache_node_nr_slabs: %ld\n", ++ OFFSET(kmem_cache_node_nr_slabs)); ++ fprintf(fp, " kmem_cache_node_partial: %ld\n", ++ OFFSET(kmem_cache_node_partial)); ++ fprintf(fp, " kmem_cache_node_full: %ld\n", ++ OFFSET(kmem_cache_node_full)); ++ ++ fprintf(fp, " kmem_cache_cpu_freelist: %ld\n", ++ OFFSET(kmem_cache_cpu_freelist)); ++ fprintf(fp, " kmem_cache_cpu_page: %ld\n", ++ OFFSET(kmem_cache_cpu_page)); ++ fprintf(fp, " kmem_cache_cpu_node: %ld\n", ++ OFFSET(kmem_cache_cpu_node)); ++ fprintf(fp, " kmem_cache_flags: %ld\n", ++ OFFSET(kmem_cache_flags)); ++ + fprintf(fp, " net_device_next: %ld\n", + OFFSET(net_device_next)); + fprintf(fp, " net_device_name: %ld\n", +@@ -6175,6 +6712,11 @@ + OFFSET(net_device_addr_len)); + fprintf(fp, " net_device_ip_ptr: %ld\n", + OFFSET(net_device_ip_ptr)); ++ fprintf(fp, " net_device_dev_list: %ld\n", ++ OFFSET(net_device_dev_list)); ++ fprintf(fp, " net_dev_base_head: %ld\n", ++ OFFSET(net_dev_base_head)); ++ + fprintf(fp, " device_next: %ld\n", + OFFSET(device_next)); + fprintf(fp, " device_name: %ld\n", +@@ -6217,6 +6759,11 @@ + fprintf(fp, " inet_opt_num: %ld\n", + OFFSET(inet_opt_num)); + ++ fprintf(fp, " ipv6_pinfo_rcv_saddr: %ld\n", ++ OFFSET(ipv6_pinfo_rcv_saddr)); ++ fprintf(fp, " ipv6_pinfo_daddr: %ld\n", ++ OFFSET(ipv6_pinfo_daddr)); ++ + fprintf(fp, " timer_list_list: %ld\n", + OFFSET(timer_list_list)); + fprintf(fp, " timer_list_next: %ld\n", +@@ -6291,6 +6838,8 @@ + OFFSET(zone_struct_size)); + fprintf(fp, " zone_struct_memsize: %ld\n", + OFFSET(zone_struct_memsize)); ++ fprintf(fp, " zone_struct_zone_start_pfn: %ld\n", ++ OFFSET(zone_struct_zone_start_pfn)); + fprintf(fp, " zone_struct_zone_start_paddr: %ld\n", + OFFSET(zone_struct_zone_start_paddr)); + fprintf(fp, " zone_struct_zone_start_mapnr: %ld\n", +@@ -6324,6 +6873,8 @@ + OFFSET(zone_name)); + fprintf(fp, " zone_spanned_pages: %ld\n", + OFFSET(zone_spanned_pages)); ++ fprintf(fp, " zone_present_pages: %ld\n", ++ OFFSET(zone_present_pages)); + fprintf(fp, " zone_zone_start_pfn: %ld\n", + OFFSET(zone_zone_start_pfn)); + fprintf(fp, " zone_pages_min: %ld\n", +@@ -6332,6 +6883,18 @@ + OFFSET(zone_pages_low)); + fprintf(fp, " zone_pages_high: %ld\n", + OFFSET(zone_pages_high)); ++ fprintf(fp, " zone_vm_stat: %ld\n", ++ OFFSET(zone_vm_stat)); ++ fprintf(fp, " zone_nr_active: %ld\n", ++ OFFSET(zone_nr_active)); ++ fprintf(fp, " zone_nr_inactive: %ld\n", ++ OFFSET(zone_nr_inactive)); ++ fprintf(fp, " zone_all_unreclaimable: %ld\n", ++ OFFSET(zone_all_unreclaimable)); ++ fprintf(fp, " zone_flags: %ld\n", ++ OFFSET(zone_flags)); ++ fprintf(fp, " zone_pages_scanned: %ld\n", ++ OFFSET(zone_pages_scanned)); + + fprintf(fp, " neighbour_next: %ld\n", + OFFSET(neighbour_next)); +@@ -6471,10 +7034,61 @@ + OFFSET(x8664_pda_irqstackptr)); + fprintf(fp, " x8664_pda_level4_pgt: %ld\n", + OFFSET(x8664_pda_level4_pgt)); ++ fprintf(fp, " x8664_pda_me: %ld\n", ++ OFFSET(x8664_pda_me)); + + fprintf(fp, " tss_struct_ist: %ld\n", + OFFSET(tss_struct_ist)); ++ fprintf(fp, " mem_section_section_mem_map: %ld\n", ++ OFFSET(mem_section_section_mem_map)); + ++ fprintf(fp, " vcpu_guest_context_user_regs: %ld\n", ++ OFFSET(vcpu_guest_context_user_regs)); ++ fprintf(fp, " cpu_user_regs_eip: %ld\n", ++ OFFSET(cpu_user_regs_eip)); ++ fprintf(fp, " cpu_user_regs_esp: %ld\n", ++ OFFSET(cpu_user_regs_esp)); ++ fprintf(fp, " cpu_user_regs_rip: %ld\n", ++ OFFSET(cpu_user_regs_rip)); ++ fprintf(fp, " cpu_user_regs_rsp: %ld\n", ++ OFFSET(cpu_user_regs_rsp)); ++ fprintf(fp, " unwind_table_core: %ld\n", ++ OFFSET(unwind_table_core)); ++ fprintf(fp, " unwind_table_init: %ld\n", ++ OFFSET(unwind_table_init)); ++ fprintf(fp, " unwind_table_address: %ld\n", ++ OFFSET(unwind_table_address)); ++ fprintf(fp, " unwind_table_size: %ld\n", ++ OFFSET(unwind_table_size)); ++ fprintf(fp, " unwind_table_link: %ld\n", ++ OFFSET(unwind_table_link)); ++ fprintf(fp, " unwind_table_name: %ld\n", ++ OFFSET(unwind_table_name)); ++ ++ fprintf(fp, " rq_cfs: %ld\n", ++ OFFSET(rq_cfs)); ++ fprintf(fp, " rq_rt: %ld\n", ++ OFFSET(rq_rt)); ++ fprintf(fp, " rq_nr_running: %ld\n", ++ OFFSET(rq_nr_running)); ++ fprintf(fp, " task_struct_se: %ld\n", ++ OFFSET(task_struct_se)); ++ fprintf(fp, " sched_entity_run_node: %ld\n", ++ OFFSET(sched_entity_run_node)); ++ fprintf(fp, " cfs_rq_nr_running: %ld\n", ++ OFFSET(cfs_rq_nr_running)); ++ fprintf(fp, " cfs_rq_rb_leftmost: %ld\n", ++ OFFSET(cfs_rq_rb_leftmost)); ++ fprintf(fp, " cfs_rq_tasks_timeline: %ld\n", ++ OFFSET(cfs_rq_tasks_timeline)); ++ fprintf(fp, " rt_rq_active: %ld\n", ++ OFFSET(rt_rq_active)); ++ fprintf(fp, " pcpu_info_vcpu: %ld\n", ++ OFFSET(pcpu_info_vcpu)); ++ fprintf(fp, " pcpu_info_idle: %ld\n", ++ OFFSET(pcpu_info_idle)); ++ fprintf(fp, " vcpu_struct_rq: %ld\n", ++ OFFSET(vcpu_struct_rq)); + + fprintf(fp, "\n size_table:\n"); + fprintf(fp, " page: %ld\n", SIZE(page)); +@@ -6493,6 +7107,10 @@ + fprintf(fp, " array_cache: %ld\n", SIZE(array_cache)); + fprintf(fp, " kmem_bufctl_t: %ld\n", + SIZE(kmem_bufctl_t)); ++ fprintf(fp, " kmem_cache: %ld\n", SIZE(kmem_cache)); ++ fprintf(fp, " kmem_cache_node: %ld\n", SIZE(kmem_cache_node)); ++ fprintf(fp, " kmem_cache_cpu: %ld\n", SIZE(kmem_cache_cpu)); ++ + fprintf(fp, " swap_info_struct: %ld\n", + SIZE(swap_info_struct)); + fprintf(fp, " vm_area_struct: %ld\n", +@@ -6512,6 +7130,7 @@ + fprintf(fp, " fs_struct: %ld\n", SIZE(fs_struct)); + fprintf(fp, " files_struct: %ld\n", + SIZE(files_struct)); ++ fprintf(fp, " fdtable: %ld\n", SIZE(fdtable)); + fprintf(fp, " file: %ld\n", SIZE(file)); + fprintf(fp, " inode: %ld\n", SIZE(inode)); + fprintf(fp, " vfsmount: %ld\n", SIZE(vfsmount)); +@@ -6546,8 +7165,11 @@ + fprintf(fp, " sock: %ld\n", SIZE(sock)); + fprintf(fp, " inet_sock: %ld\n", SIZE(inet_sock)); + fprintf(fp, " socket: %ld\n", SIZE(socket)); ++ fprintf(fp, " in6_addr: %ld\n", SIZE(in6_addr)); + fprintf(fp, " signal_struct: %ld\n", + SIZE(signal_struct)); ++ fprintf(fp, " sigpending_signal: %ld\n", ++ SIZE(sigpending_signal)); + fprintf(fp, " signal_queue: %ld\n", + SIZE(signal_queue)); + fprintf(fp, " sigqueue: %ld\n", SIZE(sigqueue)); +@@ -6601,6 +7223,8 @@ + + fprintf(fp, " x8664_pda: %ld\n", + SIZE(x8664_pda)); ++ fprintf(fp, " ppc64_paca: %ld\n", ++ SIZE(ppc64_paca)); + fprintf(fp, " gate_struct: %ld\n", + SIZE(gate_struct)); + fprintf(fp, " tss_struct: %ld\n", +@@ -6609,7 +7233,22 @@ + SIZE(task_struct_start_time)); + fprintf(fp, " cputime_t: %ld\n", + SIZE(cputime_t)); +- ++ fprintf(fp, " mem_section: %ld\n", ++ SIZE(mem_section)); ++ fprintf(fp, " pid_link: %ld\n", ++ SIZE(pid_link)); ++ fprintf(fp, " upid: %ld\n", ++ SIZE(upid)); ++ fprintf(fp, " unwind_table: %ld\n", ++ SIZE(unwind_table)); ++ fprintf(fp, " rlimit: %ld\n", ++ SIZE(rlimit)); ++ fprintf(fp, " cfs_rq: %ld\n", ++ SIZE(cfs_rq)); ++ fprintf(fp, " pcpu_info: %ld\n", ++ SIZE(pcpu_info)); ++ fprintf(fp, " vcpu_struct: %ld\n", ++ SIZE(vcpu_struct)); + + fprintf(fp, "\n array_table:\n"); + /* +@@ -6663,6 +7302,12 @@ + get_array_length("prio_array.queue", NULL, SIZE(list_head))); + fprintf(fp, " height_to_maxindex: %d\n", + ARRAY_LENGTH(height_to_maxindex)); ++ fprintf(fp, " pid_hash: %d\n", ++ ARRAY_LENGTH(pid_hash)); ++ fprintf(fp, " kmem_cache_node: %d\n", ++ ARRAY_LENGTH(kmem_cache_node)); ++ fprintf(fp, " kmem_cache_cpu_slab: %d\n", ++ ARRAY_LENGTH(kmem_cache_cpu_slab)); + + if (spec) { + int in_size_table, in_array_table, arrays, offsets, sizes; +@@ -6862,14 +7507,14 @@ + { + int i; + struct load_module *lm; +- int request; ++ ulong request; + asection **sec; + +- request = (int)((ulong)reqptr); ++ request = ((ulong)reqptr); + + switch (request) + { +- case (uint)KERNEL_SECTIONS: ++ case (ulong)KERNEL_SECTIONS: + sec = (asection **)st->sections; + for (i = 0; (i < st->bfd->section_count) && *sec; i++) + sec++; +@@ -6890,14 +7535,18 @@ + SEC_HAS_CONTENTS)) + st->flags |= NO_SEC_CONTENTS; + } ++ if (STREQ(bfd_get_section_name(bfd, section), ".eh_frame")) { ++ st->dwarf_eh_frame_file_offset = (off_t)section->filepos; ++ st->dwarf_eh_frame_size = (ulong)bfd_section_size(bfd, section); ++ } + break; + +- case (uint)MODULE_SECTIONS: ++ case (ulong)MODULE_SECTIONS: + lm = st->current; + store_section_data(lm, bfd, section); + break; + +- case (uint)VERIFY_SECTIONS: ++ case (ulong)VERIFY_SECTIONS: + if (STREQ(bfd_get_section_name(bfd, section), ".text") || + STREQ(bfd_get_section_name(bfd, section), ".data")) { + if (!(bfd_get_section_flags(bfd, section) & SEC_LOAD)) +@@ -6906,6 +7555,10 @@ + SEC_HAS_CONTENTS)) + st->flags |= NO_SEC_CONTENTS; + } ++ if (STREQ(bfd_get_section_name(bfd, section), ".eh_frame")) { ++ st->dwarf_eh_frame_file_offset = (off_t)section->filepos; ++ st->dwarf_eh_frame_size = (ulong)bfd_section_size(bfd, section); ++ } + break; + + default: +@@ -6960,8 +7613,9 @@ + i = lm->mod_sections; + lm->mod_section_data[i].section = section; + lm->mod_section_data[i].priority = prio; +- lm->mod_section_data[i].flags = section->flags; ++ lm->mod_section_data[i].flags = section->flags & ~SEC_FOUND; + lm->mod_section_data[i].size = bfd_section_size(bfd, section); ++ lm->mod_section_data[i].offset = 0; + if (strlen(name) < MAX_MOD_SEC_NAME) + strcpy(lm->mod_section_data[i].name, name); + else +@@ -7013,7 +7667,7 @@ + */ + + static void +-calculate_load_order(struct load_module *lm, bfd *bfd) ++calculate_load_order_v1(struct load_module *lm, bfd *bfd) + { + int i; + asection *section; +@@ -7073,6 +7727,134 @@ + } + + /* ++ * Later versions of kmod no longer get the help from insmod, ++ * and while the heuristics might work, it's relatively ++ * straightforward to just try to match the sections in the object file ++ * with exported symbols. ++ * ++ * This works well if kallsyms is set, but may not work so well in other ++ * instances. ++ */ ++static void ++calculate_load_order_v2(struct load_module *lm, bfd *bfd, int dynamic, ++ void *minisyms, long symcount, unsigned int size) ++{ ++ struct syment *s1, *s2; ++ ulong sec_start, sec_end; ++ bfd_byte *from, *fromend; ++ asymbol *store; ++ asymbol *sym; ++ symbol_info syminfo; ++ char *secname; ++ int i; ++ ++ if ((store = bfd_make_empty_symbol(bfd)) == NULL) ++ error(FATAL, "bfd_make_empty_symbol() failed\n"); ++ ++ s1 = lm->mod_symtable; ++ s2 = lm->mod_symend; ++ while (s1 < s2) { ++ ulong sym_offset = s1->value - lm->mod_base; ++ if (MODULE_PSEUDO_SYMBOL(s1)) { ++ s1++; ++ continue; ++ } ++ ++ /* Skip over symbols whose sections have been identified. */ ++ for (i = 0; i < lm->mod_sections; i++) { ++ if ((lm->mod_section_data[i].flags & SEC_FOUND) == 0) ++ continue; ++ if (sym_offset >= lm->mod_section_data[i].offset ++ && sym_offset < lm->mod_section_data[i].offset ++ + lm->mod_section_data[i].size) { ++ break; ++ } ++ } ++ ++ /* Matched one of the sections. Skip symbol. */ ++ if (i < lm->mod_sections) { ++ if (CRASHDEBUG(2)) { ++ fprintf(fp, "skip %lx %s %s\n", s1->value, s1->name, ++ lm->mod_section_data[i].name); ++ } ++ s1++; ++ continue; ++ } ++ ++ /* Find the symbol in the object file. */ ++ from = (bfd_byte *) minisyms; ++ fromend = from + symcount * size; ++ secname = NULL; ++ for (; from < fromend; from += size) { ++ if ((sym = bfd_minisymbol_to_symbol(bfd, dynamic, from, ++ store)) == NULL) ++ error(FATAL, ++ "bfd_minisymbol_to_symbol() failed\n"); ++ ++ bfd_get_symbol_info(bfd, sym, &syminfo); ++ if (CRASHDEBUG(3)) { ++ fprintf(fp,"matching sym %s %lx against bfd %s %lx\n", ++ s1->name, (long) s1->value, syminfo.name, ++ (long) syminfo.value); ++ } ++ if (strcmp(syminfo.name, s1->name) == 0) { ++ secname = (char *)bfd_get_section_name(bfd, sym->section); ++ break; ++ } ++ ++ } ++ if (secname == NULL) { ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "symbol %s not found in module\n", s1->name); ++ } ++ s1++; ++ continue; ++ } ++ ++ /* Match the section it came in. */ ++ for (i = 0; i < lm->mod_sections; i++) { ++ if (STREQ(lm->mod_section_data[i].name, secname)) { ++ break; ++ } ++ } ++ ++ if (i == lm->mod_sections) { ++ fprintf(fp, "?? Section %s not found for symbol %s\n", ++ secname, s1->name); ++ s1++; ++ continue; ++ } ++ ++ /* Update the offset information for the section */ ++ sec_start = s1->value - syminfo.value; ++ sec_end = sec_start + lm->mod_section_data[i].size; ++ lm->mod_section_data[i].offset = sec_start - lm->mod_base; ++ lm->mod_section_data[i].flags |= SEC_FOUND; ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "update sec offset sym %s @ %lx val %lx section %s\n", ++ s1->name, s1->value, syminfo.value, secname); ++ } ++ ++ if (strcmp(secname, ".text") == 0) ++ lm->mod_text_start = sec_start; ++ ++ if (strcmp(secname, ".bss") == 0) ++ lm->mod_bss_start = sec_start; ++ ++ if (strcmp(secname, ".data") == 0) ++ lm->mod_data_start = sec_start; ++ ++ if (strcmp(secname, ".data") == 0) ++ lm->mod_data_start = sec_start; ++ ++ if (strcmp(secname, ".rodata") == 0) ++ lm->mod_rodata_start = sec_start; ++ s1++; ++ } ++} ++ ++/* + * Later versons of insmod store basic address information of each + * module in a format that looks like the following example of the + * nfsd module: +@@ -7185,8 +7967,8 @@ + } + + if (CRASHDEBUG(1)) +- fprintf(fp, "load_module_symbols: %s %s %lx\n", +- modref, namelist, base_addr); ++ fprintf(fp, "load_module_symbols: %s %s %lx %lx\n", ++ modref, namelist, base_addr, kt->flags); + + switch (kt->flags & (KMOD_V1|KMOD_V2)) + { +@@ -7199,7 +7981,8 @@ + strcpy(lm->mod_namelist, namelist); + else + strncpy(lm->mod_namelist, namelist, MAX_MOD_NAMELIST-1); +- goto add_symbols; ++ if (st->flags & USE_OLD_ADD_SYM) ++ goto add_symbols; + } + + if ((mbfd = bfd_openr(namelist, NULL)) == NULL) +@@ -7209,8 +7992,11 @@ + error(FATAL, "cannot determine object file format: %s\n", + namelist); + +- if (!(bfd_get_file_flags(mbfd) & HAS_SYMS)) +- error(FATAL, "no symbols in object file: %s\n", namelist); ++ if (LKCD_KERNTYPES() && (file_elf_version(namelist) == EV_DWARFEXTRACT)) ++ goto add_symbols; /* no symbols, add the debuginfo */ ++ ++ if (!(bfd_get_file_flags(mbfd) & HAS_SYMS)) ++ error(FATAL, "no symbols in object file: %s\n", namelist); + + symcount = bfd_read_minisymbols(mbfd, FALSE, &minisyms, &size); + if (symcount < 0) +@@ -7219,6 +8005,10 @@ + else if (symcount == 0) + error(FATAL, "no symbols in object file: %s\n", namelist); + ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "%ld symbols found in obj file %s\n", symcount, ++ namelist); ++ } + sort_x = bfd_make_empty_symbol(mbfd); + sort_y = bfd_make_empty_symbol(mbfd); + if (sort_x == NULL || sort_y == NULL) +@@ -7251,17 +8041,33 @@ + add_symbol_file(struct load_module *lm) + { + struct gnu_request request, *req; +- char buf[BUFSIZE]; ++ char buf[BUFSIZE]; ++ int i, len; ++ char *secname; ++ ++ for (i = len = 0; i < lm->mod_sections; i++) ++ { ++ secname = lm->mod_section_data[i].name; ++ if ((lm->mod_section_data[i].flags & SEC_FOUND) && ++ !STREQ(secname, ".text")) { ++ sprintf(buf, " -s %s 0x%lx", secname, ++ lm->mod_section_data[i].offset + lm->mod_base); ++ len += strlen(buf); ++ } ++ } + + req = &request; + BZERO(req, sizeof(struct gnu_request)); + req->command = GNU_ADD_SYMBOL_FILE; + req->addr = (ulong)lm; +- req->buf = buf; ++ req->buf = GETBUF(len+BUFSIZE); + if (!CRASHDEBUG(1)) + req->fp = pc->nullfp; + +- gdb_interface(req); ++ st->flags |= ADD_SYMBOL_FILE; ++ gdb_interface(req); ++ st->flags &= ~ADD_SYMBOL_FILE; ++ FREEBUF(req->buf); + + sprintf(buf, "set complaints 0"); + gdb_pass_through(buf, NULL, 0); +@@ -7382,7 +8188,12 @@ + + bfd_map_over_sections(bfd, section_header_info, MODULE_SECTIONS); + +- calculate_load_order(lm, bfd); ++ if (kt->flags & KMOD_V1) ++ calculate_load_order_v1(lm, bfd); ++ else ++ calculate_load_order_v2(lm, bfd, dynamic, minisyms, ++ symcount, size); ++ + + from = (bfd_byte *) minisyms; + fromend = from + symcount * size; +@@ -7395,104 +8206,112 @@ + bfd_get_symbol_info(bfd, sym, &syminfo); + + secname = (char *)bfd_get_section_name(bfd, sym->section); ++ found = 0; + +- switch (syminfo.type) +- { +- case 'b': +- case 'B': +- if (CRASHDEBUG(2)) +- fprintf(fp, "%08lx (%c) [%s] %s\n", +- (ulong)syminfo.value, +- syminfo.type, secname, syminfo.name); ++ if (kt->flags & KMOD_V1) { ++ switch (syminfo.type) ++ { ++ case 'b': ++ case 'B': ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%08lx (%c) [%s] %s\n", ++ (ulong)syminfo.value, ++ syminfo.type, secname, syminfo.name); + +- syminfo.value += lm->mod_bss_start; +- strcpy(name, syminfo.name); +- strip_module_symbol_end(name); ++ if (!lm->mod_bss_start) ++ break; + +- if (machdep->verify_symbol(name, syminfo.value, +- syminfo.type)) { +- sp->value = syminfo.value; +- sp->type = syminfo.type; +- +- namespace_ctl(NAMESPACE_INSTALL, +- &lm->mod_load_namespace, sp, name); ++ syminfo.value += lm->mod_bss_start; ++ found = 1; ++ break; + +- if (CRASHDEBUG(1)) +- fprintf(fp, "%08lx %s\n", sp->value, +- name); ++ case 'd': ++ case 'D': ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%08lx (%c) [%s] %s\n", ++ (ulong)syminfo.value, ++ syminfo.type, secname, syminfo.name); ++ ++ if (STREQ(secname, ".rodata")) { ++ if (!lm->mod_rodata_start) ++ break; ++ syminfo.value += lm->mod_rodata_start; ++ } else { ++ if (!lm->mod_data_start) ++ break; ++ syminfo.value += lm->mod_data_start; ++ } ++ found = 1; ++ break; + +- sp++; +- lm->mod_load_symcnt++; +- } +- break; ++ case 't': ++ case 'T': ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%08lx (%c) [%s] %s\n", ++ (ulong)syminfo.value, ++ syminfo.type, secname, syminfo.name); + +- case 'd': +- case 'D': +- if (CRASHDEBUG(2)) +- fprintf(fp, "%08lx (%c) [%s] %s\n", +- (ulong)syminfo.value, +- syminfo.type, secname, syminfo.name); ++ if (! lm->mod_text_start) { ++ break; ++ } + +- if (STREQ(secname, ".rodata")) +- syminfo.value += lm->mod_rodata_start; +- else +- syminfo.value += lm->mod_data_start; ++ if ((st->flags & INSMOD_BUILTIN) && ++ (STREQ(name, "init_module") || ++ STREQ(name, "cleanup_module"))) ++ break; ++ ++ syminfo.value += lm->mod_text_start; ++ found = 1; ++ break; + ++ default: ++ break; ++ } ++ ++ } else { ++ /* Match the section it came in. */ ++ for (i = 0; i < lm->mod_sections; i++) { ++ if (STREQ(lm->mod_section_data[i].name, secname) ++ && (lm->mod_section_data[i].flags & SEC_FOUND)) { ++ break; ++ } ++ } ++ if (i < lm->mod_sections) { ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%08lx (%c) [%s] %s\n", ++ (ulong)syminfo.value, ++ syminfo.type, secname, syminfo.name); ++ ++ if ((st->flags & INSMOD_BUILTIN) && ++ (STREQ(name, "init_module") || ++ STREQ(name, "cleanup_module"))) { ++ found = 0; ++ } else { ++ syminfo.value += lm->mod_section_data[i].offset + lm->mod_base; ++ found = 1; ++ } ++ } ++ } ++ ++ if (found) { + strcpy(name, syminfo.name); + strip_module_symbol_end(name); + +- if (machdep->verify_symbol(name, syminfo.value, +- syminfo.type)) { ++ if (machdep->verify_symbol(name, syminfo.value, ++ syminfo.type)) { + sp->value = syminfo.value; +- sp->type = syminfo.type; ++ sp->type = syminfo.type; + namespace_ctl(NAMESPACE_INSTALL, +- &lm->mod_load_namespace, sp, name); ++ &lm->mod_load_namespace, sp, name); + + if (CRASHDEBUG(1)) +- fprintf(fp, "%08lx %s\n", sp->value, ++ fprintf(fp, "installing %c %08lx %s\n", syminfo.type, sp->value, + name); + + sp++; + lm->mod_load_symcnt++; + } +- break; +- +- case 't': +- case 'T': +- if (CRASHDEBUG(2)) +- fprintf(fp, "%08lx (%c) [%s] %s\n", +- (ulong)syminfo.value, +- syminfo.type, secname, syminfo.name); +- +- syminfo.value += lm->mod_text_start; +- strcpy(name, syminfo.name); +- strip_module_symbol_end(name); +- +- if ((st->flags & INSMOD_BUILTIN) && +- (STREQ(name, "init_module") || +- STREQ(name, "cleanup_module"))) +- break; +- +- if (machdep->verify_symbol(name, syminfo.value, +- syminfo.type)) { +- sp->value = syminfo.value; +- sp->type = syminfo.type; +- namespace_ctl(NAMESPACE_INSTALL, +- &lm->mod_load_namespace, sp, name); +- +- if (CRASHDEBUG(1)) +- fprintf(fp, "%08lx %s\n", sp->value, +- name); +- +- sp++; +- lm->mod_load_symcnt++; +- } +- +- break; +- +- default: +- break; +- } ++ } + } + + lm->mod_load_symend = &lm->mod_load_symtable[lm->mod_load_symcnt]; +@@ -7713,7 +8532,7 @@ + ulong start, end; + char *modbuf; + ulong maxchunk, alloc; +- long offset; ++ long offset = 0; + + start = roundup(lm->mod_size_of_struct, sizeof(long)) + lm->mod_base; + end = lm->mod_base + lm->mod_size; +@@ -8089,6 +8908,10 @@ + struct syment *sp_array[200], *sp; + + if (req->name == PATCH_KERNEL_SYMBOLS_START) { ++ if (kt->flags & RELOC_FORCE) ++ error(WARNING, ++ "\nkernel relocated [%ldMB]: patching %ld gdb minimal_symbol values\n", ++ kt->relocate >> 20, st->symcnt); + fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" : + "\nplease wait... (patching %ld gdb minimal_symbol values) ", + st->symcnt); +@@ -8164,8 +8987,8 @@ + return offset2; + + if (pc->flags & DATADEBUG) { +- ulong retaddr[4] = { 0 }; +- save_return_address(retaddr); ++ ulong retaddr[NUMBER_STACKFRAMES] = { 0 }; ++ SAVE_RETURN_ADDRESS(retaddr); + sprintf(errmsg, + "invalid (optional) structure member offsets: %s or %s", + item1, item2); +@@ -8187,8 +9010,8 @@ + return size2; + + if (pc->flags & DATADEBUG) { +- ulong retaddr[4] = { 0 }; +- save_return_address(retaddr); ++ ulong retaddr[NUMBER_STACKFRAMES] = { 0 }; ++ SAVE_RETURN_ADDRESS(retaddr); + sprintf(errmsg, "invalid (optional) structure sizes: %s or %s", + item1, item2); + datatype_error(retaddr, errmsg, func, file, line); +@@ -8215,8 +9038,8 @@ + return offset; + + if (offset < 0) { +- ulong retaddr[4] = { 0 }; +- save_return_address(retaddr); ++ ulong retaddr[NUMBER_STACKFRAMES] = { 0 }; ++ SAVE_RETURN_ADDRESS(retaddr); + sprintf(errmsg, "invalid structure member offset: %s", + item); + datatype_error(retaddr, errmsg, func, file, line); +@@ -8233,8 +9056,8 @@ + return size; + + if (size < 0) { +- ulong retaddr[4] = { 0 }; +- save_return_address(retaddr); ++ ulong retaddr[NUMBER_STACKFRAMES] = { 0 }; ++ SAVE_RETURN_ADDRESS(retaddr); + sprintf(errmsg, "invalid structure size: %s", item); + datatype_error(retaddr, errmsg, func, file, line); + } +@@ -8267,7 +9090,8 @@ + if (pc->flags & DROP_CORE) + drop_core("DROP_CORE flag set: forcing a segmentation fault\n"); + +- gdb_readnow_warning(); ++ if (CRASHDEBUG(1)) ++ gdb_readnow_warning(); + + if (pc->flags & RUNTIME) { + sprintf(buf, "%s\n%s FILE: %s LINE: %d FUNCTION: %s()\n", +@@ -8290,9 +9114,10 @@ + char *arglist[MAXARGS]; + char buf[BUFSIZE]; + FILE *pipe; +- ulong vaddr, lookfor; +- ulong last_vaddr; ++ ulong vaddr, size, lookfor; ++ ulong last_vaddr, last_size; + char symbol[BUFSIZE]; ++ const char *nm_call; + + fflush(fp); + fflush(stdout); +@@ -8301,7 +9126,7 @@ + thisfile = get_thisfile(); + + fprintf(stderr, "[%s] error trace: ", thisfile); +- for (i = 3; i >= 0; i--) { ++ for (i = (NUMBER_STACKFRAMES-1); i >= 0; i--) { + if (retaddr[i]) + fprintf(stderr, "%s%lx%s", + i == 3 ? "" : "=> ", +@@ -8315,11 +9140,16 @@ + return; + } + +- for (i = 0; i < 4; i++) { ++ if (is_binary_stripped(thisfile)) ++ nm_call = "/usr/bin/nm -DSBn %s"; ++ else ++ nm_call = "/usr/bin/nm -BSn %s"; ++ ++ for (i = 0; i < NUMBER_STACKFRAMES; i++) { + if (!(lookfor = retaddr[i])) + continue; + +- sprintf(buf, "/usr/bin/nm -Bn %s", thisfile); ++ sprintf(buf, nm_call, thisfile); + if (!(pipe = popen(buf, "r"))) { + perror("pipe"); + break; +@@ -8328,20 +9158,27 @@ + last_vaddr = 0; + BZERO(symbol, BUFSIZE); + +- while (fgets(buf, 80, pipe)) { ++ while (fgets(buf, BUFSIZE, pipe)) { + c = parse_line(strip_linefeeds(buf), arglist); +- if (c != 3) ++ if (c != 4) + continue; + vaddr = htol(arglist[0], FAULT_ON_ERROR, NULL); ++ size = htol(arglist[1], FAULT_ON_ERROR, NULL); + if (vaddr > lookfor) { +- fprintf(stderr, "%s %lx: %s+%ld\n", +- i == 0 ? "\n" : "", +- lookfor, symbol, +- lookfor-last_vaddr); ++ if ((lookfor - last_vaddr) > last_size) ++ fprintf(stderr, "%s %lx: (undetermined)\n", ++ i == 0 ? "\n" : "", ++ lookfor); ++ else ++ fprintf(stderr, "%s %lx: %s+%ld\n", ++ i == 0 ? "\n" : "", ++ lookfor, symbol, ++ lookfor-last_vaddr); + break; + } +- strcpy(symbol, arglist[2]); ++ strcpy(symbol, arglist[3]); + last_vaddr = vaddr; ++ last_size = size; + } + + pclose(pipe); +--- crash/xendump.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/xendump.c 2008-01-02 09:15:13.000000000 -0500 +@@ -0,0 +1,2848 @@ ++/* ++ * xendump.c ++ * ++ * Copyright (C) 2006, 2007, 2008 David Anderson ++ * Copyright (C) 2006, 2007, 2008 Red Hat, Inc. All rights reserved. ++ * ++ * This software may be freely redistributed under the terms of the ++ * GNU General Public License. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "defs.h" ++#include "xendump.h" ++ ++static struct xendump_data xendump_data = { 0 }; ++static struct xendump_data *xd = &xendump_data; ++ ++static int xc_save_verify(char *); ++static int xc_core_verify(char *, char *); ++static int xc_save_read(void *, int, ulong, physaddr_t); ++static int xc_core_read(void *, int, ulong, physaddr_t); ++static int xc_core_mfns(ulong, FILE *); ++ ++static void poc_store(ulong, off_t); ++static off_t poc_get(ulong, int *); ++ ++static void xen_dump_vmconfig(FILE *); ++ ++static void xc_core_create_pfn_tables(void); ++static ulong xc_core_pfn_to_page_index(ulong); ++static int xc_core_pfn_valid(ulong); ++ ++static void xendump_print(char *fmt, ...); ++ ++static int xc_core_elf_verify(char *, char *); ++static void xc_core_elf_dump(void); ++static char *xc_core_elf_mfn_to_page(ulong, char *); ++static int xc_core_elf_mfn_to_page_index(ulong); ++static ulong xc_core_elf_pfn_valid(ulong); ++static ulong xc_core_elf_pfn_to_page_index(ulong); ++static void xc_core_dump_Elf32_Ehdr(Elf32_Ehdr *); ++static void xc_core_dump_Elf64_Ehdr(Elf64_Ehdr *); ++static void xc_core_dump_Elf32_Shdr(Elf32_Off offset, int); ++static void xc_core_dump_Elf64_Shdr(Elf64_Off offset, int); ++static char *xc_core_strtab(uint32_t, char *); ++static void xc_core_dump_elfnote(off_t, size_t, int); ++static void xc_core_elf_pfn_init(void); ++ ++#define ELFSTORE 1 ++#define ELFREAD 0 ++ ++/* ++ * Determine whether a file is a xendump creation, and if TRUE, ++ * initialize the xendump_data structure. ++ */ ++int ++is_xendump(char *file) ++{ ++ int verified; ++ char buf[BUFSIZE]; ++ ++ if ((xd->xfd = open(file, O_RDWR)) < 0) { ++ if ((xd->xfd = open(file, O_RDONLY)) < 0) { ++ sprintf(buf, "%s: open", file); ++ perror(buf); ++ return FALSE; ++ } ++ } ++ ++ if (read(xd->xfd, buf, BUFSIZE) != BUFSIZE) ++ return FALSE; ++ ++ if (machine_type("X86") || machine_type("X86_64")) ++ xd->page_size = 4096; ++ else if (machine_type("IA64") && !machdep->pagesize) ++ xd->page_size = 16384; ++ else ++ xd->page_size = machdep->pagesize; ++ ++ verified = xc_save_verify(buf) || xc_core_verify(file, buf); ++ ++ if (!verified) ++ close(xd->xfd); ++ ++ return (verified); ++} ++ ++/* ++ * Verify whether the dump was created by the xc_domain_dumpcore() ++ * library function in libxc/xc_core.c. ++ */ ++static int ++xc_core_verify(char *file, char *buf) ++{ ++ struct xc_core_header *xcp; ++ ++ xcp = (struct xc_core_header *)buf; ++ ++ if (xc_core_elf_verify(file, buf)) ++ return TRUE; ++ ++ if ((xcp->xch_magic != XC_CORE_MAGIC) && ++ (xcp->xch_magic != XC_CORE_MAGIC_HVM)) ++ return FALSE; ++ ++ if (!xcp->xch_nr_vcpus) { ++ error(INFO, ++ "faulty xc_core dump file header: xch_nr_vcpus is 0\n\n"); ++ ++ fprintf(stderr, " xch_magic: %x\n", xcp->xch_magic); ++ fprintf(stderr, " xch_nr_vcpus: %d\n", xcp->xch_nr_vcpus); ++ fprintf(stderr, " xch_nr_pages: %d\n", xcp->xch_nr_pages); ++ fprintf(stderr, " xch_ctxt_offset: %d\n", xcp->xch_ctxt_offset); ++ fprintf(stderr, " xch_index_offset: %d\n", xcp->xch_index_offset); ++ fprintf(stderr, " xch_pages_offset: %d\n\n", xcp->xch_pages_offset); ++ ++ clean_exit(1); ++ } ++ ++ BCOPY(xcp, &xd->xc_core.header, ++ sizeof(struct xc_core_header)); ++ ++ xd->flags |= (XENDUMP_LOCAL | XC_CORE_ORIG | XC_CORE_P2M_CREATE); ++ ++ if (xc_core_mfns(XC_CORE_64BIT_HOST, stderr)) ++ xd->flags |= XC_CORE_64BIT_HOST; ++ ++ if (!xd->page_size) ++ error(FATAL, ++ "unknown page size: use -p command line option\n"); ++ ++ if (!(xd->page = (char *)malloc(xd->page_size))) ++ error(FATAL, "cannot malloc page space."); ++ ++ if (!(xd->poc = (struct pfn_offset_cache *)calloc ++ (PFN_TO_OFFSET_CACHE_ENTRIES, ++ sizeof(struct pfn_offset_cache)))) ++ error(FATAL, "cannot malloc pfn_offset_cache\n"); ++ xd->last_pfn = ~(0UL); ++ ++ if (CRASHDEBUG(1)) ++ xendump_memory_dump(stderr); ++ ++ return TRUE; ++} ++ ++/* ++ * Do the work for read_xendump() for the XC_CORE dumpfile format. ++ */ ++static int ++xc_core_read(void *bufptr, int cnt, ulong addr, physaddr_t paddr) ++{ ++ ulong pfn, page_index; ++ off_t offset; ++ int redundant; ++ ++ if (xd->flags & (XC_CORE_P2M_CREATE|XC_CORE_PFN_CREATE)) ++ xc_core_create_pfn_tables(); ++ ++ pfn = (ulong)BTOP(paddr); ++ ++ if ((offset = poc_get(pfn, &redundant))) { ++ if (!redundant) { ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ return SEEK_ERROR; ++ if (read(xd->xfd, xd->page, xd->page_size) != ++ xd->page_size) ++ return READ_ERROR; ++ xd->last_pfn = pfn; ++ } ++ ++ BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt); ++ return cnt; ++ } ++ ++ if ((page_index = xc_core_pfn_to_page_index(pfn)) == ++ PFN_NOT_FOUND) ++ return READ_ERROR; ++ ++ offset = (off_t)xd->xc_core.header.xch_pages_offset + ++ ((off_t)(page_index) * (off_t)xd->page_size); ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ return SEEK_ERROR; ++ ++ if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size) ++ return READ_ERROR; ++ ++ poc_store(pfn, offset); ++ ++ BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt); ++ ++ return cnt; ++} ++ ++/* ++ * Verify whether the dumpfile was created by the "xm save" facility. ++ * This gets started by the "save" function in XendCheckpoint.py, and ++ * then by xc_save.c, with the work done in the xc_linux_save() library ++ * function in libxc/xc_linux_save.c. ++ */ ++ ++#define MAX_BATCH_SIZE 1024 ++/* ++ * Number of P2M entries in a page. ++ */ ++#define ULPP (xd->page_size/sizeof(unsigned long)) ++/* ++ * Number of P2M entries in the pfn_to_mfn_frame_list. ++ */ ++#define P2M_FL_ENTRIES (((xd->xc_save.nr_pfns)+ULPP-1)/ULPP) ++/* ++ * Size in bytes of the pfn_to_mfn_frame_list. ++ */ ++#define P2M_FL_SIZE ((P2M_FL_ENTRIES)*sizeof(unsigned long)) ++ ++#define XTAB (0xf<<28) /* invalid page */ ++#define LTAB_MASK XTAB ++ ++static int ++xc_save_verify(char *buf) ++{ ++ int i, batch_count, done_batch, *intptr; ++ ulong flags, *ulongptr; ++ ulong batch_index, total_pages_read; ++ ulong N; ++ ++ if (!STRNEQ(buf, XC_SAVE_SIGNATURE)) ++ return FALSE; ++ ++ if (lseek(xd->xfd, strlen(XC_SAVE_SIGNATURE), SEEK_SET) == -1) ++ return FALSE; ++ ++ flags = XC_SAVE; ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(stderr, "\"%s\"\n", buf); ++ fprintf(stderr, "endian: %d %s\n", __BYTE_ORDER, ++ __BYTE_ORDER == __BIG_ENDIAN ? "__BIG_ENDIAN" : ++ (__BYTE_ORDER == __LITTLE_ENDIAN ? ++ "__LITTLE_ENDIAN" : "???")); ++ } ++ ++ /* ++ * size of vmconfig data structure (big-endian) ++ */ ++ if (read(xd->xfd, buf, sizeof(int)) != sizeof(int)) ++ return FALSE; ++ ++ intptr = (int *)buf; ++ ++ if (CRASHDEBUG(1) && BYTE_SWAP_REQUIRED(__BIG_ENDIAN)) { ++ fprintf(stderr, "byte-swap required for this:\n"); ++ for (i = 0; i < sizeof(int); i++) ++ fprintf(stderr, "[%x]", buf[i] & 0xff); ++ fprintf(stderr, ": %x -> ", *intptr); ++ } ++ ++ xd->xc_save.vmconfig_size = swab32(*intptr); ++ ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, "%x\n", xd->xc_save.vmconfig_size); ++ ++ if (!(xd->xc_save.vmconfig_buf = (char *)malloc ++ (xd->xc_save.vmconfig_size))) ++ error(FATAL, "cannot malloc xc_save vmconfig space."); ++ ++ if (!xd->page_size) ++ error(FATAL, ++ "unknown page size: use -p command line option\n"); ++ ++ if (!(xd->page = (char *)malloc(xd->page_size))) ++ error(FATAL, "cannot malloc page space."); ++ ++ if (!(xd->poc = (struct pfn_offset_cache *)calloc ++ (PFN_TO_OFFSET_CACHE_ENTRIES, ++ sizeof(struct pfn_offset_cache)))) ++ error(FATAL, "cannot malloc pfn_offset_cache\n"); ++ xd->last_pfn = ~(0UL); ++ ++ if (!(xd->xc_save.region_pfn_type = (ulong *)calloc ++ (MAX_BATCH_SIZE, sizeof(ulong)))) ++ error(FATAL, "cannot malloc region_pfn_type\n"); ++ ++ if (read(xd->xfd, xd->xc_save.vmconfig_buf, ++ xd->xc_save.vmconfig_size) != xd->xc_save.vmconfig_size) ++ goto xc_save_bailout; ++ ++ /* ++ * nr_pfns (native byte order) ++ */ ++ if (read(xd->xfd, buf, sizeof(ulong)) != sizeof(ulong)) ++ goto xc_save_bailout; ++ ++ ulongptr = (ulong *)buf; ++ ++ if (CRASHDEBUG(1)) { ++ for (i = 0; i < sizeof(ulong); i++) ++ fprintf(stderr, "[%x]", buf[i] & 0xff); ++ fprintf(stderr, ": %lx (nr_pfns)\n", *ulongptr); ++ } ++ ++ xd->xc_save.nr_pfns = *ulongptr; ++ ++ if (machine_type("IA64")) ++ goto xc_save_ia64; ++ ++ /* ++ * Get a local copy of the live_P2M_frame_list ++ */ ++ if (!(xd->xc_save.p2m_frame_list = (unsigned long *)malloc(P2M_FL_SIZE))) ++ error(FATAL, "cannot allocate p2m_frame_list array"); ++ ++ if (!(xd->xc_save.batch_offsets = (off_t *)calloc((size_t)P2M_FL_ENTRIES, ++ sizeof(off_t)))) ++ error(FATAL, "cannot allocate batch_offsets array"); ++ ++ xd->xc_save.batch_count = P2M_FL_ENTRIES; ++ ++ if (read(xd->xfd, xd->xc_save.p2m_frame_list, P2M_FL_SIZE) != ++ P2M_FL_SIZE) ++ goto xc_save_bailout; ++ ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, "pre-batch file pointer: %lld\n", ++ (ulonglong)lseek(xd->xfd, 0L, SEEK_CUR)); ++ ++ /* ++ * ... ++ * int batch_count ++ * ulong region pfn_type[batch_count] ++ * page 0 ++ * page 1 ++ * ... ++ * page batch_count-1 ++ * (repeat) ++ */ ++ ++ total_pages_read = 0; ++ batch_index = 0; ++ done_batch = FALSE; ++ ++ while (!done_batch) { ++ ++ xd->xc_save.batch_offsets[batch_index] = (off_t) ++ lseek(xd->xfd, 0L, SEEK_CUR); ++ ++ if (read(xd->xfd, &batch_count, sizeof(int)) != sizeof(int)) ++ goto xc_save_bailout; ++ ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, "batch[%ld]: %d ", ++ batch_index, batch_count); ++ ++ batch_index++; ++ ++ if (batch_index >= P2M_FL_ENTRIES) { ++ fprintf(stderr, "more than %ld batches encountered?\n", ++ P2M_FL_ENTRIES); ++ goto xc_save_bailout; ++ } ++ ++ switch (batch_count) ++ { ++ case 0: ++ if (CRASHDEBUG(1)) { ++ fprintf(stderr, ++ ": Batch work is done: %ld pages read (P2M_FL_ENTRIES: %ld)\n", ++ total_pages_read, P2M_FL_ENTRIES); ++ } ++ done_batch = TRUE; ++ continue; ++ ++ case -1: ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, ": Entering page verify mode\n"); ++ continue; ++ ++ default: ++ if (batch_count > MAX_BATCH_SIZE) { ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, ++ ": Max batch size exceeded. Giving up.\n"); ++ done_batch = TRUE; ++ continue; ++ } ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, "\n"); ++ break; ++ } ++ ++ if (read(xd->xfd, xd->xc_save.region_pfn_type, batch_count * sizeof(ulong)) != ++ batch_count * sizeof(ulong)) ++ goto xc_save_bailout; ++ ++ for (i = 0; i < batch_count; i++) { ++ unsigned long pagetype; ++ unsigned long pfn; ++ ++ pfn = xd->xc_save.region_pfn_type[i] & ~LTAB_MASK; ++ pagetype = xd->xc_save.region_pfn_type[i] & LTAB_MASK; ++ ++ if (pagetype == XTAB) ++ /* a bogus/unmapped page: skip it */ ++ continue; ++ ++ if (pfn > xd->xc_save.nr_pfns) { ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, ++ "batch_count: %d pfn %ld out of range", ++ batch_count, pfn); ++ } ++ ++ if (lseek(xd->xfd, xd->page_size, SEEK_CUR) == -1) ++ goto xc_save_bailout; ++ ++ total_pages_read++; ++ } ++ } ++ ++ /* ++ * Get the list of PFNs that are not in the psuedo-phys map ++ */ ++ if (read(xd->xfd, &xd->xc_save.pfns_not, ++ sizeof(xd->xc_save.pfns_not)) != sizeof(xd->xc_save.pfns_not)) ++ goto xc_save_bailout; ++ ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, "PFNs not in pseudo-phys map: %d\n", ++ xd->xc_save.pfns_not); ++ ++ if ((total_pages_read + xd->xc_save.pfns_not) != ++ xd->xc_save.nr_pfns) ++ error(WARNING, ++ "nr_pfns: %ld != (total pages: %ld + pages not saved: %d)\n", ++ xd->xc_save.nr_pfns, total_pages_read, ++ xd->xc_save.pfns_not); ++ ++ xd->xc_save.pfns_not_offset = lseek(xd->xfd, 0L, SEEK_CUR); ++ ++ if (lseek(xd->xfd, sizeof(ulong) * xd->xc_save.pfns_not, SEEK_CUR) == -1) ++ goto xc_save_bailout; ++ ++ xd->xc_save.vcpu_ctxt_offset = lseek(xd->xfd, 0L, SEEK_CUR); ++ ++ lseek(xd->xfd, 0, SEEK_END); ++ lseek(xd->xfd, -((off_t)(xd->page_size)), SEEK_CUR); ++ ++ xd->xc_save.shared_info_page_offset = lseek(xd->xfd, 0L, SEEK_CUR); ++ ++ xd->flags |= (XENDUMP_LOCAL | flags); ++ kt->xen_flags |= (CANONICAL_PAGE_TABLES|XEN_SUSPEND); ++ ++ if (CRASHDEBUG(1)) ++ xendump_memory_dump(stderr); ++ ++ return TRUE; ++ ++xc_save_ia64: ++ ++ /* ++ * Completely different format for ia64: ++ * ++ * ... ++ * pfn # ++ * page data ++ * pfn # ++ * page data ++ * ... ++ */ ++ free(xd->poc); ++ xd->poc = NULL; ++ free(xd->xc_save.region_pfn_type); ++ xd->xc_save.region_pfn_type = NULL; ++ ++ if (!(xd->xc_save.ia64_page_offsets = ++ (ulong *)calloc(xd->xc_save.nr_pfns, sizeof(off_t)))) ++ error(FATAL, "cannot allocate ia64_page_offsets array"); ++ ++ /* ++ * version ++ */ ++ if (read(xd->xfd, buf, sizeof(ulong)) != sizeof(ulong)) ++ goto xc_save_bailout; ++ ++ xd->xc_save.ia64_version = *((ulong *)buf); ++ ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, "ia64 version: %lx\n", ++ xd->xc_save.ia64_version); ++ ++ /* ++ * xen_domctl_arch_setup structure ++ */ ++ if (read(xd->xfd, buf, sizeof(xen_domctl_arch_setup_t)) != ++ sizeof(xen_domctl_arch_setup_t)) ++ goto xc_save_bailout; ++ ++ if (CRASHDEBUG(1)) { ++ xen_domctl_arch_setup_t *setup = ++ (xen_domctl_arch_setup_t *)buf; ++ ++ fprintf(stderr, "xen_domctl_arch_setup:\n"); ++ fprintf(stderr, " flags: %lx\n", (ulong)setup->flags); ++ fprintf(stderr, " bp: %lx\n", (ulong)setup->bp); ++ fprintf(stderr, " maxmem: %lx\n", (ulong)setup->maxmem); ++ fprintf(stderr, " xsi_va: %lx\n", (ulong)setup->xsi_va); ++ fprintf(stderr, "hypercall_imm: %x\n", setup->hypercall_imm); ++ } ++ ++ for (i = N = 0; i < xd->xc_save.nr_pfns; i++) { ++ if (read(xd->xfd, &N, sizeof(N)) != sizeof(N)) ++ goto xc_save_bailout; ++ ++ if (N < xd->xc_save.nr_pfns) ++ xd->xc_save.ia64_page_offsets[N] = ++ lseek(xd->xfd, 0, SEEK_CUR); ++ else ++ error(WARNING, ++ "[%d]: pfn of %lx (0x%lx) in ia64 canonical page list exceeds %ld\n", ++ i, N, N, xd->xc_save.nr_pfns); ++ ++ if (CRASHDEBUG(1)) { ++ if ((i < 10) || (N >= (xd->xc_save.nr_pfns-10))) ++ fprintf(stderr, "[%d]: %ld\n%s", i, N, ++ i == 9 ? "...\n" : ""); ++ } ++ ++ if ((N+1) >= xd->xc_save.nr_pfns) ++ break; ++ ++ if (lseek(xd->xfd, xd->page_size, SEEK_CUR) == -1) ++ goto xc_save_bailout; ++ } ++ ++ if (CRASHDEBUG(1)) { ++ for (i = N = 0; i < xd->xc_save.nr_pfns; i++) { ++ if (!xd->xc_save.ia64_page_offsets[i]) ++ N++; ++ } ++ fprintf(stderr, "%ld out of %ld pfns not dumped\n", ++ N, xd->xc_save.nr_pfns); ++ } ++ ++ xd->flags |= (XENDUMP_LOCAL | flags | XC_SAVE_IA64); ++ kt->xen_flags |= (CANONICAL_PAGE_TABLES|XEN_SUSPEND); ++ ++ if (CRASHDEBUG(1)) ++ xendump_memory_dump(stderr); ++ ++ return TRUE; ++ ++xc_save_bailout: ++ ++ error(INFO, ++ "xc_save_verify: \"LinuxGuestRecord\" file handling/format error\n"); ++ ++ if (xd->xc_save.p2m_frame_list) { ++ free(xd->xc_save.p2m_frame_list); ++ xd->xc_save.p2m_frame_list = NULL; ++ } ++ if (xd->xc_save.batch_offsets) { ++ free(xd->xc_save.batch_offsets); ++ xd->xc_save.batch_offsets = NULL; ++ } ++ if (xd->xc_save.vmconfig_buf) { ++ free(xd->xc_save.vmconfig_buf); ++ xd->xc_save.vmconfig_buf = NULL; ++ } ++ if (xd->page) { ++ free(xd->page); ++ xd->page = NULL; ++ } ++ ++ return FALSE; ++} ++ ++/* ++ * Do the work for read_xendump() for the XC_SAVE dumpfile format. ++ */ ++static int ++xc_save_read(void *bufptr, int cnt, ulong addr, physaddr_t paddr) ++{ ++ int b, i, redundant; ++ ulong reqpfn; ++ int batch_count; ++ off_t file_offset; ++ ++ reqpfn = (ulong)BTOP(paddr); ++ ++ if (CRASHDEBUG(8)) ++ fprintf(xd->ofp, ++ "xc_save_read(bufptr: %lx cnt: %d addr: %lx paddr: %llx (%ld, 0x%lx)\n", ++ (ulong)bufptr, cnt, addr, (ulonglong)paddr, reqpfn, reqpfn); ++ ++ if (xd->flags & XC_SAVE_IA64) { ++ if (reqpfn >= xd->xc_save.nr_pfns) { ++ if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, ++ "xc_save_read: pfn %lx too large: nr_pfns: %lx\n", ++ reqpfn, xd->xc_save.nr_pfns); ++ return SEEK_ERROR; ++ } ++ ++ file_offset = xd->xc_save.ia64_page_offsets[reqpfn]; ++ if (!file_offset) { ++ if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, ++ "xc_save_read: pfn %lx not stored in xendump\n", ++ reqpfn); ++ return SEEK_ERROR; ++ } ++ ++ if (reqpfn != xd->last_pfn) { ++ if (lseek(xd->xfd, file_offset, SEEK_SET) == -1) ++ return SEEK_ERROR; ++ ++ if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size) ++ return READ_ERROR; ++ } else { ++ xd->redundant++; ++ xd->cache_hits++; ++ } ++ ++ xd->accesses++; ++ xd->last_pfn = reqpfn; ++ ++ BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt); ++ return cnt; ++ } ++ ++ if ((file_offset = poc_get(reqpfn, &redundant))) { ++ if (!redundant) { ++ if (lseek(xd->xfd, file_offset, SEEK_SET) == -1) ++ return SEEK_ERROR; ++ if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size) ++ return READ_ERROR; ++ xd->last_pfn = reqpfn; ++ } else if (CRASHDEBUG(1)) ++ console("READ %ld (0x%lx) skipped!\n", reqpfn, reqpfn); ++ ++ BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt); ++ return cnt; ++ } ++ ++ /* ++ * ... ++ * int batch_count ++ * ulong region pfn_type[batch_count] ++ * page 0 ++ * page 1 ++ * ... ++ * page batch_count-1 ++ * (repeat) ++ */ ++ for (b = 0; b < xd->xc_save.batch_count; b++) { ++ ++ if (lseek(xd->xfd, xd->xc_save.batch_offsets[b], SEEK_SET) == -1) ++ return SEEK_ERROR; ++ ++ if (CRASHDEBUG(8)) ++ fprintf(xd->ofp, "check batch[%d]: offset: %llx\n", ++ b, (ulonglong)xd->xc_save.batch_offsets[b]); ++ ++ if (read(xd->xfd, &batch_count, sizeof(int)) != sizeof(int)) ++ return READ_ERROR; ++ ++ switch (batch_count) ++ { ++ case 0: ++ if (CRASHDEBUG(1)) { ++ fprintf(xd->ofp, ++ "batch[%d]: has count of zero -- bailing out on pfn %ld\n", ++ b, reqpfn); ++ } ++ return READ_ERROR; ++ ++ case -1: ++ return READ_ERROR; ++ ++ default: ++ if (CRASHDEBUG(8)) ++ fprintf(xd->ofp, ++ "batch[%d]: offset: %llx batch count: %d\n", ++ b, (ulonglong)xd->xc_save.batch_offsets[b], ++ batch_count); ++ break; ++ } ++ ++ if (read(xd->xfd, xd->xc_save.region_pfn_type, batch_count * sizeof(ulong)) != ++ batch_count * sizeof(ulong)) ++ return READ_ERROR; ++ ++ for (i = 0; i < batch_count; i++) { ++ unsigned long pagetype; ++ unsigned long pfn; ++ ++ pfn = xd->xc_save.region_pfn_type[i] & ~LTAB_MASK; ++ pagetype = xd->xc_save.region_pfn_type[i] & LTAB_MASK; ++ ++ if (pagetype == XTAB) ++ /* a bogus/unmapped page: skip it */ ++ continue; ++ ++ if (pfn > xd->xc_save.nr_pfns) { ++ if (CRASHDEBUG(1)) ++ fprintf(stderr, ++ "batch_count: %d pfn %ld out of range", ++ batch_count, pfn); ++ } ++ ++ if (pfn == reqpfn) { ++ file_offset = lseek(xd->xfd, 0, SEEK_CUR); ++ poc_store(pfn, file_offset); ++ ++ if (read(xd->xfd, xd->page, xd->page_size) != ++ xd->page_size) ++ return READ_ERROR; ++ ++ BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt); ++ return cnt; ++ } ++ ++ if (lseek(xd->xfd, xd->page_size, SEEK_CUR) == -1) ++ return SEEK_ERROR; ++ } ++ } ++ ++ return READ_ERROR; ++} ++ ++/* ++ * Stash a pfn's offset. If they're all in use, put it in the ++ * least-used slot that's closest to the beginning of the array. ++ */ ++static void ++poc_store(ulong pfn, off_t file_offset) ++{ ++ int i; ++ struct pfn_offset_cache *poc, *plow; ++ ulong curlow; ++ ++ curlow = ~(0UL); ++ plow = NULL; ++ poc = xd->poc; ++ ++ for (i = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++, poc++) { ++ if (poc->cnt == 0) { ++ poc->cnt = 1; ++ poc->pfn = pfn; ++ poc->file_offset = file_offset; ++ xd->last_pfn = pfn; ++ return; ++ } ++ ++ if (poc->cnt < curlow) { ++ curlow = poc->cnt; ++ plow = poc; ++ } ++ } ++ ++ plow->cnt = 1; ++ plow->pfn = pfn; ++ plow->file_offset = file_offset; ++ xd->last_pfn = pfn; ++} ++ ++/* ++ * Check whether a pfn's offset has been cached. ++ */ ++static off_t ++poc_get(ulong pfn, int *redundant) ++{ ++ int i; ++ struct pfn_offset_cache *poc; ++ ++ xd->accesses++; ++ ++ if (pfn == xd->last_pfn) { ++ xd->redundant++; ++ *redundant = TRUE; ++ return 1; ++ } else ++ *redundant = FALSE; ++ ++ poc = xd->poc; ++ ++ for (i = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++, poc++) { ++ if (poc->cnt && (poc->pfn == pfn)) { ++ poc->cnt++; ++ xd->cache_hits++; ++ return poc->file_offset; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * Perform any post-dumpfile determination stuff here. ++ */ ++int ++xendump_init(char *unused, FILE *fptr) ++{ ++ if (!XENDUMP_VALID()) ++ return FALSE; ++ ++ xd->ofp = fptr; ++ return TRUE; ++} ++ ++int ++read_xendump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) ++{ ++ if (pc->curcmd_flags & XEN_MACHINE_ADDR) ++ return READ_ERROR; ++ ++ switch (xd->flags & (XC_SAVE|XC_CORE_ORIG|XC_CORE_ELF)) ++ { ++ case XC_SAVE: ++ return xc_save_read(bufptr, cnt, addr, paddr); ++ ++ case XC_CORE_ORIG: ++ case XC_CORE_ELF: ++ return xc_core_read(bufptr, cnt, addr, paddr); ++ ++ default: ++ return READ_ERROR; ++ } ++} ++ ++int ++read_xendump_hyper(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) ++{ ++ ulong pfn, page_index; ++ off_t offset; ++ ++ pfn = (ulong)BTOP(paddr); ++ ++ /* ODA: pfn == mfn !!! */ ++ if ((page_index = xc_core_mfn_to_page_index(pfn)) == PFN_NOT_FOUND) ++ return READ_ERROR; ++ ++ offset = (off_t)xd->xc_core.header.xch_pages_offset + ++ ((off_t)(page_index) * (off_t)xd->page_size); ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ return SEEK_ERROR; ++ ++ if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size) ++ return READ_ERROR; ++ ++ BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt); ++ ++ return cnt; ++} ++ ++int ++write_xendump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) ++{ ++ return WRITE_ERROR; ++} ++ ++uint ++xendump_page_size(void) ++{ ++ if (!XENDUMP_VALID()) ++ return 0; ++ ++ return xd->page_size; ++} ++ ++/* ++ * xendump_free_memory(), and xendump_memory_used() ++ * are debug only, and typically unnecessary to implement. ++ */ ++int ++xendump_free_memory(void) ++{ ++ return 0; ++} ++ ++int ++xendump_memory_used(void) ++{ ++ return 0; ++} ++ ++/* ++ * This function is dump-type independent, used here to ++ * to dump the xendump_data structure contents. ++ */ ++int ++xendump_memory_dump(FILE *fp) ++{ ++ int i, linefeed, used, others; ++ ulong *ulongptr; ++ Elf32_Off offset32; ++ Elf64_Off offset64; ++ FILE *fpsave; ++ ++ fprintf(fp, " flags: %lx (", xd->flags); ++ others = 0; ++ if (xd->flags & XENDUMP_LOCAL) ++ fprintf(fp, "%sXENDUMP_LOCAL", others++ ? "|" : ""); ++ if (xd->flags & XC_SAVE) ++ fprintf(fp, "%sXC_SAVE", others++ ? "|" : ""); ++ if (xd->flags & XC_CORE_ORIG) ++ fprintf(fp, "%sXC_CORE_ORIG", others++ ? "|" : ""); ++ if (xd->flags & XC_CORE_ELF) ++ fprintf(fp, "%sXC_CORE_ELF", others++ ? "|" : ""); ++ if (xd->flags & XC_CORE_P2M_CREATE) ++ fprintf(fp, "%sXC_CORE_P2M_CREATE", others++ ? "|" : ""); ++ if (xd->flags & XC_CORE_PFN_CREATE) ++ fprintf(fp, "%sXC_CORE_PFN_CREATE", others++ ? "|" : ""); ++ if (xd->flags & XC_CORE_NO_P2M) ++ fprintf(fp, "%sXC_CORE_NO_P2M", others++ ? "|" : ""); ++ if (xd->flags & XC_SAVE_IA64) ++ fprintf(fp, "%sXC_SAVE_IA64", others++ ? "|" : ""); ++ if (xd->flags & XC_CORE_64BIT_HOST) ++ fprintf(fp, "%sXC_CORE_64BIT_HOST", others++ ? "|" : ""); ++ fprintf(fp, ")\n"); ++ fprintf(fp, " xfd: %d\n", xd->xfd); ++ fprintf(fp, " page_size: %d\n", xd->page_size); ++ fprintf(fp, " ofp: %lx\n", (ulong)xd->ofp); ++ fprintf(fp, " page: %lx\n", (ulong)xd->page); ++ fprintf(fp, " panic_pc: %lx\n", xd->panic_pc); ++ fprintf(fp, " panic_sp: %lx\n", xd->panic_sp); ++ fprintf(fp, " accesses: %ld\n", (ulong)xd->accesses); ++ fprintf(fp, " cache_hits: %ld ", (ulong)xd->cache_hits); ++ if (xd->accesses) ++ fprintf(fp, "(%ld%%)\n", xd->cache_hits * 100 / xd->accesses); ++ else ++ fprintf(fp, "\n"); ++ fprintf(fp, " last_pfn: %ld\n", xd->last_pfn); ++ fprintf(fp, " redundant: %ld ", (ulong)xd->redundant); ++ if (xd->accesses) ++ fprintf(fp, "(%ld%%)\n", xd->redundant * 100 / xd->accesses); ++ else ++ fprintf(fp, "\n"); ++ for (i = used = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++) ++ if (xd->poc && xd->poc[i].cnt) ++ used++; ++ if (xd->poc) ++ fprintf(fp, " poc[%d]: %lx %s", PFN_TO_OFFSET_CACHE_ENTRIES, ++ (ulong)xd->poc, xd->poc ? "" : "(none)"); ++ else ++ fprintf(fp, " poc[0]: (unused)\n"); ++ for (i = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++) { ++ if (!xd->poc) ++ break; ++ if (!xd->poc[i].cnt) { ++ if (!i) ++ fprintf(fp, "(none used)\n"); ++ break; ++ } else if (!i) ++ fprintf(fp, "(%d used)\n", used); ++ if (CRASHDEBUG(2)) ++ fprintf(fp, ++ " [%d]: pfn: %ld (0x%lx) count: %ld file_offset: %llx\n", ++ i, ++ xd->poc[i].pfn, ++ xd->poc[i].pfn, ++ xd->poc[i].cnt, ++ (ulonglong)xd->poc[i].file_offset); ++ } ++ if (!xd->poc) ++ fprintf(fp, "\n"); ++ ++ fprintf(fp, "\n xc_save:\n"); ++ fprintf(fp, " nr_pfns: %ld (0x%lx)\n", ++ xd->xc_save.nr_pfns, xd->xc_save.nr_pfns); ++ fprintf(fp, " vmconfig_size: %d (0x%x)\n", xd->xc_save.vmconfig_size, ++ xd->xc_save.vmconfig_size); ++ fprintf(fp, " vmconfig_buf: %lx\n", (ulong)xd->xc_save.vmconfig_buf); ++ if (xd->flags & XC_SAVE) ++ xen_dump_vmconfig(fp); ++ fprintf(fp, " p2m_frame_list: %lx ", (ulong)xd->xc_save.p2m_frame_list); ++ if ((xd->flags & XC_SAVE) && xd->xc_save.p2m_frame_list) { ++ fprintf(fp, "\n"); ++ ulongptr = xd->xc_save.p2m_frame_list; ++ for (i = 0; i < P2M_FL_ENTRIES; i++, ulongptr++) ++ fprintf(fp, "%ld ", *ulongptr); ++ fprintf(fp, "\n"); ++ } else ++ fprintf(fp, "(none)\n"); ++ fprintf(fp, " pfns_not: %d\n", xd->xc_save.pfns_not); ++ fprintf(fp, " pfns_not_offset: %lld\n", ++ (ulonglong)xd->xc_save.pfns_not_offset); ++ fprintf(fp, " vcpu_ctxt_offset: %lld\n", ++ (ulonglong)xd->xc_save.vcpu_ctxt_offset); ++ fprintf(fp, " shared_info_page_offset: %lld\n", ++ (ulonglong)xd->xc_save.shared_info_page_offset); ++ fprintf(fp, " region_pfn_type: %lx\n", (ulong)xd->xc_save.region_pfn_type); ++ fprintf(fp, " batch_count: %ld\n", (ulong)xd->xc_save.batch_count); ++ fprintf(fp, " batch_offsets: %lx %s\n", ++ (ulong)xd->xc_save.batch_offsets, ++ xd->xc_save.batch_offsets ? "" : "(none)"); ++ for (i = linefeed = 0; i < xd->xc_save.batch_count; i++) { ++ fprintf(fp, "[%d]: %llx ", i, ++ (ulonglong)xd->xc_save.batch_offsets[i]); ++ if (((i+1)%4) == 0) { ++ fprintf(fp, "\n"); ++ linefeed = FALSE; ++ } else ++ linefeed = TRUE; ++ } ++ if (linefeed) ++ fprintf(fp, "\n"); ++ fprintf(fp, " ia64_version: %ld\n", (ulong)xd->xc_save.ia64_version); ++ fprintf(fp, " ia64_page_offsets: %lx ", (ulong)xd->xc_save.ia64_page_offsets); ++ if (xd->xc_save.ia64_page_offsets) ++ fprintf(fp, "(%ld entries)\n\n", xd->xc_save.nr_pfns); ++ else ++ fprintf(fp, "(none)\n\n"); ++ ++ fprintf(fp, " xc_core:\n"); ++ fprintf(fp, " header:\n"); ++ fprintf(fp, " xch_magic: %x ", ++ xd->xc_core.header.xch_magic); ++ if (xd->xc_core.header.xch_magic == XC_CORE_MAGIC) ++ fprintf(fp, "(XC_CORE_MAGIC)\n"); ++ else if (xd->xc_core.header.xch_magic == XC_CORE_MAGIC_HVM) ++ fprintf(fp, "(XC_CORE_MAGIC_HVM)\n"); ++ else ++ fprintf(fp, "(unknown)\n"); ++ fprintf(fp, " xch_nr_vcpus: %d\n", ++ xd->xc_core.header.xch_nr_vcpus); ++ fprintf(fp, " xch_nr_pages: %d (0x%x)\n", ++ xd->xc_core.header.xch_nr_pages, ++ xd->xc_core.header.xch_nr_pages); ++ fprintf(fp, " xch_ctxt_offset: %d (0x%x)\n", ++ xd->xc_core.header.xch_ctxt_offset, ++ xd->xc_core.header.xch_ctxt_offset); ++ fprintf(fp, " xch_index_offset: %d (0x%x)\n", ++ xd->xc_core.header.xch_index_offset, ++ xd->xc_core.header.xch_index_offset); ++ fprintf(fp, " xch_pages_offset: %d (0x%x)\n", ++ xd->xc_core.header.xch_pages_offset, ++ xd->xc_core.header.xch_pages_offset); ++ ++ fprintf(fp, " elf_class: %s\n", xd->xc_core.elf_class == ELFCLASS64 ? "ELFCLASS64" : ++ xd->xc_core.elf_class == ELFCLASS32 ? "ELFCLASS32" : "n/a"); ++ fprintf(fp, " elf_strtab_offset: %lld (0x%llx)\n", ++ (ulonglong)xd->xc_core.elf_strtab_offset, ++ (ulonglong)xd->xc_core.elf_strtab_offset); ++ fprintf(fp, " format_version: %016llx\n", ++ (ulonglong)xd->xc_core.format_version); ++ fprintf(fp, " shared_info_offset: %lld (0x%llx)\n", ++ (ulonglong)xd->xc_core.shared_info_offset, ++ (ulonglong)xd->xc_core.shared_info_offset); ++ if (machine_type("IA64")) ++ fprintf(fp, " ia64_mapped_regs_offset: %lld (0x%llx)\n", ++ (ulonglong)xd->xc_core.ia64_mapped_regs_offset, ++ (ulonglong)xd->xc_core.ia64_mapped_regs_offset); ++ fprintf(fp, " elf_index_pfn[%d]: %s", INDEX_PFN_COUNT, ++ xd->xc_core.elf_class ? "\n" : "(none used)\n"); ++ if (xd->xc_core.elf_class) { ++ for (i = 0; i < INDEX_PFN_COUNT; i++) { ++ fprintf(fp, "%ld:%ld ", ++ xd->xc_core.elf_index_pfn[i].index, ++ xd->xc_core.elf_index_pfn[i].pfn); ++ } ++ fprintf(fp, "\n"); ++ } ++ fprintf(fp, " last_batch:\n"); ++ fprintf(fp, " index: %ld (%ld - %ld)\n", ++ xd->xc_core.last_batch.index, ++ xd->xc_core.last_batch.start, xd->xc_core.last_batch.end); ++ fprintf(fp, " accesses: %ld\n", ++ xd->xc_core.last_batch.accesses); ++ fprintf(fp, " duplicates: %ld ", ++ xd->xc_core.last_batch.duplicates); ++ if (xd->xc_core.last_batch.accesses) ++ fprintf(fp, "(%ld%%)\n", ++ xd->xc_core.last_batch.duplicates * 100 / ++ xd->xc_core.last_batch.accesses); ++ else ++ fprintf(fp, "\n"); ++ ++ fprintf(fp, " elf32: %lx\n", (ulong)xd->xc_core.elf32); ++ fprintf(fp, " elf64: %lx\n", (ulong)xd->xc_core.elf64); ++ ++ fprintf(fp, " p2m_frames: %d\n", ++ xd->xc_core.p2m_frames); ++ fprintf(fp, " p2m_frame_index_list: %s\n", ++ (xd->flags & (XC_CORE_NO_P2M|XC_SAVE)) ? "(not used)" : ""); ++ for (i = 0; i < xd->xc_core.p2m_frames; i++) { ++ fprintf(fp, "%ld ", ++ xd->xc_core.p2m_frame_index_list[i]); ++ } ++ fprintf(fp, xd->xc_core.p2m_frames ? "\n" : ""); ++ ++ if ((xd->flags & XC_CORE_ORIG) && CRASHDEBUG(8)) ++ xc_core_mfns(XENDUMP_LOCAL, fp); ++ ++ switch (xd->xc_core.elf_class) ++ { ++ case ELFCLASS32: ++ fpsave = xd->ofp; ++ xd->ofp = fp; ++ xc_core_elf_dump(); ++ offset32 = xd->xc_core.elf32->e_shoff; ++ for (i = 0; i < xd->xc_core.elf32->e_shnum; i++) { ++ xc_core_dump_Elf32_Shdr(offset32, ELFREAD); ++ offset32 += xd->xc_core.elf32->e_shentsize; ++ } ++ xendump_print("\n"); ++ xd->ofp = fpsave; ++ break; ++ ++ case ELFCLASS64: ++ fpsave = xd->ofp; ++ xd->ofp = fp; ++ xc_core_elf_dump(); ++ offset64 = xd->xc_core.elf64->e_shoff; ++ for (i = 0; i < xd->xc_core.elf64->e_shnum; i++) { ++ xc_core_dump_Elf64_Shdr(offset64, ELFREAD); ++ offset64 += xd->xc_core.elf64->e_shentsize; ++ } ++ xendump_print("\n"); ++ xd->ofp = fpsave; ++ break; ++ } ++ ++ return 0; ++} ++ ++static void ++xen_dump_vmconfig(FILE *fp) ++{ ++ int i, opens, closes; ++ char *p; ++ ++ opens = closes = 0; ++ p = xd->xc_save.vmconfig_buf; ++ for (i = 0; i < xd->xc_save.vmconfig_size; i++, p++) { ++ if (ascii(*p)) ++ fprintf(fp, "%c", *p); ++ else ++ fprintf(fp, "<%x>", *p); ++ ++ if (*p == '(') ++ opens++; ++ else if (*p == ')') ++ closes++; ++ } ++ fprintf(fp, "\n"); ++ ++ if (opens != closes) ++ error(WARNING, "invalid vmconfig contents?\n"); ++} ++ ++/* ++ * Looking at the active set, try to determine who panicked, ++ * or who was the "suspend" kernel thread. ++ */ ++ulong get_xendump_panic_task(void) ++{ ++ int i; ++ ulong task; ++ struct task_context *tc; ++ ++ switch (xd->flags & (XC_CORE_ORIG|XC_CORE_ELF|XC_SAVE)) ++ { ++ case XC_CORE_ORIG: ++ case XC_CORE_ELF: ++ if (machdep->xendump_panic_task) ++ return (machdep->xendump_panic_task((void *)xd)); ++ break; ++ ++ case XC_SAVE: ++ for (i = 0; i < NR_CPUS; i++) { ++ if (!(task = tt->active_set[i])) ++ continue; ++ tc = task_to_context(task); ++ if (is_kernel_thread(task) && ++ STREQ(tc->comm, "suspend")) ++ return tc->task; ++ } ++ break; ++ } ++ ++ return NO_TASK; ++} ++ ++/* ++ * Figure out the back trace hooks. ++ */ ++void get_xendump_regs(struct bt_info *bt, ulong *pc, ulong *sp) ++{ ++ int i; ++ ulong *up; ++ ++ if ((tt->panic_task == bt->task) && ++ (xd->panic_pc && xd->panic_sp)) { ++ *pc = xd->panic_pc; ++ *sp = xd->panic_sp; ++ return; ++ } ++ ++ switch (xd->flags & (XC_CORE_ORIG|XC_CORE_ELF|XC_SAVE)) ++ { ++ case XC_CORE_ORIG: ++ case XC_CORE_ELF: ++ if (machdep->get_xendump_regs) ++ return (machdep->get_xendump_regs(xd, bt, pc, sp)); ++ break; ++ ++ case XC_SAVE: ++ if (tt->panic_task != bt->task) ++ break; ++ ++ for (i = 0, up = (ulong *)bt->stackbuf; ++ i < LONGS_PER_STACK; i++, up++) { ++ if (is_kernel_text(*up) && ++ (STREQ(closest_symbol(*up), ++ "__do_suspend"))) { ++ *pc = *up; ++ *sp = tt->flags & THREAD_INFO ? ++ bt->tc->thread_info + ++ (i * sizeof(long)) : ++ bt->task + ++ (i * sizeof(long)); ++ xd->panic_pc = *pc; ++ xd->panic_sp = *sp; ++ return; ++ } ++ } ++ } ++ ++ machdep->get_stack_frame(bt, pc, sp); ++} ++ ++/* ++ * Farm out most of the work to the proper architecture to create ++ * the p2m table. For ELF core dumps, create the index;pfn table. ++ */ ++static void ++xc_core_create_pfn_tables(void) ++{ ++ if (xd->flags & XC_CORE_P2M_CREATE) { ++ if (!machdep->xendump_p2m_create) ++ error(FATAL, ++ "xen xc_core dumpfiles not supported on this architecture"); ++ ++ if (!machdep->xendump_p2m_create((void *)xd)) ++ error(FATAL, ++ "cannot create xen pfn-to-mfn mapping\n"); ++ } ++ ++ if (xd->flags & XC_CORE_ELF) ++ xc_core_elf_pfn_init(); ++ ++ xd->flags &= ~(XC_CORE_P2M_CREATE|XC_CORE_PFN_CREATE); ++ ++ if (CRASHDEBUG(1)) ++ xendump_memory_dump(xd->ofp); ++} ++ ++/* ++ * Find the page index containing the mfn, and read the ++ * machine page into the buffer. ++ */ ++char * ++xc_core_mfn_to_page(ulong mfn, char *pgbuf) ++{ ++ int i, b, idx, done; ++ ulong tmp[MAX_BATCH_SIZE]; ++ off_t offset; ++ size_t size; ++ uint nr_pages; ++ ++ if (xd->flags & XC_CORE_ELF) ++ return xc_core_elf_mfn_to_page(mfn, pgbuf); ++ ++ if (lseek(xd->xfd, (off_t)xd->xc_core.header.xch_index_offset, ++ SEEK_SET) == -1) { ++ error(INFO, "cannot lseek to page index\n"); ++ return NULL; ++ } ++ ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ if (xd->flags & XC_CORE_64BIT_HOST) ++ nr_pages *= 2; ++ ++ for (b = 0, idx = -1, done = FALSE; ++ !done && (b < nr_pages); b += MAX_BATCH_SIZE) { ++ size = sizeof(ulong) * MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, tmp, size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return NULL; ++ } ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) { ++ done = TRUE; ++ break; ++ } ++ if (tmp[i] == mfn) { ++ idx = i+b; ++ if (CRASHDEBUG(4)) ++ fprintf(xd->ofp, ++ "page: found mfn 0x%lx (%ld) at index %d\n", ++ mfn, mfn, idx); ++ done = TRUE; ++ } ++ } ++ } ++ ++ if (idx == -1) { ++ error(INFO, "cannot find mfn %ld (0x%lx) in page index\n", ++ mfn, mfn); ++ return NULL; ++ } ++ ++ if (lseek(xd->xfd, (off_t)xd->xc_core.header.xch_pages_offset, ++ SEEK_SET) == -1) { ++ error(INFO, "cannot lseek to xch_pages_offset\n"); ++ return NULL; ++ } ++ ++ offset = (off_t)(idx) * (off_t)xd->page_size; ++ ++ if (lseek(xd->xfd, offset, SEEK_CUR) == -1) { ++ error(INFO, "cannot lseek to mfn-specified page\n"); ++ return NULL; ++ } ++ ++ if (read(xd->xfd, pgbuf, xd->page_size) != xd->page_size) { ++ error(INFO, "cannot read mfn-specified page\n"); ++ return NULL; ++ } ++ ++ return pgbuf; ++} ++ ++/* ++ * Find the page index containing the mfn, and read the ++ * machine page into the buffer. ++ */ ++static char * ++xc_core_elf_mfn_to_page(ulong mfn, char *pgbuf) ++{ ++ int i, b, idx, done; ++ off_t offset; ++ size_t size; ++ uint nr_pages; ++ ulong tmp; ++ struct xen_dumpcore_p2m p2m_batch[MAX_BATCH_SIZE]; ++ ++ offset = xd->xc_core.header.xch_index_offset; ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, "cannot lseek to page index\n"); ++ ++ for (b = 0, idx = -1, done = FALSE; ++ !done && (b < nr_pages); b += MAX_BATCH_SIZE) { ++ size = sizeof(struct xen_dumpcore_p2m) * ++ MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, &p2m_batch[0], size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return NULL; ++ } ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) { ++ done = TRUE; ++ break; ++ } ++ ++ tmp = (ulong)p2m_batch[i].gmfn; ++ ++ if (tmp == mfn) { ++ idx = i+b; ++ if (CRASHDEBUG(4)) ++ fprintf(xd->ofp, ++ "page: found mfn 0x%lx (%ld) at index %d\n", ++ mfn, mfn, idx); ++ done = TRUE; ++ } ++ } ++ } ++ ++ if (idx == -1) { ++ error(INFO, "cannot find mfn %ld (0x%lx) in page index\n", ++ mfn, mfn); ++ return NULL; ++ } ++ ++ if (lseek(xd->xfd, (off_t)xd->xc_core.header.xch_pages_offset, ++ SEEK_SET) == -1) ++ error(FATAL, "cannot lseek to xch_pages_offset\n"); ++ ++ offset = (off_t)(idx) * (off_t)xd->page_size; ++ ++ if (lseek(xd->xfd, offset, SEEK_CUR) == -1) { ++ error(INFO, "cannot lseek to mfn-specified page\n"); ++ return NULL; ++ } ++ ++ if (read(xd->xfd, pgbuf, xd->page_size) != xd->page_size) { ++ error(INFO, "cannot read mfn-specified page\n"); ++ return NULL; ++ } ++ ++ return pgbuf; ++} ++ ++ ++/* ++ * Find and return the page index containing the mfn. ++ */ ++int ++xc_core_mfn_to_page_index(ulong mfn) ++{ ++ int i, b; ++ ulong tmp[MAX_BATCH_SIZE]; ++ uint nr_pages; ++ size_t size; ++ ++ if (xd->flags & XC_CORE_ELF) ++ return xc_core_elf_mfn_to_page_index(mfn); ++ ++ if (lseek(xd->xfd, (off_t)xd->xc_core.header.xch_index_offset, ++ SEEK_SET) == -1) { ++ error(INFO, "cannot lseek to page index\n"); ++ return MFN_NOT_FOUND; ++ } ++ ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ if (xd->flags & XC_CORE_64BIT_HOST) ++ nr_pages *= 2; ++ ++ for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) { ++ size = sizeof(ulong) * MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, tmp, size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return MFN_NOT_FOUND; ++ } ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) ++ break; ++ ++ if (tmp[i] == mfn) { ++ if (CRASHDEBUG(4)) ++ fprintf(xd->ofp, ++ "index: batch: %d found mfn %ld (0x%lx) at index %d\n", ++ b/MAX_BATCH_SIZE, mfn, mfn, i+b); ++ return (i+b); ++ } ++ } ++ } ++ ++ return MFN_NOT_FOUND; ++} ++ ++/* ++ * Find and return the page index containing the mfn. ++ */ ++static int ++xc_core_elf_mfn_to_page_index(ulong mfn) ++{ ++ int i, b; ++ off_t offset; ++ size_t size; ++ uint nr_pages; ++ ulong tmp; ++ struct xen_dumpcore_p2m p2m_batch[MAX_BATCH_SIZE]; ++ ++ offset = xd->xc_core.header.xch_index_offset; ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, "cannot lseek to page index\n"); ++ ++ for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) { ++ size = sizeof(struct xen_dumpcore_p2m) * ++ MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, &p2m_batch[0], size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return MFN_NOT_FOUND; ++ } ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) ++ break; ++ ++ tmp = (ulong)p2m_batch[i].gmfn; ++ ++ if (tmp == mfn) { ++ if (CRASHDEBUG(4)) ++ fprintf(xd->ofp, ++ "index: batch: %d found mfn %ld (0x%lx) at index %d\n", ++ b/MAX_BATCH_SIZE, mfn, mfn, i+b); ++ return (i+b); ++ } ++ } ++ } ++ ++ return MFN_NOT_FOUND; ++} ++ ++ ++/* ++ * XC_CORE mfn-related utility function. ++ */ ++static int ++xc_core_mfns(ulong arg, FILE *ofp) ++{ ++ int i, b; ++ uint nr_pages; ++ ulong tmp[MAX_BATCH_SIZE]; ++ ulonglong tmp64[MAX_BATCH_SIZE]; ++ size_t size; ++ ++ if (lseek(xd->xfd, (off_t)xd->xc_core.header.xch_index_offset, ++ SEEK_SET) == -1) { ++ error(INFO, "cannot lseek to page index\n"); ++ return FALSE; ++ } ++ ++ switch (arg) ++ { ++ case XC_CORE_64BIT_HOST: ++ /* ++ * Determine whether this is a 32-bit guest xendump that ++ * was taken on a 64-bit xen host. ++ */ ++ if (machine_type("X86_64") || machine_type("IA64")) ++ return FALSE; ++check_next_4: ++ if (read(xd->xfd, tmp, sizeof(ulong) * 4) != (4 * sizeof(ulong))) { ++ error(INFO, "cannot read index pages\n"); ++ return FALSE; ++ } ++ ++ if ((tmp[0] == 0xffffffff) || (tmp[1] == 0xffffffff) || ++ (tmp[2] == 0xffffffff) || (tmp[3] == 0xffffffff) || ++ (!tmp[0] && !tmp[1]) || (!tmp[2] && !tmp[3])) ++ goto check_next_4; ++ ++ if (CRASHDEBUG(2)) ++ fprintf(ofp, "mfns: %08lx %08lx %08lx %08lx\n", ++ tmp[0], tmp[1], tmp[2], tmp[3]); ++ ++ if (tmp[0] && !tmp[1] && tmp[2] && !tmp[3]) ++ return TRUE; ++ else ++ return FALSE; ++ ++ case XENDUMP_LOCAL: ++ if (BITS64() || (xd->flags & XC_CORE_64BIT_HOST)) ++ goto show_64bit_mfns; ++ ++ fprintf(ofp, "xch_index_offset mfn list:\n"); ++ ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ ++ for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) { ++ size = sizeof(ulong) * ++ MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, tmp, size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return FALSE; ++ } ++ ++ if (b) fprintf(ofp, "\n"); ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) ++ break; ++ if ((i%8) == 0) ++ fprintf(ofp, "%s[%d]:", ++ i ? "\n" : "", b+i); ++ if (tmp[i] == 0xffffffff) ++ fprintf(ofp, " INVALID"); ++ else ++ fprintf(ofp, " %lx", tmp[i]); ++ } ++ } ++ ++ fprintf(ofp, "\nxch_nr_pages: %d\n", ++ xd->xc_core.header.xch_nr_pages); ++ return TRUE; ++ ++show_64bit_mfns: ++ fprintf(ofp, "xch_index_offset mfn list: %s\n", ++ BITS32() ? "(64-bit mfns)" : ""); ++ ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ ++ for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) { ++ size = sizeof(ulonglong) * ++ MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, tmp64, size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return FALSE; ++ } ++ ++ if (b) fprintf(ofp, "\n"); ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) ++ break; ++ if ((i%8) == 0) ++ fprintf(ofp, "%s[%d]:", ++ i ? "\n" : "", b+i); ++ if (tmp64[i] == 0xffffffffffffffffULL) ++ fprintf(ofp, " INVALID"); ++ else ++ fprintf(ofp, " %llx", tmp64[i]); ++ } ++ } ++ ++ fprintf(ofp, "\nxch_nr_pages: %d\n", nr_pages); ++ return TRUE; ++ ++ default: ++ return FALSE; ++ } ++} ++ ++/* ++ * Given a normal kernel pfn, determine the page index in the dumpfile. ++ * ++ * - First determine which of the pages making up the ++ * phys_to_machine_mapping[] array would contain the pfn. ++ * - From the phys_to_machine_mapping page, determine the mfn. ++ * - Find the mfn in the dumpfile page index. ++ */ ++#define PFNS_PER_PAGE (xd->page_size/sizeof(unsigned long)) ++ ++static ulong ++xc_core_pfn_to_page_index(ulong pfn) ++{ ++ ulong idx, p2m_idx, mfn_idx; ++ ulong *up, mfn; ++ off_t offset; ++ ++ /* ++ * This function does not apply when there's no p2m ++ * mapping and/or if this is an ELF format dumpfile. ++ */ ++ switch (xd->flags & (XC_CORE_NO_P2M|XC_CORE_ELF)) ++ { ++ case (XC_CORE_NO_P2M|XC_CORE_ELF): ++ return xc_core_elf_pfn_valid(pfn); ++ ++ case XC_CORE_NO_P2M: ++ return(xc_core_pfn_valid(pfn) ? pfn : PFN_NOT_FOUND); ++ ++ case XC_CORE_ELF: ++ return xc_core_elf_pfn_to_page_index(pfn); ++ } ++ ++ idx = pfn/PFNS_PER_PAGE; ++ ++ if (idx >= xd->xc_core.p2m_frames) { ++ error(INFO, "pfn: %lx is too large for dumpfile\n", ++ pfn); ++ return PFN_NOT_FOUND; ++ } ++ ++ p2m_idx = xd->xc_core.p2m_frame_index_list[idx]; ++ ++ if (lseek(xd->xfd, (off_t)xd->xc_core.header.xch_pages_offset, ++ SEEK_SET) == -1) { ++ error(INFO, "cannot lseek to xch_pages_offset\n"); ++ return PFN_NOT_FOUND; ++ } ++ ++ offset = (off_t)(p2m_idx) * (off_t)xd->page_size; ++ ++ if (lseek(xd->xfd, offset, SEEK_CUR) == -1) { ++ error(INFO, "cannot lseek to pfn-specified page\n"); ++ return PFN_NOT_FOUND; ++ } ++ ++ if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size) { ++ error(INFO, "cannot read pfn-specified page\n"); ++ return PFN_NOT_FOUND; ++ } ++ ++ up = (ulong *)xd->page; ++ up += (pfn%PFNS_PER_PAGE); ++ ++ mfn = *up; ++ ++ if ((mfn_idx = xc_core_mfn_to_page_index(mfn)) == MFN_NOT_FOUND) { ++ error(INFO, "cannot find mfn in page index\n"); ++ return PFN_NOT_FOUND; ++ } ++ ++ return mfn_idx; ++} ++ ++ ++/* ++ * Search the .xen_p2m array for the target pfn, starting at a ++ * higher batch if appropriate. This presumes that the pfns ++ * are laid out in ascending order. ++ */ ++static ulong ++xc_core_elf_pfn_to_page_index(ulong pfn) ++{ ++ int i, b, start_index; ++ off_t offset; ++ size_t size; ++ uint nr_pages; ++ ulong tmp; ++ struct xen_dumpcore_p2m p2m_batch[MAX_BATCH_SIZE]; ++ ++ offset = xd->xc_core.header.xch_index_offset; ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ ++ /* ++ * Initialize the start_index. ++ */ ++ xd->xc_core.last_batch.accesses++; ++ ++ if ((pfn >= xd->xc_core.last_batch.start) && ++ (pfn <= xd->xc_core.last_batch.end)) { ++ xd->xc_core.last_batch.duplicates++; ++ start_index = xd->xc_core.last_batch.index; ++ } else { ++ for (i = 0; i <= INDEX_PFN_COUNT; i++) { ++ if ((i == INDEX_PFN_COUNT) || ++ (pfn < xd->xc_core.elf_index_pfn[i].pfn)) { ++ if (--i < 0) ++ i = 0; ++ start_index = xd->xc_core.elf_index_pfn[i].index; ++ break; ++ } ++ } ++ } ++ ++ offset += (start_index * sizeof(struct xen_dumpcore_p2m)); ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, "cannot lseek to page index\n"); ++ ++ for (b = start_index; b < nr_pages; b += MAX_BATCH_SIZE) { ++ size = sizeof(struct xen_dumpcore_p2m) * ++ MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, &p2m_batch[0], size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return PFN_NOT_FOUND; ++ } ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) ++ break; ++ ++ tmp = (ulong)p2m_batch[i].pfn; ++ ++ if (tmp == pfn) { ++ if (CRASHDEBUG(4)) ++ fprintf(xd->ofp, ++ "index: batch: %d found pfn %ld (0x%lx) at index %d\n", ++ b/MAX_BATCH_SIZE, pfn, pfn, i+b); ++ ++ if ((b+MAX_BATCH_SIZE) < nr_pages) { ++ xd->xc_core.last_batch.index = b; ++ xd->xc_core.last_batch.start = p2m_batch[0].pfn; ++ xd->xc_core.last_batch.end = p2m_batch[MAX_BATCH_SIZE-1].pfn; ++ } ++ ++ return (i+b); ++ } ++ } ++ } ++ ++ return PFN_NOT_FOUND; ++} ++ ++/* ++ * In xendumps containing INVALID_MFN markers in the page index, ++ * return the validity of the pfn. ++ */ ++static int ++xc_core_pfn_valid(ulong pfn) ++{ ++ ulong mfn; ++ off_t offset; ++ ++ if (pfn >= (ulong)xd->xc_core.header.xch_nr_pages) ++ return FALSE; ++ ++ offset = (off_t)xd->xc_core.header.xch_index_offset; ++ ++ if (xd->flags & XC_CORE_64BIT_HOST) ++ offset += (off_t)(pfn * sizeof(ulonglong)); ++ else ++ offset += (off_t)(pfn * sizeof(ulong)); ++ ++ /* ++ * The lseek and read should never fail, so report ++ * any errors unconditionally. ++ */ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) { ++ error(INFO, ++ "xendump: cannot lseek to page index for pfn %lx\n", ++ pfn); ++ return FALSE; ++ } ++ ++ if (read(xd->xfd, &mfn, sizeof(ulong)) != sizeof(ulong)) { ++ error(INFO, ++ "xendump: cannot read index page for pfn %lx\n", ++ pfn); ++ return FALSE; ++ } ++ ++ /* ++ * If it's an invalid mfn, let the caller decide whether ++ * to display an error message (unless debugging). ++ */ ++ if (mfn == INVALID_MFN) { ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "xendump: pfn %lx contains INVALID_MFN\n", ++ pfn); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++/* ++ * Return the index into the .xen_pfn array containing the pfn. ++ * If not found, return PFN_NOT_FOUND. ++ */ ++static ulong ++xc_core_elf_pfn_valid(ulong pfn) ++{ ++ int i, b, start_index; ++ off_t offset; ++ size_t size; ++ uint nr_pages; ++ ulong tmp; ++ uint64_t pfn_batch[MAX_BATCH_SIZE]; ++ ++ offset = xd->xc_core.header.xch_index_offset; ++ nr_pages = xd->xc_core.header.xch_nr_pages; ++ ++ /* ++ * Initialize the start_index. ++ */ ++ xd->xc_core.last_batch.accesses++; ++ ++ if ((pfn >= xd->xc_core.last_batch.start) && ++ (pfn <= xd->xc_core.last_batch.end)) { ++ xd->xc_core.last_batch.duplicates++; ++ start_index = xd->xc_core.last_batch.index; ++ } else { ++ for (i = 0; i <= INDEX_PFN_COUNT; i++) { ++ if ((i == INDEX_PFN_COUNT) || ++ (pfn < xd->xc_core.elf_index_pfn[i].pfn)) { ++ if (--i < 0) ++ i = 0; ++ start_index = xd->xc_core.elf_index_pfn[i].index; ++ break; ++ } ++ } ++ } ++ ++ offset += (start_index * sizeof(uint64_t)); ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, "cannot lseek to page index\n"); ++ ++ for (b = start_index; b < nr_pages; b += MAX_BATCH_SIZE) { ++ size = sizeof(uint64_t) * MIN(MAX_BATCH_SIZE, nr_pages - b); ++ if (read(xd->xfd, &pfn_batch[0], size) != size) { ++ error(INFO, "cannot read index page %d\n", b); ++ return PFN_NOT_FOUND; ++ } ++ ++ for (i = 0; i < MAX_BATCH_SIZE; i++) { ++ if ((b+i) >= nr_pages) ++ break; ++ ++ tmp = (ulong)pfn_batch[i]; ++ ++ if (tmp == pfn) { ++ if (CRASHDEBUG(4)) ++ fprintf(xd->ofp, ++ "index: batch: %d found pfn %ld (0x%lx) at index %d\n", ++ b/MAX_BATCH_SIZE, pfn, pfn, i+b); ++ ++ if ((b+MAX_BATCH_SIZE) < nr_pages) { ++ xd->xc_core.last_batch.index = b; ++ xd->xc_core.last_batch.start = (ulong)pfn_batch[0]; ++ xd->xc_core.last_batch.end = (ulong)pfn_batch[MAX_BATCH_SIZE-1]; ++ } ++ ++ return (i+b); ++ } ++ } ++ } ++ ++ return PFN_NOT_FOUND; ++} ++ ++/* ++ * Store the panic task's stack hooks from where it was found ++ * in get_active_set_panic_task(). ++ */ ++void ++xendump_panic_hook(char *stack) ++{ ++ int i, err, argc; ++ char *arglist[MAXARGS]; ++ char buf[BUFSIZE]; ++ ulong value, *sp; ++ ++ if (machine_type("IA64")) /* needs switch_stack address */ ++ return; ++ ++ strcpy(buf, stack); ++ ++ argc = parse_line(buf, arglist); ++ ++ if ((value = htol(strip_ending_char(arglist[0], ':'), ++ RETURN_ON_ERROR, &err)) == BADADDR) ++ return; ++ for (sp = (ulong *)value, i = 1; i < argc; i++, sp++) { ++ if (strstr(arglist[i], "xen_panic_event")) { ++ if (!readmem((ulong)sp, KVADDR, &value, ++ sizeof(ulong), "xen_panic_event address", ++ RETURN_ON_ERROR)) ++ return; ++ ++ xd->panic_sp = (ulong)sp; ++ xd->panic_pc = value; ++ } else if (strstr(arglist[i], "panic") && !xd->panic_sp) { ++ if (!readmem((ulong)sp, KVADDR, &value, ++ sizeof(ulong), "xen_panic_event address", ++ RETURN_ON_ERROR)) ++ return; ++ ++ xd->panic_sp = (ulong)sp; ++ xd->panic_pc = value; ++ } ++ } ++} ++ ++static void ++xendump_print(char *fmt, ...) ++{ ++ char buf[BUFSIZE]; ++ va_list ap; ++ ++ if (!fmt || !strlen(fmt)) ++ return; ++ ++ va_start(ap, fmt); ++ (void)vsnprintf(buf, BUFSIZE, fmt, ap); ++ va_end(ap); ++ ++ if (xd->ofp) ++ fprintf(xd->ofp, buf); ++ else if (!XENDUMP_VALID() && CRASHDEBUG(7)) ++ fprintf(stderr, buf); ++ ++} ++ ++/* ++ * Support for xc_core ELF dumpfile format. ++ */ ++static int ++xc_core_elf_verify(char *file, char *buf) ++{ ++ int i; ++ Elf32_Ehdr *elf32; ++ Elf64_Ehdr *elf64; ++ Elf32_Off offset32; ++ Elf64_Off offset64; ++ char *eheader; ++ int swap; ++ ++ eheader = buf; ++ ++ if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT) ++ goto bailout; ++ ++ swap = (((eheader[EI_DATA] == ELFDATA2LSB) && ++ (__BYTE_ORDER == __BIG_ENDIAN)) || ++ ((eheader[EI_DATA] == ELFDATA2MSB) && ++ (__BYTE_ORDER == __LITTLE_ENDIAN))); ++ ++ elf32 = (Elf32_Ehdr *)buf; ++ elf64 = (Elf64_Ehdr *)buf; ++ ++ if ((elf32->e_ident[EI_CLASS] == ELFCLASS32) && ++ (swap16(elf32->e_type, swap) == ET_CORE) && ++ (swap32(elf32->e_version, swap) == EV_CURRENT) && ++ (swap16(elf32->e_shnum, swap) > 0)) { ++ switch (swap16(elf32->e_machine, swap)) ++ { ++ case EM_386: ++ if (machine_type_mismatch(file, "X86", NULL, 0)) ++ goto bailout; ++ break; ++ ++ default: ++ if (machine_type_mismatch(file, "(unknown)", NULL, 0)) ++ goto bailout; ++ break; ++ } ++ ++ if (endian_mismatch(file, elf32->e_ident[EI_DATA], 0)) ++ goto bailout; ++ ++ xd->xc_core.elf_class = ELFCLASS32; ++ if ((xd->xc_core.elf32 = (Elf32_Ehdr *)malloc(sizeof(Elf32_Ehdr))) == NULL) { ++ fprintf(stderr, "cannot malloc ELF header buffer\n"); ++ clean_exit(1); ++ } ++ BCOPY(buf, xd->xc_core.elf32, sizeof(Elf32_Ehdr)); ++ ++ } else if ((elf64->e_ident[EI_CLASS] == ELFCLASS64) && ++ (swap16(elf64->e_type, swap) == ET_CORE) && ++ (swap32(elf64->e_version, swap) == EV_CURRENT) && ++ (swap16(elf64->e_shnum, swap) > 0)) { ++ switch (swap16(elf64->e_machine, swap)) ++ { ++ case EM_IA_64: ++ if (machine_type_mismatch(file, "IA64", NULL, 0)) ++ goto bailout; ++ break; ++ ++ case EM_X86_64: ++ if (machine_type_mismatch(file, "X86_64", "X86", 0)) ++ goto bailout; ++ break; ++ ++ case EM_386: ++ if (machine_type_mismatch(file, "X86", NULL, 0)) ++ goto bailout; ++ break; ++ ++ default: ++ if (machine_type_mismatch(file, "(unknown)", NULL, 0)) ++ goto bailout; ++ } ++ ++ if (endian_mismatch(file, elf64->e_ident[EI_DATA], 0)) ++ goto bailout; ++ ++ xd->xc_core.elf_class = ELFCLASS64; ++ if ((xd->xc_core.elf64 = (Elf64_Ehdr *)malloc(sizeof(Elf64_Ehdr))) == NULL) { ++ fprintf(stderr, "cannot malloc ELF header buffer\n"); ++ clean_exit(1); ++ } ++ BCOPY(buf, xd->xc_core.elf64, sizeof(Elf64_Ehdr)); ++ ++ } else { ++ if (CRASHDEBUG(1)) ++ error(INFO, "%s: not a xen ELF core file\n", file); ++ goto bailout; ++ } ++ ++ xc_core_elf_dump(); ++ ++ switch (xd->xc_core.elf_class) ++ { ++ case ELFCLASS32: ++ offset32 = xd->xc_core.elf32->e_shoff; ++ for (i = 0; i < xd->xc_core.elf32->e_shnum; i++) { ++ xc_core_dump_Elf32_Shdr(offset32, ELFSTORE); ++ offset32 += xd->xc_core.elf32->e_shentsize; ++ } ++ xendump_print("\n"); ++ break; ++ ++ case ELFCLASS64: ++ offset64 = xd->xc_core.elf64->e_shoff; ++ for (i = 0; i < xd->xc_core.elf64->e_shnum; i++) { ++ xc_core_dump_Elf64_Shdr(offset64, ELFSTORE); ++ offset64 += xd->xc_core.elf64->e_shentsize; ++ } ++ xendump_print("\n"); ++ break; ++ } ++ ++ xd->flags |= (XENDUMP_LOCAL | XC_CORE_ELF); ++ ++ if (!xd->page_size) ++ error(FATAL, ++ "unknown page size: use -p command line option\n"); ++ ++ if (!(xd->page = (char *)malloc(xd->page_size))) ++ error(FATAL, "cannot malloc page space."); ++ ++ if (!(xd->poc = (struct pfn_offset_cache *)calloc ++ (PFN_TO_OFFSET_CACHE_ENTRIES, ++ sizeof(struct pfn_offset_cache)))) ++ error(FATAL, "cannot malloc pfn_offset_cache\n"); ++ xd->last_pfn = ~(0UL); ++ ++ for (i = 0; i < INDEX_PFN_COUNT; i++) ++ xd->xc_core.elf_index_pfn[i].pfn = ~0UL; ++ ++ if (CRASHDEBUG(1)) ++ xendump_memory_dump(fp); ++ ++ return TRUE; ++ ++bailout: ++ return FALSE; ++} ++ ++/* ++ * Dump the relevant ELF header. ++ */ ++static void ++xc_core_elf_dump(void) ++{ ++ switch (xd->xc_core.elf_class) ++ { ++ case ELFCLASS32: ++ xc_core_dump_Elf32_Ehdr(xd->xc_core.elf32); ++ break; ++ case ELFCLASS64: ++ xc_core_dump_Elf64_Ehdr(xd->xc_core.elf64); ++ break; ++ } ++} ++ ++ ++/* ++ * Dump the 32-bit ELF header, and grab a pointer to the strtab section. ++ */ ++static void ++xc_core_dump_Elf32_Ehdr(Elf32_Ehdr *elf) ++{ ++ char buf[BUFSIZE]; ++ Elf32_Off offset32; ++ Elf32_Shdr shdr; ++ ++ BZERO(buf, BUFSIZE); ++ BCOPY(elf->e_ident, buf, SELFMAG); ++ xendump_print("\nElf32_Ehdr:\n"); ++ xendump_print(" e_ident: \\%o%s\n", buf[0], ++ &buf[1]); ++ xendump_print(" e_ident[EI_CLASS]: %d ", elf->e_ident[EI_CLASS]); ++ switch (elf->e_ident[EI_CLASS]) ++ { ++ case ELFCLASSNONE: ++ xendump_print("(ELFCLASSNONE)"); ++ break; ++ case ELFCLASS32: ++ xendump_print("(ELFCLASS32)\n"); ++ break; ++ case ELFCLASS64: ++ xendump_print("(ELFCLASS64)\n"); ++ break; ++ case ELFCLASSNUM: ++ xendump_print("(ELFCLASSNUM)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ break; ++ } ++ xendump_print(" e_ident[EI_DATA]: %d ", elf->e_ident[EI_DATA]); ++ switch (elf->e_ident[EI_DATA]) ++ { ++ case ELFDATANONE: ++ xendump_print("(ELFDATANONE)\n"); ++ break; ++ case ELFDATA2LSB: ++ xendump_print("(ELFDATA2LSB)\n"); ++ break; ++ case ELFDATA2MSB: ++ xendump_print("(ELFDATA2MSB)\n"); ++ break; ++ case ELFDATANUM: ++ xendump_print("(ELFDATANUM)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ } ++ xendump_print(" e_ident[EI_VERSION]: %d ", ++ elf->e_ident[EI_VERSION]); ++ if (elf->e_ident[EI_VERSION] == EV_CURRENT) ++ xendump_print("(EV_CURRENT)\n"); ++ else ++ xendump_print("(?)\n"); ++ xendump_print(" e_ident[EI_OSABI]: %d ", elf->e_ident[EI_OSABI]); ++ switch (elf->e_ident[EI_OSABI]) ++ { ++ case ELFOSABI_SYSV: ++ xendump_print("(ELFOSABI_SYSV)\n"); ++ break; ++ case ELFOSABI_HPUX: ++ xendump_print("(ELFOSABI_HPUX)\n"); ++ break; ++ case ELFOSABI_ARM: ++ xendump_print("(ELFOSABI_ARM)\n"); ++ break; ++ case ELFOSABI_STANDALONE: ++ xendump_print("(ELFOSABI_STANDALONE)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ } ++ xendump_print(" e_ident[EI_ABIVERSION]: %d\n", ++ elf->e_ident[EI_ABIVERSION]); ++ ++ xendump_print(" e_type: %d ", elf->e_type); ++ switch (elf->e_type) ++ { ++ case ET_NONE: ++ xendump_print("(ET_NONE)\n"); ++ break; ++ case ET_REL: ++ xendump_print("(ET_REL)\n"); ++ break; ++ case ET_EXEC: ++ xendump_print("(ET_EXEC)\n"); ++ break; ++ case ET_DYN: ++ xendump_print("(ET_DYN)\n"); ++ break; ++ case ET_CORE: ++ xendump_print("(ET_CORE)\n"); ++ break; ++ case ET_NUM: ++ xendump_print("(ET_NUM)\n"); ++ break; ++ case ET_LOOS: ++ xendump_print("(ET_LOOS)\n"); ++ break; ++ case ET_HIOS: ++ xendump_print("(ET_HIOS)\n"); ++ break; ++ case ET_LOPROC: ++ xendump_print("(ET_LOPROC)\n"); ++ break; ++ case ET_HIPROC: ++ xendump_print("(ET_HIPROC)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ } ++ ++ xendump_print(" e_machine: %d ", elf->e_machine); ++ switch (elf->e_machine) ++ { ++ case EM_386: ++ xendump_print("(EM_386)\n"); ++ break; ++ default: ++ xendump_print("(unsupported)\n"); ++ break; ++ } ++ ++ xendump_print(" e_version: %ld ", (ulong)elf->e_version); ++ xendump_print("%s\n", elf->e_version == EV_CURRENT ? ++ "(EV_CURRENT)" : ""); ++ ++ xendump_print(" e_entry: %lx\n", (ulong)elf->e_entry); ++ xendump_print(" e_phoff: %lx\n", (ulong)elf->e_phoff); ++ xendump_print(" e_shoff: %lx\n", (ulong)elf->e_shoff); ++ xendump_print(" e_flags: %lx\n", (ulong)elf->e_flags); ++ xendump_print(" e_ehsize: %x\n", elf->e_ehsize); ++ xendump_print(" e_phentsize: %x\n", elf->e_phentsize); ++ xendump_print(" e_phnum: %x\n", elf->e_phnum); ++ xendump_print(" e_shentsize: %x\n", elf->e_shentsize); ++ xendump_print(" e_shnum: %x\n", elf->e_shnum); ++ xendump_print(" e_shstrndx: %x\n", elf->e_shstrndx); ++ ++ /* Determine the strtab location. */ ++ ++ offset32 = elf->e_shoff + ++ (elf->e_shstrndx * elf->e_shentsize); ++ ++ if (lseek(xd->xfd, offset32, SEEK_SET) != offset32) ++ error(FATAL, ++ "xc_core_dump_Elf32_Ehdr: cannot seek to strtab Elf32_Shdr\n"); ++ if (read(xd->xfd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) ++ error(FATAL, ++ "xc_core_dump_Elf32_Ehdr: cannot read strtab Elf32_Shdr\n"); ++ ++ xd->xc_core.elf_strtab_offset = (ulonglong)shdr.sh_offset; ++} ++ ++/* ++ * Dump the 64-bit ELF header, and grab a pointer to the strtab section. ++ */ ++static void ++xc_core_dump_Elf64_Ehdr(Elf64_Ehdr *elf) ++{ ++ char buf[BUFSIZE]; ++ Elf64_Off offset64; ++ Elf64_Shdr shdr; ++ ++ BZERO(buf, BUFSIZE); ++ BCOPY(elf->e_ident, buf, SELFMAG); ++ xendump_print("\nElf64_Ehdr:\n"); ++ xendump_print(" e_ident: \\%o%s\n", buf[0], ++ &buf[1]); ++ xendump_print(" e_ident[EI_CLASS]: %d ", elf->e_ident[EI_CLASS]); ++ switch (elf->e_ident[EI_CLASS]) ++ { ++ case ELFCLASSNONE: ++ xendump_print("(ELFCLASSNONE)"); ++ break; ++ case ELFCLASS32: ++ xendump_print("(ELFCLASS32)\n"); ++ break; ++ case ELFCLASS64: ++ xendump_print("(ELFCLASS64)\n"); ++ break; ++ case ELFCLASSNUM: ++ xendump_print("(ELFCLASSNUM)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ break; ++ } ++ xendump_print(" e_ident[EI_DATA]: %d ", elf->e_ident[EI_DATA]); ++ switch (elf->e_ident[EI_DATA]) ++ { ++ case ELFDATANONE: ++ xendump_print("(ELFDATANONE)\n"); ++ break; ++ case ELFDATA2LSB: ++ xendump_print("(ELFDATA2LSB)\n"); ++ break; ++ case ELFDATA2MSB: ++ xendump_print("(ELFDATA2MSB)\n"); ++ break; ++ case ELFDATANUM: ++ xendump_print("(ELFDATANUM)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ } ++ xendump_print(" e_ident[EI_VERSION]: %d ", ++ elf->e_ident[EI_VERSION]); ++ if (elf->e_ident[EI_VERSION] == EV_CURRENT) ++ xendump_print("(EV_CURRENT)\n"); ++ else ++ xendump_print("(?)\n"); ++ xendump_print(" e_ident[EI_OSABI]: %d ", elf->e_ident[EI_OSABI]); ++ switch (elf->e_ident[EI_OSABI]) ++ { ++ case ELFOSABI_SYSV: ++ xendump_print("(ELFOSABI_SYSV)\n"); ++ break; ++ case ELFOSABI_HPUX: ++ xendump_print("(ELFOSABI_HPUX)\n"); ++ break; ++ case ELFOSABI_ARM: ++ xendump_print("(ELFOSABI_ARM)\n"); ++ break; ++ case ELFOSABI_STANDALONE: ++ xendump_print("(ELFOSABI_STANDALONE)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ } ++ xendump_print(" e_ident[EI_ABIVERSION]: %d\n", ++ elf->e_ident[EI_ABIVERSION]); ++ ++ xendump_print(" e_type: %d ", elf->e_type); ++ switch (elf->e_type) ++ { ++ case ET_NONE: ++ xendump_print("(ET_NONE)\n"); ++ break; ++ case ET_REL: ++ xendump_print("(ET_REL)\n"); ++ break; ++ case ET_EXEC: ++ xendump_print("(ET_EXEC)\n"); ++ break; ++ case ET_DYN: ++ xendump_print("(ET_DYN)\n"); ++ break; ++ case ET_CORE: ++ xendump_print("(ET_CORE)\n"); ++ break; ++ case ET_NUM: ++ xendump_print("(ET_NUM)\n"); ++ break; ++ case ET_LOOS: ++ xendump_print("(ET_LOOS)\n"); ++ break; ++ case ET_HIOS: ++ xendump_print("(ET_HIOS)\n"); ++ break; ++ case ET_LOPROC: ++ xendump_print("(ET_LOPROC)\n"); ++ break; ++ case ET_HIPROC: ++ xendump_print("(ET_HIPROC)\n"); ++ break; ++ default: ++ xendump_print("(?)\n"); ++ } ++ ++ xendump_print(" e_machine: %d ", elf->e_machine); ++ switch (elf->e_machine) ++ { ++ case EM_386: ++ xendump_print("(EM_386)\n"); ++ break; ++ case EM_IA_64: ++ xendump_print("(EM_IA_64)\n"); ++ break; ++ case EM_PPC64: ++ xendump_print("(EM_PPC64)\n"); ++ break; ++ case EM_X86_64: ++ xendump_print("(EM_X86_64)\n"); ++ break; ++ default: ++ xendump_print("(unsupported)\n"); ++ break; ++ } ++ ++ xendump_print(" e_version: %ld ", (ulong)elf->e_version); ++ xendump_print("%s\n", elf->e_version == EV_CURRENT ? ++ "(EV_CURRENT)" : ""); ++ ++ xendump_print(" e_entry: %lx\n", (ulong)elf->e_entry); ++ xendump_print(" e_phoff: %lx\n", (ulong)elf->e_phoff); ++ xendump_print(" e_shoff: %lx\n", (ulong)elf->e_shoff); ++ xendump_print(" e_flags: %lx\n", (ulong)elf->e_flags); ++ xendump_print(" e_ehsize: %x\n", elf->e_ehsize); ++ xendump_print(" e_phentsize: %x\n", elf->e_phentsize); ++ xendump_print(" e_phnum: %x\n", elf->e_phnum); ++ xendump_print(" e_shentsize: %x\n", elf->e_shentsize); ++ xendump_print(" e_shnum: %x\n", elf->e_shnum); ++ xendump_print(" e_shstrndx: %x\n", elf->e_shstrndx); ++ ++ /* Determine the strtab location. */ ++ ++ offset64 = elf->e_shoff + ++ (elf->e_shstrndx * elf->e_shentsize); ++ ++ if (lseek(xd->xfd, offset64, SEEK_SET) != offset64) ++ error(FATAL, ++ "xc_core_dump_Elf64_Ehdr: cannot seek to strtab Elf32_Shdr\n"); ++ if (read(xd->xfd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) ++ error(FATAL, ++ "xc_core_dump_Elf64_Ehdr: cannot read strtab Elf32_Shdr\n"); ++ ++ xd->xc_core.elf_strtab_offset = (ulonglong)shdr.sh_offset; ++} ++ ++/* ++ * Dump each 32-bit section header and the data that they reference. ++ */ ++static void ++xc_core_dump_Elf32_Shdr(Elf32_Off offset, int store) ++{ ++ Elf32_Shdr shdr; ++ char name[BUFSIZE]; ++ int i; ++ char c; ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) != offset) ++ error(FATAL, ++ "xc_core_dump_Elf32_Shdr: cannot seek to Elf32_Shdr\n"); ++ if (read(xd->xfd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) ++ error(FATAL, ++ "xc_core_dump_Elf32_Shdr: cannot read Elf32_Shdr\n"); ++ ++ xendump_print("\nElf32_Shdr:\n"); ++ xendump_print(" sh_name: %lx ", shdr.sh_name); ++ xendump_print("\"%s\"\n", xc_core_strtab(shdr.sh_name, name)); ++ xendump_print(" sh_type: %lx ", shdr.sh_type); ++ switch (shdr.sh_type) ++ { ++ case SHT_NULL: ++ xendump_print("(SHT_NULL)\n"); ++ break; ++ case SHT_PROGBITS: ++ xendump_print("(SHT_PROGBITS)\n"); ++ break; ++ case SHT_STRTAB: ++ xendump_print("(SHT_STRTAB)\n"); ++ break; ++ case SHT_NOTE: ++ xendump_print("(SHT_NOTE)\n"); ++ break; ++ default: ++ xendump_print("\n"); ++ break; ++ } ++ xendump_print(" sh_flags: %lx\n", shdr.sh_flags); ++ xendump_print(" sh_addr: %lx\n", shdr.sh_addr); ++ xendump_print(" sh_offset: %lx\n", shdr.sh_offset); ++ xendump_print(" sh_size: %lx\n", shdr.sh_size); ++ xendump_print(" sh_link: %lx\n", shdr.sh_link); ++ xendump_print(" sh_info: %lx\n", shdr.sh_info); ++ xendump_print(" sh_addralign: %lx\n", shdr.sh_addralign); ++ xendump_print(" sh_entsize: %lx\n", shdr.sh_entsize); ++ ++ if (STREQ(name, ".shstrtab")) { ++ if (lseek(xd->xfd, xd->xc_core.elf_strtab_offset, SEEK_SET) != ++ xd->xc_core.elf_strtab_offset) ++ error(FATAL, ++ "xc_core_dump_Elf32_Shdr: cannot seek to strtab data\n"); ++ ++ xendump_print(" "); ++ for (i = 0; i < shdr.sh_size; i++) { ++ if (read(xd->xfd, &c, sizeof(char)) != sizeof(char)) ++ error(FATAL, ++ "xc_core_dump_Elf32_Shdr: cannot read strtab data\n"); ++ if (i && !c) ++ xendump_print("\n "); ++ else ++ xendump_print("%c", c); ++ } ++ } ++ ++ if (STREQ(name, ".note.Xen")) ++ xc_core_dump_elfnote((off_t)shdr.sh_offset, ++ (size_t)shdr.sh_size, store); ++ ++ if (!store) ++ return; ++ ++ if (STREQ(name, ".xen_prstatus")) ++ xd->xc_core.header.xch_ctxt_offset = ++ (unsigned int)shdr.sh_offset; ++ ++ if (STREQ(name, ".xen_shared_info")) ++ xd->xc_core.shared_info_offset = (off_t)shdr.sh_offset; ++ ++ if (STREQ(name, ".xen_pfn")) { ++ xd->xc_core.header.xch_index_offset = shdr.sh_offset; ++ xd->flags |= (XC_CORE_NO_P2M|XC_CORE_PFN_CREATE); ++ } ++ ++ if (STREQ(name, ".xen_p2m")) { ++ xd->xc_core.header.xch_index_offset = shdr.sh_offset; ++ xd->flags |= XC_CORE_P2M_CREATE; ++ } ++ ++ if (STREQ(name, ".xen_pages")) ++ xd->xc_core.header.xch_pages_offset = ++ (unsigned int)shdr.sh_offset; ++ ++ if (STREQ(name, ".xen_ia64_mapped_regs")) ++ xd->xc_core.ia64_mapped_regs_offset = ++ (off_t)shdr.sh_offset; ++} ++ ++/* ++ * Dump each 64-bit section header and the data that they reference. ++ */ ++static void ++xc_core_dump_Elf64_Shdr(Elf64_Off offset, int store) ++{ ++ Elf64_Shdr shdr; ++ char name[BUFSIZE]; ++ int i; ++ char c; ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) != offset) ++ error(FATAL, ++ "xc_core_dump_Elf64_Shdr: cannot seek to Elf64_Shdr\n"); ++ if (read(xd->xfd, &shdr, sizeof(Elf64_Shdr)) != sizeof(Elf64_Shdr)) ++ error(FATAL, ++ "xc_core_dump_Elf64_Shdr: cannot read Elf64_Shdr\n"); ++ ++ xendump_print("\nElf64_Shdr:\n"); ++ xendump_print(" sh_name: %x ", shdr.sh_name); ++ xendump_print("\"%s\"\n", xc_core_strtab(shdr.sh_name, name)); ++ xendump_print(" sh_type: %x ", shdr.sh_type); ++ switch (shdr.sh_type) ++ { ++ case SHT_NULL: ++ xendump_print("(SHT_NULL)\n"); ++ break; ++ case SHT_PROGBITS: ++ xendump_print("(SHT_PROGBITS)\n"); ++ break; ++ case SHT_STRTAB: ++ xendump_print("(SHT_STRTAB)\n"); ++ break; ++ case SHT_NOTE: ++ xendump_print("(SHT_NOTE)\n"); ++ break; ++ default: ++ xendump_print("\n"); ++ break; ++ } ++ xendump_print(" sh_flags: %lx\n", shdr.sh_flags); ++ xendump_print(" sh_addr: %lx\n", shdr.sh_addr); ++ xendump_print(" sh_offset: %lx\n", shdr.sh_offset); ++ xendump_print(" sh_size: %lx\n", shdr.sh_size); ++ xendump_print(" sh_link: %x\n", shdr.sh_link); ++ xendump_print(" sh_info: %x\n", shdr.sh_info); ++ xendump_print(" sh_addralign: %lx\n", shdr.sh_addralign); ++ xendump_print(" sh_entsize: %lx\n", shdr.sh_entsize); ++ ++ if (STREQ(name, ".shstrtab")) { ++ if (lseek(xd->xfd, xd->xc_core.elf_strtab_offset, SEEK_SET) != ++ xd->xc_core.elf_strtab_offset) ++ error(FATAL, ++ "xc_core_dump_Elf64_Shdr: cannot seek to strtab data\n"); ++ ++ xendump_print(" "); ++ for (i = 0; i < shdr.sh_size; i++) { ++ if (read(xd->xfd, &c, sizeof(char)) != sizeof(char)) ++ error(FATAL, ++ "xc_core_dump_Elf64_Shdr: cannot read strtab data\n"); ++ if (i && !c) ++ xendump_print("\n "); ++ else ++ xendump_print("%c", c); ++ } ++ } ++ ++ if (STREQ(name, ".note.Xen")) ++ xc_core_dump_elfnote((off_t)shdr.sh_offset, ++ (size_t)shdr.sh_size, store); ++ ++ if (!store) ++ return; ++ ++ if (STREQ(name, ".xen_prstatus")) ++ xd->xc_core.header.xch_ctxt_offset = ++ (unsigned int)shdr.sh_offset; ++ ++ if (STREQ(name, ".xen_shared_info")) ++ xd->xc_core.shared_info_offset = (off_t)shdr.sh_offset; ++ ++ if (STREQ(name, ".xen_pfn")) { ++ xd->xc_core.header.xch_index_offset = shdr.sh_offset; ++ xd->flags |= (XC_CORE_NO_P2M|XC_CORE_PFN_CREATE); ++ } ++ ++ if (STREQ(name, ".xen_p2m")) { ++ xd->xc_core.header.xch_index_offset = shdr.sh_offset; ++ xd->flags |= XC_CORE_P2M_CREATE; ++ } ++ ++ if (STREQ(name, ".xen_pages")) ++ xd->xc_core.header.xch_pages_offset = ++ (unsigned int)shdr.sh_offset; ++ ++ if (STREQ(name, ".xen_ia64_mapped_regs")) ++ xd->xc_core.ia64_mapped_regs_offset = ++ (off_t)shdr.sh_offset; ++} ++ ++/* ++ * Return the string found at the specified index into ++ * the dumpfile's strtab. ++ */ ++static char * ++xc_core_strtab(uint32_t index, char *buf) ++{ ++ off_t offset; ++ int i; ++ ++ offset = xd->xc_core.elf_strtab_offset + index; ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) != offset) ++ error(FATAL, ++ "xc_core_strtab: cannot seek to Elf64_Shdr\n"); ++ ++ BZERO(buf, BUFSIZE); ++ i = 0; ++ ++ while (read(xd->xfd, &buf[i], sizeof(char)) == sizeof(char)) { ++ if (buf[i] == NULLCHAR) ++ break; ++ i++; ++ } ++ ++ return buf; ++} ++ ++ ++/* ++ * Dump the array of elfnote structures, storing relevant info ++ * when requested during initialization. This function is ++ * common to both 32-bit and 64-bit ELF files. ++ */ ++static void ++xc_core_dump_elfnote(off_t sh_offset, size_t sh_size, int store) ++{ ++ int i, lf, index; ++ char *notes_buffer; ++ struct elfnote *elfnote; ++ ulonglong *data; ++ struct xen_dumpcore_elfnote_header_desc *elfnote_header; ++ struct xen_dumpcore_elfnote_format_version_desc *format_version; ++ ++ elfnote_header = NULL; ++ ++ if (!(notes_buffer = (char *)malloc(sh_size))) ++ error(FATAL, "cannot malloc notes space."); ++ ++ if (lseek(xd->xfd, sh_offset, SEEK_SET) != sh_offset) ++ error(FATAL, ++ "xc_core_dump_elfnote: cannot seek to sh_offset\n"); ++ ++ if (read(xd->xfd, notes_buffer, sh_size) != sh_size) ++ error(FATAL, ++ "xc_core_dump_elfnote: cannot read elfnote data\n"); ++ ++ for (index = 0; index < sh_size; ) { ++ elfnote = (struct elfnote *)¬es_buffer[index]; ++ xendump_print(" namesz: %d\n", elfnote->namesz); ++ xendump_print(" descz: %d\n", elfnote->descsz); ++ xendump_print(" type: %x ", elfnote->type); ++ switch (elfnote->type) ++ { ++ case XEN_ELFNOTE_DUMPCORE_NONE: ++ xendump_print("(XEN_ELFNOTE_DUMPCORE_NONE)\n"); ++ break; ++ case XEN_ELFNOTE_DUMPCORE_HEADER: ++ xendump_print("(XEN_ELFNOTE_DUMPCORE_HEADER)\n"); ++ elfnote_header = (struct xen_dumpcore_elfnote_header_desc *) ++ (elfnote+1); ++ break; ++ case XEN_ELFNOTE_DUMPCORE_XEN_VERSION: ++ xendump_print("(XEN_ELFNOTE_DUMPCORE_XEN_VERSION)\n"); ++ break; ++ case XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION: ++ xendump_print("(XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION)\n"); ++ format_version = (struct xen_dumpcore_elfnote_format_version_desc *) ++ (elfnote+1); ++ break; ++ default: ++ xendump_print("(unknown)\n"); ++ break; ++ } ++ xendump_print(" name: %s\n", elfnote->name); ++ ++ data = (ulonglong *)(elfnote+1); ++ for (i = lf = 0; i < elfnote->descsz/sizeof(ulonglong); i++) { ++ if (((i%2)==0)) { ++ xendump_print("%s ", ++ i ? "\n" : ""); ++ lf++; ++ } else ++ lf = 0; ++ xendump_print("%016llx ", *data++); ++ } ++ if (!elfnote->descsz) ++ xendump_print(" (empty)"); ++ xendump_print("\n"); ++ ++ index += sizeof(struct elfnote) + elfnote->descsz; ++ } ++ ++ if (!store) ++ return; ++ ++ if (elfnote_header) { ++ xd->xc_core.header.xch_magic = elfnote_header->xch_magic; ++ xd->xc_core.header.xch_nr_vcpus = elfnote_header->xch_nr_vcpus; ++ xd->xc_core.header.xch_nr_pages = elfnote_header->xch_nr_pages; ++ xd->page_size = elfnote_header->xch_page_size; ++ } ++ ++ if (format_version) { ++ switch (format_version->version) ++ { ++ case FORMAT_VERSION_0000000000000001: ++ break; ++ default: ++ error(WARNING, ++ "unsupported xen dump-core format version: %016llx\n", ++ format_version->version); ++ } ++ xd->xc_core.format_version = format_version->version; ++ } ++ ++} ++ ++/* ++ * Initialize the batching list for the .xen_p2m or .xen_pfn ++ * arrays. ++ */ ++static void ++xc_core_elf_pfn_init(void) ++{ ++ int i, c, chunk; ++ off_t offset; ++ struct xen_dumpcore_p2m p2m; ++ uint64_t pfn; ++ ++ switch (xd->flags & (XC_CORE_ELF|XC_CORE_NO_P2M)) ++ { ++ case (XC_CORE_ELF|XC_CORE_NO_P2M): ++ chunk = xd->xc_core.header.xch_nr_pages/INDEX_PFN_COUNT; ++ ++ for (i = c = 0; i < INDEX_PFN_COUNT; i++, c += chunk) { ++ offset = (off_t)xd->xc_core.header.xch_index_offset + ++ (off_t)(c * sizeof(uint64_t)); ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, ++ "cannot lseek to page index %d\n", c); ++ if (read(xd->xfd, &pfn, sizeof(uint64_t)) != ++ sizeof(uint64_t)) ++ error(FATAL, ++ "cannot read page index %d\n", c); ++ ++ xd->xc_core.elf_index_pfn[i].index = c; ++ xd->xc_core.elf_index_pfn[i].pfn = (ulong)pfn; ++ } ++ break; ++ ++ case XC_CORE_ELF: ++ chunk = xd->xc_core.header.xch_nr_pages/INDEX_PFN_COUNT; ++ ++ for (i = c = 0; i < INDEX_PFN_COUNT; i++, c += chunk) { ++ offset = (off_t)xd->xc_core.header.xch_index_offset + ++ (off_t)(c * sizeof(struct xen_dumpcore_p2m)); ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, ++ "cannot lseek to page index %d\n", c); ++ if (read(xd->xfd, &p2m, sizeof(struct xen_dumpcore_p2m)) != ++ sizeof(struct xen_dumpcore_p2m)) ++ error(FATAL, ++ "cannot read page index %d\n", c); ++ ++ xd->xc_core.elf_index_pfn[i].index = c; ++ xd->xc_core.elf_index_pfn[i].pfn = (ulong)p2m.pfn; ++ } ++ break; ++ } ++} ++ ++struct xendump_data * ++get_xendump_data(void) ++{ ++ return (XENDUMP_VALID() ? xd : NULL); ++} +--- crash/ppc.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/ppc.c 2007-03-21 15:02:37.000000000 -0400 +@@ -51,6 +51,9 @@ + void + ppc_init(int when) + { ++ uint cpu_features; ++ ulong cur_cpu_spec; ++ + switch (when) + { + case PRE_SYMTAB: +@@ -135,9 +138,23 @@ + "irq_desc", NULL, 0); + else + machdep->nr_irqs = 0; +- machdep->hz = HZ; +- if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) +- machdep->hz = 1000; ++ if (!machdep->hz) { ++ machdep->hz = HZ; ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) ++ machdep->hz = 1000; ++ } ++ if (symbol_exists("cur_cpu_spec")) { ++ get_symbol_data("cur_cpu_spec", sizeof(void *), &cur_cpu_spec); ++ readmem(cur_cpu_spec + MEMBER_OFFSET("cpu_spec", "cpu_user_features"), ++ KVADDR, &cpu_features, sizeof(uint), "cpu user features", ++ FAULT_ON_ERROR); ++ if (cpu_features & CPU_BOOKE) ++ machdep->flags |= CPU_BOOKE; ++ } ++ else ++ machdep->flags |= CPU_BOOKE; ++ machdep->section_size_bits = _SECTION_SIZE_BITS; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; + break; + + case POST_INIT: +@@ -154,8 +171,6 @@ + fprintf(fp, " flags: %lx (", machdep->flags); + if (machdep->flags & KSYMS_START) + fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); +@@ -205,6 +220,9 @@ + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); ++ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); ++ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); ++ fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); + fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); + } + +@@ -280,7 +298,11 @@ + + page_middle = (ulong *)pgd_pte; + +- page_table = page_middle + (BTOP(vaddr) & (PTRS_PER_PTE - 1)); ++ if (machdep->flags & CPU_BOOKE) ++ page_table = page_middle + (BTOP(vaddr) & (PTRS_PER_PTE - 1)); ++ else ++ page_table = (ulong *)(((pgd_pte & (ulong)machdep->pagemask) + machdep->kvbase) + ++ ((ulong)BTOP(vaddr) & (PTRS_PER_PTE-1))); + + if (verbose) + fprintf(fp, " PMD: %lx => %lx\n",(ulong)page_middle, +@@ -364,7 +386,11 @@ + + page_middle = (ulong *)pgd_pte; + +- page_table = page_middle + (BTOP(kvaddr) & (PTRS_PER_PTE-1)); ++ if (machdep->flags & CPU_BOOKE) ++ page_table = page_middle + (BTOP(kvaddr) & (PTRS_PER_PTE - 1)); ++ else ++ page_table = (ulong *)(((pgd_pte & (ulong)machdep->pagemask) + machdep->kvbase) + ++ ((ulong)BTOP(kvaddr) & (PTRS_PER_PTE-1))); + + if (verbose) + fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, +--- crash/x86.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/x86.c 2009-02-04 15:30:50.000000000 -0500 +@@ -1,8 +1,8 @@ + /* x86.c - core analysis suite + * + * Portions Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 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 +@@ -51,6 +51,7 @@ + * rights to redistribute these changes. + */ + #include "defs.h" ++#include "xen_hyper_defs.h" + + #ifndef MCLX + +@@ -176,6 +177,7 @@ + static void db_symbol_values(db_sym_t, char **, db_expr_t *); + static int db_sym_numargs(db_sym_t, int *, char **); + static void x86_dump_line_number(ulong); ++static void x86_clear_machdep_cache(void); + + static ulong mach_debug = 0; + +@@ -215,7 +217,7 @@ + + argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE, bt); + /* +- * XXX etext is wrong for LKMs. We should attempt to interpret ++ * etext is wrong for LKMs. We should attempt to interpret + * the instruction at the return address in all cases. This + * may require better fault handling. + */ +@@ -685,6 +687,7 @@ + bt->debug || + (bt->flags & BT_FRAMESIZE_DEBUG) || + !(bt->flags & BT_OLD_BACK_TRACE)) { ++ bt->flags &= ~BT_OLD_BACK_TRACE; + lkcd_x86_back_trace(bt, 0, fp); + return; + } +@@ -962,8 +965,12 @@ + */ + static int x86_uvtop(struct task_context *, ulong, physaddr_t *, int); + static int x86_kvtop(struct task_context *, ulong, physaddr_t *, int); +-static int x86_uvtop_pae(struct task_context *, ulong, physaddr_t *, int); +-static int x86_kvtop_pae(struct task_context *, ulong, physaddr_t *, int); ++static int x86_uvtop_PAE(struct task_context *, ulong, physaddr_t *, int); ++static int x86_kvtop_PAE(struct task_context *, ulong, physaddr_t *, int); ++static int x86_uvtop_xen_wpt(struct task_context *, ulong, physaddr_t *, int); ++static int x86_kvtop_xen_wpt(struct task_context *, ulong, physaddr_t *, int); ++static int x86_uvtop_xen_wpt_PAE(struct task_context *, ulong, physaddr_t *, int); ++static int x86_kvtop_xen_wpt_PAE(struct task_context *, ulong, physaddr_t *, int); + static ulong x86_get_task_pgd(ulong); + static ulong x86_processor_speed(void); + static ulong x86_get_pc(struct bt_info *); +@@ -973,6 +980,7 @@ + static uint64_t x86_memory_size(void); + static ulong x86_vmalloc_start(void); + static ulong *read_idt_table(int); ++static void eframe_init(void); + #define READ_IDT_INIT 1 + #define READ_IDT_RUNTIME 2 + static char *extract_idt_function(ulong *, char *, ulong *); +@@ -983,26 +991,42 @@ + static int x86_dis_filter(ulong, char *); + static struct line_number_hook x86_line_number_hooks[]; + static int x86_is_uvaddr(ulong, struct task_context *); ++static void x86_init_kernel_pgd(void); ++static ulong xen_m2p_nonPAE(ulong); ++static int x86_xendump_p2m_create(struct xendump_data *); ++static int x86_xen_kdump_p2m_create(struct xen_kdump_data *); ++static char *x86_xen_kdump_load_page(ulong, char *); ++static char *x86_xen_kdump_load_page_PAE(ulong, char *); ++static ulong x86_xen_kdump_page_mfn(ulong); ++static ulong x86_xen_kdump_page_mfn_PAE(ulong); ++static ulong x86_xendump_panic_task(struct xendump_data *); ++static void x86_get_xendump_regs(struct xendump_data *, struct bt_info *, ulong *, ulong *); ++static char *x86_xendump_load_page(ulong, char *); ++static char *x86_xendump_load_page_PAE(ulong, char *); ++static int x86_xendump_page_index(ulong); ++static int x86_xendump_page_index_PAE(ulong); ++static void x86_init_hyper(int); ++static ulong x86_get_stackbase_hyper(ulong); ++static ulong x86_get_stacktop_hyper(ulong); ++ ++int INT_EFRAME_SS = 14; ++int INT_EFRAME_ESP = 13; ++int INT_EFRAME_EFLAGS = 12; /* CS lcall7 */ ++int INT_EFRAME_CS = 11; /* EIP lcall7 */ ++int INT_EFRAME_EIP = 10; /* EFLAGS lcall7 */ ++int INT_EFRAME_ERR = 9; ++int INT_EFRAME_ES = 8; ++int INT_EFRAME_DS = 7; ++int INT_EFRAME_EAX = 6; ++int INT_EFRAME_EBP = 5; ++int INT_EFRAME_EDI = 4; ++int INT_EFRAME_ESI = 3; ++int INT_EFRAME_EDX = 2; ++int INT_EFRAME_ECX = 1; ++int INT_EFRAME_EBX = 0; ++int INT_EFRAME_GS = -1; + +- +-#define INT_EFRAME_SS (14) +-#define INT_EFRAME_ESP (13) +-#define INT_EFRAME_EFLAGS (12) /* CS lcall7 */ +-#define INT_EFRAME_CS (11) /* EIP lcall7 */ +-#define INT_EFRAME_EIP (10) /* EFLAGS lcall7 */ +-#define INT_EFRAME_ERR (9) +- +-#define INT_EFRAME_ES (8) +-#define INT_EFRAME_DS (7) +-#define INT_EFRAME_EAX (6) +-#define INT_EFRAME_EBP (5) +-#define INT_EFRAME_EDI (4) +-#define INT_EFRAME_ESI (3) +-#define INT_EFRAME_EDX (2) +-#define INT_EFRAME_ECX (1) +-#define INT_EFRAME_EBX (0) +- +-#define USER_EFRAME_SIZE (INT_EFRAME_SS+1) ++#define MAX_USER_EFRAME_SIZE (16) + #define KERNEL_EFRAME_SIZE (INT_EFRAME_EFLAGS+1) + + #define EFRAME_USER (1) +@@ -1015,7 +1039,7 @@ + { + int i; + char buf[BUFSIZE], *sp; +- ulong int_eframe[USER_EFRAME_SIZE]; ++ ulong int_eframe[MAX_USER_EFRAME_SIZE]; + int eframe_type, args; + ulong value, *argp; + +@@ -1025,11 +1049,11 @@ + return(frame_number); + + GET_STACK_DATA(ep->eframe_addr, (char *)int_eframe, +- USER_EFRAME_SIZE * sizeof(ulong)); ++ SIZE(pt_regs)); + + if (int_eframe[INT_EFRAME_CS] & DPL_BITS) { + if (!INSTACK(ep->eframe_addr + +- (USER_EFRAME_SIZE*sizeof(ulong)) - 1, bt)) ++ SIZE(pt_regs) - 1, bt)) + return(frame_number); + /* error(FATAL, "read of exception frame would go beyond stack\n"); */ + eframe_type = EFRAME_USER; +@@ -1158,17 +1182,24 @@ + int_eframe[INT_EFRAME_EDX]); + + fprintf(fp, +- " DS: %04x ESI: %08lx ES: %04x EDI: %08lx \n", ++ " DS: %04x ESI: %08lx ES: %04x EDI: %08lx", + (short)int_eframe[INT_EFRAME_DS], + int_eframe[INT_EFRAME_ESI], + (short)int_eframe[INT_EFRAME_ES], + int_eframe[INT_EFRAME_EDI]); ++ if (kernel && (INT_EFRAME_GS != -1)) ++ fprintf(fp, " GS: %04x", (short)int_eframe[INT_EFRAME_GS]); ++ fprintf(fp, "\n"); + +- if (!kernel) +- fprintf(fp, " SS: %04x ESP: %08lx EBP: %08lx \n", ++ if (!kernel) { ++ fprintf(fp, " SS: %04x ESP: %08lx EBP: %08lx", + (short)int_eframe[INT_EFRAME_SS], + int_eframe[INT_EFRAME_ESP], + int_eframe[INT_EFRAME_EBP]); ++ if (INT_EFRAME_GS != -1) ++ fprintf(fp, " GS: %04x", (short)int_eframe[INT_EFRAME_GS]); ++ fprintf(fp, "\n"); ++ } + + fprintf(fp, + " CS: %04x EIP: %08lx ERR: %08lx EFLAGS: %08lx \n", +@@ -1355,7 +1386,7 @@ + */ + + struct x86_pt_regs { +- ulong reg_value[USER_EFRAME_SIZE]; ++ ulong reg_value[MAX_USER_EFRAME_SIZE]; + }; + + /* +@@ -1420,6 +1451,17 @@ + break; + } + ++ if (XEN() && ((short)pt->reg_value[INT_EFRAME_CS] == 0x61) && ++ ((short)pt->reg_value[INT_EFRAME_DS] == 0x7b) && ++ ((short)pt->reg_value[INT_EFRAME_ES] == 0x7b) && ++ IS_KVADDR(pt->reg_value[INT_EFRAME_EIP])) { ++ if (!(machdep->flags & OMIT_FRAME_PTR) && ++ !INSTACK(pt->reg_value[INT_EFRAME_EBP], bt)) ++ continue; ++ rv = bt->stackbase + sizeof(ulong) * (first - stack); ++ break; ++ } ++ + /* check for user exception frame */ + + if (((short)pt->reg_value[INT_EFRAME_CS] == 0x23) && +@@ -1441,6 +1483,20 @@ + rv = bt->stackbase + sizeof(ulong) * (first - stack); + break; + } ++ ++ /* ++ * 2.6 kernels using sysenter_entry instead of system_call ++ * have a funky trampoline EIP address. ++ */ ++ if (((short)pt->reg_value[INT_EFRAME_CS] == 0x73) && ++ ((short)pt->reg_value[INT_EFRAME_DS] == 0x7b) && ++ ((short)pt->reg_value[INT_EFRAME_ES] == 0x7b) && ++ ((short)pt->reg_value[INT_EFRAME_SS] == 0x7b) && ++ (pt->reg_value[INT_EFRAME_EFLAGS] == 0x246) && ++ IS_UVADDR(pt->reg_value[INT_EFRAME_ESP], bt->tc)) { ++ rv = bt->stackbase + sizeof(ulong) * (first - stack); ++ break; ++ } + } + return(rv); + } +@@ -1536,6 +1592,8 @@ + mode = "USER-MODE"; + } else if ((cs == 0x10) || (cs == 0x60)) { + mode = "KERNEL-MODE"; ++ } else if (XEN() && (cs == 0x61)) { ++ mode = "KERNEL-MODE"; + } else { + mode = "UNKNOWN-MODE"; + } +@@ -1559,6 +1617,9 @@ + x86_in_irqstack(ulong addr) + { + int c; ++ ++ if (!(tt->flags & IRQSTACKS)) ++ return 0; + + for (c = 0; c < NR_CPUS; c++) { + if (tt->hardirq_ctx[c]) { +@@ -1626,6 +1687,11 @@ + { + struct syment *sp, *spn; + ++ if (XEN_HYPER_MODE()) { ++ x86_init_hyper(when); ++ return; ++ } ++ + switch (when) + { + case PRE_SYMTAB: +@@ -1639,7 +1705,7 @@ + machdep->stacksize = machdep->pagesize * 2; + if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pgd space."); +- if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) ++ if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pmd space."); + if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc ptbl space."); +@@ -1659,8 +1725,8 @@ + PGDIR_SHIFT = PGDIR_SHIFT_3LEVEL; + PTRS_PER_PTE = PTRS_PER_PTE_3LEVEL; + PTRS_PER_PGD = PTRS_PER_PGD_3LEVEL; +- machdep->uvtop = x86_uvtop_pae; +- machdep->kvtop = x86_kvtop_pae; ++ machdep->uvtop = x86_uvtop_PAE; ++ machdep->kvtop = x86_kvtop_PAE; + } else { + PGDIR_SHIFT = PGDIR_SHIFT_2LEVEL; + PTRS_PER_PTE = PTRS_PER_PTE_2LEVEL; +@@ -1696,19 +1762,53 @@ + machdep->cmd_mach = x86_cmd_mach; + machdep->get_smp_cpus = x86_get_smp_cpus; + machdep->line_number_hooks = x86_line_number_hooks; +- if (x86_omit_frame_pointer()) +- machdep->flags |= OMIT_FRAME_PTR; + machdep->flags |= FRAMESIZE_DEBUG; + machdep->value_to_symbol = generic_machdep_value_to_symbol; +- machdep->init_kernel_pgd = NULL; ++ machdep->init_kernel_pgd = x86_init_kernel_pgd; ++ machdep->xendump_p2m_create = x86_xendump_p2m_create; ++ machdep->xen_kdump_p2m_create = x86_xen_kdump_p2m_create; ++ machdep->xendump_panic_task = x86_xendump_panic_task; ++ machdep->get_xendump_regs = x86_get_xendump_regs; ++ machdep->clear_machdep_cache = x86_clear_machdep_cache; + break; + + case POST_GDB: ++ if (x86_omit_frame_pointer()) ++ machdep->flags |= OMIT_FRAME_PTR; + STRUCT_SIZE_INIT(user_regs_struct, "user_regs_struct"); +- MEMBER_OFFSET_INIT(user_regs_struct_ebp, +- "user_regs_struct", "ebp"); +- MEMBER_OFFSET_INIT(user_regs_struct_esp, +- "user_regs_struct", "esp"); ++ if (MEMBER_EXISTS("user_regs_struct", "ebp")) ++ MEMBER_OFFSET_INIT(user_regs_struct_ebp, ++ "user_regs_struct", "ebp"); ++ else ++ MEMBER_OFFSET_INIT(user_regs_struct_ebp, ++ "user_regs_struct", "bp"); ++ if (MEMBER_EXISTS("user_regs_struct", "esp")) ++ MEMBER_OFFSET_INIT(user_regs_struct_esp, ++ "user_regs_struct", "esp"); ++ else ++ MEMBER_OFFSET_INIT(user_regs_struct_esp, ++ "user_regs_struct", "sp"); ++ if (!VALID_STRUCT(user_regs_struct)) { ++ /* Use this hardwired version -- sometimes the ++ * debuginfo doesn't pick this up even though ++ * it exists in the kernel; it shouldn't change. ++ */ ++ struct x86_user_regs_struct { ++ long ebx, ecx, edx, esi, edi, ebp, eax; ++ unsigned short ds, __ds, es, __es; ++ unsigned short fs, __fs, gs, __gs; ++ long orig_eax, eip; ++ unsigned short cs, __cs; ++ long eflags, esp; ++ unsigned short ss, __ss; ++ }; ++ ASSIGN_SIZE(user_regs_struct) = ++ sizeof(struct x86_user_regs_struct); ++ ASSIGN_OFFSET(user_regs_struct_ebp) = ++ offsetof(struct x86_user_regs_struct, ebp); ++ ASSIGN_OFFSET(user_regs_struct_esp) = ++ offsetof(struct x86_user_regs_struct, esp); ++ } + MEMBER_OFFSET_INIT(thread_struct_cr3, "thread_struct", "cr3"); + STRUCT_SIZE_INIT(cpuinfo_x86, "cpuinfo_x86"); + STRUCT_SIZE_INIT(e820map, "e820map"); +@@ -1723,9 +1823,42 @@ + "irq_desc", NULL, 0); + else + machdep->nr_irqs = 224; /* NR_IRQS */ +- machdep->hz = HZ; +- if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) +- machdep->hz = 1000; ++ if (!machdep->hz) { ++ machdep->hz = HZ; ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) ++ machdep->hz = 1000; ++ } ++ ++ if (machdep->flags & PAE) { ++ if (THIS_KERNEL_VERSION < LINUX(2,6,26)) ++ machdep->section_size_bits = ++ _SECTION_SIZE_BITS_PAE_ORIG; ++ else ++ machdep->section_size_bits = ++ _SECTION_SIZE_BITS_PAE_2_6_26; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS_PAE; ++ } else { ++ machdep->section_size_bits = _SECTION_SIZE_BITS; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; ++ } ++ ++ if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) { ++ if (machdep->flags & PAE) ++ machdep->uvtop = x86_uvtop_xen_wpt_PAE; ++ else ++ machdep->uvtop = x86_uvtop_xen_wpt; ++ } ++ ++ if (XEN()) { ++ MEMBER_OFFSET_INIT(vcpu_guest_context_user_regs, ++ "vcpu_guest_context", "user_regs"); ++ MEMBER_OFFSET_INIT(cpu_user_regs_esp, ++ "cpu_user_regs", "esp"); ++ MEMBER_OFFSET_INIT(cpu_user_regs_eip, ++ "cpu_user_regs", "eip"); ++ } ++ ++ eframe_init(); + break; + + case POST_INIT: +@@ -1735,6 +1868,67 @@ + } + + /* ++ * Account for addition of pt_regs.xgs field in 2.6.20+ kernels. ++ */ ++static void ++eframe_init(void) ++{ ++ if (INVALID_SIZE(pt_regs)) { ++ if (THIS_KERNEL_VERSION < LINUX(2,6,20)) ++ ASSIGN_SIZE(pt_regs) = (MAX_USER_EFRAME_SIZE-1)*sizeof(ulong); ++ else { ++ ASSIGN_SIZE(pt_regs) = MAX_USER_EFRAME_SIZE*sizeof(ulong); ++ INT_EFRAME_SS = 15; ++ INT_EFRAME_ESP = 14; ++ INT_EFRAME_EFLAGS = 13; ++ INT_EFRAME_CS = 12; ++ INT_EFRAME_EIP = 11; ++ INT_EFRAME_ERR = 10; ++ INT_EFRAME_GS = 9; ++ } ++ return; ++ } ++ ++ if (MEMBER_EXISTS("pt_regs", "esp")) { ++ INT_EFRAME_SS = MEMBER_OFFSET("pt_regs", "xss") / 4; ++ INT_EFRAME_ESP = MEMBER_OFFSET("pt_regs", "esp") / 4; ++ INT_EFRAME_EFLAGS = MEMBER_OFFSET("pt_regs", "eflags") / 4; ++ INT_EFRAME_CS = MEMBER_OFFSET("pt_regs", "xcs") / 4; ++ INT_EFRAME_EIP = MEMBER_OFFSET("pt_regs", "eip") / 4; ++ INT_EFRAME_ERR = MEMBER_OFFSET("pt_regs", "orig_eax") / 4; ++ if ((INT_EFRAME_GS = MEMBER_OFFSET("pt_regs", "xgs")) != -1) ++ INT_EFRAME_GS /= 4; ++ INT_EFRAME_ES = MEMBER_OFFSET("pt_regs", "xes") / 4; ++ INT_EFRAME_DS = MEMBER_OFFSET("pt_regs", "xds") / 4; ++ INT_EFRAME_EAX = MEMBER_OFFSET("pt_regs", "eax") / 4; ++ INT_EFRAME_EBP = MEMBER_OFFSET("pt_regs", "ebp") / 4; ++ INT_EFRAME_EDI = MEMBER_OFFSET("pt_regs", "edi") / 4; ++ INT_EFRAME_ESI = MEMBER_OFFSET("pt_regs", "esi") / 4; ++ INT_EFRAME_EDX = MEMBER_OFFSET("pt_regs", "edx") / 4; ++ INT_EFRAME_ECX = MEMBER_OFFSET("pt_regs", "ecx") / 4; ++ INT_EFRAME_EBX = MEMBER_OFFSET("pt_regs", "ebx") / 4; ++ } else { ++ INT_EFRAME_SS = MEMBER_OFFSET("pt_regs", "ss") / 4; ++ INT_EFRAME_ESP = MEMBER_OFFSET("pt_regs", "sp") / 4; ++ INT_EFRAME_EFLAGS = MEMBER_OFFSET("pt_regs", "flags") / 4; ++ INT_EFRAME_CS = MEMBER_OFFSET("pt_regs", "cs") / 4; ++ INT_EFRAME_EIP = MEMBER_OFFSET("pt_regs", "ip") / 4; ++ INT_EFRAME_ERR = MEMBER_OFFSET("pt_regs", "orig_ax") / 4; ++ if ((INT_EFRAME_GS = MEMBER_OFFSET("pt_regs", "gs")) != -1) ++ INT_EFRAME_GS /= 4; ++ INT_EFRAME_ES = MEMBER_OFFSET("pt_regs", "es") / 4; ++ INT_EFRAME_DS = MEMBER_OFFSET("pt_regs", "ds") / 4; ++ INT_EFRAME_EAX = MEMBER_OFFSET("pt_regs", "ax") / 4; ++ INT_EFRAME_EBP = MEMBER_OFFSET("pt_regs", "bp") / 4; ++ INT_EFRAME_EDI = MEMBER_OFFSET("pt_regs", "di") / 4; ++ INT_EFRAME_ESI = MEMBER_OFFSET("pt_regs", "si") / 4; ++ INT_EFRAME_EDX = MEMBER_OFFSET("pt_regs", "dx") / 4; ++ INT_EFRAME_ECX = MEMBER_OFFSET("pt_regs", "cx") / 4; ++ INT_EFRAME_EBX = MEMBER_OFFSET("pt_regs", "bx") / 4; ++ } ++} ++ ++/* + * Needs to be done this way because of potential 4G/4G split. + */ + static int +@@ -1825,7 +2019,7 @@ + fprintf(fp, " PAGE: %s (4MB)\n\n", + mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, + MKSTR(NONPAE_PAGEBASE(pgd_pte)))); +- x86_translate_pte(0, 0, pgd_pte); ++ x86_translate_pte(pgd_pte, 0, 0); + } + + *paddr = NONPAE_PAGEBASE(pgd_pte) + (vaddr & ~_4MB_PAGE_MASK); +@@ -1892,7 +2086,170 @@ + } + + static int +-x86_uvtop_pae(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) ++x86_uvtop_xen_wpt(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) ++{ ++ ulong mm, active_mm; ++ ulong *pgd; ++ ulong *page_dir; ++ ulong *page_middle; ++ ulong *machine_page_table, *pseudo_page_table; ++ ulong pgd_pte, pseudo_pgd_pte; ++ ulong pmd_pte; ++ ulong machine_pte, pseudo_pte; ++ char buf[BUFSIZE]; ++ ++ if (!tc) ++ error(FATAL, "current context invalid\n"); ++ ++ *paddr = 0; ++ ++ if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { ++ if (VALID_MEMBER(thread_struct_cr3)) ++ pgd = (ulong *)machdep->get_task_pgd(tc->task); ++ else { ++ if (INVALID_MEMBER(task_struct_active_mm)) ++ error(FATAL, "no cr3 or active_mm?\n"); ++ ++ readmem(tc->task + OFFSET(task_struct_active_mm), ++ KVADDR, &active_mm, sizeof(void *), ++ "task active_mm contents", FAULT_ON_ERROR); ++ ++ if (!active_mm) ++ error(FATAL, ++ "no active_mm for this kernel thread\n"); ++ ++ readmem(active_mm + OFFSET(mm_struct_pgd), ++ KVADDR, &pgd, sizeof(long), ++ "mm_struct pgd", FAULT_ON_ERROR); ++ } ++ } else { ++ if ((mm = task_mm(tc->task, TRUE))) ++ pgd = ULONG_PTR(tt->mm_struct + ++ OFFSET(mm_struct_pgd)); ++ else ++ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), ++ KVADDR, &pgd, sizeof(long), "mm_struct pgd", ++ FAULT_ON_ERROR); ++ } ++ ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ ++ page_dir = pgd + (vaddr >> PGDIR_SHIFT); ++ ++ FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %s => %lx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)page_dir)), ++ pgd_pte); ++ ++ if (!pgd_pte) ++ goto no_upage; ++ ++ if (pgd_pte & _PAGE_4M) { ++ if (verbose) ++ fprintf(fp, " PAGE: %s (4MB) [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(pgd_pte)))); ++ ++ pseudo_pgd_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(pgd_pte)); ++ ++ if (pseudo_pgd_pte == XEN_MFN_NOT_FOUND) { ++ if (verbose) ++ fprintf(fp, " PAGE: page not available\n"); ++ *paddr = PADDR_NOT_AVAILABLE; ++ return FALSE; ++ } ++ ++ pseudo_pgd_pte |= PAGEOFFSET(pgd_pte); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s (4MB)\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(pseudo_pgd_pte)))); ++ ++ x86_translate_pte(pseudo_pgd_pte, 0, 0); ++ } ++ ++ *paddr = NONPAE_PAGEBASE(pseudo_pgd_pte) + ++ (vaddr & ~_4MB_PAGE_MASK); ++ ++ return TRUE; ++ } ++ ++ page_middle = page_dir; ++ ++ FILL_PMD(NONPAE_PAGEBASE(page_middle), KVADDR, PAGESIZE()); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); ++ ++ if (verbose) ++ fprintf(fp, " PMD: %s => %lx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)page_middle)), ++ pmd_pte); ++ ++ if (!pmd_pte) ++ goto no_upage; ++ ++ machine_page_table = (ulong *)((NONPAE_PAGEBASE(pmd_pte)) + ++ ((vaddr>>10) & ((PTRS_PER_PTE-1)<<2))); ++ ++ pseudo_page_table = (ulong *) ++ xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_page_table)); ++ ++ FILL_PTBL(NONPAE_PAGEBASE(pseudo_page_table), PHYSADDR, PAGESIZE()); ++ machine_pte = ULONG(machdep->ptbl + PAGEOFFSET(machine_page_table)); ++ ++ if (verbose) { ++ fprintf(fp, " PTE: %s [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)machine_page_table))); ++ ++ fprintf(fp, " PTE: %s => %lx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)pseudo_page_table + ++ PAGEOFFSET(machine_page_table))), machine_pte); ++ } ++ ++ if (!(machine_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) { ++ *paddr = machine_pte; ++ ++ if (machine_pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_translate_pte(machine_pte, 0, 0); ++ } ++ ++ goto no_upage; ++ } ++ ++ pseudo_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_pte)); ++ pseudo_pte |= PAGEOFFSET(machine_pte); ++ ++ *paddr = NONPAE_PAGEBASE(pseudo_pte) + PAGEOFFSET(vaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(machine_pte)))); ++ ++ fprintf(fp, " PAGE: %s\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(pseudo_pte)))); ++ ++ x86_translate_pte(pseudo_pte, 0, 0); ++ } ++ ++ return TRUE; ++ ++no_upage: ++ return FALSE; ++} ++ ++static int ++x86_uvtop_PAE(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) + { + ulong mm, active_mm; + ulonglong *pgd; +@@ -1962,7 +2319,7 @@ + + page_middle = PAE_PAGEBASE(page_dir_entry); + +- FILL_PMD(page_middle, PHYSADDR, PAGESIZE()); ++ FILL_PMD_PAE(page_middle, PHYSADDR, PAGESIZE()); + + offset = ((vaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); + +@@ -1998,7 +2355,7 @@ + + page_table = PAE_PAGEBASE(page_middle_entry); + +- FILL_PTBL(page_table, PHYSADDR, PAGESIZE()); ++ FILL_PTBL_PAE(page_table, PHYSADDR, PAGESIZE()); + + offset = ((vaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * + sizeof(ulonglong); +@@ -2028,9 +2385,10 @@ + *paddr = physpage; + + if (verbose) { +- fprintf(fp, " PAGE: %s\n\n", +- mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, +- MKSTR(&physpage))); ++ ull = PAE_PAGEBASE(page_table_entry); ++ fprintf(fp, " PAGE: %s\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull))); + x86_translate_pte(0, 0, page_table_entry); + } + +@@ -2040,50 +2398,247 @@ + return FALSE; + } + +-/* +- * Translates a kernel virtual address to its physical address. cmd_vtop() +- * sets the verbose flag so that the pte translation gets displayed; all +- * other callers quietly accept the translation. +- */ +- + static int +-x86_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) ++x86_uvtop_xen_wpt_PAE(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) + { +- ulong *pgd; +- ulong *page_dir; +- ulong *page_middle; +- ulong *page_table; +- ulong pgd_pte; +- ulong pmd_pte; +- ulong pte; ++ ulong mm, active_mm; ++ ulonglong *pgd; ++ ulonglong page_dir_entry; ++ ulonglong page_middle, pseudo_page_middle; ++ ulonglong page_middle_entry; ++ ulonglong page_table, pseudo_page_table; ++ ulonglong page_table_entry; ++ ulonglong physpage, pseudo_physpage; ++ ulonglong ull; ++ ulong offset; + char buf[BUFSIZE]; + +- if (!IS_KVADDR(kvaddr)) +- return FALSE; ++ if (!tc) ++ error(FATAL, "current context invalid\n"); + +- if (!vt->vmalloc_start) { +- *paddr = VTOP(kvaddr); +- return TRUE; +- } ++ *paddr = 0; + +- if (!IS_VMALLOC_ADDR(kvaddr)) { +- *paddr = VTOP(kvaddr); +- if (!verbose) +- return TRUE; +- } ++ if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { ++ if (VALID_MEMBER(thread_struct_cr3)) ++ pgd = (ulonglong *)machdep->get_task_pgd(tc->task); ++ else { ++ if (INVALID_MEMBER(task_struct_active_mm)) ++ error(FATAL, "no cr3 or active_mm?\n"); + +- pgd = (ulong *)vt->kernel_pgd[0]; ++ readmem(tc->task + OFFSET(task_struct_active_mm), ++ KVADDR, &active_mm, sizeof(void *), ++ "task active_mm contents", FAULT_ON_ERROR); ++ ++ if (!active_mm) ++ error(FATAL, ++ "no active_mm for this kernel thread\n"); ++ ++ readmem(active_mm + OFFSET(mm_struct_pgd), ++ KVADDR, &pgd, sizeof(long), ++ "mm_struct pgd", FAULT_ON_ERROR); ++ } ++ } else { ++ if ((mm = task_mm(tc->task, TRUE))) ++ pgd = (ulonglong *)(ULONG_PTR(tt->mm_struct + ++ OFFSET(mm_struct_pgd))); ++ else ++ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), ++ KVADDR, &pgd, sizeof(long), "mm_struct pgd", ++ FAULT_ON_ERROR); ++ } + + if (verbose) + fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); + +- page_dir = pgd + (kvaddr >> PGDIR_SHIFT); ++ FILL_PGD(pgd, KVADDR, PTRS_PER_PGD * sizeof(ulonglong)); + +- FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); +- pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ offset = ((vaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) * ++ sizeof(ulonglong); ++ ++ page_dir_entry = *((ulonglong *)&machdep->pgd[offset]); + + if (verbose) +- fprintf(fp, " PGD: %s => %lx\n", ++ fprintf(fp, " PGD: %s => %llx [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)pgd + offset)), ++ page_dir_entry); ++ ++ if (!(page_dir_entry & _PAGE_PRESENT)) { ++ goto no_upage; ++ } ++ ++ page_middle = PAE_PAGEBASE(page_dir_entry); ++ pseudo_page_middle = xen_m2p(page_middle); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %s => %llx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)pgd + offset)), ++ pseudo_page_middle | PAGEOFFSET(page_dir_entry) | ++ (page_dir_entry & _PAGE_NX)); ++ ++ FILL_PMD_PAE(pseudo_page_middle, PHYSADDR, PAGESIZE()); ++ ++ offset = ((vaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); ++ ++ page_middle_entry = *((ulonglong *)&machdep->pmd[offset]); ++ ++ if (verbose) { ++ ull = page_middle + offset; ++ fprintf(fp, " PMD: %s => %llx [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), ++ page_middle_entry); ++ } ++ ++ if (!(page_middle_entry & _PAGE_PRESENT)) { ++ goto no_upage; ++ } ++ ++ if (page_middle_entry & _PAGE_PSE) { ++ error(FATAL, "_PAGE_PSE in an mfn not supported\n"); /* XXX */ ++ if (verbose) { ++ ull = PAE_PAGEBASE(page_middle_entry); ++ fprintf(fp, " PAGE: %s (2MB)\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull))); ++ x86_translate_pte(0, 0, page_middle_entry); ++ } ++ ++ physpage = PAE_PAGEBASE(page_middle_entry) + ++ (vaddr & ~_2MB_PAGE_MASK); ++ *paddr = physpage; ++ ++ return TRUE; ++ } ++ ++ page_table = PAE_PAGEBASE(page_middle_entry); ++ pseudo_page_table = xen_m2p(page_table); ++ ++ if (verbose) { ++ ull = page_middle + offset; ++ fprintf(fp, " PMD: %s => %llx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), ++ pseudo_page_table | PAGEOFFSET(page_middle_entry) | ++ (page_middle_entry & _PAGE_NX)); ++ } ++ ++ FILL_PTBL_PAE(pseudo_page_table, PHYSADDR, PAGESIZE()); ++ ++ offset = ((vaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * ++ sizeof(ulonglong); ++ ++ page_table_entry = *((ulonglong *)&machdep->ptbl[offset]); ++ ++ if (verbose) { ++ ull = page_table + offset; ++ fprintf(fp, " PTE: %s => %llx [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), page_table_entry); ++ } ++ ++ if (!(page_table_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { ++ *paddr = page_table_entry; ++ ++ if (page_table_entry && verbose) { ++ fprintf(fp, "\n"); ++ x86_translate_pte(0, 0, page_table_entry); ++ } ++ ++ goto no_upage; ++ } ++ ++ physpage = PAE_PAGEBASE(page_table_entry) + PAGEOFFSET(vaddr); ++ pseudo_physpage = xen_m2p(physpage); ++ ++ if (verbose) { ++ ull = page_table + offset; ++ fprintf(fp, " PTE: %s => %llx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), ++ pseudo_physpage | PAGEOFFSET(page_table_entry) | ++ (page_table_entry & _PAGE_NX)); ++ } ++ ++ *paddr = pseudo_physpage + PAGEOFFSET(vaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&physpage))); ++ ++ pseudo_physpage += (PAGEOFFSET(vaddr) | ++ (page_table_entry & (_PAGE_NX|machdep->pageoffset))); ++ ++ fprintf(fp, " PAGE: %s\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&pseudo_physpage))); ++ ++ x86_translate_pte(0, 0, pseudo_physpage); ++ } ++ ++ return TRUE; ++ ++no_upage: ++ return FALSE; ++} ++ ++/* ++ * Translates a kernel virtual address to its physical address. cmd_vtop() ++ * sets the verbose flag so that the pte translation gets displayed; all ++ * other callers quietly accept the translation. ++ */ ++ ++static int ++x86_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) ++{ ++ ulong *pgd; ++ ulong *page_dir; ++ ulong *page_middle; ++ ulong *page_table; ++ ulong pgd_pte; ++ ulong pmd_pte; ++ ulong pte; ++ char buf[BUFSIZE]; ++ ++ if (!IS_KVADDR(kvaddr)) ++ return FALSE; ++ ++ if (XEN_HYPER_MODE()) { ++ if (DIRECTMAP_VIRT_ADDR(kvaddr)) { ++ *paddr = kvaddr - DIRECTMAP_VIRT_START; ++ return TRUE; ++ } ++ pgd = (ulong *)symbol_value("idle_pg_table_l2"); ++ } else { ++ if (!vt->vmalloc_start) { ++ *paddr = VTOP(kvaddr); ++ return TRUE; ++ } ++ ++ if (!IS_VMALLOC_ADDR(kvaddr)) { ++ *paddr = VTOP(kvaddr); ++ if (!verbose) ++ return TRUE; ++ } ++ ++ if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) ++ return (x86_kvtop_xen_wpt(tc, kvaddr, paddr, verbose)); ++ ++ pgd = (ulong *)vt->kernel_pgd[0]; ++ } ++ ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ ++ page_dir = pgd + (kvaddr >> PGDIR_SHIFT); ++ ++ FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %s => %lx\n", + mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, + MKSTR((ulong)page_dir)), pgd_pte); + +@@ -2095,7 +2650,7 @@ + fprintf(fp, " PAGE: %s (4MB)\n\n", + mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, + MKSTR(NONPAE_PAGEBASE(pgd_pte)))); +- x86_translate_pte(0, 0, pgd_pte); ++ x86_translate_pte(pgd_pte, 0, 0); + } + + *paddr = NONPAE_PAGEBASE(pgd_pte) + (kvaddr & ~_4MB_PAGE_MASK); +@@ -2158,9 +2713,134 @@ + return FALSE; + } + ++static int ++x86_kvtop_xen_wpt(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) ++{ ++ ulong *pgd; ++ ulong *page_dir; ++ ulong *page_middle; ++ ulong *machine_page_table, *pseudo_page_table; ++ ulong pgd_pte, pseudo_pgd_pte; ++ ulong pmd_pte; ++ ulong machine_pte, pseudo_pte; ++ char buf[BUFSIZE]; ++ ++ pgd = (ulong *)vt->kernel_pgd[0]; ++ ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ ++ page_dir = pgd + (kvaddr >> PGDIR_SHIFT); ++ ++ FILL_PGD(NONPAE_PAGEBASE(pgd), KVADDR, PAGESIZE()); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %s => %lx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)page_dir)), pgd_pte); ++ ++ if (!pgd_pte) ++ goto no_kpage; ++ ++ if (pgd_pte & _PAGE_4M) { ++ if (verbose) ++ fprintf(fp, " PAGE: %s (4MB) [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(pgd_pte)))); ++ ++ pseudo_pgd_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(pgd_pte)); ++ ++ if (pseudo_pgd_pte == XEN_MFN_NOT_FOUND) { ++ if (verbose) ++ fprintf(fp, " PAGE: page not available\n"); ++ *paddr = PADDR_NOT_AVAILABLE; ++ return FALSE; ++ } ++ ++ pseudo_pgd_pte |= PAGEOFFSET(pgd_pte); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s (4MB)\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(pseudo_pgd_pte)))); ++ ++ x86_translate_pte(pseudo_pgd_pte, 0, 0); ++ } ++ ++ *paddr = NONPAE_PAGEBASE(pseudo_pgd_pte) + ++ (kvaddr & ~_4MB_PAGE_MASK); ++ ++ return TRUE; ++ } ++ ++ page_middle = page_dir; ++ ++ FILL_PMD(NONPAE_PAGEBASE(page_middle), KVADDR, PAGESIZE()); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); ++ ++ if (verbose) ++ fprintf(fp, " PMD: %s => %lx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)page_middle)), pmd_pte); ++ ++ if (!pmd_pte) ++ goto no_kpage; ++ ++ machine_page_table = (ulong *)((NONPAE_PAGEBASE(pmd_pte)) + ++ ((kvaddr>>10) & ((PTRS_PER_PTE-1)<<2))); ++ ++ pseudo_page_table = (ulong *) ++ xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_page_table)); ++ ++ FILL_PTBL(NONPAE_PAGEBASE(pseudo_page_table), PHYSADDR, PAGESIZE()); ++ machine_pte = ULONG(machdep->ptbl + PAGEOFFSET(machine_page_table)); ++ ++ if (verbose) { ++ fprintf(fp, " PTE: %s [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)machine_page_table))); ++ ++ fprintf(fp, " PTE: %s => %lx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)pseudo_page_table + ++ PAGEOFFSET(machine_page_table))), machine_pte); ++ } ++ ++ if (!(machine_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) { ++ if (machine_pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_translate_pte(machine_pte, 0, 0); ++ } ++ goto no_kpage; ++ } ++ ++ pseudo_pte = xen_m2p_nonPAE(NONPAE_PAGEBASE(machine_pte)); ++ pseudo_pte |= PAGEOFFSET(machine_pte); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(machine_pte)))); ++ ++ fprintf(fp, " PAGE: %s\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(NONPAE_PAGEBASE(pseudo_pte)))); ++ ++ x86_translate_pte(pseudo_pte, 0, 0); ++ } ++ ++ *paddr = NONPAE_PAGEBASE(pseudo_pte) + PAGEOFFSET(kvaddr); ++ ++ return TRUE; ++ ++no_kpage: ++ return FALSE; ++} ++ + + static int +-x86_kvtop_pae(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) ++x86_kvtop_PAE(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) + { + ulonglong *pgd; + ulonglong page_dir_entry; +@@ -2177,18 +2857,32 @@ + if (!IS_KVADDR(kvaddr)) + return FALSE; + +- if (!vt->vmalloc_start) { +- *paddr = VTOP(kvaddr); +- return TRUE; +- } +- +- if (!IS_VMALLOC_ADDR(kvaddr)) { +- *paddr = VTOP(kvaddr); +- if (!verbose) ++ if (XEN_HYPER_MODE()) { ++ if (DIRECTMAP_VIRT_ADDR(kvaddr)) { ++ *paddr = kvaddr - DIRECTMAP_VIRT_START; + return TRUE; +- } ++ } ++ if (symbol_exists("idle_pg_table_l3")) ++ pgd = (ulonglong *)symbol_value("idle_pg_table_l3"); ++ else ++ pgd = (ulonglong *)symbol_value("idle_pg_table"); ++ } else { ++ if (!vt->vmalloc_start) { ++ *paddr = VTOP(kvaddr); ++ return TRUE; ++ } + +- pgd = (ulonglong *)vt->kernel_pgd[0]; ++ if (!IS_VMALLOC_ADDR(kvaddr)) { ++ *paddr = VTOP(kvaddr); ++ if (!verbose) ++ return TRUE; ++ } ++ ++ if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) ++ return (x86_kvtop_xen_wpt_PAE(tc, kvaddr, paddr, verbose)); ++ ++ pgd = (ulonglong *)vt->kernel_pgd[0]; ++ } + + if (verbose) + fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); +@@ -2212,7 +2906,7 @@ + + page_middle = PAE_PAGEBASE(page_dir_entry); + +- FILL_PMD(page_middle, PHYSADDR, PAGESIZE()); ++ FILL_PMD_PAE(page_middle, PHYSADDR, PAGESIZE()); + + offset = ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); + +@@ -2249,7 +2943,7 @@ + + page_table = PAE_PAGEBASE(page_middle_entry); + +- FILL_PTBL(page_table, PHYSADDR, PAGESIZE()); ++ FILL_PTBL_PAE(page_table, PHYSADDR, PAGESIZE()); + + offset = ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * + sizeof(ulonglong); +@@ -2277,9 +2971,10 @@ + *paddr = physpage; + + if (verbose) { ++ ull = PAE_PAGEBASE(page_table_entry); + fprintf(fp, " PAGE: %s\n\n", + mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, +- MKSTR(&physpage))); ++ MKSTR(&ull))); + x86_translate_pte(0, 0, page_table_entry); + } + +@@ -2289,13 +2984,172 @@ + return FALSE; + } + +-/* +- * Get the relevant page directory pointer from a task structure. +- */ +-static ulong +-x86_get_task_pgd(ulong task) ++static int ++x86_kvtop_xen_wpt_PAE(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) + { +- long offset; ++ ulonglong *pgd; ++ ulonglong page_dir_entry; ++ ulonglong page_middle, pseudo_page_middle; ++ ulonglong page_middle_entry; ++ ulonglong page_table, pseudo_page_table; ++ ulonglong page_table_entry; ++ ulonglong physpage, pseudo_physpage; ++ ulonglong ull; ++ ulong offset; ++ char buf[BUFSIZE]; ++ ++ pgd = (ulonglong *)vt->kernel_pgd[0]; ++ ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ ++ FILL_PGD(pgd, KVADDR, PTRS_PER_PGD * sizeof(ulonglong)); ++ ++ offset = ((kvaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) * ++ sizeof(ulonglong); ++ ++ page_dir_entry = *((ulonglong *)&machdep->pgd[offset]); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %s => %llx [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)pgd + offset)), ++ page_dir_entry); ++ ++ if (!(page_dir_entry & _PAGE_PRESENT)) { ++ goto no_kpage; ++ } ++ ++ page_middle = PAE_PAGEBASE(page_dir_entry); ++ pseudo_page_middle = xen_m2p(page_middle); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %s => %llx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR((ulong)pgd + offset)), ++ pseudo_page_middle | PAGEOFFSET(page_dir_entry) | ++ (page_dir_entry & _PAGE_NX)); ++ ++ FILL_PMD_PAE(pseudo_page_middle, PHYSADDR, PAGESIZE()); ++ ++ offset = ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)) * sizeof(ulonglong); ++ ++ page_middle_entry = *((ulonglong *)&machdep->pmd[offset]); ++ ++ if (verbose) { ++ ull = page_middle + offset; ++ fprintf(fp, " PMD: %s => %llx [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), ++ page_middle_entry); ++ } ++ ++ if (!(page_middle_entry & _PAGE_PRESENT)) { ++ goto no_kpage; ++ } ++ ++ if (page_middle_entry & _PAGE_PSE) { ++ error(FATAL, "_PAGE_PSE in an mfn not supported\n"); /* XXX */ ++ if (verbose) { ++ ull = PAE_PAGEBASE(page_middle_entry); ++ fprintf(fp, " PAGE: %s (2MB)\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull))); ++ x86_translate_pte(0, 0, page_middle_entry); ++ } ++ ++ physpage = PAE_PAGEBASE(page_middle_entry) + ++ (kvaddr & ~_2MB_PAGE_MASK); ++ *paddr = physpage; ++ ++ ++ return TRUE; ++ } ++ ++ page_table = PAE_PAGEBASE(page_middle_entry); ++ pseudo_page_table = xen_m2p(page_table); ++ ++ if (verbose) { ++ ull = page_middle + offset; ++ fprintf(fp, " PMD: %s => %llx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), ++ pseudo_page_table | PAGEOFFSET(page_middle_entry) | ++ (page_middle_entry & _PAGE_NX)); ++ } ++ ++ FILL_PTBL_PAE(pseudo_page_table, PHYSADDR, PAGESIZE()); ++ ++ offset = ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)) * ++ sizeof(ulonglong); ++ ++ page_table_entry = *((ulonglong *)&machdep->ptbl[offset]); ++ ++ if (verbose) { ++ ull = page_table + offset; ++ fprintf(fp, " PTE: %s => %llx [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), page_table_entry); ++ } ++ ++ if (!(page_table_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { ++ if (page_table_entry && verbose) { ++ fprintf(fp, "\n"); ++ x86_translate_pte(0, 0, page_table_entry); ++ } ++ ++ goto no_kpage; ++ } ++ ++ physpage = PAE_PAGEBASE(page_table_entry) + PAGEOFFSET(kvaddr); ++ pseudo_physpage = xen_m2p(physpage); ++ ++ if (verbose) { ++ ull = page_table + offset; ++ fprintf(fp, " PTE: %s => %llx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&ull)), ++ pseudo_physpage | PAGEOFFSET(page_table_entry) | ++ (page_table_entry & _PAGE_NX)); ++ } ++ ++ *paddr = pseudo_physpage + PAGEOFFSET(kvaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s [machine]\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&physpage))); ++ ++ pseudo_physpage += (PAGEOFFSET(kvaddr) | ++ (page_table_entry & _PAGE_NX)); ++ ++ fprintf(fp, " PAGE: %s\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONGLONG_HEX, ++ MKSTR(&pseudo_physpage))); ++ ++ x86_translate_pte(0, 0, pseudo_physpage); ++ } ++ ++ return TRUE; ++ ++no_kpage: ++ return FALSE; ++} ++ ++void ++x86_clear_machdep_cache(void) ++{ ++ machdep->machspec->last_pmd_read_PAE = 0; ++ machdep->machspec->last_ptbl_read_PAE = 0; ++} ++ ++/* ++ * Get the relevant page directory pointer from a task structure. ++ */ ++static ulong ++x86_get_task_pgd(ulong task) ++{ ++ long offset; + ulong cr3; + + offset = OFFSET_OPTION(task_struct_thread, task_struct_tss); +@@ -2341,6 +3195,8 @@ + x86_dump_machdep_table(ulong arg) + { + int others; ++ ulong xen_wpt; ++ char buf[BUFSIZE]; + + switch (arg) { + default: +@@ -2355,8 +3211,6 @@ + fprintf(fp, "%sPAE", others++ ? "|" : ""); + if (machdep->flags & OMIT_FRAME_PTR) + fprintf(fp, "%sOMIT_FRAME_PTR", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + if (machdep->flags & FRAMESIZE_DEBUG) + fprintf(fp, "%sFRAMESIZE_DEBUG", others++ ? "|" : ""); + fprintf(fp, ")\n"); +@@ -2376,12 +3230,17 @@ + fprintf(fp, " eframe_search: x86_eframe_search()\n"); + fprintf(fp, " back_trace: x86_back_trace_cmd()\n"); + fprintf(fp, "get_processor_speed: x86_processor_speed()\n"); ++ xen_wpt = XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES); + if (machdep->flags & PAE) { +- fprintf(fp, " uvtop: x86_uvtop_pae()\n"); +- fprintf(fp, " kvtop: x86_uvtop_pae()\n"); ++ fprintf(fp, " uvtop: %s()\n", ++ xen_wpt ? "x86_uvtop_xen_wpt_PAE" : "x86_uvtop_PAE"); ++ fprintf(fp, " kvtop: x86_kvtop_PAE()%s\n", ++ xen_wpt ? " -> x86_kvtop_xen_wpt_PAE()" : ""); + } else { +- fprintf(fp, " uvtop: x86_uvtop()\n"); +- fprintf(fp, " kvtop: x86_uvtop()\n"); ++ fprintf(fp, " uvtop: %s()\n", ++ xen_wpt ? "x86_uvtop_xen_wpt" : "x86_uvtop"); ++ fprintf(fp, " kvtop: x86_kvtop()%s\n", ++ xen_wpt ? " -> x86_kvtop_xen_wpt()" : ""); + } + fprintf(fp, " get_task_pgd: x86_get_task_pgd()\n"); + fprintf(fp, " dump_irq: generic_dump_irq()\n"); +@@ -2397,9 +3256,10 @@ + fprintf(fp, " cmd_mach: x86_cmd_mach()\n"); + fprintf(fp, " get_smp_cpus: x86_get_smp_cpus()\n"); + fprintf(fp, " is_kvaddr: generic_is_kvaddr()\n"); +- fprintf(fp, " is_uvaddr: generic_is_uvaddr()\n"); ++ fprintf(fp, " is_uvaddr: %s\n", COMMON_VADDR_SPACE() ? ++ "x86_is_uvaddr()" : "generic_is_uvaddr()"); + fprintf(fp, " verify_paddr: generic_verify_paddr()\n"); +- fprintf(fp, " init_kernel_pgd: NULL\n"); ++ fprintf(fp, " init_kernel_pgd: x86_init_kernel_pgd()\n"); + fprintf(fp, " value_to_symbol: %s\n", + machdep->value_to_symbol == generic_machdep_value_to_symbol ? + "generic_machdep_value_to_symbol()" : +@@ -2412,6 +3272,48 @@ + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); ++ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); ++ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); ++ fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); ++ fprintf(fp, " xendump_p2m_create: x86_xendump_p2m_create()\n"); ++ fprintf(fp, " xendump_panic_task: x86_xendump_panic_task()\n"); ++ fprintf(fp, " get_xendump_regs: x86_get_xendump_regs()\n"); ++ fprintf(fp, "xen_kdump_p2m_create: x86_xen_kdump_p2m_create()\n"); ++ fprintf(fp, "clear_machdep_cache: x86_clear_machdep_cache()\n"); ++ fprintf(fp, " INT_EFRAME_[reg]:\n"); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "SS: "), INT_EFRAME_SS); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "ESP: "), INT_EFRAME_ESP); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "EFLAGS: "), INT_EFRAME_EFLAGS); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "CS: "), INT_EFRAME_CS); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "IP: "), INT_EFRAME_EIP); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "ERR: "), INT_EFRAME_ERR); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "ES: "), INT_EFRAME_ES); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "DS: "), INT_EFRAME_DS); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "EAX: "), INT_EFRAME_EAX); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "EBP: "), INT_EFRAME_EBP); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "EDI: "), INT_EFRAME_EDI); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "ESI: "), INT_EFRAME_ESI); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "EDX: "), INT_EFRAME_EDX); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "ECX: "), INT_EFRAME_ECX); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "EBX: "), INT_EFRAME_EBX); ++ fprintf(fp, "%s %d\n", ++ mkstring(buf, 21, RJUST, "GS: "), INT_EFRAME_GS); ++ + fprintf(fp, " machspec: x86_machine_specific\n"); + fprintf(fp, " idt_table: %lx\n", + (ulong)machdep->machspec->idt_table); +@@ -2421,6 +3323,11 @@ + machdep->machspec->entry_tramp_end); + fprintf(fp, " entry_tramp_start_phys: %llx\n", + machdep->machspec->entry_tramp_start_phys); ++ fprintf(fp, " last_pmd_read_PAE: %llx\n", ++ machdep->machspec->last_pmd_read_PAE); ++ fprintf(fp, " last_ptbl_read_PAE: %llx\n", ++ machdep->machspec->last_ptbl_read_PAE); ++ + } + + /* +@@ -2732,6 +3639,9 @@ + switch (flag) + { + case READ_IDT_INIT: ++ if (!symbol_exists("idt_table")) ++ return NULL; ++ + if (!(idt = (ulong *)malloc(desc_struct_size))) { + error(WARNING, "cannot malloc idt_table\n\n"); + return NULL; +@@ -2779,6 +3689,10 @@ + break; + + case READ_IDT_RUNTIME: ++ if (!symbol_exists("idt_table")) ++ error(FATAL, ++ "idt_table does not exist on this architecture\n"); ++ + idt = (ulong *)GETBUF(desc_struct_size); + readmem(symbol_value("idt_table"), KVADDR, idt, + desc_struct_size, "idt_table", FAULT_ON_ERROR); +@@ -2942,7 +3856,11 @@ + !strstr(buf2, "+")) + sprintf(p1, buf1); + } +- } ++ } ++ else if (STREQ(argv[2], "ud2a")) ++ pc->curcmd_flags |= UD2A_INSTRUCTION; ++ else if (STREQ(argv[2], "(bad)")) ++ pc->curcmd_flags |= BAD_INSTRUCTION; + + if (CRASHDEBUG(1)) + console(" %s", inbuf); +@@ -2969,6 +3887,16 @@ + } + } + ++ if (XEN() && (count == 1) && symbol_exists("cpu_present_map")) { ++ ulong cpu_present_map; ++ ++ get_symbol_data("cpu_present_map", sizeof(ulong), ++ &cpu_present_map); ++ ++ cpucount = count_bits_long(cpu_present_map); ++ count = MAX(cpucount, kt->cpus); ++ } ++ + return count; + } + +@@ -3026,7 +3954,7 @@ + fprintf(fp, "(unknown)\n"); + fprintf(fp, " HZ: %d\n", machdep->hz); + fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); +- fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); ++// fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); + fprintf(fp, "KERNEL VIRTUAL BASE: %lx\n", machdep->kvbase); + fprintf(fp, "KERNEL VMALLOC BASE: %lx\n", vt->vmalloc_start); + fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); +@@ -3092,31 +4020,31 @@ + * with the -fomit-frame-pointer flag. + */ + #define PUSH_BP_MOV_ESP_BP 0xe58955 ++#define PUSH_BP_CLR_EAX_MOV_ESP_BP 0xe589c03155ULL + + static int + x86_omit_frame_pointer(void) + { +- ulong push_bp_mov_esp_bp[3]; ++ ulonglong push_bp_mov_esp_bp; ++ int i; ++ char *checkfuncs[] = {"sys_open", "sys_fork", "sys_read"}; + + if (pc->flags & KERNEL_DEBUG_QUERY) + return FALSE; + +- if (!readmem(symbol_value("sys_open"), KVADDR, &push_bp_mov_esp_bp[0], +- sizeof(ulong), "x86_omit_frame_pointer", RETURN_ON_ERROR)) +- return TRUE; +- if (!readmem(symbol_value("sys_fork"), KVADDR, &push_bp_mov_esp_bp[1], +- sizeof(ulong), "x86_omit_frame_pointer", RETURN_ON_ERROR)) +- return TRUE; +- if (!readmem(symbol_value("sys_read"), KVADDR, &push_bp_mov_esp_bp[2], +- sizeof(ulong), "x86_omit_frame_pointer", RETURN_ON_ERROR)) +- return TRUE; +- +- if (((push_bp_mov_esp_bp[0] & 0xffffff) == PUSH_BP_MOV_ESP_BP) && +- ((push_bp_mov_esp_bp[1] & 0xffffff) == PUSH_BP_MOV_ESP_BP) && +- ((push_bp_mov_esp_bp[2] & 0xffffff) == PUSH_BP_MOV_ESP_BP)) +- return FALSE; ++ for (i = 0; i < 2; i++) { ++ if (!readmem(symbol_value(checkfuncs[i]), KVADDR, ++ &push_bp_mov_esp_bp, sizeof(ulonglong), ++ "x86_omit_frame_pointer", RETURN_ON_ERROR)) ++ return TRUE; ++ if (!(((push_bp_mov_esp_bp & 0x0000ffffffULL) == ++ PUSH_BP_MOV_ESP_BP) || ++ ((push_bp_mov_esp_bp & 0xffffffffffULL) == ++ PUSH_BP_CLR_EAX_MOV_ESP_BP))) ++ return TRUE; ++ } + +- return TRUE; ++ return FALSE; + } + + /* +@@ -3207,4 +4135,921 @@ + + return ((sp = value_search(value, offset))); + } ++ ++static void ++x86_init_kernel_pgd(void) ++{ ++ int i; ++ ulong value; ++ ++ if (XEN()) ++ get_symbol_data("swapper_pg_dir", sizeof(ulong), &value); ++ else ++ value = symbol_value("swapper_pg_dir"); ++ ++ for (i = 0; i < NR_CPUS; i++) ++ vt->kernel_pgd[i] = value; ++ ++} ++ ++static ulong ++xen_m2p_nonPAE(ulong machine) ++{ ++ ulonglong pseudo; ++ ++ pseudo = xen_m2p((ulonglong)machine); ++ ++ if (pseudo == XEN_MACHADDR_NOT_FOUND) ++ return XEN_MFN_NOT_FOUND; ++ ++ return ((ulong)pseudo); ++} ++ ++#include "netdump.h" ++ ++/* ++ * From the xen vmcore, create an index of mfns for each page that makes ++ * up the dom0 kernel's complete phys_to_machine_mapping[max_pfn] array. ++ */ ++ ++#define MAX_X86_FRAMES (16) ++#define MFNS_PER_FRAME (PAGESIZE()/sizeof(ulong)) ++ ++static int ++x86_xen_kdump_p2m_create(struct xen_kdump_data *xkd) ++{ ++ int i, j; ++ ulong kvaddr; ++ ulong *up; ++ ulonglong *ulp; ++ ulong frames; ++ ulong frame_mfn[MAX_X86_FRAMES] = { 0 }; ++ int mfns[MAX_X86_FRAMES] = { 0 }; ++ ++ /* ++ * Temporarily read physical (machine) addresses from vmcore by ++ * going directly to read_netdump() instead of via read_kdump(). ++ */ ++ pc->readmem = read_netdump; ++ ++ if (xkd->flags & KDUMP_CR3) ++ goto use_cr3; ++ ++ xkd->p2m_frames = 0; ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "x86_xen_kdump_p2m_create: p2m_mfn: %lx\n", ++ xkd->p2m_mfn); ++ ++ if (!readmem(PTOB(xkd->p2m_mfn), PHYSADDR, xkd->page, PAGESIZE(), ++ "xen kdump p2m mfn page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump p2m mfn page\n"); ++ ++ if (CRASHDEBUG(1)) { ++ up = (ulong *)xkd->page; ++ for (i = 0; i < 4; i++) { ++ fprintf(fp, "%08lx: %08lx %08lx %08lx %08lx\n", ++ (ulong)((i * 4) * sizeof(ulong)), ++ *up, *(up+1), *(up+2), *(up+3)); ++ up += 4; ++ } ++ fprintf(fp, "\n"); ++ } ++ ++ for (i = 0, up = (ulong *)xkd->page; i < MAX_X86_FRAMES; i++, up++) ++ frame_mfn[i] = *up; ++ ++ for (i = 0; i < MAX_X86_FRAMES; i++) { ++ if (!frame_mfn[i]) ++ break; ++ ++ if (!readmem(PTOB(frame_mfn[i]), PHYSADDR, xkd->page, ++ PAGESIZE(), "xen kdump p2m mfn list page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump p2m mfn list page\n"); ++ ++ for (j = 0, up = (ulong *)xkd->page; j < MFNS_PER_FRAME; j++, up++) ++ if (*up) ++ mfns[i]++; ++ ++ xkd->p2m_frames += mfns[i]; ++ ++ if (CRASHDEBUG(7)) { ++ up = (ulong *)xkd->page; ++ for (j = 0; j < 256; j++) { ++ fprintf(fp, "%08lx: %08lx %08lx %08lx %08lx\n", ++ (ulong)((j * 4) * sizeof(ulong)), ++ *up, *(up+1), *(up+2), *(up+3)); ++ up += 4; ++ } ++ } ++ } ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "p2m_frames: %d\n", xkd->p2m_frames); ++ ++ if ((xkd->p2m_mfn_frame_list = (ulong *) ++ malloc(xkd->p2m_frames * sizeof(ulong))) == NULL) ++ error(FATAL, "cannot malloc p2m_frame_index_list"); ++ ++ for (i = 0, frames = xkd->p2m_frames; frames; i++) { ++ if (!readmem(PTOB(frame_mfn[i]), PHYSADDR, ++ &xkd->p2m_mfn_frame_list[i * MFNS_PER_FRAME], ++ mfns[i] * sizeof(ulong), "xen kdump p2m mfn list page", ++ RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump p2m mfn list page\n"); ++ ++ frames -= mfns[i]; ++ } ++ ++ if (CRASHDEBUG(2)) { ++ for (i = 0; i < xkd->p2m_frames; i++) ++ fprintf(fp, "%lx ", xkd->p2m_mfn_frame_list[i]); ++ fprintf(fp, "\n"); ++ } ++ ++ pc->readmem = read_kdump; ++ return TRUE; ++ ++use_cr3: ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "x86_xen_kdump_p2m_create: cr3: %lx\n", xkd->cr3); ++ ++ if (!readmem(PTOB(xkd->cr3), PHYSADDR, machdep->pgd, PAGESIZE(), ++ "xen kdump cr3 page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump cr3 page\n"); ++ ++ if (CRASHDEBUG(7)) { ++ fprintf(fp, "contents of page directory page:\n"); ++ ++ if (machdep->flags & PAE) { ++ ulp = (ulonglong *)machdep->pgd; ++ fprintf(fp, ++ "%016llx %016llx %016llx %016llx\n", ++ *ulp, *(ulp+1), *(ulp+2), *(ulp+3)); ++ } else { ++ up = (ulong *)machdep->pgd; ++ for (i = 0; i < 256; i++) { ++ fprintf(fp, ++ "%08lx: %08lx %08lx %08lx %08lx\n", ++ (ulong)((i * 4) * sizeof(ulong)), ++ *up, *(up+1), *(up+2), *(up+3)); ++ up += 4; ++ } ++ } ++ } ++ ++ kvaddr = symbol_value("max_pfn"); ++ if (!x86_xen_kdump_load_page(kvaddr, xkd->page)) ++ return FALSE; ++ up = (ulong *)(xkd->page + PAGEOFFSET(kvaddr)); ++ ++ xkd->p2m_frames = (*up/(PAGESIZE()/sizeof(ulong))) + ++ ((*up%(PAGESIZE()/sizeof(ulong))) ? 1 : 0); ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "max_pfn at %lx: %lx (%ld) -> %d p2m_frames\n", ++ kvaddr, *up, *up, xkd->p2m_frames); ++ ++ if ((xkd->p2m_mfn_frame_list = (ulong *) ++ malloc(xkd->p2m_frames * sizeof(ulong))) == NULL) ++ error(FATAL, "cannot malloc p2m_frame_index_list"); ++ ++ kvaddr = symbol_value("phys_to_machine_mapping"); ++ if (!x86_xen_kdump_load_page(kvaddr, xkd->page)) ++ return FALSE; ++ up = (ulong *)(xkd->page + PAGEOFFSET(kvaddr)); ++ kvaddr = *up; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "phys_to_machine_mapping: %lx\n", kvaddr); ++ ++ if (CRASHDEBUG(7)) { ++ fprintf(fp, "contents of first phys_to_machine_mapping page:\n"); ++ if (!x86_xen_kdump_load_page(kvaddr, xkd->page)) ++ error(INFO, ++ "cannot read first phys_to_machine_mapping page\n"); ++ ++ up = (ulong *)xkd->page; ++ for (i = 0; i < 256; i++) { ++ fprintf(fp, "%08lx: %08lx %08lx %08lx %08lx\n", ++ (ulong)((i * 4) * sizeof(ulong)), ++ *up, *(up+1), *(up+2), *(up+3)); ++ up += 4; ++ } ++ } ++ ++ machdep->last_ptbl_read = BADADDR; ++ machdep->last_pmd_read = BADADDR; ++ ++ for (i = 0; i < xkd->p2m_frames; i++) { ++ xkd->p2m_mfn_frame_list[i] = x86_xen_kdump_page_mfn(kvaddr); ++ kvaddr += PAGESIZE(); ++ } ++ ++ if (CRASHDEBUG(1)) { ++ for (i = 0; i < xkd->p2m_frames; i++) ++ fprintf(fp, "%lx ", xkd->p2m_mfn_frame_list[i]); ++ fprintf(fp, "\n"); ++ } ++ ++ machdep->last_ptbl_read = 0; ++ machdep->last_pmd_read = 0; ++ pc->readmem = read_kdump; ++ ++ return TRUE; ++} ++ ++/* ++ * Find the page associate with the kvaddr, and read its contents ++ * into the passed-in buffer. ++ */ ++static char * ++x86_xen_kdump_load_page(ulong kvaddr, char *pgbuf) ++{ ++ ulong *entry; ++ ulong *up; ++ ulong mfn; ++ ++ if (machdep->flags & PAE) ++ return x86_xen_kdump_load_page_PAE(kvaddr, pgbuf); ++ ++ up = (ulong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (*entry) >> PAGESHIFT(); ++ ++ if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), ++ "xen kdump pgd entry", RETURN_ON_ERROR)) { ++ error(INFO, "cannot read/find pgd entry from cr3 page\n"); ++ return NULL; ++ } ++ ++ up = (ulong *)pgbuf; ++ entry = up + ((kvaddr >> 12) & (PTRS_PER_PTE-1)); ++ mfn = (*entry) >> PAGESHIFT(); ++ ++ if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), ++ "xen page table page", RETURN_ON_ERROR)) { ++ error(INFO, "cannot read/find page table page\n"); ++ return NULL; ++ } ++ ++ return pgbuf; ++} ++ ++static char * ++x86_xen_kdump_load_page_PAE(ulong kvaddr, char *pgbuf) ++{ ++ ulonglong *entry; ++ ulonglong *up; ++ ulong mfn; ++ ++ up = (ulonglong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), ++ "xen kdump pgd entry", RETURN_ON_ERROR)) { ++ error(INFO, "cannot read/find pgd entry from cr3 page\n"); ++ return NULL; ++ } ++ ++ up = (ulonglong *)pgbuf; ++ entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), ++ "xen kdump pmd entry", RETURN_ON_ERROR)) { ++ error(INFO, "cannot read/find pmd entry from pgd\n"); ++ return NULL; ++ } ++ ++ up = (ulonglong *)pgbuf; ++ entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), ++ "xen kdump page table page", RETURN_ON_ERROR)) { ++ error(INFO, "cannot read/find page table page from pmd\n"); ++ return NULL; ++ } ++ ++ return pgbuf; ++} ++ ++/* ++ * Return the mfn value associated with a virtual address. ++ */ ++static ulong ++x86_xen_kdump_page_mfn(ulong kvaddr) ++{ ++ ulong *entry; ++ ulong *up; ++ ulong mfn; ++ ++ if (machdep->flags & PAE) ++ return x86_xen_kdump_page_mfn_PAE(kvaddr); ++ ++ up = (ulong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (*entry) >> PAGESHIFT(); ++ ++ if ((mfn != machdep->last_ptbl_read) && ++ !readmem(PTOB(mfn), PHYSADDR, machdep->ptbl, PAGESIZE(), ++ "xen kdump pgd entry", RETURN_ON_ERROR)) ++ error(FATAL, ++ "cannot read/find pgd entry from cr3 page (mfn: %lx)\n", ++ mfn); ++ machdep->last_ptbl_read = mfn; ++ ++ up = (ulong *)machdep->ptbl; ++ entry = up + ((kvaddr >> 12) & (PTRS_PER_PTE-1)); ++ mfn = (*entry) >> PAGESHIFT(); ++ ++ return mfn; ++} ++ ++static ulong ++x86_xen_kdump_page_mfn_PAE(ulong kvaddr) ++{ ++ ulonglong *entry; ++ ulonglong *up; ++ ulong mfn; ++ ++ up = (ulonglong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if ((mfn != machdep->last_pmd_read) && ++ !readmem(PTOB(mfn), PHYSADDR, machdep->pmd, PAGESIZE(), ++ "xen kdump pgd entry", RETURN_ON_ERROR)) ++ error(FATAL, ++ "cannot read/find pgd entry from cr3 page (mfn: %lx)\n", ++ mfn); ++ machdep->last_pmd_read = mfn; ++ ++ up = (ulonglong *)machdep->pmd; ++ entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if ((mfn != machdep->last_ptbl_read) && ++ !readmem(PTOB(mfn), PHYSADDR, machdep->ptbl, PAGESIZE(), ++ "xen kdump pmd entry", RETURN_ON_ERROR)) ++ error(FATAL, ++ "cannot read/find pmd entry from pgd (mfn: %lx)\n", ++ mfn); ++ machdep->last_ptbl_read = mfn; ++ ++ up = (ulonglong *)machdep->ptbl; ++ entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ return mfn; ++} ++ ++#include "xendump.h" ++ ++/* ++ * Create an index of mfns for each page that makes up the ++ * kernel's complete phys_to_machine_mapping[max_pfn] array. ++ */ ++static int ++x86_xendump_p2m_create(struct xendump_data *xd) ++{ ++ int i, idx; ++ ulong mfn, kvaddr, ctrlreg[8], ctrlreg_offset; ++ ulong *up; ++ ulonglong *ulp; ++ off_t offset; ++ ++ if (!symbol_exists("phys_to_machine_mapping")) { ++ xd->flags |= XC_CORE_NO_P2M; ++ return TRUE; ++ } ++ ++ if ((ctrlreg_offset = MEMBER_OFFSET("vcpu_guest_context", "ctrlreg")) == ++ INVALID_OFFSET) ++ error(FATAL, ++ "cannot determine vcpu_guest_context.ctrlreg offset\n"); ++ else if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, ++ "MEMBER_OFFSET(vcpu_guest_context, ctrlreg): %ld\n", ++ ctrlreg_offset); ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)ctrlreg_offset; ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, "cannot lseek to xch_ctxt_offset\n"); ++ ++ if (read(xd->xfd, &ctrlreg, sizeof(ctrlreg)) != ++ sizeof(ctrlreg)) ++ error(FATAL, "cannot read vcpu_guest_context ctrlreg[8]\n"); ++ ++ mfn = (ctrlreg[3] >> PAGESHIFT()) | (ctrlreg[3] << (BITS()-PAGESHIFT())); ++ ++ for (i = 0; CRASHDEBUG(1) && (i < 8); i++) { ++ fprintf(xd->ofp, "ctrlreg[%d]: %lx", i, ctrlreg[i]); ++ if (i == 3) ++ fprintf(xd->ofp, " -> mfn: %lx", mfn); ++ fprintf(xd->ofp, "\n"); ++ } ++ ++ if (!xc_core_mfn_to_page(mfn, machdep->pgd)) ++ error(FATAL, "cannot read/find cr3 page\n"); ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(xd->ofp, "contents of page directory page:\n"); ++ ++ if (machdep->flags & PAE) { ++ ulp = (ulonglong *)machdep->pgd; ++ fprintf(xd->ofp, ++ "%016llx %016llx %016llx %016llx\n", ++ *ulp, *(ulp+1), *(ulp+2), *(ulp+3)); ++ } else { ++ up = (ulong *)machdep->pgd; ++ for (i = 0; i < 256; i++) { ++ fprintf(xd->ofp, ++ "%08lx: %08lx %08lx %08lx %08lx\n", ++ (ulong)((i * 4) * sizeof(ulong)), ++ *up, *(up+1), *(up+2), *(up+3)); ++ up += 4; ++ } ++ } ++ } ++ ++ kvaddr = symbol_value("max_pfn"); ++ if (!x86_xendump_load_page(kvaddr, xd->page)) ++ return FALSE; ++ up = (ulong *)(xd->page + PAGEOFFSET(kvaddr)); ++ if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, "max_pfn: %lx\n", *up); ++ ++ xd->xc_core.p2m_frames = (*up/(PAGESIZE()/sizeof(ulong))) + ++ ((*up%(PAGESIZE()/sizeof(ulong))) ? 1 : 0); ++ ++ if ((xd->xc_core.p2m_frame_index_list = (ulong *) ++ malloc(xd->xc_core.p2m_frames * sizeof(int))) == NULL) ++ error(FATAL, "cannot malloc p2m_frame_index_list"); ++ ++ kvaddr = symbol_value("phys_to_machine_mapping"); ++ if (!x86_xendump_load_page(kvaddr, xd->page)) ++ return FALSE; ++ up = (ulong *)(xd->page + PAGEOFFSET(kvaddr)); ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "phys_to_machine_mapping: %lx\n", *up); ++ ++ kvaddr = *up; ++ machdep->last_ptbl_read = BADADDR; ++ machdep->last_pmd_read = BADADDR; ++ ++ for (i = 0; i < xd->xc_core.p2m_frames; i++) { ++ if ((idx = x86_xendump_page_index(kvaddr)) == MFN_NOT_FOUND) ++ return FALSE; ++ xd->xc_core.p2m_frame_index_list[i] = idx; ++ kvaddr += PAGESIZE(); ++ } ++ ++ machdep->last_ptbl_read = 0; ++ machdep->last_pmd_read = 0; ++ ++ return TRUE; ++} ++ ++/* ++ * Find the page associate with the kvaddr, and read its contents ++ * into the passed-in buffer. ++ */ ++static char * ++x86_xendump_load_page(ulong kvaddr, char *pgbuf) ++{ ++ ulong *entry; ++ ulong *up; ++ ulong mfn; ++ ++ if (machdep->flags & PAE) ++ return x86_xendump_load_page_PAE(kvaddr, pgbuf); ++ ++ up = (ulong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (*entry) >> PAGESHIFT(); ++ ++ if (!xc_core_mfn_to_page(mfn, pgbuf)) { ++ error(INFO, "cannot read/find pgd entry from cr3 page\n"); ++ return NULL; ++ } ++ ++ up = (ulong *)pgbuf; ++ entry = up + ((kvaddr >> 12) & (PTRS_PER_PTE-1)); ++ mfn = (*entry) >> PAGESHIFT(); ++ ++ if (!xc_core_mfn_to_page(mfn, pgbuf)) { ++ error(INFO, "cannot read/find page table page\n"); ++ return NULL; ++ } ++ ++ return pgbuf; ++} ++ ++static char * ++x86_xendump_load_page_PAE(ulong kvaddr, char *pgbuf) ++{ ++ ulonglong *entry; ++ ulonglong *up; ++ ulong mfn; ++ ++ up = (ulonglong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if (!xc_core_mfn_to_page(mfn, pgbuf)) { ++ error(INFO, "cannot read/find pgd entry from cr3 page\n"); ++ return NULL; ++ } ++ ++ up = (ulonglong *)pgbuf; ++ entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if (!xc_core_mfn_to_page(mfn, pgbuf)) { ++ error(INFO, "cannot read/find pmd entry from pgd\n"); ++ return NULL; ++ } ++ ++ up = (ulonglong *)pgbuf; ++ entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ ++ if (!xc_core_mfn_to_page(mfn, pgbuf)) { ++ error(INFO, "cannot read/find page table page from pmd\n"); ++ return NULL; ++ } ++ ++ return pgbuf; ++} ++ ++/* ++ * Find the dumpfile page index associated with the kvaddr. ++ */ ++static int ++x86_xendump_page_index(ulong kvaddr) ++{ ++ int idx; ++ ulong *entry; ++ ulong *up; ++ ulong mfn; ++ ++ if (machdep->flags & PAE) ++ return x86_xendump_page_index_PAE(kvaddr); ++ ++ up = (ulong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (*entry) >> PAGESHIFT(); ++ if ((mfn != machdep->last_ptbl_read) && ++ !xc_core_mfn_to_page(mfn, machdep->ptbl)) { ++ error(INFO, "cannot read/find pgd entry from cr3 page\n"); ++ return MFN_NOT_FOUND; ++ } ++ machdep->last_ptbl_read = mfn; ++ ++ up = (ulong *)machdep->ptbl; ++ entry = up + ((kvaddr>>12) & (PTRS_PER_PTE-1)); ++ mfn = (*entry) >> PAGESHIFT(); ++ if ((idx = xc_core_mfn_to_page_index(mfn)) == MFN_NOT_FOUND) ++ error(INFO, "cannot determine page index for %lx\n", ++ kvaddr); ++ ++ return idx; ++} ++ ++static int ++x86_xendump_page_index_PAE(ulong kvaddr) ++{ ++ int idx; ++ ulonglong *entry; ++ ulonglong *up; ++ ulong mfn; ++ ++ up = (ulonglong *)machdep->pgd; ++ entry = up + (kvaddr >> PGDIR_SHIFT); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ if ((mfn != machdep->last_pmd_read) && ++ !xc_core_mfn_to_page(mfn, machdep->pmd)) { ++ error(INFO, "cannot read/find pgd entry from cr3 page\n"); ++ return MFN_NOT_FOUND; ++ } ++ machdep->last_pmd_read = mfn; ++ ++ up = (ulonglong *)machdep->pmd; ++ entry = up + ((kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ if ((mfn != machdep->last_ptbl_read) && ++ !xc_core_mfn_to_page(mfn, machdep->ptbl)) { ++ error(INFO, "cannot read/find pmd entry from pgd\n"); ++ return MFN_NOT_FOUND; ++ } ++ machdep->last_ptbl_read = mfn; ++ ++ up = (ulonglong *)machdep->ptbl; ++ entry = up + ((kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE-1)); ++ mfn = (ulong)((*entry) >> PAGESHIFT()); ++ if ((idx = xc_core_mfn_to_page_index(mfn)) == MFN_NOT_FOUND) ++ error(INFO, "cannot determine page index for %lx\n", ++ kvaddr); ++ ++ return idx; ++} ++ ++/* ++ * Pull the esp from the cpu_user_regs struct in the header ++ * turn it into a task, and match it with the active_set. ++ * Unfortunately, the registers in the vcpu_guest_context ++ * are not necessarily those of the panic task, so for now ++ * let get_active_set_panic_task() get the right task. ++ */ ++static ulong ++x86_xendump_panic_task(struct xendump_data *xd) ++{ ++ return NO_TASK; ++ ++#ifdef TO_BE_REVISITED ++ int i; ++ ulong esp; ++ off_t offset; ++ ulong task; ++ ++ ++ if (INVALID_MEMBER(vcpu_guest_context_user_regs) || ++ INVALID_MEMBER(cpu_user_regs_esp)) ++ return NO_TASK; ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)OFFSET(vcpu_guest_context_user_regs) + ++ (off_t)OFFSET(cpu_user_regs_esp); ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ return NO_TASK; ++ ++ if (read(xd->xfd, &esp, sizeof(ulong)) != sizeof(ulong)) ++ return NO_TASK; ++ ++ if (IS_KVADDR(esp) && (task = stkptr_to_task(esp))) { ++ ++ for (i = 0; i < NR_CPUS; i++) { ++ if (task == tt->active_set[i]) { ++ if (CRASHDEBUG(0)) ++ error(INFO, ++ "x86_xendump_panic_task: esp: %lx -> task: %lx\n", ++ esp, task); ++ return task; ++ } ++ } ++ ++ error(WARNING, ++ "x86_xendump_panic_task: esp: %lx -> task: %lx (not active)\n", ++ esp); ++ } ++ ++ return NO_TASK; ++#endif ++} ++ ++/* ++ * Because of an off-by-one vcpu bug in early xc_domain_dumpcore() ++ * instantiations, the registers in the vcpu_guest_context are not ++ * necessarily those of the panic task. If not, the eip/esp will be ++ * in stop_this_cpu, as a result of the IP interrupt in panic(), ++ * but the trace is strange because it comes out of the hypervisor ++ * at least if the vcpu had been idle. ++ */ ++static void ++x86_get_xendump_regs(struct xendump_data *xd, struct bt_info *bt, ulong *eip, ulong *esp) ++{ ++ ulong task, xeip, xesp; ++ off_t offset; ++ ++ if (INVALID_MEMBER(vcpu_guest_context_user_regs) || ++ INVALID_MEMBER(cpu_user_regs_eip) || ++ INVALID_MEMBER(cpu_user_regs_esp)) ++ goto generic; ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)OFFSET(vcpu_guest_context_user_regs) + ++ (off_t)OFFSET(cpu_user_regs_esp); ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ goto generic; ++ if (read(xd->xfd, &xesp, sizeof(ulong)) != sizeof(ulong)) ++ goto generic; ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)OFFSET(vcpu_guest_context_user_regs) + ++ (off_t)OFFSET(cpu_user_regs_eip); ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ goto generic; ++ if (read(xd->xfd, &xeip, sizeof(ulong)) != sizeof(ulong)) ++ goto generic; ++ ++ if (IS_KVADDR(xesp) && (task = stkptr_to_task(xesp)) && ++ (task == bt->task)) { ++ if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, ++ "hooks from vcpu_guest_context: eip: %lx esp: %lx\n", xeip, xesp); ++ *eip = xeip; ++ *esp = xesp; ++ return; ++ } ++ ++generic: ++ return machdep->get_stack_frame(bt, eip, esp); ++} ++ ++/* for Xen Hypervisor analysis */ ++ ++static int ++x86_xenhyper_is_kvaddr(ulong addr) ++{ ++ if (machdep->flags & PAE) { ++ return (addr >= HYPERVISOR_VIRT_START_PAE); ++ } ++ return (addr >= HYPERVISOR_VIRT_START); ++} ++ ++static ulong ++x86_get_stackbase_hyper(ulong task) ++{ ++ struct xen_hyper_vcpu_context *vcc; ++ int pcpu; ++ ulong init_tss; ++ ulong esp, base; ++ char *buf; ++ ++ /* task means vcpu here */ ++ vcc = xen_hyper_vcpu_to_vcpu_context(task); ++ if (!vcc) ++ error(FATAL, "invalid vcpu\n"); ++ ++ pcpu = vcc->processor; ++ if (!xen_hyper_test_pcpu_id(pcpu)) { ++ error(FATAL, "invalid pcpu number\n"); ++ } ++ init_tss = symbol_value("init_tss"); ++ buf = GETBUF(XEN_HYPER_SIZE(tss_struct)); ++ init_tss += XEN_HYPER_SIZE(tss_struct) * pcpu; ++ if (!readmem(init_tss, KVADDR, buf, ++ XEN_HYPER_SIZE(tss_struct), "init_tss", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read init_tss.\n"); ++ } ++ esp = ULONG(buf + XEN_HYPER_OFFSET(tss_struct_esp0)); ++ FREEBUF(buf); ++ base = esp & (~(STACKSIZE() - 1)); ++ ++ return base; ++} ++ ++static ulong ++x86_get_stacktop_hyper(ulong task) ++{ ++ return x86_get_stackbase_hyper(task) + STACKSIZE(); ++} ++ ++static void ++x86_get_stack_frame_hyper(struct bt_info *bt, ulong *pcp, ulong *spp) ++{ ++ struct xen_hyper_vcpu_context *vcc; ++ int pcpu; ++ ulong *regs; ++ ulong esp, eip; ++ ++ /* task means vcpu here */ ++ vcc = xen_hyper_vcpu_to_vcpu_context(bt->task); ++ if (!vcc) ++ error(FATAL, "invalid vcpu\n"); ++ ++ pcpu = vcc->processor; ++ if (!xen_hyper_test_pcpu_id(pcpu)) { ++ error(FATAL, "invalid pcpu number\n"); ++ } ++ ++ if (bt->flags & BT_TEXT_SYMBOLS_ALL) { ++ if (spp) ++ *spp = x86_get_stackbase_hyper(bt->task); ++ if (pcp) ++ *pcp = 0; ++ bt->flags &= ~BT_TEXT_SYMBOLS_ALL; ++ return; ++ } ++ ++ regs = (ulong *)xen_hyper_id_to_dumpinfo_context(pcpu)->pr_reg_ptr; ++ esp = XEN_HYPER_X86_NOTE_ESP(regs); ++ eip = XEN_HYPER_X86_NOTE_EIP(regs); ++ ++ if (spp) { ++ if (esp < x86_get_stackbase_hyper(bt->task) || ++ esp >= x86_get_stacktop_hyper(bt->task)) ++ *spp = x86_get_stackbase_hyper(bt->task); ++ else ++ *spp = esp; ++ } ++ if (pcp) { ++ if (is_kernel_text(eip)) ++ *pcp = eip; ++ else ++ *pcp = 0; ++ } ++} ++ ++static void ++x86_init_hyper(int when) ++{ ++ switch (when) ++ { ++ case PRE_SYMTAB: ++ machdep->verify_symbol = x86_verify_symbol; ++ if (pc->flags & KERNEL_DEBUG_QUERY) ++ return; ++ machdep->pagesize = memory_page_size(); ++ machdep->pageshift = ffs(machdep->pagesize) - 1; ++ machdep->pageoffset = machdep->pagesize - 1; ++ machdep->pagemask = ~((ulonglong)machdep->pageoffset); ++ machdep->stacksize = machdep->pagesize * 4; /* ODA: magic num */ ++ if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pgd space."); ++ if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pmd space."); ++ if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc ptbl space."); ++ machdep->last_pgd_read = 0; ++ machdep->last_pmd_read = 0; ++ machdep->last_ptbl_read = 0; ++ machdep->machspec = &x86_machine_specific; /* some members used */ ++ break; ++ ++ case PRE_GDB: ++ if (symbol_exists("create_pae_xen_mappings") || ++ symbol_exists("idle_pg_table_l3")) { ++ machdep->flags |= PAE; ++ PGDIR_SHIFT = PGDIR_SHIFT_3LEVEL; ++ PTRS_PER_PTE = PTRS_PER_PTE_3LEVEL; ++ PTRS_PER_PGD = PTRS_PER_PGD_3LEVEL; ++ machdep->kvtop = x86_kvtop_PAE; ++ machdep->kvbase = HYPERVISOR_VIRT_START_PAE; ++ } else { ++ PGDIR_SHIFT = PGDIR_SHIFT_2LEVEL; ++ PTRS_PER_PTE = PTRS_PER_PTE_2LEVEL; ++ PTRS_PER_PGD = PTRS_PER_PGD_2LEVEL; ++ machdep->kvtop = x86_kvtop; ++ free(machdep->pmd); ++ machdep->pmd = machdep->pgd; ++ machdep->kvbase = HYPERVISOR_VIRT_START; ++ } ++ machdep->ptrs_per_pgd = PTRS_PER_PGD; ++ machdep->identity_map_base = DIRECTMAP_VIRT_START; ++ machdep->is_kvaddr = x86_xenhyper_is_kvaddr; ++ machdep->eframe_search = x86_eframe_search; ++ machdep->back_trace = x86_back_trace_cmd; ++ machdep->processor_speed = x86_processor_speed; /* ODA: check */ ++ machdep->dump_irq = generic_dump_irq; /* ODA: check */ ++ machdep->get_stack_frame = x86_get_stack_frame_hyper; ++ machdep->get_stackbase = x86_get_stackbase_hyper; ++ machdep->get_stacktop = x86_get_stacktop_hyper; ++ machdep->translate_pte = x86_translate_pte; ++ machdep->memory_size = xen_hyper_x86_memory_size; ++ machdep->dis_filter = x86_dis_filter; ++// machdep->cmd_mach = x86_cmd_mach; /* ODA: check */ ++ machdep->get_smp_cpus = xen_hyper_x86_get_smp_cpus; ++// machdep->line_number_hooks = x86_line_number_hooks; /* ODA: check */ ++ machdep->flags |= FRAMESIZE_DEBUG; /* ODA: check */ ++ machdep->value_to_symbol = generic_machdep_value_to_symbol; ++ machdep->clear_machdep_cache = x86_clear_machdep_cache; ++ ++ /* machdep table for Xen Hypervisor */ ++ xhmachdep->pcpu_init = xen_hyper_x86_pcpu_init; ++ break; ++ ++ case POST_GDB: ++#if 0 /* ODA: need this ? */ ++ if (x86_omit_frame_pointer()) { ++ machdep->flags |= OMIT_FRAME_PTR; ++#endif ++ XEN_HYPER_STRUCT_SIZE_INIT(cpu_time, "cpu_time"); ++ XEN_HYPER_STRUCT_SIZE_INIT(cpuinfo_x86, "cpuinfo_x86"); ++ XEN_HYPER_STRUCT_SIZE_INIT(tss_struct, "tss_struct"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(tss_struct_esp0, "tss_struct", "esp0"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_local_tsc_stamp, "cpu_time", "local_tsc_stamp"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_stime_local_stamp, "cpu_time", "stime_local_stamp"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_stime_master_stamp, "cpu_time", "stime_master_stamp"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_tsc_scale, "cpu_time", "tsc_scale"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_time_calibration_timer, "cpu_time", "calibration_timer"); ++ if (symbol_exists("cpu_data")) { ++ xht->cpu_data_address = symbol_value("cpu_data"); ++ } ++/* KAK Can this be calculated? */ ++ if (!machdep->hz) { ++ machdep->hz = XEN_HYPER_HZ; ++ } ++ break; ++ ++ case POST_INIT: ++ break; ++ } ++} ++ + #endif /* X86 */ +--- crash/lkcd_x86_trace.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_x86_trace.h 2007-12-19 16:36:56.000000000 -0500 +@@ -35,6 +35,25 @@ + + typedef uint32_t kaddr_t; + ++extern int INT_EFRAME_SS; ++extern int INT_EFRAME_ESP; ++extern int INT_EFRAME_EFLAGS; ++extern int INT_EFRAME_CS; ++extern int INT_EFRAME_EIP; ++extern int INT_EFRAME_ERR; ++extern int INT_EFRAME_ES; ++extern int INT_EFRAME_DS; ++extern int INT_EFRAME_EAX; ++extern int INT_EFRAME_EBP; ++extern int INT_EFRAME_EDI; ++extern int INT_EFRAME_ESI; ++extern int INT_EFRAME_EDX; ++extern int INT_EFRAME_ECX; ++extern int INT_EFRAME_EBX; ++extern int INT_EFRAME_GS; ++ ++extern ulong int_eframe[]; ++ + #endif /* REDHAT */ + + +--- crash/gdb_interface.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb_interface.c 2008-10-24 14:07:45.000000000 -0400 +@@ -1,8 +1,8 @@ + /* gdb_interface.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 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 +@@ -31,9 +31,6 @@ + { + argc = 1; + +- if (CRASHDEBUG(1)) +- gdb_readnow_warning(); +- + if (pc->flags & SILENT) { + if (pc->flags & READNOW) + argv[argc++] = "--readnow"; +@@ -198,20 +195,28 @@ + retry: + BZERO(req->buf, BUFSIZE); + req->command = GNU_GET_DATATYPE; +- req->name = "task_struct"; ++ req->name = XEN_HYPER_MODE() ? "page_info" : "task_struct"; + req->flags = GNU_RETURN_ON_ERROR; + gdb_interface(req); + + if (req->flags & GNU_COMMAND_FAILED) { ++ if (XEN_HYPER_MODE()) ++ no_debugging_data(WARNING); /* just bail out */ ++ + if (!debug_data_pulled_in) { + if (CRASHDEBUG(1)) + error(INFO, +- "gdb_session_init: pulling in debug data by accessing init_mm.mmap\n"); ++ "gdb_session_init: pulling in debug data by accessing init_mm.mmap %s\n", ++ symbol_exists("sysfs_mount") ? ++ "and syfs_mount" : ""); + debug_data_pulled_in = TRUE; + req->command = GNU_PASS_THROUGH; + req->flags = GNU_RETURN_ON_ERROR|GNU_NO_READMEM; + req->name = NULL; +- sprintf(req->buf, "print init_mm.mmap"); ++ if (symbol_exists("sysfs_mount")) ++ sprintf(req->buf, "print sysfs_mount, init_mm.mmap"); ++ else ++ sprintf(req->buf, "print init_mm.mmap"); + gdb_interface(req); + if (!(req->flags & GNU_COMMAND_FAILED)) + goto retry; +@@ -237,11 +242,16 @@ + sprintf(req->buf, "set height 0"); + gdb_interface(req); + ++ req->command = GNU_PASS_THROUGH; ++ req->name = NULL, req->flags = 0; ++ sprintf(req->buf, "set width 0"); ++ gdb_interface(req); ++ + /* + * Patch gdb's symbol values with the correct values from either + * the System.map or non-debug vmlinux, whichever is in effect. + */ +- if ((pc->flags & SYSMAP) || ++ if ((pc->flags & SYSMAP) || (kt->flags & (RELOC_SET|RELOC_FORCE)) || + (pc->namelist_debug && !pc->debuginfo_file)) { + req->command = GNU_PATCH_SYMBOL_VALUES; + req->flags = GNU_RETURN_ON_ERROR; +@@ -364,6 +374,7 @@ + fprintf(fp, " prettyprint_arrays: %d\n", prettyprint_arrays); + fprintf(fp, " prettyprint_structs: %d\n", prettyprint_structs); + fprintf(fp, "repeat_count_threshold: %x\n", repeat_count_threshold); ++ fprintf(fp, " stop_print_at_null: %d\n", stop_print_at_null); + fprintf(fp, " print_max: %d\n", print_max); + fprintf(fp, " output_radix: %d\n", output_radix); + fprintf(fp, " output_format: "); +@@ -556,6 +567,14 @@ + + error_hook = NULL; + ++ if (st->flags & ADD_SYMBOL_FILE) { ++ error(INFO, ++ "%s\n gdb add-symbol-file command failed\n", ++ st->current->mod_namelist); ++ delete_load_module(st->current->mod_base); ++ st->flags &= ~ADD_SYMBOL_FILE; ++ } ++ + if (pc->cur_gdb_cmd) { + pc->last_gdb_cmd = pc->cur_gdb_cmd; + pc->cur_gdb_cmd = 0; +@@ -619,6 +638,7 @@ + "clear", "disable", "enable", "condition", "ignore", "frame", + "select-frame", "f", "up", "down", "catch", "tcatch", "return", + "file", "exec-file", "core-file", "symbol-file", "load", "si", "ni", ++ "shell", + NULL /* must be last */ + }; + +@@ -628,7 +648,7 @@ + }; + + #define RESTRICTED_GDB_COMMAND \ +- "restricted gdb command: %s\n%s\"%s\" may only be used in a .gdbinit file or in a command file.\n%sThe .gdbinit file is read automatically during %s initialization.\n%sOther user-defined command files may be read interactively during\n%s%s runtime by using the gdb \"source\" command." ++ "restricted gdb command: %s\n%s\"%s\" may only be used in a .gdbinit file or in a command file.\n%sThe .gdbinit file is read automatically during %s initialization.\n%sOther user-defined command files may be read interactively during\n%s%s runtime by using the gdb \"source\" command.\n" + + static int + is_restricted_command(char *cmd, ulong flags) +@@ -668,6 +688,7 @@ + cmd_gdb(void) + { + int c; ++ char buf[BUFSIZE]; + + while ((c = getopt(argcnt, args, "")) != EOF) { + switch(c) +@@ -694,14 +715,8 @@ + * If the command is not restricted, pass it on. + */ + if (!is_restricted_command(args[1], FAULT_ON_ERROR)) { +- if (pc->redirect & (REDIRECT_TO_PIPE|REDIRECT_TO_FILE)) +- pc->orig_line[pc->eoc_index] = NULLCHAR; +- +- if (STRNEQ(pc->orig_line, "gdb") && +- whitespace(pc->orig_line[3])) +- shift_string_left(pc->orig_line, strlen("gdb")+1); +- +- gdb_pass_through(clean_line(pc->orig_line), NULL, 0); ++ concat_args(buf, 1, 0); ++ gdb_pass_through(buf, NULL, 0); + } + } + +@@ -722,8 +737,10 @@ + if (pc->cur_req->flags & GNU_NO_READMEM) + return TRUE; + +- if (UNIQUE_COMMAND("dis")) ++ if (pc->curcmd_flags & MEMTYPE_UVADDR) + memtype = UVADDR; ++ else if (pc->curcmd_flags & MEMTYPE_FILEADDR) ++ memtype = FILEADDR; + else if (!IS_KVADDR(addr)) { + if (STREQ(pc->curcmd, "gdb") && + STRNEQ(pc->cur_req->buf, "x/")) { +@@ -740,12 +757,11 @@ + if (CRASHDEBUG(1)) + console("gdb_readmem_callback[%d]: %lx %d\n", + memtype, addr, len); +- +-#ifdef OLDWAY +- return(readmem(addr, KVADDR, buf, len, +- "gdb_readmem_callback", RETURN_ON_ERROR)); +-#endif + ++ if (memtype == FILEADDR) ++ return(readmem(pc->curcmd_private, memtype, buf, len, ++ "gdb_readmem_callback", RETURN_ON_ERROR)); ++ + switch (len) + { + case SIZEOF_8BIT: +--- crash/README.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/README 2009-02-12 09:31:02.000000000 -0500 +@@ -69,7 +69,7 @@ + After the kernel is re-compiled, the uncompressed "vmlinux" kernel + that is created in the top-level kernel build directory must be saved. + +- To build this utility, simply uncompress the tar file, enter the crash-4.0 ++ To build this utility, simply uncompress the tar file, enter the crash-4.0-7.7p1 + subdirectory, and type "make". The initial build will take several minutes + because the gdb module must be configured and and built. Alternatively, the + crash source RPM file may be installed and built, and the resultant crash +@@ -89,11 +89,14 @@ + + $ crash + +- crash 4.0 +- Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. +- Copyright (C) 2004, 2005 IBM Corporation +- Copyright (C) 1999-2005 Hewlett-Packard Co +- Copyright (C) 1999, 2002 Silicon Graphics, Inc. ++ crash 4.0-7.7p1 ++ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. ++ Copyright (C) 2004, 2005, 2006 IBM Corporation ++ Copyright (C) 1999-2006 Hewlett-Packard Co ++ Copyright (C) 2005, 2006 Fujitsu Limited ++ Copyright (C) 2006, 2007 VA Linux Systems Japan K.K. ++ Copyright (C) 2005 NEC Corporation ++ Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc. + Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. + This program is free software, covered by the GNU General Public License, + and you are welcome to change it and/or distribute copies of it under +@@ -111,7 +114,7 @@ + KERNEL: /boot/vmlinux + DUMPFILE: /dev/mem + CPUS: 1 +- DATE: Wed Jul 13 13:26:00 2005 ++ DATE: Thu Feb 12 09:31:02 2009 + UPTIME: 10 days, 22:55:18 + LOAD AVERAGE: 0.08, 0.03, 0.01 + TASKS: 42 +@@ -139,7 +142,7 @@ + exit log rd task + extend mach repeat timer + +- crash version: 4.0 gdb version: 6.1 ++ crash version: 4.0-7.7p1 gdb version: 6.1 + For help on any command above, enter "help ". + For help on input options, enter "help input". + For help on output options, enter "help output". +@@ -152,11 +155,14 @@ + + $ crash vmlinux vmcore + +- crash 4.0 +- Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. +- Copyright (C) 2004, 2005 IBM Corporation +- Copyright (C) 1999-2005 Hewlett-Packard Co +- Copyright (C) 1999, 2002 Silicon Graphics, Inc. ++ crash 4.0-7.7p1 ++ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. ++ Copyright (C) 2004, 2005, 2006 IBM Corporation ++ Copyright (C) 1999-2006 Hewlett-Packard Co ++ Copyright (C) 2005, 2006 Fujitsu Limited ++ Copyright (C) 2006, 2007 VA Linux Systems Japan K.K. ++ Copyright (C) 2005 NEC Corporation ++ Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc. + Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. + This program is free software, covered by the GNU General Public License, + and you are welcome to change it and/or distribute copies of it under +@@ -196,11 +202,14 @@ + + $ crash vmlinux.17 lcore.cr.17 + +- crash 4.0 +- Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. +- Copyright (C) 2004, 2005 IBM Corporation +- Copyright (C) 1999-2005 Hewlett-Packard Co +- Copyright (C) 1999, 2002 Silicon Graphics, Inc. ++ crash 4.0-7.7p1 ++ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. ++ Copyright (C) 2004, 2005, 2006 IBM Corporation ++ Copyright (C) 1999-2006 Hewlett-Packard Co ++ Copyright (C) 2005, 2006 Fujitsu Limited ++ Copyright (C) 2006, 2007 VA Linux Systems Japan K.K. ++ Copyright (C) 2005 NEC Corporation ++ Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc. + Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. + This program is free software, covered by the GNU General Public License, + and you are welcome to change it and/or distribute copies of it under +--- crash/diskdump.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/diskdump.h 2007-04-10 10:24:02.000000000 -0400 +@@ -1,8 +1,10 @@ + /* + * diskdump.h + * +- * Copyright (C) 2004, 2005 David Anderson +- * Copyright (C) 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2004, 2005, 2006 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2005 FUJITSU LIMITED ++ * Copyright (C) 2005 NEC Corporation + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. +@@ -10,7 +12,65 @@ + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * Author: David Anderson + */ + ++#include ++ ++#define divideup(x, y) (((x) + ((y) - 1)) / (y)) ++#define round(x, y) (((x) / (y)) * (y)) ++ ++#define DUMP_PARTITION_SIGNATURE "diskdump" ++#define SIG_LEN (sizeof(DUMP_PARTITION_SIGNATURE) - 1) ++#define DISK_DUMP_SIGNATURE "DISKDUMP" ++#define KDUMP_SIGNATURE "KDUMP " ++ ++#define DUMP_HEADER_COMPLETED 0 ++#define DUMP_HEADER_INCOMPLETED 1 ++#define DUMP_HEADER_COMPRESSED 8 ++ ++struct disk_dump_header { ++ char signature[SIG_LEN]; /* = "DISKDUMP" */ ++ int header_version; /* Dump header version */ ++ struct new_utsname utsname; /* copy of system_utsname */ ++ struct timeval timestamp; /* Time stamp */ ++ unsigned int status; /* Above flags */ ++ int block_size; /* Size of a block in byte */ ++ int sub_hdr_size; /* Size of arch dependent ++ header in blocks */ ++ unsigned int bitmap_blocks; /* Size of Memory bitmap in ++ block */ ++ unsigned int max_mapnr; /* = max_mapnr */ ++ unsigned int total_ram_blocks;/* Number of blocks should be ++ written */ ++ unsigned int device_blocks; /* Number of total blocks in ++ * the dump device */ ++ unsigned int written_blocks; /* Number of written blocks */ ++ unsigned int current_cpu; /* CPU# which handles dump */ ++ int nr_cpus; /* Number of CPUs */ ++ struct task_struct *tasks[0]; ++}; ++ ++struct disk_dump_sub_header { ++ long elf_regs; ++}; ++ ++struct kdump_sub_header { ++ unsigned long phys_base; ++ int dump_level; /* header_version 1 and later */ ++}; ++ ++/* page flags */ ++#define DUMP_DH_COMPRESSED 0x1 /* page is compressed */ ++ ++/* descriptor of each page for vmcore */ ++typedef struct page_desc { ++ off_t offset; /* the offset of the page data*/ ++ unsigned int size; /* the size of this dump page */ ++ unsigned int flags; /* flags */ ++ unsigned long long page_flags; /* page flags */ ++} page_desc_t; ++ ++#define DISKDUMP_CACHED_PAGES (16) ++#define PAGE_VALID (0x1) /* flags */ ++#define DISKDUMP_VALID_PAGE(flags) ((flags) & PAGE_VALID) ++ +--- crash/s390x.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/s390x.c 2009-01-15 14:07:23.000000000 -0500 +@@ -1,9 +1,9 @@ + /* s390.c - core analysis suite + * + * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. +- * Copyright (C) 2005 Michael Holzheu, IBM Corporation ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2005, 2006 Michael Holzheu, IBM Corporation + * + * 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 +@@ -20,24 +20,6 @@ + + #define S390X_WORD_SIZE 8 + +-#define S390X_PAGE_SHIFT 12 +-#define S390X_PAGE_SIZE (1ULL << S390X_PAGE_SHIFT) +-#define S390X_PAGE_MASK (~(S390X_PAGE_SIZE-1)) +- +-#define S390X_PGDIR_SHIFT 31 +-#define S390X_PGDIR_SIZE (1ULL << S390X_PGDIR_SHIFT) +-#define S390X_PGDIR_MASK (~(S390X_PGDIR_SIZE-1)) +- +-#define S390X_PMD_SHIFT 20 +-#define S390X_PMD_SIZE (1ULL << S390X_PMD_SHIFT) +-#define S390X_PMD_MASK (~(S390X_PMD_SIZE-1)) +- +-#define S390X_PTRS_PER_PGD 2048 +-#define S390X_PTRS_PER_PMD 2048 +-#define S390X_PTRS_PER_PTE 256 +- +-#define S390X_PMD_BASE_MASK (~((1ULL<<12)-1)) +-#define S390X_PT_BASE_MASK (~((1ULL<<11)-1)) + #define S390X_PAGE_BASE_MASK (~((1ULL<<12)-1)) + + /* Flags used in entries of page dirs and page tables. +@@ -48,37 +30,11 @@ + #define S390X_PAGE_INVALID 0x400ULL /* HW invalid */ + #define S390X_PAGE_INVALID_MASK 0x601ULL /* for linux 2.6 */ + #define S390X_PAGE_INVALID_NONE 0x401ULL /* for linux 2.6 */ +-#define S390X_PMD_ENTRY_INV 0x20ULL /* invalid segment table entry */ +-#define S390X_PGD_ENTRY_INV 0x20ULL /* invalid region table entry */ +-#define S390X_PMD_ENTRY 0x00 +-#define S390X_PGD_ENTRY_FIRST 0x05 /* first part of pmd is valid */ +-#define S390X_PGD_ENTRY_SECOND 0xc7 /* second part of pmd is valid */ +-#define S390X_PGD_ENTRY_FULL 0x07 /* complete pmd is valid */ + + /* bits 52, 55 must contain zeroes in a pte */ + #define S390X_PTE_INVALID_MASK 0x900ULL + #define S390X_PTE_INVALID(x) ((x) & S390X_PTE_INVALID_MASK) + +-/* pgd/pmd/pte query macros */ +-#define s390x_pgd_none(x) ((x) & S390X_PGD_ENTRY_INV) +-#define s390x_pgd_bad(x) !( (((x) & S390X_PGD_ENTRY_FIRST) == \ +- S390X_PGD_ENTRY_FIRST) || \ +- (((x) & S390X_PGD_ENTRY_SECOND) == \ +- S390X_PGD_ENTRY_SECOND) || \ +- (((x) & S390X_PGD_ENTRY_FULL) == \ +- S390X_PGD_ENTRY_FULL)) +- +-#define s390x_pmd_none(x) ((x) & S390X_PMD_ENTRY_INV) +-#define s390x_pmd_bad(x) (((x) & (~S390X_PT_BASE_MASK & \ +- ~S390X_PMD_ENTRY_INV)) != \ +- S390X_PMD_ENTRY) +- +-#define s390x_pte_none(x) (((x) & (S390X_PAGE_INVALID | \ +- S390X_PAGE_RO | \ +- S390X_PAGE_PRESENT)) == \ +- S390X_PAGE_INVALID) +- +- + #define ASYNC_STACK_SIZE STACKSIZE() // can be 8192 or 16384 + #define KERNEL_STACK_SIZE STACKSIZE() // can be 8192 or 16384 + +@@ -88,9 +44,6 @@ + * declarations of static functions + */ + static void s390x_print_lowcore(char*, struct bt_info*,int); +-static unsigned long s390x_pgd_offset(unsigned long, unsigned long); +-static unsigned long s390x_pmd_offset(unsigned long, unsigned long); +-static unsigned long s390x_pte_offset(unsigned long, unsigned long); + static int s390x_kvtop(struct task_context *, ulong, physaddr_t *, int); + static int s390x_uvtop(struct task_context *, ulong, physaddr_t *, int); + static int s390x_vtop(unsigned long, ulong, physaddr_t*, int); +@@ -173,7 +126,10 @@ + machdep->nr_irqs = 0; /* TBD */ + machdep->vmalloc_start = s390x_vmalloc_start; + machdep->dump_irq = s390x_dump_irq; +- machdep->hz = HZ; ++ if (!machdep->hz) ++ machdep->hz = HZ; ++ machdep->section_size_bits = _SECTION_SIZE_BITS; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; + break; + + case POST_INIT: +@@ -193,8 +149,6 @@ + fprintf(fp, " flags: %lx (", machdep->flags); + if (machdep->flags & KSYMS_START) + fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); +@@ -207,7 +161,8 @@ + fprintf(fp, " hz: %d\n", machdep->hz); + fprintf(fp, " mhz: %ld\n", machdep->mhz); + fprintf(fp, " memsize: %lld (0x%llx)\n", +- machdep->memsize, machdep->memsize); ++ (unsigned long long)machdep->memsize, ++ (unsigned long long)machdep->memsize); + fprintf(fp, " bits: %d\n", machdep->bits); + fprintf(fp, " nr_irqs: %d\n", machdep->nr_irqs); + fprintf(fp, " eframe_search: s390x_eframe_search()\n"); +@@ -241,23 +196,12 @@ + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); ++ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); ++ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); + fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); + } + + /* +- * Check if address is in the vmalloc area +- */ +-int +-s390x_IS_VMALLOC_ADDR(ulong addr) +-{ +- static unsigned long high_memory = 0; +- if(!high_memory){ +- high_memory = s390x_vmalloc_start(); +- } +- return (addr > high_memory); +-} +- +-/* + * Check if address is in context's address space + */ + static int +@@ -308,7 +252,7 @@ + /* + * Check if page is mapped + */ +-inline int s390x_pte_present(unsigned long x){ ++static inline int s390x_pte_present(unsigned long x){ + if(THIS_KERNEL_VERSION >= LINUX(2,6,0)){ + return !((x) & S390X_PAGE_INVALID) || + ((x) & S390X_PAGE_INVALID_MASK) == S390X_PAGE_INVALID_NONE; +@@ -317,81 +261,102 @@ + } + } + +-/* ++/* + * page table traversal functions + */ +-unsigned long s390x_pgd_offset(unsigned long pgd_base, unsigned long vaddr) +-{ +- unsigned long pgd_off, pmd_base; +- +- pgd_off = ((vaddr >> S390X_PGDIR_SHIFT) & +- (S390X_PTRS_PER_PGD - 1)) * 8; +- readmem(pgd_base + pgd_off, PHYSADDR, &pmd_base, sizeof(long), +- "pmd_base",FAULT_ON_ERROR); +- +- return pmd_base; +-} + +-unsigned long s390x_pmd_offset(unsigned long pmd_base, unsigned long vaddr) +-{ +- unsigned long pmd_off, pte_base; +- +- pmd_off = ((vaddr >> S390X_PMD_SHIFT) & (S390X_PTRS_PER_PMD - 1)) +- * 8; +- readmem(pmd_base + pmd_off, PHYSADDR, &pte_base, sizeof(long), +- "pte_base",FAULT_ON_ERROR); +- return pte_base; +-} +- +-unsigned long s390x_pte_offset(unsigned long pte_base, unsigned long vaddr) +-{ +- unsigned long pte_off, pte_val; +- +- pte_off = ((vaddr >> S390X_PAGE_SHIFT) & (S390X_PTRS_PER_PTE - 1)) +- * 8; +- readmem(pte_base + pte_off, PHYSADDR, &pte_val, sizeof(long), +- "pte_val",FAULT_ON_ERROR); +- return pte_val; ++/* Region or segment table traversal function */ ++static ulong _kl_rsg_table_deref_s390x(ulong vaddr, ulong table, ++ int len, int level) ++{ ++ ulong offset, entry; ++ ++ offset = ((vaddr >> (11*level + 20)) & 0x7ffULL) * 8; ++ if (offset >= (len + 1)*4096) ++ /* Offset is over the table limit. */ ++ return 0; ++ readmem(table + offset, KVADDR, &entry, sizeof(entry), "entry", ++ FAULT_ON_ERROR); ++ /* ++ * Check if the segment table entry could be read and doesn't have ++ * any of the reserved bits set. ++ */ ++ if ((entry & 0xcULL) != (level << 2)) ++ return 0; ++ /* Check if the region table entry has the invalid bit set. */ ++ if (entry & 0x40ULL) ++ return 0; ++ /* Region table entry is valid and well formed. */ ++ return entry; + } + +-/* +- * Generic vtop function for user and kernel addresses +- */ +-static int +-s390x_vtop(unsigned long pgd_base, ulong kvaddr, physaddr_t *paddr, int verbose) ++/* Page table traversal function */ ++static ulong _kl_pg_table_deref_s390x(ulong vaddr, ulong table) + { +- unsigned long pmd_base, pte_base, pte_val; ++ ulong offset, entry; + +- /* get the pgd entry */ +- pmd_base = s390x_pgd_offset(pgd_base,kvaddr); +- if(s390x_pgd_bad(pmd_base) || +- s390x_pgd_none(pmd_base)){ +- *paddr = 0; +- return FALSE; +- } +- /* get the pmd */ +- pmd_base = pmd_base & S390X_PMD_BASE_MASK; +- pte_base = s390x_pmd_offset(pmd_base,kvaddr); +- if(s390x_pmd_bad(pte_base) || +- s390x_pmd_none(pte_base)) { +- *paddr = 0; +- return FALSE; +- } +- /* get the pte */ +- pte_base = pte_base & S390X_PT_BASE_MASK; +- pte_val = s390x_pte_offset(pte_base,kvaddr); +- if (S390X_PTE_INVALID(pte_val) || +- s390x_pte_none(pte_val)){ +- *paddr = 0; ++ offset = ((vaddr >> 12) & 0xffULL) * 8; ++ readmem(table + offset, KVADDR, &entry, sizeof(entry), "entry", ++ FAULT_ON_ERROR); ++ /* ++ * Check if the page table entry could be read and doesn't have ++ * any of the reserved bits set. ++ */ ++ if (entry & 0x900ULL) ++ return 0; ++ /* Check if the page table entry has the invalid bit set. */ ++ if (entry & 0x400ULL) ++ return 0; ++ /* Page table entry is valid and well formed. */ ++ return entry; ++} ++ ++/* lookup virtual address in page tables */ ++int s390x_vtop(ulong table, ulong vaddr, physaddr_t *phys_addr, int verbose) ++{ ++ ulong entry, paddr; ++ int level, len; ++ ++ /* ++ * Walk the region and segment tables. ++ * We assume that the table length field in the asce is set to the ++ * maximum value of 3 (which translates to a region first, region ++ * second, region third or segment table with 2048 entries) and that ++ * the addressing mode is 64 bit. ++ */ ++ len = 3; ++ /* Read the first entry to find the number of page table levels. */ ++ readmem(table, KVADDR, &entry, sizeof(entry), "entry", FAULT_ON_ERROR); ++ level = (entry & 0xcULL) >> 2; ++ if ((vaddr >> (31 + 11*level)) != 0ULL) { ++ /* Address too big for the number of page table levels. */ + return FALSE; + } +- if(!s390x_pte_present(pte_val)){ +- /* swapped out */ +- *paddr = pte_val; ++ while (level >= 0) { ++ entry = _kl_rsg_table_deref_s390x(vaddr, table, len, level); ++ if (!entry) ++ return 0; ++ table = entry & ~0xfffULL; ++ len = entry & 0x3ULL; ++ level--; ++ } ++ ++ /* Check if this is a large page. */ ++ if (entry & 0x400ULL) ++ /* Add the 1MB page offset and return the final value. */ ++ return table + (vaddr & 0xfffffULL); ++ ++ /* Get the page table entry */ ++ entry = _kl_pg_table_deref_s390x(vaddr, entry & ~0x7ffULL); ++ if (!entry) + return FALSE; +- } +- *paddr = (pte_val & S390X_PAGE_BASE_MASK) | +- (kvaddr & (~(S390X_PAGE_MASK))); ++ ++ /* Isolate the page origin from the page table entry. */ ++ paddr = entry & ~0xfffULL; ++ ++ /* Add the page offset and return the final value. */ ++ *phys_addr = paddr + (vaddr & 0xfffULL); ++ + return TRUE; + } + +@@ -453,6 +418,10 @@ + if (STREQ(name, "Letext") || STREQ(name, "gcc2_compiled.")) + return FALSE; + ++ /* reject L2^B symbols */ ++ if (strstr(name, "L2\002") == name) ++ return FALSE; ++ + /* throw away all symbols containing a '.' */ + for(i = 0; i < strlen(name);i++){ + if(name[i] == '.') +@@ -514,7 +483,7 @@ + return FALSE; + } + fprintf(fp,"PTE PHYSICAL FLAGS\n"); +- fprintf(fp,"%08x %08x",pte, pte & S390X_PAGE_BASE_MASK); ++ fprintf(fp,"%08lx %08llx",pte, pte & S390X_PAGE_BASE_MASK); + fprintf(fp," ("); + if(pte & S390X_PAGE_INVALID) + fprintf(fp,"INVALID "); +@@ -541,7 +510,7 @@ + /* + * returns cpu number of task + */ +-int ++static int + s390x_cpu_of_task(unsigned long task) + { + unsigned int cpu; +@@ -583,12 +552,13 @@ + return FALSE; + } else { + /* Linux 2.6 */ +- unsigned long runqueue_addr, runqueue_offset, per_cpu_offset; ++ unsigned long runqueue_addr, runqueue_offset; + unsigned long cpu_offset, per_cpu_offset_addr, running_task; +- char runqueue[4096]; ++ char *runqueue; + int cpu; + + cpu = s390x_cpu_of_task(task); ++ runqueue = GETBUF(SIZE(runqueue)); + + runqueue_offset=symbol_value("per_cpu__runqueues"); + per_cpu_offset_addr=symbol_value("__per_cpu_offset"); +@@ -596,10 +566,10 @@ + &cpu_offset, sizeof(long),"per_cpu_offset", + FAULT_ON_ERROR); + runqueue_addr=runqueue_offset + cpu_offset; +- readmem(runqueue_addr,KVADDR,&runqueue,sizeof(runqueue), ++ readmem(runqueue_addr,KVADDR,runqueue,SIZE(runqueue), + "runqueue", FAULT_ON_ERROR); +- running_task = *((unsigned long*)&runqueue[MEMBER_OFFSET( +- "runqueue", "curr")]); ++ running_task = ULONG(runqueue + OFFSET(runqueue_curr)); ++ FREEBUF(runqueue); + if(running_task == task) + return TRUE; + else +@@ -733,7 +703,7 @@ + } else if(skip_first_frame){ + skip_first_frame=0; + } else { +- fprintf(fp," #%i [%08x] ",i,backchain); ++ fprintf(fp," #%i [%08lx] ",i,backchain); + fprintf(fp,"%s at %x\n", closest_symbol(r14), r14); + if (bt->flags & BT_LINE_NUMBERS) + s390x_dump_line_number(r14); +@@ -743,22 +713,25 @@ + backchain = ULONG(&stack[backchain - stack_base + bc_offset]); + + /* print stack content if -f is specified */ +- if((bt->flags & BT_FULL) && !BT_REFERENCE_CHECK(bt)){ ++ if ((bt->flags & BT_FULL) && !BT_REFERENCE_CHECK(bt)) { + int frame_size; +- if(backchain == 0){ ++ if (backchain == 0) { + frame_size = stack_base - old_backchain + + KERNEL_STACK_SIZE; + } else { +- frame_size = backchain - old_backchain; ++ frame_size = MIN((backchain - old_backchain), ++ (stack_base - old_backchain + ++ KERNEL_STACK_SIZE)); + } +- for(j=0; j< frame_size; j+=4){ ++ for (j = 0; j < frame_size; j += 8) { + if(j % 16 == 0){ +- fprintf(fp,"\n%08x: ",old_backchain+j); ++ fprintf(fp, "%s %016lx: ", ++ j ? "\n" : "", old_backchain + j); + } +- fprintf(fp," %08x",ULONG(&stack[old_backchain - +- stack_base + j])); ++ fprintf(fp," %016lx", ++ ULONG(&stack[old_backchain - stack_base + j])); + } +- fprintf(fp,"\n\n"); ++ fprintf(fp, "\n"); + } + + /* Check for interrupt stackframe */ +@@ -804,26 +777,26 @@ + return; + } + fprintf(fp," LOWCORE INFO:\n"); +- fprintf(fp," -psw : %#018x %#018x\n", tmp[0], tmp[1]); ++ fprintf(fp," -psw : %#018lx %#018lx\n", tmp[0], tmp[1]); + if(show_symbols){ +- fprintf(fp," -function : %s at %x\n", ++ fprintf(fp," -function : %s at %lx\n", + closest_symbol(tmp[1]), tmp[1]); + if (bt->flags & BT_LINE_NUMBERS) + s390x_dump_line_number(tmp[1]); + } + ptr = lc + MEMBER_OFFSET("_lowcore","prefixreg_save_area"); + tmp[0] = UINT(ptr); +- fprintf(fp," -prefix : %#010x\n", tmp[0]); ++ fprintf(fp," -prefix : %#010lx\n", tmp[0]); + + ptr = lc + MEMBER_OFFSET("_lowcore","cpu_timer_save_area"); + tmp[0]=UINT(ptr); + tmp[1]=UINT(ptr + S390X_WORD_SIZE); +- fprintf(fp," -cpu timer: %#010x %#010x\n", tmp[0],tmp[1]); ++ fprintf(fp," -cpu timer: %#010lx %#010lx\n", tmp[0],tmp[1]); + + ptr = lc + MEMBER_OFFSET("_lowcore","clock_comp_save_area"); + tmp[0]=UINT(ptr); + tmp[1]=UINT(ptr + S390X_WORD_SIZE); +- fprintf(fp," -clock cmp: %#010x %#010x\n", tmp[0], tmp[1]); ++ fprintf(fp," -clock cmp: %#010lx %#010lx\n", tmp[0], tmp[1]); + + fprintf(fp," -general registers:\n"); + ptr = lc + MEMBER_OFFSET("_lowcore","gpregs_save_area"); +@@ -831,26 +804,26 @@ + tmp[1]=ULONG(ptr + S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 8 * S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 9 * S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 10* S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 11* S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 12* S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 13* S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 14* S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 15* S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + + fprintf(fp," -access registers:\n"); + ptr = lc + MEMBER_OFFSET("_lowcore","access_regs_save_area"); +@@ -858,25 +831,25 @@ + tmp[1]=ULONG(ptr + 4); + tmp[2]=ULONG(ptr + 2 * 4); + tmp[3]=ULONG(ptr + 3 * 4); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 4 * 4); + tmp[1]=ULONG(ptr + 5 * 4); + tmp[2]=ULONG(ptr + 6 * 4); + tmp[3]=ULONG(ptr + 7 * 4); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 8 * 4); + tmp[1]=ULONG(ptr + 9 * 4); + tmp[2]=ULONG(ptr + 10* 4); + tmp[3]=ULONG(ptr + 11* 4); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 12* 4); + tmp[1]=ULONG(ptr + 13* 4); + tmp[2]=ULONG(ptr + 14* 4); + tmp[3]=ULONG(ptr + 15* 4); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + + fprintf(fp," -control registers:\n"); +@@ -885,26 +858,26 @@ + tmp[1]=ULONG(ptr + S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr); + tmp[1]=ULONG(ptr + S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + + ptr = lc + MEMBER_OFFSET("_lowcore","floating_pt_save_area"); + fprintf(fp," -floating point registers 0,2,4,6:\n"); +@@ -912,26 +885,26 @@ + tmp[1]=ULONG(ptr + S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 6 * S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 7 * S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 8 * S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 9 * S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 10* S390X_WORD_SIZE); + tmp[1]=ULONG(ptr + 11* S390X_WORD_SIZE); + tmp[2]=ULONG(ptr + 12* S390X_WORD_SIZE); + tmp[3]=ULONG(ptr + 13* S390X_WORD_SIZE); +- fprintf(fp," %#018x %#018x\n", tmp[0],tmp[1]); +- fprintf(fp," %#018x %#018x\n", tmp[2],tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0],tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2],tmp[3]); + } + + /* +--- crash/dev.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/dev.c 2007-12-11 14:54:56.000000000 -0500 +@@ -91,13 +91,13 @@ + switch(c) + { + case 'i': +- if (machine_type("X86") || machine_type("S390X")) ++ if (machine_type("S390X")) + option_not_supported(c); + do_io(); + return; + + case 'p': +- if (machine_type("X86") || machine_type("S390X")) ++ if (machine_type("S390X")) + option_not_supported(c); + do_pci(); + return; +@@ -141,6 +141,8 @@ + char *char_device_struct_buf; + ulong next, savenext, name, fops; + int major; ++ int name_typecode; ++ size_t name_size; + + if (!symbol_exists("chrdevs")) + error(FATAL, "chrdevs: symbol does not exist\n"); +@@ -188,6 +190,8 @@ + + char_device_struct_buf = GETBUF(SIZE(char_device_struct)); + cdp = (ulong *)&chrdevs[0]; ++ name_typecode = MEMBER_TYPE("char_device_struct", "name"); ++ name_size = (size_t)MEMBER_SIZE("char_device_struct", "name"); + + for (i = 0; i < MAX_DEV; i++, cdp++) { + if (!(*cdp)) +@@ -201,11 +205,18 @@ + OFFSET(char_device_struct_next)); + name = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_name)); +- if (name) { +- if (!read_string(name, buf, BUFSIZE-1)) +- sprintf(buf, "(unknown)"); +- } else +- sprintf(buf, "(unknown)"); ++ switch (name_typecode) ++ { ++ case TYPE_CODE_ARRAY: ++ snprintf(buf, name_size, char_device_struct_buf + ++ OFFSET(char_device_struct_name)); ++ break; ++ case TYPE_CODE_PTR: ++ default: ++ if (!name || !read_string(name, buf, BUFSIZE-1)) ++ break; ++ } ++ + fops = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_fops)); + major = INT(char_device_struct_buf + +@@ -243,11 +254,19 @@ + OFFSET(char_device_struct_next)); + name = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_name)); +- if (name) { +- if (!read_string(name, buf, BUFSIZE-1)) +- sprintf(buf, "(unknown)"); +- } else +- sprintf(buf, "(unknown)"); ++ switch (name_typecode) ++ { ++ case TYPE_CODE_ARRAY: ++ snprintf(buf, name_size, char_device_struct_buf + ++ OFFSET(char_device_struct_name)); ++ break; ++ case TYPE_CODE_PTR: ++ default: ++ if (!name || !read_string(name, buf, BUFSIZE-1)) ++ sprintf(buf, "(unknown)"); ++ break; ++ } ++ + fops = ULONG(char_device_struct_buf + + OFFSET(char_device_struct_fops)); + major = INT(char_device_struct_buf + +@@ -1957,29 +1974,44 @@ + unsigned int class; + unsigned short device, vendor; + unsigned char busno; +- ulong *devlist, bus, devfn, tmp; ++ ulong *devlist, bus, devfn, prev, next; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + +- fprintf(fp, "%s BU:SL.FN CLASS: VENDOR-DEVICE\n", +- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "PCI_DEV")); ++ if (!symbol_exists("pci_devices")) ++ error(FATAL, "no PCI devices found on this system.\n"); + + BZERO(&pcilist_data, sizeof(struct list_data)); + + if (VALID_MEMBER(pci_dev_global_list)) { +- get_symbol_data("pci_devices", sizeof(void *), &tmp); +- readmem(tmp + OFFSET(list_head_next), KVADDR, +- &pcilist_data.start, sizeof(void *), "pci devices", +- FAULT_ON_ERROR); ++ get_symbol_data("pci_devices", sizeof(void *), &pcilist_data.start); + pcilist_data.end = symbol_value("pci_devices"); + pcilist_data.list_head_offset = OFFSET(pci_dev_global_list); ++ readmem(symbol_value("pci_devices") + OFFSET(list_head_prev), ++ KVADDR, &prev, sizeof(void *), "list head prev", ++ FAULT_ON_ERROR); ++ /* ++ * Check if this system does not have any PCI devices. ++ */ ++ if ((pcilist_data.start == pcilist_data.end) && ++ (prev == pcilist_data.end)) ++ error(FATAL, "no PCI devices found on this system.\n"); + +- } else { ++ } else if (VALID_MEMBER(pci_dev_next)) { + get_symbol_data("pci_devices", sizeof(void *), + &pcilist_data.start); + pcilist_data.member_offset = OFFSET(pci_dev_next); +- } ++ /* ++ * Check if this system does not have any PCI devices. ++ */ ++ readmem(pcilist_data.start + pcilist_data.member_offset, ++ KVADDR, &next, sizeof(void *), "pci dev next", ++ FAULT_ON_ERROR); ++ if (!next) ++ error(FATAL, "no PCI devices found on this system.\n"); ++ } else ++ option_not_supported('p'); + + hq_open(); + devcnt = do_list(&pcilist_data); +@@ -1987,6 +2019,9 @@ + devcnt = retrieve_list(devlist, devcnt); + hq_close(); + ++ fprintf(fp, "%s BU:SL.FN CLASS: VENDOR-DEVICE\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "PCI_DEV")); ++ + for (i = 0; i < devcnt; i++) { + + /* +--- crash/task.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/task.c 2009-02-04 16:49:49.000000000 -0500 +@@ -1,8 +1,8 @@ + /* task.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -27,11 +27,18 @@ + static void refresh_pidhash_task_table(void); + static void refresh_pid_hash_task_table(void); + static void refresh_hlist_task_table(void); ++static void refresh_hlist_task_table_v2(void); ++static void refresh_hlist_task_table_v3(void); ++static void refresh_active_task_table(void); + static struct task_context *store_context(struct task_context *, ulong, char *); + static void refresh_context(ulong, ulong); + static void parent_list(ulong); + static void child_list(ulong); ++static void initialize_task_state(void); + static void show_task_times(struct task_context *, ulong); ++static void show_task_args(struct task_context *); ++static void show_task_rlimit(struct task_context *); ++static void show_tgid_list(ulong); + static int compare_start_time(const void *, const void *); + static int start_time_timespec(void); + static ulonglong convert_start_time(ulonglong, ulonglong); +@@ -46,11 +53,26 @@ + static void dump_runq(void); + static void dump_runqueues(void); + static void dump_prio_array(int, ulong, char *); ++struct rb_root; ++static struct rb_node *rb_first(struct rb_root *); ++struct rb_node; ++static struct rb_node *rb_next(struct rb_node *); ++static struct rb_node *rb_parent(struct rb_node *, struct rb_node *); ++static struct rb_node *rb_right(struct rb_node *, struct rb_node *); ++static struct rb_node *rb_left(struct rb_node *, struct rb_node *); ++static void dump_CFS_runqueues(void); ++static void dump_RT_prio_array(int, ulong, char *); + static void task_struct_member(struct task_context *,ulong,struct reference *); + static void signal_reference(struct task_context *, ulong, struct reference *); +-static void dump_signal_data(struct task_context *); ++static void do_sig_thread_group(ulong); ++static void dump_signal_data(struct task_context *, ulong); ++#define TASK_LEVEL (0x1) ++#define THREAD_GROUP_LEVEL (0x2) ++#define TASK_INDENT (0x4) ++static int sigrt_minmax(int *, int *); + static void signame_list(void); +-static ulonglong task_signal(ulong); ++static void sigqueue_list(ulong); ++static ulonglong task_signal(ulong, ulong*); + static ulonglong task_blocked(ulong); + static void translate_sigset(ulonglong); + static ulonglong sigaction_mask(ulong); +@@ -80,6 +102,12 @@ + struct gnu_request req; + ulong active_pid; + ++ if (!(tt->idle_threads = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) ++ error(FATAL, "cannot malloc idle_threads array"); ++ if (DUMPFILE() && ++ !(tt->panic_threads = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) ++ error(FATAL, "cannot malloc panic_threads array"); ++ + if (kernel_symbol_exists("nr_tasks")) { + /* + * Figure out what maximum NR_TASKS would be by getting the +@@ -133,6 +161,15 @@ + "thread_struct", "eip"); + esp_offset = MEMBER_OFFSET_INIT(thread_struct_esp, + "thread_struct", "esp"); ++ /* ++ * Handle x86/x86_64 merger. ++ */ ++ if (eip_offset == INVALID_OFFSET) ++ eip_offset = MEMBER_OFFSET_INIT(thread_struct_eip, ++ "thread_struct", "ip"); ++ if (esp_offset == INVALID_OFFSET) ++ esp_offset = MEMBER_OFFSET_INIT(thread_struct_esp, ++ "thread_struct", "sp"); + ksp_offset = MEMBER_OFFSET_INIT(thread_struct_ksp, + "thread_struct", "ksp"); + ASSIGN_OFFSET(task_struct_thread_eip) = +@@ -151,8 +188,15 @@ + get_idle_threads(&tt->idle_threads[0], kt->cpus); + } + +- MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", +- "thread_info"); ++ if (MEMBER_EXISTS("task_struct", "thread_info")) ++ MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", ++ "thread_info"); ++ else if (MEMBER_EXISTS("task_struct", "stack")) ++ MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", ++ "stack"); ++ else ++ ASSIGN_OFFSET(task_struct_thread_info) = INVALID_OFFSET; ++ + if (VALID_MEMBER(task_struct_thread_info)) { + MEMBER_OFFSET_INIT(thread_info_task, "thread_info", "task"); + MEMBER_OFFSET_INIT(thread_info_cpu, "thread_info", "cpu"); +@@ -170,6 +214,9 @@ + MEMBER_OFFSET_INIT(task_struct_processor, "task_struct", "processor"); + MEMBER_OFFSET_INIT(task_struct_p_pptr, "task_struct", "p_pptr"); + MEMBER_OFFSET_INIT(task_struct_parent, "task_struct", "parent"); ++ if (INVALID_MEMBER(task_struct_parent)) ++ MEMBER_OFFSET_INIT(task_struct_parent, "task_struct", ++ "real_parent"); + MEMBER_OFFSET_INIT(task_struct_has_cpu, "task_struct", "has_cpu"); + MEMBER_OFFSET_INIT(task_struct_cpus_runnable, + "task_struct", "cpus_runnable"); +@@ -184,8 +231,13 @@ + MEMBER_OFFSET_INIT(task_struct_pids, "task_struct", "pids"); + MEMBER_OFFSET_INIT(task_struct_last_run, "task_struct", "last_run"); + MEMBER_OFFSET_INIT(task_struct_timestamp, "task_struct", "timestamp"); ++ MEMBER_OFFSET_INIT(task_struct_sched_info, "task_struct", "sched_info"); ++ if (VALID_MEMBER(task_struct_sched_info)) ++ MEMBER_OFFSET_INIT(sched_info_last_arrival, ++ "sched_info", "last_arrival"); + if (VALID_MEMBER(task_struct_last_run) || +- VALID_MEMBER(task_struct_timestamp)) { ++ VALID_MEMBER(task_struct_timestamp) || ++ VALID_MEMBER(sched_info_last_arrival)) { + char buf[BUFSIZE]; + strcpy(buf, "alias last ps -l"); + alias_init(buf); +@@ -193,6 +245,17 @@ + MEMBER_OFFSET_INIT(pid_link_pid, "pid_link", "pid"); + MEMBER_OFFSET_INIT(pid_hash_chain, "pid", "hash_chain"); + ++ STRUCT_SIZE_INIT(pid_link, "pid_link"); ++ STRUCT_SIZE_INIT(upid, "upid"); ++ if (VALID_STRUCT(upid)) { ++ MEMBER_OFFSET_INIT(upid_nr, "upid", "nr"); ++ MEMBER_OFFSET_INIT(upid_ns, "upid", "ns"); ++ MEMBER_OFFSET_INIT(upid_pid_chain, "upid", "pid_chain"); ++ MEMBER_OFFSET_INIT(pid_numbers, "pid", "numbers"); ++ MEMBER_OFFSET_INIT(pid_tasks, "pid", "tasks"); ++ tt->init_pid_ns = symbol_value("init_pid_ns"); ++ } ++ + MEMBER_OFFSET_INIT(pid_pid_chain, "pid", "pid_chain"); + + STRUCT_SIZE_INIT(task_struct, "task_struct"); +@@ -207,6 +270,8 @@ + + MEMBER_OFFSET_INIT(signal_struct_count, "signal_struct", "count"); + MEMBER_OFFSET_INIT(signal_struct_action, "signal_struct", "action"); ++ MEMBER_OFFSET_INIT(signal_struct_shared_pending, "signal_struct", ++ "shared_pending"); + + MEMBER_OFFSET_INIT(k_sigaction_sa, "k_sigaction", "sa"); + +@@ -217,17 +282,10 @@ + if (INVALID_MEMBER(sigpending_head)) + MEMBER_OFFSET_INIT(sigpending_list, "sigpending", "list"); + MEMBER_OFFSET_INIT(sigpending_signal, "sigpending", "signal"); ++ MEMBER_SIZE_INIT(sigpending_signal, "sigpending", "signal"); + + STRUCT_SIZE_INIT(sigqueue, "sigqueue"); +- if (VALID_STRUCT(sigqueue)) { +- MEMBER_OFFSET_INIT(sigqueue_next, "sigqueue", "next"); +- MEMBER_OFFSET_INIT(sigqueue_list, "sigqueue", "list"); +- MEMBER_OFFSET_INIT(sigqueue_info, "sigqueue", "info"); +- } else { +- STRUCT_SIZE_INIT(signal_queue, "signal_queue"); +- MEMBER_OFFSET_INIT(signal_queue_next, "signal_queue", "next"); +- MEMBER_OFFSET_INIT(signal_queue_info, "signal_queue", "info"); +- } ++ STRUCT_SIZE_INIT(signal_queue, "signal_queue"); + + STRUCT_SIZE_INIT(sighand_struct, "sighand_struct"); + if (VALID_STRUCT(sighand_struct)) +@@ -249,6 +307,22 @@ + + STRUCT_SIZE_INIT(cputime_t, "cputime_t"); + ++ if (symbol_exists("cfq_slice_async")) { ++ uint cfq_slice_async; ++ ++ get_symbol_data("cfq_slice_async", sizeof(int), ++ &cfq_slice_async); ++ ++ if (cfq_slice_async) { ++ machdep->hz = cfq_slice_async * 25; ++ ++ if (CRASHDEBUG(2)) ++ fprintf(fp, ++ "cfq_slice_async exists: setting hz to %d\n", ++ machdep->hz); ++ } ++ } ++ + if (VALID_MEMBER(runqueue_arrays)) + MEMBER_OFFSET_INIT(task_struct_run_list, "task_struct", + "run_list"); +@@ -279,12 +353,6 @@ + error(FATAL, + "pidhash and pid_hash both exist -- cannot distinquish between them\n"); + +- /* +- * NOTE: We rely on PIDTYPE_PID staying at enum value of 0, because +- * evan at the lowest level in gdb, I can't seem to find where +- * the actual value is stored via the struct type. (?) +- * Should be safe, though... +- */ + if (symbol_exists("pid_hash") && symbol_exists("pidhash_shift")) { + int pidhash_shift; + +@@ -302,7 +370,33 @@ + tt->refresh_task_table = refresh_pid_hash_task_table; + } else { + tt->pidhash_addr = symbol_value("pid_hash"); +- tt->refresh_task_table = refresh_hlist_task_table; ++ if (LKCD_KERNTYPES()) { ++ if (VALID_STRUCT(pid_link)) { ++ if (VALID_STRUCT(upid) && VALID_MEMBER(pid_numbers)) ++ tt->refresh_task_table = ++ refresh_hlist_task_table_v3; ++ else ++ tt->refresh_task_table = ++ refresh_hlist_task_table_v2; ++ } else ++ tt->refresh_task_table = ++ refresh_hlist_task_table; ++ builtin_array_length("pid_hash", ++ tt->pidhash_len, NULL); ++ } else { ++ if (!get_array_length("pid_hash", NULL, ++ sizeof(void *)) && VALID_STRUCT(pid_link)) { ++ if (VALID_STRUCT(upid) && VALID_MEMBER(pid_numbers)) ++ tt->refresh_task_table = ++ refresh_hlist_task_table_v3; ++ else ++ tt->refresh_task_table = ++ refresh_hlist_task_table_v2; ++ } ++ else ++ tt->refresh_task_table = ++ refresh_hlist_task_table; ++ } + } + + tt->flags |= PID_HASH; +@@ -343,6 +437,10 @@ + irqstacks_init(); + + get_active_set(); ++ ++ if (tt->flags & ACTIVE_ONLY) ++ tt->refresh_task_table = refresh_active_task_table; ++ + tt->refresh_task_table(); + + if (tt->flags & TASK_REFRESH_OFF) +@@ -353,11 +451,17 @@ + set_context(NO_TASK, active_pid); + tt->this_task = pid_to_task(active_pid); + } +- else ++ else { ++ please_wait("determining panic task"); + set_context(get_panic_context(), NO_PID); ++ please_wait_done(); ++ } + + sort_context_array(); + ++ if (pc->flags & SILENT) ++ initialize_task_state(); ++ + tt->flags |= TASK_INIT_DONE; + } + +@@ -371,6 +475,15 @@ + int i; + char *thread_info_buf; + ++ if (!(tt->hardirq_ctx = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) ++ error(FATAL, "cannot malloc hardirq_ctx space."); ++ if (!(tt->hardirq_tasks = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) ++ error(FATAL, "cannot malloc hardirq_tasks space."); ++ if (!(tt->softirq_ctx = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) ++ error(FATAL, "cannot malloc softirq_ctx space."); ++ if (!(tt->softirq_tasks = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) ++ error(FATAL, "cannot malloc softirq_tasks space."); ++ + thread_info_buf = GETBUF(SIZE(irq_ctx)); + + i = get_array_length("hardirq_ctx", NULL, 0); +@@ -545,6 +658,7 @@ + static int + verify_task(struct task_context *tc, int level) + { ++ int i; + ulong next_task; + ulong readflag; + +@@ -565,11 +679,27 @@ + + /* fall through */ + case 2: +- if ((tc->processor < 0) || (tc->processor >= NR_CPUS)) ++ if (!IS_TASK_ADDR(tc->ptask)) + return FALSE; + +- if (!IS_TASK_ADDR(tc->ptask)) ++ if ((tc->processor < 0) || (tc->processor >= NR_CPUS)) { ++ for (i = 0; i < NR_CPUS; i++) { ++ if (tc->task == tt->active_set[i]) { ++ error(WARNING, ++ "active task %lx on cpu %d: corrupt cpu value: %d\n\n", ++ tc->task, i, tc->processor); ++ tc->processor = i; ++ return TRUE; ++ } ++ } ++ ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "verify_task: task: %lx invalid processor: %d", ++ tc->task, tc->processor); + return FALSE; ++ } ++ + break; + } + +@@ -987,9 +1117,7 @@ + return; + + if (DUMPFILE()) { /* impossible */ +- fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? +- "" : "\rplease wait... (gathering task table data)"); +- fflush(fp); ++ please_wait("gathering task table data"); + if (!symbol_exists("panic_threads")) + tt->flags |= POPULATE_PANIC; + } +@@ -1152,11 +1280,7 @@ + + FREEBUF(pid_hash); + +- if (DUMPFILE()) { +- fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" : +- "\r \r"); +- fflush(fp); +- } ++ please_wait_done(); + + if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) + refresh_context(curtask, curpid); +@@ -1176,12 +1300,14 @@ + { + int i; + ulong *pid_hash; ++ struct syment *sp; + ulong pidhash_array; + ulong kpp; + char *tp; + ulong next, pnext, pprev; + char *nodebuf; + int plen, len, cnt; ++ long value; + struct task_context *tc; + ulong curtask; + ulong curpid; +@@ -1192,9 +1318,7 @@ + return; + + if (DUMPFILE()) { /* impossible */ +- fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? +- "" : "\rplease wait... (gathering task table data)"); +- fflush(fp); ++ please_wait("gathering task table data"); + if (!symbol_exists("panic_threads")) + tt->flags |= POPULATE_PANIC; + } +@@ -1211,8 +1335,21 @@ + curpid = CURRENT_PID(); + } + +- if (!(plen = get_array_length("pid_hash", NULL, sizeof(void *)))) +- error(FATAL, "cannot determine pid_hash array dimensions\n"); ++ if (!(plen = get_array_length("pid_hash", NULL, sizeof(void *)))) { ++ /* ++ * Workaround for gcc omitting debuginfo data for pid_hash. ++ */ ++ if (enumerator_value("PIDTYPE_MAX", &value)) { ++ if ((sp = next_symbol("pid_hash", NULL)) && ++ (((sp->value - tt->pidhash_addr) / sizeof(void *)) < value)) ++ error(WARNING, "possible pid_hash array mis-handling\n"); ++ plen = (int)value; ++ } else { ++ error(WARNING, ++ "cannot determine pid_hash array dimensions\n"); ++ plen = 1; ++ } ++ } + + pid_hash = (ulong *)GETBUF(plen * sizeof(void *)); + +@@ -1228,6 +1365,16 @@ + * The zero'th (PIDTYPE_PID) entry is the hlist_head array + * that we want. + */ ++ if (CRASHDEBUG(1)) { ++ if (!enumerator_value("PIDTYPE_PID", &value)) ++ error(WARNING, ++ "possible pid_hash array mis-handling: PIDTYPE_PID: (unknown)\n"); ++ else if (value != 0) ++ error(WARNING, ++ "possible pid_hash array mis-handling: PIDTYPE_PID: %d \n", ++ value); ++ } ++ + pidhash_array = pid_hash[0]; + FREEBUF(pid_hash); + +@@ -1345,6 +1492,15 @@ + } + } + ++ if (cnt > tt->max_tasks) { ++ tt->max_tasks = cnt + TASK_SLUSH; ++ allocate_task_space(tt->max_tasks); ++ hq_close(); ++ if (!DUMPFILE()) ++ retries++; ++ goto retry_pid_hash; ++ } ++ + BZERO(tt->task_local, tt->max_tasks * sizeof(void *)); + cnt = retrieve_list((ulong *)tt->task_local, cnt); + +@@ -1394,11 +1550,7 @@ + FREEBUF(pid_hash); + FREEBUF(nodebuf); + +- if (DUMPFILE()) { +- fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" : +- "\r \r"); +- fflush(fp); +- } ++ please_wait_done(); + + if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) + refresh_context(curtask, curpid); +@@ -1406,138 +1558,738 @@ + tt->retries = MAX(tt->retries, retries); + } + +- + /* +- * Fill a task_context structure with the data from a task. If a NULL +- * task_context pointer is passed in, use the next available one. ++ * 2.6.17 replaced: ++ * static struct hlist_head *pid_hash[PIDTYPE_MAX]; ++ * with ++ * static struct hlist_head *pid_hash; + */ +-static struct task_context * +-store_context(struct task_context *tc, ulong task, char *tp) ++static void ++refresh_hlist_task_table_v2(void) + { +- pid_t *pid_addr; +- char *comm_addr; +- int *processor_addr; +- ulong *parent_addr; +- ulong *mm_addr; +- int has_cpu; +- int do_verify; +- +- if (tt->refresh_task_table == refresh_fixed_task_table) +- do_verify = 1; +- else if (tt->refresh_task_table == refresh_pid_hash_task_table) +- do_verify = 2; +- else +- do_verify = 0; ++ int i; ++ ulong *pid_hash; ++ ulong pidhash_array; ++ ulong kpp; ++ char *tp; ++ ulong next, pnext, pprev; ++ char *nodebuf; ++ int len, cnt; ++ struct task_context *tc; ++ ulong curtask; ++ ulong curpid; ++ ulong retries; ++ ulong *tlp; + +- if (!tc) +- tc = tt->context_array + tt->running_tasks; ++ if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */ ++ return; + +- pid_addr = (pid_t *)(tp + OFFSET(task_struct_pid)); +- comm_addr = (char *)(tp + OFFSET(task_struct_comm)); +- if (tt->flags & THREAD_INFO) { +- tc->thread_info = ULONG(tp + OFFSET(task_struct_thread_info)); +- fill_thread_info(tc->thread_info); +- processor_addr = (int *) (tt->thread_info + +- OFFSET(thread_info_cpu)); +- } else if (VALID_MEMBER(task_struct_processor)) +- processor_addr = (int *) (tp + OFFSET(task_struct_processor)); +- else if (VALID_MEMBER(task_struct_cpu)) +- processor_addr = (int *) (tp + OFFSET(task_struct_cpu)); +- if (VALID_MEMBER(task_struct_p_pptr)) +- parent_addr = (ulong *)(tp + OFFSET(task_struct_p_pptr)); +- else +- parent_addr = (ulong *)(tp + OFFSET(task_struct_parent)); +- mm_addr = (ulong *)(tp + OFFSET(task_struct_mm)); +- has_cpu = task_has_cpu(task, tp); ++ if (DUMPFILE()) { /* impossible */ ++ please_wait("gathering task table data"); ++ if (!symbol_exists("panic_threads")) ++ tt->flags |= POPULATE_PANIC; ++ } + +- tc->pid = (ulong)(*pid_addr); +- BCOPY(comm_addr, &tc->comm[0], 16); +- tc->comm[16] = NULLCHAR; +- tc->processor = *processor_addr; +- tc->ptask = *parent_addr; +- tc->mm_struct = *mm_addr; +- tc->task = task; +- tc->tc_next = NULL; ++ if (ACTIVE() && !(tt->flags & TASK_REFRESH)) ++ return; + +- if (do_verify && !verify_task(tc, do_verify)) { +- error(INFO, "invalid task address: %lx\n", tc->task); +- BZERO(tc, sizeof(struct task_context)); +- return NULL; ++ /* ++ * The current task's task_context entry may change, ++ * or the task may not even exist anymore. ++ */ ++ if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) { ++ curtask = CURRENT_TASK(); ++ curpid = CURRENT_PID(); + } + +- if (has_cpu && (tt->flags & POPULATE_PANIC)) +- tt->panic_threads[tc->processor] = tc->task; +- +- return tc; +-} ++ get_symbol_data("pid_hash", sizeof(void *), &pidhash_array); + +-/* +- * The current context may have moved to a new spot in the task table +- * or have exited since the last command. If it still exists, reset its +- * new position. If it doesn't exist, set the context back to the initial +- * crash context. If necessary, complain and show the restored context. +- */ +-static void +-refresh_context(ulong curtask, ulong curpid) +-{ +- ulong value, complain; +- struct task_context *tc; ++ len = tt->pidhash_len; ++ pid_hash = (ulong *)GETBUF(len * SIZE(hlist_head)); ++ nodebuf = GETBUF(SIZE(pid_link)); ++ retries = 0; + +- if (task_exists(curtask) && pid_exists(curpid)) { +- set_context(curtask, NO_PID); +- } else { +- set_context(tt->this_task, NO_PID); ++retry_pid_hash: ++ if (retries && DUMPFILE()) ++ error(FATAL, ++ "\ncannot gather a stable task list via pid_hash\n"); + +- complain = TRUE; +- if (STREQ(args[0], "set") && (argcnt == 2) && +- IS_A_NUMBER(args[1])) { ++ if ((retries == MAX_UNLIMITED_TASK_RETRIES) && ++ !(tt->flags & TASK_INIT_DONE)) ++ error(FATAL, ++ "\ncannot gather a stable task list via pid_hash (%d retries)\n", ++ retries); + +- switch (str_to_context(args[optind], &value, &tc)) +- { +- case STR_PID: +- case STR_TASK: +- complain = FALSE; +- break; +- case STR_INVALID: +- complain = TRUE; +- break; +- } +- } ++ if (!readmem(pidhash_array, KVADDR, pid_hash, ++ len * SIZE(hlist_head), "pid_hash contents", RETURN_ON_ERROR)) ++ error(FATAL, "\ncannot read pid_hash array\n"); + +- if (complain) { +- error(INFO, "current context no longer exists -- " +- "restoring \"%s\" context:\n\n", +- pc->program_name); +- show_context(CURRENT_CONTEXT()); +- fprintf(fp, "\n"); +- } ++ if (!hq_open()) { ++ error(INFO, "cannot hash task_struct entries\n"); ++ if (!(tt->flags & TASK_INIT_DONE)) ++ clean_exit(1); ++ error(INFO, "using stale task_structs\n"); ++ FREEBUF(pid_hash); ++ return; + } +-} + +-/* +- * Sort the task_context array by PID number; for PID 0, sort by processor. +- */ +-void +-sort_context_array(void) +-{ +- ulong curtask; ++ /* ++ * Get the idle threads first. ++ */ ++ cnt = 0; ++ for (i = 0; i < kt->cpus; i++) { ++ if (hq_enter(tt->idle_threads[i])) ++ cnt++; ++ else ++ error(WARNING, "%sduplicate idle tasks?\n", ++ DUMPFILE() ? "\n" : ""); ++ } + +- curtask = CURRENT_TASK(); +- qsort((void *)tt->context_array, (size_t)tt->running_tasks, +- sizeof(struct task_context), sort_by_pid); +- set_context(curtask, NO_PID); +-} ++ for (i = 0; i < len; i++) { ++ if (!pid_hash[i]) ++ continue; + +-static int +-sort_by_pid(const void *arg1, const void *arg2) +-{ +- struct task_context *t1, *t2; ++ if (!readmem(pid_hash[i], KVADDR, nodebuf, ++ SIZE(pid_link), "pid_hash node pid_link", RETURN_ON_ERROR|QUIET)) { ++ error(INFO, "\ncannot read pid_hash node pid_link\n"); ++ if (DUMPFILE()) ++ continue; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ } + +- t1 = (struct task_context *)arg1; +- t2 = (struct task_context *)arg2; ++ kpp = pid_hash[i]; ++ next = ULONG(nodebuf + OFFSET(pid_link_pid)); ++ if (next) ++ next -= OFFSET(task_struct_pids); ++ pnext = ULONG(nodebuf + OFFSET(hlist_node_next)); ++ pprev = ULONG(nodebuf + OFFSET(hlist_node_pprev)); + +- if ((t1->pid == 0) && (t2->pid == 0)) +- return (t1->processor < t2->processor ? -1 : ++ if (CRASHDEBUG(1)) ++ console("pid_hash[%d]: %lx task: %lx (node: %lx) next: %lx pprev: %lx\n", ++ i, pid_hash[i], next, kpp, pnext, pprev); ++ ++ while (next) { ++ if (!IS_TASK_ADDR(next)) { ++ error(INFO, ++ "%sinvalid task address in pid_hash: %lx\n", ++ DUMPFILE() ? "\n" : "", next); ++ if (DUMPFILE()) ++ break; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ ++ } ++ ++ if (!is_idle_thread(next) && !hq_enter(next)) { ++ error(INFO, ++ "%sduplicate task in pid_hash: %lx\n", ++ DUMPFILE() ? "\n" : "", next); ++ if (DUMPFILE()) ++ break; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ cnt++; ++ ++ if (!pnext) ++ break; ++ ++ if (!readmem((ulonglong)pnext, KVADDR, nodebuf, ++ SIZE(pid_link), "task hlist_node pid_link", RETURN_ON_ERROR|QUIET)) { ++ error(INFO, "\ncannot read hlist_node pid_link from node next\n"); ++ if (DUMPFILE()) ++ break; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ kpp = (ulong)pnext; ++ next = ULONG(nodebuf + OFFSET(pid_link_pid)); ++ if (next) ++ next -= OFFSET(task_struct_pids); ++ pnext = ULONG(nodebuf + OFFSET(hlist_node_next)); ++ pprev = ULONG(nodebuf + OFFSET(hlist_node_pprev)); ++ ++ if (CRASHDEBUG(1)) ++ console(" chained task: %lx (node: %lx) next: %lx pprev: %lx\n", ++ next, kpp, pnext, pprev); ++ } ++ } ++ ++ if (cnt > tt->max_tasks) { ++ tt->max_tasks = cnt + TASK_SLUSH; ++ allocate_task_space(tt->max_tasks); ++ hq_close(); ++ if (!DUMPFILE()) ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ BZERO(tt->task_local, tt->max_tasks * sizeof(void *)); ++ cnt = retrieve_list((ulong *)tt->task_local, cnt); ++ ++ hq_close(); ++ ++ clear_task_cache(); ++ ++ for (i = 0, tlp = (ulong *)tt->task_local, ++ tt->running_tasks = 0, tc = tt->context_array; ++ i < tt->max_tasks; i++, tlp++) { ++ if (!(*tlp)) ++ continue; ++ ++ if (!IS_TASK_ADDR(*tlp)) { ++ error(WARNING, ++ "%sinvalid task address found in task list: %lx\n", ++ DUMPFILE() ? "\n" : "", *tlp); ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (task_exists(*tlp)) { ++ error(WARNING, ++ "%sduplicate task address found in task list: %lx\n", ++ DUMPFILE() ? "\n" : "", *tlp); ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (!(tp = fill_task_struct(*tlp))) { ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (store_context(tc, *tlp, tp)) { ++ tc++; ++ tt->running_tasks++; ++ } ++ } ++ ++ FREEBUF(pid_hash); ++ FREEBUF(nodebuf); ++ ++ please_wait_done(); ++ ++ if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) ++ refresh_context(curtask, curpid); ++ ++ tt->retries = MAX(tt->retries, retries); ++} ++ ++ ++/* ++ * 2.6.24: The pid_hash[] hlist_head entries were changed to point ++ * to the hlist_node structure embedded in a upid structure. ++ */ ++static void ++refresh_hlist_task_table_v3(void) ++{ ++ int i; ++ ulong *pid_hash; ++ ulong pidhash_array; ++ ulong kpp; ++ char *tp; ++ ulong next, pnext, pprev; ++ ulong upid; ++ char *nodebuf; ++ int len, cnt; ++ struct task_context *tc; ++ ulong curtask; ++ ulong curpid; ++ ulong retries; ++ ulong *tlp; ++ uint upid_nr; ++ ulong upid_ns; ++ int chained; ++ ulong pid; ++ ulong pid_tasks_0; ++ ++ if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */ ++ return; ++ ++ if (DUMPFILE()) { /* impossible */ ++ please_wait("gathering task table data"); ++ if (!symbol_exists("panic_threads")) ++ tt->flags |= POPULATE_PANIC; ++ } ++ ++ if (ACTIVE() && !(tt->flags & TASK_REFRESH)) ++ return; ++ ++ /* ++ * The current task's task_context entry may change, ++ * or the task may not even exist anymore. ++ */ ++ if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) { ++ curtask = CURRENT_TASK(); ++ curpid = CURRENT_PID(); ++ } ++ ++ get_symbol_data("pid_hash", sizeof(void *), &pidhash_array); ++ ++ len = tt->pidhash_len; ++ pid_hash = (ulong *)GETBUF(len * SIZE(hlist_head)); ++ nodebuf = GETBUF(SIZE(upid)); ++ retries = 0; ++ ++retry_pid_hash: ++ if (retries && DUMPFILE()) ++ error(FATAL, ++ "\ncannot gather a stable task list via pid_hash\n"); ++ ++ if ((retries == MAX_UNLIMITED_TASK_RETRIES) && ++ !(tt->flags & TASK_INIT_DONE)) ++ error(FATAL, ++ "\ncannot gather a stable task list via pid_hash (%d retries)\n", ++ retries); ++ ++ if (!readmem(pidhash_array, KVADDR, pid_hash, ++ len * SIZE(hlist_head), "pid_hash contents", RETURN_ON_ERROR)) ++ error(FATAL, "\ncannot read pid_hash array\n"); ++ ++ if (!hq_open()) { ++ error(INFO, "cannot hash task_struct entries\n"); ++ if (!(tt->flags & TASK_INIT_DONE)) ++ clean_exit(1); ++ error(INFO, "using stale task_structs\n"); ++ FREEBUF(pid_hash); ++ return; ++ } ++ ++ /* ++ * Get the idle threads first. ++ */ ++ cnt = 0; ++ for (i = 0; i < kt->cpus; i++) { ++ if (hq_enter(tt->idle_threads[i])) ++ cnt++; ++ else ++ error(WARNING, "%sduplicate idle tasks?\n", ++ DUMPFILE() ? "\n" : ""); ++ } ++ ++ for (i = 0; i < len; i++) { ++ if (!pid_hash[i]) ++ continue; ++ ++ kpp = pid_hash[i]; ++ upid = pid_hash[i] - OFFSET(upid_pid_chain); ++ chained = 0; ++do_chained: ++ if (!readmem(upid, KVADDR, nodebuf, SIZE(upid), ++ "pid_hash upid", RETURN_ON_ERROR|QUIET)) { ++ error(INFO, "\ncannot read pid_hash upid\n"); ++ if (DUMPFILE()) ++ continue; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ pnext = ULONG(nodebuf + OFFSET(upid_pid_chain) + OFFSET(hlist_node_next)); ++ pprev = ULONG(nodebuf + OFFSET(upid_pid_chain) + OFFSET(hlist_node_pprev)); ++ upid_nr = UINT(nodebuf + OFFSET(upid_nr)); ++ upid_ns = ULONG(nodebuf + OFFSET(upid_ns)); ++ /* ++ * Use init_pid_ns level 0 (PIDTYPE_PID). ++ */ ++ if (upid_ns != tt->init_pid_ns) ++ continue; ++ ++ pid = upid - OFFSET(pid_numbers); ++ ++ if (!readmem(pid + OFFSET(pid_tasks), KVADDR, &pid_tasks_0, ++ sizeof(void *), "pid tasks", RETURN_ON_ERROR|QUIET)) { ++ error(INFO, "\ncannot read pid.tasks[0]\n"); ++ if (DUMPFILE()) ++ continue; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (pid_tasks_0 == 0) ++ continue; ++ ++ next = pid_tasks_0 - OFFSET(task_struct_pids); ++ ++ if (CRASHDEBUG(1)) { ++ if (chained) ++ console(" %lx upid: %lx nr: %d pid: %lx\n" ++ " pnext/pprev: %.*lx/%lx task: %lx\n", ++ kpp, upid, upid_nr, pid, VADDR_PRLEN, pnext, pprev, next); ++ else ++ console("pid_hash[%4d]: %lx upid: %lx nr: %d pid: %lx\n" ++ " pnext/pprev: %.*lx/%lx task: %lx\n", ++ i, kpp, upid, upid_nr, pid, VADDR_PRLEN, pnext, pprev, next); ++ } ++ ++ if (!IS_TASK_ADDR(next)) { ++ error(INFO, "%sinvalid task address in pid_hash: %lx\n", ++ DUMPFILE() ? "\n" : "", next); ++ if (DUMPFILE()) ++ break; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (!is_idle_thread(next) && !hq_enter(next)) { ++ error(INFO, "%sduplicate task in pid_hash: %lx\n", ++ DUMPFILE() ? "\n" : "", next); ++ if (DUMPFILE()) ++ break; ++ hq_close(); ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ cnt++; ++ ++ if (pnext) { ++ kpp = pnext; ++ upid = pnext - OFFSET(upid_pid_chain); ++ chained++; ++ goto do_chained; ++ } ++ } ++ ++ if (cnt > tt->max_tasks) { ++ tt->max_tasks = cnt + TASK_SLUSH; ++ allocate_task_space(tt->max_tasks); ++ hq_close(); ++ if (!DUMPFILE()) ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ BZERO(tt->task_local, tt->max_tasks * sizeof(void *)); ++ cnt = retrieve_list((ulong *)tt->task_local, cnt); ++ ++ hq_close(); ++ ++ clear_task_cache(); ++ ++ for (i = 0, tlp = (ulong *)tt->task_local, ++ tt->running_tasks = 0, tc = tt->context_array; ++ i < tt->max_tasks; i++, tlp++) { ++ if (!(*tlp)) ++ continue; ++ ++ if (!IS_TASK_ADDR(*tlp)) { ++ error(WARNING, ++ "%sinvalid task address found in task list: %lx\n", ++ DUMPFILE() ? "\n" : "", *tlp); ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (task_exists(*tlp)) { ++ error(WARNING, ++ "%sduplicate task address found in task list: %lx\n", ++ DUMPFILE() ? "\n" : "", *tlp); ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (!(tp = fill_task_struct(*tlp))) { ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_pid_hash; ++ } ++ ++ if (store_context(tc, *tlp, tp)) { ++ tc++; ++ tt->running_tasks++; ++ } ++ } ++ ++ FREEBUF(pid_hash); ++ FREEBUF(nodebuf); ++ ++ please_wait_done(); ++ ++ if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) ++ refresh_context(curtask, curpid); ++ ++ tt->retries = MAX(tt->retries, retries); ++} ++ ++static void ++refresh_active_task_table(void) ++{ ++ int i; ++ char *tp; ++ int cnt; ++ struct task_context *tc; ++ ulong curtask; ++ ulong curpid; ++ ulong retries; ++ ulong *tlp; ++ ++ if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */ ++ return; ++ ++ if (DUMPFILE()) { ++ please_wait("gathering task table data"); ++ if (!symbol_exists("panic_threads")) ++ tt->flags |= POPULATE_PANIC; ++ } ++ ++ if (ACTIVE() && !(tt->flags & TASK_REFRESH)) ++ return; ++ ++ get_active_set(); ++ /* ++ * The current task's task_context entry may change, ++ * or the task may not even exist anymore. ++ */ ++ if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) { ++ curtask = CURRENT_TASK(); ++ curpid = CURRENT_PID(); ++ } ++ ++retry_active: ++ ++ if (!hq_open()) { ++ error(INFO, "cannot hash task_struct entries\n"); ++ if (!(tt->flags & TASK_INIT_DONE)) ++ clean_exit(1); ++ error(INFO, "using stale task_structs\n"); ++ return; ++ } ++ ++ /* ++ * Get the active tasks. ++ */ ++ cnt = 0; ++ for (i = 0; i < kt->cpus; i++) { ++ if (hq_enter(tt->active_set[i])) ++ cnt++; ++ else ++ error(WARNING, "%sduplicate active tasks?\n", ++ DUMPFILE() ? "\n" : ""); ++ } ++ ++ BZERO(tt->task_local, tt->max_tasks * sizeof(void *)); ++ cnt = retrieve_list((ulong *)tt->task_local, cnt); ++ ++ hq_close(); ++ ++ clear_task_cache(); ++ ++ for (i = 0, tlp = (ulong *)tt->task_local, ++ tt->running_tasks = 0, tc = tt->context_array; ++ i < tt->max_tasks; i++, tlp++) { ++ if (!(*tlp)) ++ continue; ++ ++ if (!IS_TASK_ADDR(*tlp)) { ++ error(WARNING, ++ "%sinvalid task address found in task list: %lx\n", ++ DUMPFILE() ? "\n" : "", *tlp); ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_active; ++ } ++ ++ if (task_exists(*tlp)) { ++ error(WARNING, ++ "%sduplicate task address found in task list: %lx\n", ++ DUMPFILE() ? "\n" : "", *tlp); ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_active; ++ } ++ ++ if (!(tp = fill_task_struct(*tlp))) { ++ if (DUMPFILE()) ++ continue; ++ retries++; ++ goto retry_active; ++ } ++ ++ if (store_context(tc, *tlp, tp)) { ++ tc++; ++ tt->running_tasks++; ++ } else if (DUMPFILE()) ++ error(WARNING, "corrupt/invalid active task: %lx\n", ++ *tlp); ++ } ++ ++ if (!tt->running_tasks) { ++ if (DUMPFILE()) ++ error(FATAL, "cannot determine any active tasks!\n"); ++ retries++; ++ goto retry_active; ++ } ++ ++ please_wait_done(); ++ ++ if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) ++ refresh_context(curtask, curpid); ++ ++ tt->retries = MAX(tt->retries, retries); ++} ++ ++/* ++ * Fill a task_context structure with the data from a task. If a NULL ++ * task_context pointer is passed in, use the next available one. ++ */ ++static struct task_context * ++store_context(struct task_context *tc, ulong task, char *tp) ++{ ++ pid_t *pid_addr; ++ char *comm_addr; ++ int *processor_addr; ++ ulong *parent_addr; ++ ulong *mm_addr; ++ int has_cpu; ++ int do_verify; ++ ++ if (tt->refresh_task_table == refresh_fixed_task_table) ++ do_verify = 1; ++ else if (tt->refresh_task_table == refresh_pid_hash_task_table) ++ do_verify = 2; ++ else if (tt->refresh_task_table == refresh_hlist_task_table) ++ do_verify = 2; ++ else if (tt->refresh_task_table == refresh_hlist_task_table_v2) ++ do_verify = 2; ++ else if (tt->refresh_task_table == refresh_hlist_task_table_v3) ++ do_verify = 2; ++ else if (tt->refresh_task_table == refresh_active_task_table) ++ do_verify = 2; ++ else ++ do_verify = 0; ++ ++ if (!tc) ++ tc = tt->context_array + tt->running_tasks; ++ ++ pid_addr = (pid_t *)(tp + OFFSET(task_struct_pid)); ++ comm_addr = (char *)(tp + OFFSET(task_struct_comm)); ++ if (tt->flags & THREAD_INFO) { ++ tc->thread_info = ULONG(tp + OFFSET(task_struct_thread_info)); ++ fill_thread_info(tc->thread_info); ++ processor_addr = (int *) (tt->thread_info + ++ OFFSET(thread_info_cpu)); ++ } else if (VALID_MEMBER(task_struct_processor)) ++ processor_addr = (int *) (tp + OFFSET(task_struct_processor)); ++ else if (VALID_MEMBER(task_struct_cpu)) ++ processor_addr = (int *) (tp + OFFSET(task_struct_cpu)); ++ if (VALID_MEMBER(task_struct_p_pptr)) ++ parent_addr = (ulong *)(tp + OFFSET(task_struct_p_pptr)); ++ else ++ parent_addr = (ulong *)(tp + OFFSET(task_struct_parent)); ++ mm_addr = (ulong *)(tp + OFFSET(task_struct_mm)); ++ has_cpu = task_has_cpu(task, tp); ++ ++ tc->pid = (ulong)(*pid_addr); ++ BCOPY(comm_addr, &tc->comm[0], 16); ++ tc->comm[16] = NULLCHAR; ++ tc->processor = *processor_addr; ++ tc->ptask = *parent_addr; ++ tc->mm_struct = *mm_addr; ++ tc->task = task; ++ tc->tc_next = NULL; ++ ++ if (do_verify && !verify_task(tc, do_verify)) { ++ error(INFO, "invalid task address: %lx\n", tc->task); ++ BZERO(tc, sizeof(struct task_context)); ++ return NULL; ++ } ++ ++ if (has_cpu && (tt->flags & POPULATE_PANIC)) ++ tt->panic_threads[tc->processor] = tc->task; ++ ++ return tc; ++} ++ ++/* ++ * The current context may have moved to a new spot in the task table ++ * or have exited since the last command. If it still exists, reset its ++ * new position. If it doesn't exist, set the context back to the initial ++ * crash context. If necessary, complain and show the restored context. ++ */ ++static void ++refresh_context(ulong curtask, ulong curpid) ++{ ++ ulong value, complain; ++ struct task_context *tc; ++ ++ if (task_exists(curtask) && pid_exists(curpid)) { ++ set_context(curtask, NO_PID); ++ } else { ++ set_context(tt->this_task, NO_PID); ++ ++ complain = TRUE; ++ if (STREQ(args[0], "set") && (argcnt == 2) && ++ IS_A_NUMBER(args[1])) { ++ ++ switch (str_to_context(args[optind], &value, &tc)) ++ { ++ case STR_PID: ++ case STR_TASK: ++ complain = FALSE; ++ break; ++ case STR_INVALID: ++ complain = TRUE; ++ break; ++ } ++ } ++ ++ if (complain) { ++ error(INFO, "current context no longer exists -- " ++ "restoring \"%s\" context:\n\n", ++ pc->program_name); ++ show_context(CURRENT_CONTEXT()); ++ fprintf(fp, "\n"); ++ } ++ } ++} ++ ++/* ++ * Sort the task_context array by PID number; for PID 0, sort by processor. ++ */ ++void ++sort_context_array(void) ++{ ++ ulong curtask; ++ ++ curtask = CURRENT_TASK(); ++ qsort((void *)tt->context_array, (size_t)tt->running_tasks, ++ sizeof(struct task_context), sort_by_pid); ++ set_context(curtask, NO_PID); ++} ++ ++static int ++sort_by_pid(const void *arg1, const void *arg2) ++{ ++ struct task_context *t1, *t2; ++ ++ t1 = (struct task_context *)arg1; ++ t2 = (struct task_context *)arg2; ++ ++ if ((t1->pid == 0) && (t2->pid == 0)) ++ return (t1->processor < t2->processor ? -1 : + t1->processor == t2->processor ? 0 : 1); + else + return (t1->pid < t2->pid ? -1 : +@@ -1581,6 +2333,9 @@ + char * + fill_task_struct(ulong task) + { ++ if (XEN_HYPER_MODE()) ++ return NULL; ++ + if (!IS_LAST_TASK_READ(task)) { + if (!readmem(task, KVADDR, tt->task_struct, + SIZE(task_struct), "fill_task_struct", +@@ -1632,6 +2387,9 @@ + bt->stackbase); + } + ++ if (XEN_HYPER_MODE()) ++ return; ++ + if (!IS_LAST_TASK_READ(bt->task)) { + if (bt->stackbase == bt->task) { + BCOPY(bt->stackbuf, tt->task_struct, SIZE(task_struct)); +@@ -1893,7 +2651,7 @@ + BZERO(&psinfo, sizeof(struct psinfo)); + flag = 0; + +- while ((c = getopt(argcnt, args, "stcpkul")) != EOF) { ++ while ((c = getopt(argcnt, args, "gstcpkular")) != EOF) { + switch(c) + { + case 'k': +@@ -1907,39 +2665,55 @@ + break; + + /* +- * The remaining flags are all mutually-exclusive. ++ * The a, t, c, p, g and l flags are all mutually-exclusive. + */ ++ case 'g': ++ flag &= ~(PS_EXCLUSIVE); ++ flag |= PS_TGID_LIST; ++ break; ++ ++ case 'a': ++ flag &= ~(PS_EXCLUSIVE); ++ flag |= PS_ARGV_ENVP; ++ break; ++ + case 't': ++ flag &= ~(PS_EXCLUSIVE); + flag |= PS_TIMES; +- flag &= ~(PS_CHILD_LIST|PS_PPID_LIST|PS_LAST_RUN); + break; + + case 'c': ++ flag &= ~(PS_EXCLUSIVE); + flag |= PS_CHILD_LIST; +- flag &= ~(PS_PPID_LIST|PS_TIMES|PS_LAST_RUN); + break; + + case 'p': ++ flag &= ~(PS_EXCLUSIVE); + flag |= PS_PPID_LIST; +- flag &= ~(PS_CHILD_LIST|PS_TIMES|PS_LAST_RUN); + break; + + case 'l': + if (INVALID_MEMBER(task_struct_last_run) && +- INVALID_MEMBER(task_struct_timestamp)) { ++ INVALID_MEMBER(task_struct_timestamp) && ++ INVALID_MEMBER(sched_info_last_arrival)) { + error(INFO, +-"neither task_struct.last_run nor task_struct.timestamp exist in this kernel\n"); ++ "last-run timestamps do not exist in this kernel\n"); + argerrs++; + break; + } ++ flag &= ~(PS_EXCLUSIVE); + flag |= PS_LAST_RUN; +- flag &= ~(PS_CHILD_LIST|PS_TIMES|PS_PPID_LIST); + break; + + case 's': + flag |= PS_KSTACKP; + break; + ++ case 'r': ++ flag &= ~(PS_EXCLUSIVE); ++ flag |= PS_RLIMIT; ++ break; ++ + default: + argerrs++; + break; +@@ -2020,6 +2794,18 @@ + show_last_run(tc); \ + continue; \ + } \ ++ if (flag & PS_ARGV_ENVP) { \ ++ show_task_args(tc); \ ++ continue; \ ++ } \ ++ if (flag & PS_RLIMIT) { \ ++ show_task_rlimit(tc); \ ++ continue; \ ++ } \ ++ if (flag & PS_TGID_LIST) { \ ++ show_tgid_list(tc->task); \ ++ continue; \ ++ } \ + get_task_mem_usage(tc->task, tm); \ + fprintf(fp, "%s", is_task_active(tc->task) ? "> " : " "); \ + fprintf(fp, "%5ld %5ld %2s %s %3s", \ +@@ -2050,7 +2836,7 @@ + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + +- if (!(flag & (PS_PPID_LIST|PS_CHILD_LIST|PS_TIMES|PS_LAST_RUN))) ++ if (!(flag & PS_EXCLUSIVE)) + fprintf(fp, + " PID PPID CPU %s ST %%MEM VSZ RSS COMM\n", + flag & PS_KSTACKP ? +@@ -2076,6 +2862,8 @@ + return; + } + ++ pc->curcmd_flags |= TASK_SPECIFIED; ++ + for (ac = 0; ac < psi->argc; ac++) { + tm = &task_mem_usage; + tc = FIRST_CONTEXT(); +@@ -2095,53 +2883,283 @@ + print = TRUE; + break; + +- case PS_BY_CMD: +- if (STREQ(tc->comm, psi->comm[ac])) +- print = TRUE; +- break; +- } ++ case PS_BY_CMD: ++ if (STREQ(tc->comm, psi->comm[ac])) { ++ if (flag & PS_TGID_LIST) { ++ if (tc->pid == task_tgid(tc->task)) ++ print = TRUE; ++ else ++ print = FALSE; ++ } else ++ print = TRUE; ++ } ++ break; ++ } ++ ++ if (print) { ++ if (flag & PS_TIMES) ++ show_task_times(tc, flag); ++ else if (flag & PS_LAST_RUN) ++ show_last_run(tc); ++ else { ++ SHOW_PS_DATA(); ++ } ++ } ++ } ++ } ++} ++ ++/* ++ * Display the task preceded by the last_run stamp. ++ */ ++static void ++show_last_run(struct task_context *tc) ++{ ++ int i, c; ++ struct task_context *tcp; ++ char format[15]; ++ char buf[BUFSIZE]; ++ ++ tcp = FIRST_CONTEXT(); ++ sprintf(buf, pc->output_radix == 10 ? "%lld" : "%llx", ++ task_last_run(tcp->task)); ++ c = strlen(buf); ++ sprintf(format, "[%c%dll%c] ", '%', c, ++ pc->output_radix == 10 ? 'u' : 'x'); ++ ++ if (tc) { ++ fprintf(fp, format, task_last_run(tc->task)); ++ print_task_header(fp, tc, FALSE); ++ } else { ++ tcp = FIRST_CONTEXT(); ++ for (i = 0; i < RUNNING_TASKS(); i++, tcp++) { ++ fprintf(fp, format, task_last_run(tcp->task)); ++ print_task_header(fp, tcp, FALSE); ++ } ++ } ++} ++ ++/* ++ * Show the argv and envp strings pointed to by mm_struct->arg_start ++ * and mm_struct->env_start. The user addresses need to broken up ++ * into physical on a page-per-page basis because we typically are ++ * not going to be working in the context of the target task. ++ */ ++static void ++show_task_args(struct task_context *tc) ++{ ++ ulong arg_start, arg_end, env_start, env_end; ++ char *buf, *bufptr, *p1; ++ char *as, *ae, *es, *ee; ++ physaddr_t paddr; ++ ulong uvaddr, size, cnt; ++ int c, d; ++ ++ print_task_header(fp, tc, 0); ++ ++ if (!tc || !tc->mm_struct) { /* probably a kernel thread */ ++ error(INFO, "no user stack\n\n"); ++ return; ++ } ++ ++ if (!task_mm(tc->task, TRUE)) ++ return; ++ ++ if (INVALID_MEMBER(mm_struct_arg_start)) { ++ MEMBER_OFFSET_INIT(mm_struct_arg_start, "mm_struct", "arg_start"); ++ MEMBER_OFFSET_INIT(mm_struct_arg_end, "mm_struct", "arg_end"); ++ MEMBER_OFFSET_INIT(mm_struct_env_start, "mm_struct", "env_start"); ++ MEMBER_OFFSET_INIT(mm_struct_env_end, "mm_struct", "env_end"); ++ } ++ ++ arg_start = ULONG(tt->mm_struct + OFFSET(mm_struct_arg_start)); ++ arg_end = ULONG(tt->mm_struct + OFFSET(mm_struct_arg_end)); ++ env_start = ULONG(tt->mm_struct + OFFSET(mm_struct_env_start)); ++ env_end = ULONG(tt->mm_struct + OFFSET(mm_struct_env_end)); ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "arg_start: %lx arg_end: %lx (%ld)\n", ++ arg_start, arg_end, arg_end - arg_start); ++ fprintf(fp, "env_start: %lx env_end: %lx (%ld)\n", ++ env_start, env_end, env_end - env_start); ++ } ++ ++ buf = GETBUF(env_end - arg_start + 1); ++ ++ uvaddr = arg_start; ++ size = env_end - arg_start; ++ bufptr = buf; ++ ++ while (size > 0) { ++ if (!uvtop(tc, uvaddr, &paddr, 0)) { ++ error(INFO, "cannot access user stack address: %lx\n\n", ++ uvaddr); ++ goto bailout; ++ } ++ ++ cnt = PAGESIZE() - PAGEOFFSET(uvaddr); + +- if (print) { +- if (flag & PS_TIMES) +- show_task_times(tc, flag); +- else if (flag & PS_LAST_RUN) +- show_last_run(tc); +- else { +- SHOW_PS_DATA(); +- } +- } ++ if (cnt > size) ++ cnt = size; ++ ++ if (!readmem(paddr, PHYSADDR, bufptr, cnt, ++ "user stack contents", RETURN_ON_ERROR|QUIET)) { ++ error(INFO, "cannot access user stack address: %lx\n\n", ++ uvaddr); ++ goto bailout; ++ } ++ ++ uvaddr += cnt; ++ bufptr += cnt; ++ size -= cnt; ++ } ++ ++ as = buf; ++ ae = &buf[arg_end - arg_start]; ++ es = &buf[env_start - arg_start]; ++ ee = &buf[env_end - arg_start]; ++ ++ fprintf(fp, "ARG: "); ++ for (p1 = as, c = 0; p1 < ae; p1++) { ++ if (*p1 == NULLCHAR) { ++ if (c) ++ fprintf(fp, " "); ++ c = 0; ++ } else { ++ fprintf(fp, "%c", *p1); ++ c++; ++ } ++ } ++ ++ fprintf(fp, "\nENV: "); ++ for (p1 = es, c = d = 0; p1 < ee; p1++) { ++ if (*p1 == NULLCHAR) { ++ if (c) ++ fprintf(fp, "\n"); ++ c = 0; ++ } else { ++ fprintf(fp, "%s%c", !c && (p1 != es) ? " " : "", *p1); ++ c++, d++; + } + } ++ fprintf(fp, "\n%s", d ? "" : "\n"); ++ ++bailout: ++ FREEBUF(buf); + } + ++char *rlim_names[] = { ++ /* 0 */ "CPU", ++ /* 1 */ "FSIZE", ++ /* 2 */ "DATA", ++ /* 3 */ "STACK", ++ /* 4 */ "CORE", ++ /* 5 */ "RSS", ++ /* 6 */ "NPROC", ++ /* 7 */ "NOFILE", ++ /* 8 */ "MEMLOCK", ++ /* 9 */ "AS", ++ /* 10 */ "LOCKS", ++ /* 11 */ "SIGPENDING", ++ /* 12 */ "MSGQUEUE", ++ /* 13 */ "NICE", ++ /* 14 */ "RTPRIO", ++ NULL, ++}; ++ ++#ifndef RLIM_INFINITY ++#define RLIM_INFINITY (~0UL) ++#endif ++ + /* +- * Display the task preceded by the last_run stamp. ++ * Show the current and maximum rlimit values. + */ + static void +-show_last_run(struct task_context *tc) ++show_task_rlimit(struct task_context *tc) + { +- int i, c; +- struct task_context *tcp; +- char format[10]; +- char buf[BUFSIZE]; ++ int i, j, len1, len2, rlimit_index; ++ int in_task_struct, in_signal_struct; ++ char *rlimit_buffer; ++ ulong *p1, rlim_addr; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; + +- tcp = FIRST_CONTEXT(); +- sprintf(buf, pc->output_radix == 10 ? "%lld" : "%llx", +- task_last_run(tcp->task)); +- c = strlen(buf); +- sprintf(format, "[%c%dll%c] ", '%', c, +- pc->output_radix == 10 ? 'u' : 'x'); ++ if (!VALID_MEMBER(task_struct_rlim) && !VALID_MEMBER(signal_struct_rlim)) { ++ MEMBER_OFFSET_INIT(task_struct_rlim, "task_struct", "rlim"); ++ MEMBER_OFFSET_INIT(signal_struct_rlim, "signal_struct", "rlim"); ++ STRUCT_SIZE_INIT(rlimit, "rlimit"); ++ if (!VALID_MEMBER(task_struct_rlim) && ++ !VALID_MEMBER(signal_struct_rlim)) ++ error(FATAL, "cannot determine rlimit array location\n"); ++ } else if (!VALID_STRUCT(rlimit)) ++ error(FATAL, "cannot determine rlimit structure definition\n"); ++ ++ in_task_struct = in_signal_struct = FALSE; ++ ++ if (VALID_MEMBER(task_struct_rlim)) { ++ rlimit_index = get_array_length("task_struct.rlim", NULL, 0); ++ in_task_struct = TRUE; ++ } else if (VALID_MEMBER(signal_struct_rlim)) { ++ if (!VALID_MEMBER(task_struct_signal)) ++ error(FATAL, "cannot determine rlimit array location\n"); ++ rlimit_index = get_array_length("signal_struct.rlim", NULL, 0); ++ in_signal_struct = TRUE; ++ } ++ ++ if (!rlimit_index) ++ error(FATAL, "cannot determine rlimit array size\n"); ++ ++ for (i = len1 = 0; i < rlimit_index; i++) { ++ if ((j = strlen(rlim_names[i])) > len1) ++ len1 = j; ++ } ++ len2 = strlen("(unlimited)"); + +- if (tc) { +- fprintf(fp, format, task_last_run(tc->task)); +- print_task_header(fp, tc, FALSE); +- } else { +- tcp = FIRST_CONTEXT(); +- for (i = 0; i < RUNNING_TASKS(); i++, tcp++) { +- fprintf(fp, format, task_last_run(tcp->task)); +- print_task_header(fp, tcp, FALSE); ++ rlimit_buffer = GETBUF(rlimit_index * SIZE(rlimit)); ++ ++ print_task_header(fp, tc, 0); ++ ++ fill_task_struct(tc->task); ++ ++ if (in_task_struct) { ++ BCOPY(tt->task_struct + OFFSET(task_struct_rlim), ++ rlimit_buffer, rlimit_index * SIZE(rlimit)); ++ } else if (in_signal_struct) { ++ rlim_addr = ULONG(tt->task_struct + OFFSET(task_struct_signal)); ++ if (!readmem(rlim_addr + OFFSET(signal_struct_rlim), ++ KVADDR, rlimit_buffer, rlimit_index * SIZE(rlimit), ++ "signal_struct rlimit array", RETURN_ON_ERROR)) { ++ FREEBUF(rlimit_buffer); ++ return; + } + } ++ ++ fprintf(fp, " %s %s %s\n", ++ mkstring(buf1, len1, RJUST, "RLIMIT"), ++ mkstring(buf2, len2, CENTER|RJUST, "CURRENT"), ++ mkstring(buf3, len2, CENTER|RJUST, "MAXIMUM")); ++ ++ for (p1 = (ulong *)rlimit_buffer, i = 0; i < rlimit_index; i++) { ++ fprintf(fp, " %s ", mkstring(buf1, len1, RJUST, ++ rlim_names[i] ? rlim_names[i] : "(unknown)")); ++ if (*p1 == (ulong)RLIM_INFINITY) ++ fprintf(fp, "(unlimited) "); ++ else ++ fprintf(fp, "%s ", mkstring(buf1, len2, ++ CENTER|LJUST|LONG_DEC, MKSTR(*p1))); ++ p1++; ++ if (*p1 == (ulong)RLIM_INFINITY) ++ fprintf(fp, "(unlimited)\n"); ++ else ++ fprintf(fp, "%s\n", mkstring(buf1, len2, ++ CENTER|LJUST|LONG_DEC, MKSTR(*p1))); ++ p1++; ++ } ++ ++ fprintf(fp, "\n"); ++ ++ FREEBUF(rlimit_buffer); + } + + /* +@@ -2229,11 +3247,8 @@ + + use_kernel_timeval = STRUCT_EXISTS("kernel_timeval"); + get_symbol_data("jiffies", sizeof(long), &jiffies); +- if (symbol_exists("jiffies_64")) { +- get_symbol_data("jiffies_64", sizeof(long long), &jiffies_64); +- if ((jiffies_64 & 0xffffffff00000000ULL) == 0x100000000ULL) +- jiffies_64 &= 0xffffffffULL; +- } ++ if (symbol_exists("jiffies_64")) ++ get_uptime(NULL, &jiffies_64); + tsp = task_start_times; + tc = tcp ? tcp : FIRST_CONTEXT(); + +@@ -2330,8 +3345,7 @@ + for (i = 0, tsp = task_start_times; i < tasks; i++, tsp++) { + print_task_header(fp, tsp->tc, 0); + fprintf(fp, " RUN TIME: %s\n", symbol_exists("jiffies_64") ? +- convert_time(jiffies_64 - +- convert_start_time(tsp->start_time, jiffies_64), buf1) : ++ convert_time(convert_start_time(tsp->start_time, jiffies_64), buf1) : + convert_time(jiffies - tsp->start_time, buf1)); + fprintf(fp, " START TIME: %llu\n", tsp->start_time); + if (VALID_MEMBER(task_struct_times)) { +@@ -2397,15 +3411,33 @@ + static ulonglong + convert_start_time(ulonglong start_time, ulonglong current) + { ++ ulong tmp1, tmp2; ++ ulonglong wrapped; ++ + switch(tt->flags & (TIMESPEC | NO_TIMESPEC)) + { + case TIMESPEC: +- if ((start_time * (ulonglong)machdep->hz) > current) +- return current; ++ if ((start_time * (ulonglong)machdep->hz) > current) ++ return 0; + else +- return start_time * (ulonglong)machdep->hz; ++ return current - (start_time * (ulonglong)machdep->hz); + + case NO_TIMESPEC: ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) { ++ wrapped = (start_time & 0xffffffff00000000ULL); ++ if (wrapped) { ++ wrapped -= 0x100000000ULL; ++ start_time &= 0x00000000ffffffffULL; ++ start_time |= wrapped; ++ start_time += (ulonglong)(300*machdep->hz); ++ } else { ++ tmp1 = (ulong)(uint)(-300*machdep->hz); ++ tmp2 = (ulong)start_time; ++ start_time = (ulonglong)(tmp2 - tmp1); ++ } ++ } ++ break; ++ + default: + break; + } +@@ -2511,6 +3543,54 @@ + } + + /* ++ * Dump the children of a task. ++ */ ++static void ++show_tgid_list(ulong task) ++{ ++ int i; ++ int cnt; ++ struct task_context *tc; ++ ulong tgid; ++ ++ tc = task_to_context(task); ++ tgid = task_tgid(task); ++ ++ if (tc->pid != tgid) { ++ if (pc->curcmd_flags & TASK_SPECIFIED) { ++ if (!(tc = tgid_to_context(tgid))) ++ return; ++ task = tc->task; ++ } else ++ return; ++ } ++ ++ if ((tc->pid == 0) && (pc->curcmd_flags & IDLE_TASK_SHOWN)) ++ return; ++ ++ print_task_header(fp, tc, 0); ++ ++ tc = FIRST_CONTEXT(); ++ for (i = cnt = 0; i < RUNNING_TASKS(); i++, tc++) { ++ if (tc->task == task) ++ continue; ++ ++ if (task_tgid(tc->task) == tgid) { ++ INDENT(2); ++ print_task_header(fp, tc, 0); ++ cnt++; ++ if (tc->pid == 0) ++ pc->curcmd_flags |= IDLE_TASK_SHOWN; ++ } ++ } ++ ++ if (!cnt) ++ fprintf(fp, " (no threads)\n"); ++ ++ fprintf(fp, "\n"); ++} ++ ++/* + * Return the first task found that belongs to a pid. + */ + ulong +@@ -2580,6 +3660,26 @@ + return NULL; + } + ++/* ++ * Return a tgid's parent task_context structure. ++ */ ++struct task_context * ++tgid_to_context(ulong parent_tgid) ++{ ++ int i; ++ struct task_context *tc; ++ ulong tgid; ++ ++ tc = FIRST_CONTEXT(); ++ for (i = 0; i < RUNNING_TASKS(); i++, tc++) { ++ tgid = task_tgid(tc->task); ++ if ((tgid == parent_tgid) && (tgid == tc->pid)) ++ return tc; ++ } ++ ++ return NULL; ++} ++ + + /* + * Return the task_context structure of the first task found with a pid, +@@ -2816,20 +3916,39 @@ + + + /* ++ * Return the task if the vaddr is part of a task's task_struct. ++ */ ++ulong ++vaddr_in_task_struct(ulong vaddr) ++{ ++ int i; ++ struct task_context *tc; ++ ++ tc = FIRST_CONTEXT(); ++ for (i = 0; i < RUNNING_TASKS(); i++, tc++) { ++ if ((vaddr >= tc->task) && ++ (vaddr < (tc->task + SIZE(task_struct)))) ++ return tc->task; ++ } ++ ++ return NO_TASK; ++} ++ ++/* + * Verify whether any task is running a command. + */ + int + comm_exists(char *s) + { +- int i; ++ int i, cnt; + struct task_context *tc; + + tc = FIRST_CONTEXT(); +- for (i = 0; i < RUNNING_TASKS(); i++, tc++) ++ for (i = cnt = 0; i < RUNNING_TASKS(); i++, tc++) + if (STREQ(tc->comm, s)) +- return TRUE; ++ cnt++; + +- return FALSE; ++ return cnt; + } + + /* +@@ -2925,7 +4044,11 @@ + fprintf(fp, "COMMAND: \"%s\"\n", tc->comm); + INDENT(indent); + fprintf(fp, " TASK: %lx ", tc->task); +- if ((cnt = TASKS_PER_PID(tc->pid)) > 1) ++ if ((machdep->flags & (INIT|MCA)) && (tc->pid == 0)) ++ cnt = comm_exists(tc->comm); ++ else ++ cnt = TASKS_PER_PID(tc->pid); ++ if (cnt > 1) + fprintf(fp, "(1 of %d) ", cnt); + if (tt->flags & THREAD_INFO) + fprintf(fp, "[THREAD_INFO: %lx]", tc->thread_info); +@@ -2938,19 +4061,27 @@ + if (is_task_active(tc->task)) { + if (machdep->flags & HWRESET) + fprintf(fp, "(HARDWARE RESET)"); +- else if (machdep->flags & SYSRQ) ++ else if ((pc->flags & SYSRQ) && (tc->task == tt->panic_task)) + fprintf(fp, "(SYSRQ)"); + else if (machdep->flags & INIT) + fprintf(fp, "(INIT)"); +- else if (kt->cpu_flags[tc->processor] & NMI) ++ else if ((machdep->flags & MCA) && (tc->task == tt->panic_task)) ++ fprintf(fp, "(MCA)"); ++ else if ((tc->processor >= 0) && ++ (tc->processor < NR_CPUS) && ++ (kt->cpu_flags[tc->processor] & NMI)) + fprintf(fp, "(NMI)"); ++ else if ((tc->task == tt->panic_task) && ++ XENDUMP_DUMPFILE() && (kt->xen_flags & XEN_SUSPEND)) ++ fprintf(fp, "(SUSPEND)"); + else if (tc->task == tt->panic_task) + fprintf(fp, "(PANIC)"); + else + fprintf(fp, "(ACTIVE)"); + } + +- if (!(pc->flags & RUNTIME) && (tt->flags & PANIC_TASK_NOT_FOUND) && ++ if (!(pc->flags & RUNTIME) && !ACTIVE() && ++ (tt->flags & PANIC_TASK_NOT_FOUND) && + !SYSRQ_TASK(tc->task)) { + fprintf(fp, "\n"); INDENT(indent); + if (machine_type("S390") || machine_type("S390X")) +@@ -3006,6 +4137,10 @@ + cnt++ ? "" : "\n", tc->comm); + break; + } ++ ++ if (!(pc->flags & RUNTIME) && (tt->flags & ACTIVE_ONLY)) ++ error(WARNING, ++ "\nonly the active tasks on each cpu are being tracked\n"); + } + + +@@ -3182,6 +4317,22 @@ + return flags; + } + ++/* ++ * Return a task's tgid. ++ */ ++ulong ++task_tgid(ulong task) ++{ ++ uint tgid; ++ ++ fill_task_struct(task); ++ ++ tgid = tt->last_task_read ? ++ UINT(tt->task_struct + OFFSET(task_struct_tgid)) : 0; ++ ++ return (ulong)tgid; ++} ++ + ulonglong + task_last_run(ulong task) + { +@@ -3197,6 +4348,10 @@ + } else if (VALID_MEMBER(task_struct_timestamp)) + timestamp = tt->last_task_read ? ULONGLONG(tt->task_struct + + OFFSET(task_struct_timestamp)) : 0; ++ else if (VALID_MEMBER(sched_info_last_arrival)) ++ timestamp = tt->last_task_read ? ULONGLONG(tt->task_struct + ++ OFFSET(task_struct_sched_info) + ++ OFFSET(sched_info_last_arrival)) : 0; + + return timestamp; + } +@@ -3368,6 +4523,12 @@ + task = NO_TASK; + tc = FIRST_CONTEXT(); + ++ /* ++ * --no_panic command line option ++ */ ++ if (tt->flags & PANIC_TASK_NOT_FOUND) ++ goto use_task_0; ++ + if (symbol_exists("panic_threads") && + symbol_exists("panicmsg") && + symbol_exists("panic_processor")) { +@@ -3384,6 +4545,10 @@ + task = tt->panic_threads[tt->panic_processor]; + + if (symbol_exists("panic_ksp")) { ++ if (!(tt->panic_ksp = (ulong *) ++ calloc(NR_CPUS, sizeof(void *)))) ++ error(FATAL, ++ "cannot malloc panic_ksp array.\n"); + readmem(symbol_value("panic_ksp"), KVADDR, + tt->panic_ksp, + sizeof(void *)*NR_CPUS, "panic_ksp array", +@@ -3411,6 +4576,9 @@ + + use_task_0: + ++ if (CRASHDEBUG(1)) ++ error(INFO, "get_panic_context: panic task not found\n"); ++ + tt->flags |= PANIC_TASK_NOT_FOUND; + tc = FIRST_CONTEXT(); + return(tc->task); +@@ -3426,7 +4594,7 @@ + ulong task; + struct task_context *tc; + +- if ((task = tt->panic_threads[cpu])) ++ if (DUMPFILE() && (task = tt->panic_threads[cpu])) + return task; + + tc = FIRST_CONTEXT(); +@@ -3448,50 +4616,74 @@ + int msg_found; + + BZERO(buf, BUFSIZE); ++ msg_found = FALSE; + +- if (tt->panicmsg) ++ if (tt->panicmsg) { + read_string(tt->panicmsg, buf, BUFSIZE-1); +- else if (LKCD_DUMPFILE()) ++ msg_found = TRUE; ++ } else if (LKCD_DUMPFILE()) { + get_lkcd_panicmsg(buf); +- else { +- msg_found = FALSE; ++ msg_found = TRUE; ++ } + +- open_tmpfile(); +- dump_log(FALSE); ++ if (msg_found == TRUE) ++ return(buf); + +- rewind(pc->tmpfile); +- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (strstr(buf, "Kernel panic: ")) +- msg_found = TRUE; +- } +- rewind(pc->tmpfile); +- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (strstr(buf, "Oops: ") || +- strstr(buf, "kernel BUG at")) +- msg_found = TRUE; +- } +- rewind(pc->tmpfile); +- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (strstr(buf, "SysRq : Netdump") || +- strstr(buf, "SysRq : Crash")) { +- machdep->flags |= SYSRQ; +- msg_found = TRUE; +- } +- } +- rewind(pc->tmpfile); +- while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (strstr(buf, "sysrq") && +- symbol_exists("sysrq_pressed")) +- get_symbol_data("sysrq_pressed", sizeof(int), +- &msg_found); +- } ++ open_tmpfile(); ++ dump_log(FALSE); + +- close_tmpfile(); ++ /* ++ * First check for a SYSRQ-generated crash, and set the ++ * active-task flag appropriately. The message may or ++ * may not be used as the panic message. ++ */ ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "SysRq : Crash") || ++ strstr(buf, "SysRq : Trigger a crashdump")) { ++ pc->flags |= SYSRQ; ++ break; ++ } ++ } + +- if (!msg_found) +- BZERO(buf, BUFSIZE); ++ rewind(pc->tmpfile); ++ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "Kernel panic: ")) ++ msg_found = TRUE; ++ } ++ rewind(pc->tmpfile); ++ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "Oops: ") || ++ strstr(buf, "kernel BUG at")) ++ msg_found = TRUE; ++ } ++ rewind(pc->tmpfile); ++ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "SysRq : Netdump") || ++ strstr(buf, "SysRq : Trigger a crashdump") || ++ strstr(buf, "SysRq : Crash")) { ++ pc->flags |= SYSRQ; ++ msg_found = TRUE; ++ } ++ } ++ rewind(pc->tmpfile); ++ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "sysrq") && ++ symbol_exists("sysrq_pressed")) ++ get_symbol_data("sysrq_pressed", sizeof(int), ++ &msg_found); ++ } ++ rewind(pc->tmpfile); ++ while (!msg_found && fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "Kernel panic - ")) ++ msg_found = TRUE; + } + ++ close_tmpfile(); ++ ++ if (!msg_found) ++ BZERO(buf, BUFSIZE); ++ + return(buf); + } + +@@ -3517,7 +4709,7 @@ + BZERO(&foreach_data, sizeof(struct foreach_data)); + fd = &foreach_data; + +- while ((c = getopt(argcnt, args, "R:vomlgersStpukcf")) != EOF) { ++ while ((c = getopt(argcnt, args, "R:vomlgersStTpukcf")) != EOF) { + switch(c) + { + case 'R': +@@ -3560,6 +4752,10 @@ + fd->flags |= FOREACH_r_FLAG; + break; + ++ case 'T': ++ fd->flags |= FOREACH_T_FLAG; ++ break; ++ + case 't': + fd->flags |= FOREACH_t_FLAG; + break; +@@ -3754,12 +4950,14 @@ + foreach(struct foreach_data *fd) + { + int i, j, k, a; +- struct task_context *tc; ++ struct task_context *tc, *tgc; + int specified; + int doit; + int subsequent; + ulong cmdflags; ++ ulong tgid; + struct reference reference, *ref; ++ int print_header; + struct bt_info bt_info, *bt; + + /* +@@ -3797,6 +4995,8 @@ + fd->reference ? fd->reference : ""); + } + ++ print_header = TRUE; ++ + for (k = 0; k < fd->keys; k++) { + switch(fd->keyword_array[k]) + { +@@ -3881,6 +5081,14 @@ + error(FATAL, + "sig: -l and -s options are not applicable\n"); + } ++ if (fd->flags & FOREACH_g_FLAG) { ++ if (!hq_open()) { ++ error(INFO, ++ "cannot hash thread group tasks\n"); ++ fd->flags &= ~FOREACH_g_FLAG; ++ } else ++ print_header = FALSE; ++ } + break; + + case FOREACH_TEST: +@@ -3941,7 +5149,7 @@ + if (fd->reference) { + BZERO(ref, sizeof(struct reference)); + ref->str = fd->reference; +- } else ++ } else if (print_header) + print_task_header(fp, tc, subsequent++); + + for (k = 0; k < fd->keys; k++) { +@@ -3962,7 +5170,12 @@ + bt->flags |= BT_SYMBOLIC_ARGS; + if (fd->flags & FOREACH_t_FLAG) + bt->flags |= BT_TEXT_SYMBOLS; +- if (fd->flags & FOREACH_o_FLAG) ++ if (fd->flags & FOREACH_T_FLAG) { ++ bt->flags |= BT_TEXT_SYMBOLS; ++ bt->flags |= BT_TEXT_SYMBOLS_ALL; ++ } ++ if ((fd->flags & FOREACH_o_FLAG) || ++ (kt->flags & USE_OLD_BT)) + bt->flags |= BT_OLD_BACK_TRACE; + if (fd->flags & FOREACH_e_FLAG) + bt->flags |= BT_EFRAME_SEARCH; +@@ -4010,8 +5223,14 @@ + + case FOREACH_SIG: + pc->curcmd = "sig"; +- do_sig(tc->task, FOREACH_SIG, +- fd->reference ? ref : NULL); ++ if (fd->flags & FOREACH_g_FLAG) { ++ tgid = task_tgid(tc->task); ++ tgc = tgid_to_context(tgid); ++ if (hq_enter(tgc->task)) ++ do_sig_thread_group(tgc->task); ++ } else ++ do_sig(tc->task, FOREACH_SIG, ++ fd->reference ? ref : NULL); + break; + + case FOREACH_SET: +@@ -4075,6 +5294,11 @@ + nlm_files_dump(); + } + break; ++ ++ case FOREACH_SIG: ++ if (fd->flags & FOREACH_g_FLAG) ++ hq_close(); ++ break; + } + } + +@@ -4161,7 +5385,7 @@ + fd = &foreach_data; + fd->keys = 1; + fd->keyword_array[0] = FOREACH_BT; +- fd->flags |= FOREACH_t_FLAG; ++ fd->flags |= (FOREACH_t_FLAG|FOREACH_o_FLAG); + + dietask = lasttask = NO_TASK; + +@@ -4188,6 +5412,12 @@ + break; + } + ++ if (strstr(buf, " crash_kexec at ") || ++ strstr(buf, " .crash_kexec at ")) { ++ found = TRUE; ++ break; ++ } ++ + if (strstr(buf, " die at ")) { + switch (dietask) + { +@@ -4211,6 +5441,10 @@ + if (dietask == (NO_TASK+1)) + error(WARNING, "multiple active tasks have called die\n\n"); + ++ if (CRASHDEBUG(1) && found) ++ error(INFO, "panic_search: %lx (via foreach bt)\n", ++ lasttask); ++ + found_panic_task: + populate_panic_threads(); + +@@ -4229,6 +5463,9 @@ + } + } + ++ if (CRASHDEBUG(1)) ++ error(INFO, "panic_search: failed (via foreach bt)\n"); ++ + return NULL; + } + +@@ -4240,25 +5477,28 @@ + { + ulong task; + +- if (LKCD_DUMPFILE()) +- return(get_lkcd_panic_task()); +- + if (NETDUMP_DUMPFILE()) { + task = pc->flags & REM_NETDUMP ? + tt->panic_task : get_netdump_panic_task(); + if (task) + return task; +- if (get_active_set()) +- return(get_active_set_panic_task()); +- } +- +- if (DISKDUMP_DUMPFILE()) { ++ } else if (KDUMP_DUMPFILE()) { ++ task = get_kdump_panic_task(); ++ if (task) ++ return task; ++ } else if (DISKDUMP_DUMPFILE()) { + task = get_diskdump_panic_task(); + if (task) + return task; +- if (get_active_set()) +- return(get_active_set_panic_task()); +- } ++ } else if (XENDUMP_DUMPFILE()) { ++ task = get_xendump_panic_task(); ++ if (task) ++ return task; ++ } else if (LKCD_DUMPFILE()) ++ return(get_lkcd_panic_task()); ++ ++ if (get_active_set()) ++ return(get_active_set_panic_task()); + + return NO_TASK; + } +@@ -4298,14 +5538,17 @@ + + tc = FIRST_CONTEXT(); + for (i = 0; i < RUNNING_TASKS(); i++, tc++) { +- if (task_has_cpu(tc->task, NULL)) { ++ if (task_has_cpu(tc->task, NULL) && ++ (tc->processor >= 0) && ++ (tc->processor < NR_CPUS)) { + tt->panic_threads[tc->processor] = tc->task; + found++; + } + } + + if (!found && !(kt->flags & SMP) && +- (LKCD_DUMPFILE() || NETDUMP_DUMPFILE() || DISKDUMP_DUMPFILE())) ++ (LKCD_DUMPFILE() || NETDUMP_DUMPFILE() || ++ KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE())) + tt->panic_threads[0] = get_dumpfile_panic_task(); + } + +@@ -4331,7 +5574,7 @@ + void + dump_task_table(int verbose) + { +- int i; ++ int i, j, more, nr_cpus; + struct task_context *tc; + char buf[BUFSIZE]; + int others, wrap, flen; +@@ -4363,6 +5606,12 @@ + fprintf(fp, "refresh_pid_hash_task_table()\n"); + else if (tt->refresh_task_table == refresh_hlist_task_table) + fprintf(fp, "refresh_hlist_task_table()\n"); ++ else if (tt->refresh_task_table == refresh_hlist_task_table_v2) ++ fprintf(fp, "refresh_hlist_task_table_v2()\n"); ++ else if (tt->refresh_task_table == refresh_hlist_task_table_v3) ++ fprintf(fp, "refresh_hlist_task_table_v3()\n"); ++ else if (tt->refresh_task_table == refresh_active_task_table) ++ fprintf(fp, "refresh_active_task_table()\n"); + else + fprintf(fp, "%lx\n", (ulong)tt->refresh_task_table); + +@@ -4411,6 +5660,9 @@ + if (tt->flags & NO_TIMESPEC) + sprintf(&buf[strlen(buf)], + "%sNO_TIMESPEC", others++ ? "|" : ""); ++ if (tt->flags & ACTIVE_ONLY) ++ sprintf(&buf[strlen(buf)], ++ "%sACTIVE_ONLY", others++ ? "|" : ""); + sprintf(&buf[strlen(buf)], ")"); + + if (strlen(buf) > 54) +@@ -4436,76 +5688,199 @@ + fprintf(fp, " last_mm_read: %lx\n", tt->last_mm_read); + fprintf(fp, " task_struct: %lx\n", (ulong)tt->task_struct); + fprintf(fp, " mm_struct: %lx\n", (ulong)tt->mm_struct); ++ fprintf(fp, " init_pid_ns: %lx\n", tt->init_pid_ns); + + +- fprintf(fp, " panic_threads:"); +- + wrap = sizeof(void *) == SIZEOF_32BIT ? 8 : 4; + flen = sizeof(void *) == SIZEOF_32BIT ? 8 : 16; + +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) ++ nr_cpus = kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS; ++ ++ ++ fprintf(fp, " idle_threads:"); ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->idle_threads) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { ++ fprintf(fp, "\n "); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->idle_threads[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } ++ fprintf(fp, "%.*lx ", flen, tt->idle_threads[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } ++ } ++ fprintf(fp, "\n"); ++ ++ fprintf(fp, " active_set:"); ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->active_set) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { ++ fprintf(fp, "\n "); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->active_set[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } ++ fprintf(fp, "%.*lx ", flen, tt->active_set[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } ++ } ++ fprintf(fp, "\n"); ++ ++ fprintf(fp, " panic_threads:"); ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->panic_threads) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { + fprintf(fp, "\n "); +- fprintf(fp, "%.*lx ", flen, tt->panic_threads[i]); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->panic_threads[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } ++ fprintf(fp, "%.*lx ", flen, tt->panic_threads[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } + } + fprintf(fp, "\n"); + + fprintf(fp, " panic_ksp:"); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->panic_ksp) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { + fprintf(fp, "\n "); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->panic_ksp[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } + fprintf(fp, "%.*lx ", flen, tt->panic_ksp[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } + } + fprintf(fp, "\n"); + + fprintf(fp, " hardirq_ctx:"); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->hardirq_ctx) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { + fprintf(fp, "\n "); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->hardirq_ctx[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } + fprintf(fp, "%.*lx ", flen, tt->hardirq_ctx[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } + } + fprintf(fp, "\n"); + + fprintf(fp, " hardirq_tasks:"); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->hardirq_tasks) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { + fprintf(fp, "\n "); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->hardirq_tasks[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } + fprintf(fp, "%.*lx ", flen, tt->hardirq_tasks[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } + } + fprintf(fp, "\n"); + + fprintf(fp, " softirq_ctx:"); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->softirq_ctx) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { + fprintf(fp, "\n "); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->softirq_ctx[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } + fprintf(fp, "%.*lx ", flen, tt->softirq_ctx[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } + } + fprintf(fp, "\n"); + + fprintf(fp, " softirq_tasks:"); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) ++ for (i = 0; i < nr_cpus; i++) { ++ if (!tt->softirq_tasks) { ++ fprintf(fp, " (unused)"); ++ break; ++ } ++ if ((i % wrap) == 0) { + fprintf(fp, "\n "); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (tt->softirq_tasks[j]) { ++ more = TRUE; ++ break; ++ } ++ } ++ } + fprintf(fp, "%.*lx ", flen, tt->softirq_tasks[i]); ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } + } + fprintf(fp, "\n"); + +- fprintf(fp, " idle_threads:"); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) +- fprintf(fp, "\n "); +- fprintf(fp, "%.*lx ", flen, tt->idle_threads[i]); +- } +- fprintf(fp, "\n"); +- +- fprintf(fp, " active_set:"); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % wrap) == 0) +- fprintf(fp, "\n "); +- fprintf(fp, "%.*lx ", flen, tt->active_set[i]); +- } +- fprintf(fp, "\n"); +- + + if (!verbose) + return; +@@ -4546,6 +5921,9 @@ + if ((tc->pid == 0) && !STREQ(tc->comm, pc->program_name)) + return TRUE; + ++ if (_ZOMBIE_ == TASK_STATE_UNINITIALIZED) ++ initialize_task_state(); ++ + if (IS_ZOMBIE(task) || IS_EXITING(task)) + return FALSE; + +@@ -4641,6 +6019,16 @@ + cnt++; + else + BZERO(tasklist, sizeof(ulong) * NR_CPUS); ++ } else if (OPENVZ()) { ++ runq = symbol_value("pcpu_info"); ++ runqbuf = GETBUF(SIZE(pcpu_info)); ++ for (i = 0; i < nr_cpus; i++, runq += SIZE(pcpu_info)) { ++ readmem(runq, KVADDR, runqbuf, SIZE(pcpu_info), ++ "pcpu info", FAULT_ON_ERROR); ++ tasklist[i] = ULONG(runqbuf + OFFSET(pcpu_info_idle)); ++ if (IS_KVADDR(tasklist[i])) ++ cnt++; ++ } + } + + if (runqbuf) +@@ -4661,6 +6049,8 @@ + { + if (kt->runq_siblings == 1) + return cpu; ++ else if (!(kt->__rq_idx)) ++ return 0; + else + return kt->__rq_idx[cpu]; + } +@@ -4673,6 +6063,8 @@ + { + if (kt->runq_siblings == 1) + return 0; ++ else if (!(kt->__cpu_idx)) ++ return 0; + else + return kt->__cpu_idx[cpu]; + } +@@ -4734,14 +6126,41 @@ + } else if (symbol_exists("per_cpu__runqueues")) { + runq = symbol_value("per_cpu__runqueues"); + per_cpu = TRUE; +- } else ++ } else if (OPENVZ()) ++ runq = symbol_value("pcpu_info"); ++ else + return FALSE; + +- BZERO(tt->active_set, sizeof(ulong) * NR_CPUS); ++ ++ if (!(tt->active_set = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) ++ error(FATAL, "cannot malloc active_set array"); ++ + runqbuf = GETBUF(SIZE(runqueue)); + cnt = 0; + +- if (VALID_MEMBER(runqueue_curr) && per_cpu) { ++ if (OPENVZ()) { ++ ulong vcpu_struct; ++ char *pcpu_info_buf, *vcpu_struct_buf; ++ ++ pcpu_info_buf = GETBUF(SIZE(pcpu_info)); ++ vcpu_struct_buf = GETBUF(SIZE(vcpu_struct)); ++ ++ for (i = 0; i < kt->cpus; i++, runq += SIZE(pcpu_info)) { ++ readmem(runq, KVADDR, pcpu_info_buf, ++ SIZE(pcpu_info), "pcpu_info", FAULT_ON_ERROR); ++ vcpu_struct= ULONG(pcpu_info_buf + ++ OFFSET(pcpu_info_vcpu)); ++ readmem(vcpu_struct, KVADDR, vcpu_struct_buf, ++ SIZE(vcpu_struct), "pcpu_info->vcpu", ++ FAULT_ON_ERROR); ++ tt->active_set[i] = ULONG(vcpu_struct_buf + ++ OFFSET(vcpu_struct_rq) + OFFSET(runqueue_curr)); ++ if (IS_KVADDR(tt->active_set[i])) ++ cnt++; ++ } ++ FREEBUF(pcpu_info_buf); ++ FREEBUF(vcpu_struct_buf); ++ } else if (VALID_MEMBER(runqueue_curr) && per_cpu) { + for (i = 0; i < kt->cpus; i++) { + if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { + runq = symbol_value("per_cpu__runqueues") + +@@ -4759,7 +6178,8 @@ + cnt++; + } + } else if (VALID_MEMBER(runqueue_curr)) { +- for (i = 0; i < NR_CPUS; i++, runq += SIZE(runqueue)) { ++ for (i = 0; i < MAX(kt->cpus, kt->kernel_NR_CPUS); i++, ++ runq += SIZE(runqueue)) { + readmem(runq, KVADDR, runqbuf, + SIZE(runqueue), "(old) runqueues curr", + FAULT_ON_ERROR); +@@ -4799,23 +6219,55 @@ + tt->flags &= ~ACTIVE_SET; + } + +-#define RESOLVE_PANIC_AND_DIE_CALLERS() \ +- if ((panic_task > (NO_TASK+1)) && !die_task) \ +- return panic_task; \ +- \ +- if (panic_task && die_task) { \ +- error(WARNING, \ +- "multiple active tasks have called die and/or panic\n\n"); \ +- return NO_TASK; \ +- } \ +- \ +- if (die_task > (NO_TASK+1)) \ +- return die_task; \ +- else if (die_task == (NO_TASK+1)) \ +- error(WARNING, \ ++#define RESOLVE_PANIC_AND_DIE_CALLERS() \ ++ if (xen_panic_task) { \ ++ if (CRASHDEBUG(1)) \ ++ error(INFO, \ ++ "get_active_set_panic_task: %lx (xen_panic_event)\n", \ ++ xen_panic_task); \ ++ return xen_panic_task; \ ++ } \ ++ if (crash_kexec_task) { \ ++ if (CRASHDEBUG(1)) \ ++ error(INFO, \ ++ "get_active_set_panic_task: %lx (crash_kexec)\n", \ ++ crash_kexec_task); \ ++ return crash_kexec_task; \ ++ } \ ++ if ((panic_task > (NO_TASK+1)) && !die_task) { \ ++ if (CRASHDEBUG(1)) \ ++ fprintf(fp, \ ++ "get_active_set_panic_task: %lx (panic)\n", \ ++ panic_task); \ ++ return panic_task; \ ++ } \ ++ \ ++ if (panic_task && die_task) { \ ++ if ((panic_task > (NO_TASK+1)) && \ ++ (panic_task == die_task)) { \ ++ if (CRASHDEBUG(1)) \ ++ fprintf(fp, \ ++ "get_active_set_panic_task: %lx (panic)\n", \ ++ panic_task); \ ++ return panic_task; \ ++ } \ ++ error(WARNING, \ ++ "multiple active tasks have called die and/or panic\n\n"); \ ++ goto no_panic_task_found; \ ++ } \ ++ \ ++ if (die_task > (NO_TASK+1)) { \ ++ if (CRASHDEBUG(1)) \ ++ fprintf(fp, \ ++ "get_active_set_panic_task: %lx (die)\n", \ ++ die_task); \ ++ return die_task; \ ++ } \ ++ else if (die_task == (NO_TASK+1)) \ ++ error(WARNING, \ + "multiple active tasks have called die\n\n"); + +-#define SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS() \ ++#define SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS() \ + while (fgets(buf, BUFSIZE, pc->tmpfile)) { \ + if (strstr(buf, " die+")) { \ + switch (die_task) \ +@@ -4833,12 +6285,30 @@ + { \ + case NO_TASK: \ + panic_task = task; \ ++ if (XENDUMP_DUMPFILE()) \ ++ xendump_panic_hook(buf); \ + break; \ + default: \ + panic_task = NO_TASK+1; \ + break; \ + } \ + } \ ++ if (strstr(buf, " crash_kexec+") || \ ++ strstr(buf, " .crash_kexec+")) { \ ++ crash_kexec_task = task; \ ++ } \ ++ if (strstr(buf, " machine_kexec+") || \ ++ strstr(buf, " .machine_kexec+")) { \ ++ crash_kexec_task = task; \ ++ } \ ++ if (strstr(buf, " xen_panic_event+") || \ ++ strstr(buf, " .xen_panic_event+")){ \ ++ xen_panic_task = task; \ ++ xendump_panic_hook(buf); \ ++ } \ ++ if (machine_type("IA64") && XENDUMP_DUMPFILE() && !xen_panic_task && \ ++ strstr(buf, " sysrq_handle_crashdump+")) \ ++ xen_sysrq_task = task; \ + } + + /* +@@ -4850,11 +6320,14 @@ + int i, j, found; + ulong task; + char buf[BUFSIZE]; +- ulong panic_task, die_task; ++ ulong panic_task, die_task, crash_kexec_task; ++ ulong xen_panic_task; ++ ulong xen_sysrq_task; + char *tp; + struct task_context *tc; + +- panic_task = die_task = NO_TASK; ++ panic_task = die_task = crash_kexec_task = xen_panic_task = NO_TASK; ++ xen_sysrq_task = NO_TASK; + + for (i = 0; i < NR_CPUS; i++) { + if (!(task = tt->active_set[i])) +@@ -4867,15 +6340,16 @@ + if ((tp = fill_task_struct(task))) { + if ((tc = store_context(NULL, task, tp))) + tt->running_tasks++; ++ else ++ continue; + } +- continue; + } + + open_tmpfile(); + raw_stack_dump(GET_STACKBASE(task), STACKSIZE()); + rewind(pc->tmpfile); + +- SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS(); ++ SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS(); + + close_tmpfile(); + } +@@ -4903,7 +6377,7 @@ + raw_stack_dump(tt->hardirq_ctx[i], SIZE(thread_union)); + rewind(pc->tmpfile); + +- SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS(); ++ SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS(); + + close_tmpfile(); + } +@@ -4930,7 +6404,7 @@ + raw_stack_dump(tt->softirq_ctx[i], SIZE(thread_union)); + rewind(pc->tmpfile); + +- SEARCH_STACK_FOR_PANIC_AND_DIE_CALLERS(); ++ SEARCH_STACK_FOR_PANIC_DIE_AND_KEXEC_CALLERS(); + + close_tmpfile(); + } +@@ -4938,6 +6412,28 @@ + RESOLVE_PANIC_AND_DIE_CALLERS(); + } + ++ if (crash_kexec_task) { ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "get_active_set_panic_task: %lx (crash_kexec)\n", ++ crash_kexec_task); ++ return crash_kexec_task; ++ } ++ ++ if (xen_sysrq_task) { ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "get_active_set_panic_task: %lx (sysrq_handle_crashdump)\n", ++ xen_sysrq_task); ++ return xen_sysrq_task; ++ } ++ ++no_panic_task_found: ++ ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "get_active_set_panic_task: failed\n"); ++ + return NO_TASK; + } + +@@ -4997,6 +6493,11 @@ + ulong *tlist; + struct task_context *tc; + ++ if (VALID_MEMBER(rq_cfs)) { ++ dump_CFS_runqueues(); ++ return; ++ } ++ + if (VALID_MEMBER(runqueue_arrays)) { + dump_runqueues(); + return; +@@ -5017,120 +6518,370 @@ + error(FATAL, + "cannot determine run queue structures being used\n"); + +- cnt = 0; +- do { +- if (cnt == qlen) { +- FREEBUF(tlist); +- qlen += 1000; +- goto start_again; +- } ++ cnt = 0; ++ do { ++ if (cnt == qlen) { ++ FREEBUF(tlist); ++ qlen += 1000; ++ goto start_again; ++ } ++ ++ tlist[cnt++] = next; ++ ++ readmem(next+offs, KVADDR, &next, sizeof(void *), ++ "run queue entry", FAULT_ON_ERROR); ++ ++ if (next == runqueue_head) ++ break; ++ } while (next); ++ ++ for (i = 0; i < cnt; i++) { ++ if (tlist[i] == runqueue_head) ++ continue; ++ ++ if (!(tc = task_to_context(VIRTPAGEBASE(tlist[i])))) { ++ fprintf(fp, ++ "PID: ? TASK: %lx CPU: ? COMMAND: ?\n", ++ tlist[i]); ++ continue; ++ } ++ ++ if (!is_idle_thread(tc->task)) ++ print_task_header(fp, tc, 0); ++ } ++} ++ ++#define RUNQ_ACTIVE (1) ++#define RUNQ_EXPIRED (2) ++ ++static void ++dump_runqueues(void) ++{ ++ int cpu; ++ ulong runq, offset; ++ char *runqbuf; ++ ulong active, expired, arrays; ++ int per_cpu; ++ ++ ++ if (symbol_exists("runqueues")) { ++ runq = symbol_value("runqueues"); ++ per_cpu = FALSE; ++ } else if (symbol_exists("per_cpu__runqueues")) { ++ runq = symbol_value("per_cpu__runqueues"); ++ per_cpu = TRUE; ++ } ++ ++ runqbuf = GETBUF(SIZE(runqueue)); ++ ++ for (cpu = 0; cpu < kt->cpus; cpu++, runq += SIZE(runqueue)) { ++ if (per_cpu) { ++ if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { ++ runq = symbol_value("per_cpu__runqueues") + ++ kt->__per_cpu_offset[cpu]; ++ } else ++ runq = symbol_value("per_cpu__runqueues"); ++ } ++ ++ fprintf(fp, "RUNQUEUES[%d]: %lx\n", cpu, runq); ++ ++ readmem(runq, KVADDR, runqbuf, SIZE(runqueue), ++ "runqueues array entry", FAULT_ON_ERROR); ++ active = ULONG(runqbuf + OFFSET(runqueue_active)); ++ expired = ULONG(runqbuf + OFFSET(runqueue_expired)); ++ arrays = runq + OFFSET(runqueue_arrays); ++ ++ console("active: %lx\n", active); ++ console("expired: %lx\n", expired); ++ console("arrays: %lx\n", arrays); ++ ++ offset = active == arrays ? OFFSET(runqueue_arrays) : ++ OFFSET(runqueue_arrays) + SIZE(prio_array); ++ offset = active - runq; ++ dump_prio_array(RUNQ_ACTIVE, active, &runqbuf[offset]); ++ ++ offset = expired == arrays ? OFFSET(runqueue_arrays) : ++ OFFSET(runqueue_arrays) + SIZE(prio_array); ++ offset = expired - runq; ++ dump_prio_array(RUNQ_EXPIRED, expired, &runqbuf[offset]); ++ } ++} ++ ++static void ++dump_prio_array(int which, ulong k_prio_array, char *u_prio_array) ++{ ++ int i, c, cnt, qheads, nr_active; ++ ulong offset, kvaddr, uvaddr; ++ ulong list_head[2]; ++ struct list_data list_data, *ld; ++ struct task_context *tc; ++ ulong *tlist; ++ ++ qheads = (i = ARRAY_LENGTH(prio_array_queue)) ? ++ i : get_array_length("prio_array.queue", NULL, SIZE(list_head)); ++ ++ console("dump_prio_array[%d]: %lx %lx\n", ++ which, k_prio_array, (ulong)u_prio_array); ++ ++ nr_active = INT(u_prio_array + OFFSET(prio_array_nr_active)); ++ console("nr_active: %d\n", nr_active); ++ ++ fprintf(fp, " %s PRIO_ARRAY: %lx\n", ++ which == RUNQ_ACTIVE ? "ACTIVE" : "EXPIRED", k_prio_array); ++ ++ ld = &list_data; ++ ++ for (i = 0; i < 140; i++) { ++ offset = OFFSET(prio_array_queue) + (i * SIZE(list_head)); ++ kvaddr = k_prio_array + offset; ++ uvaddr = (ulong)u_prio_array + offset; ++ BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2); ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "prio_array[%d] @ %lx => %lx/%lx\n", ++ i, kvaddr, list_head[0], list_head[1]); ++ ++ if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr)) ++ continue; ++ ++ console("[%d] %lx => %lx-%lx ", i, kvaddr, list_head[0], ++ list_head[1]); ++ ++ fprintf(fp, " [%3d] ", i); ++ ++ BZERO(ld, sizeof(struct list_data)); ++ ld->start = list_head[0]; ++ ld->list_head_offset = OFFSET(task_struct_run_list); ++ ld->end = kvaddr; ++ hq_open(); ++ cnt = do_list(ld); ++ hq_close(); ++ console("%d entries\n", cnt); ++ tlist = (ulong *)GETBUF((cnt) * sizeof(ulong)); ++ cnt = retrieve_list(tlist, cnt); ++ for (c = 0; c < cnt; c++) { ++ if (!(tc = task_to_context(tlist[c]))) ++ continue; ++ if (c) ++ INDENT(8); ++ print_task_header(fp, tc, FALSE); ++ } ++ FREEBUF(tlist); ++ } ++} ++ ++/* ++ * CFS scheduler uses Red-Black trees to maintain run queue. ++ */ ++struct rb_node ++{ ++ unsigned long rb_parent_color; ++#define RB_RED 0 ++#define RB_BLACK 1 ++ struct rb_node *rb_right; ++ struct rb_node *rb_left; ++}; ++ ++struct rb_root ++{ ++ struct rb_node *rb_node; ++}; ++ ++static struct rb_node * ++rb_first(struct rb_root *root) ++{ ++ struct rb_root rloc; ++ struct rb_node *n; ++ struct rb_node nloc; ++ ++ readmem((ulong)root, KVADDR, &rloc, sizeof(struct rb_root), ++ "rb_root", FAULT_ON_ERROR); ++ ++ n = rloc.rb_node; ++ if (!n) ++ return NULL; ++ while (rb_left(n, &nloc)) ++ n = nloc.rb_left; ++ ++ return n; ++} ++ ++static struct rb_node * ++rb_parent(struct rb_node *node, struct rb_node *nloc) ++{ ++ readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node), ++ "rb_node", FAULT_ON_ERROR); ++ ++ return (struct rb_node *)(nloc->rb_parent_color & ~3); ++} ++ ++static struct rb_node * ++rb_right(struct rb_node *node, struct rb_node *nloc) ++{ ++ readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node), ++ "rb_node", FAULT_ON_ERROR); ++ ++ return nloc->rb_right; ++} + +- tlist[cnt++] = next; ++static struct rb_node * ++rb_left(struct rb_node *node, struct rb_node *nloc) ++{ ++ readmem((ulong)node, KVADDR, nloc, sizeof(struct rb_node), ++ "rb_node", FAULT_ON_ERROR); + +- readmem(next+offs, KVADDR, &next, sizeof(void *), +- "run queue entry", FAULT_ON_ERROR); ++ return nloc->rb_left; ++} + +- if (next == runqueue_head) +- break; +- } while (next); ++static struct rb_node * ++rb_next(struct rb_node *node) ++{ ++ struct rb_node nloc; ++ struct rb_node *parent; + +- for (i = 0; i < cnt; i++) { +- if (tlist[i] == runqueue_head) +- continue; ++ parent = rb_parent(node, &nloc); + +- if (!(tc = task_to_context(VIRTPAGEBASE(tlist[i])))) { +- fprintf(fp, +- "PID: ? TASK: %lx CPU: ? COMMAND: ?\n", +- tlist[i]); +- continue; +- } ++ if (parent == node) ++ return NULL; + +- if (!is_idle_thread(tc->task)) +- print_task_header(fp, tc, 0); ++ if (nloc.rb_right) { ++ node = nloc.rb_right; ++ while (rb_left(node, &nloc)) ++ node = nloc.rb_left; ++ return node; + } +-} + +-#define RUNQ_ACTIVE (1) +-#define RUNQ_EXPIRED (2) ++ while ((parent = rb_parent(node, &nloc)) && (node == rb_right(parent, &nloc))) ++ node = parent; ++ ++ return parent; ++} + + static void +-dump_runqueues(void) ++dump_CFS_runqueues(void) + { + int cpu; +- ulong runq, offset; +- char *runqbuf; +- ulong active, expired, arrays; +- int per_cpu; ++ ulong runq, cfs_rq; ++ char *runqbuf, *cfs_rq_buf; ++ ulong leftmost, tasks_timeline; ++ struct task_context *tc; ++ long nr_running, cfs_rq_nr_running; ++ struct rb_root *root; ++ struct rb_node *node; ++ ++ if (!VALID_STRUCT(cfs_rq)) { ++ STRUCT_SIZE_INIT(cfs_rq, "cfs_rq"); ++ MEMBER_OFFSET_INIT(rq_rt, "rq", "rt"); ++ MEMBER_OFFSET_INIT(rq_nr_running, "rq", "nr_running"); ++ MEMBER_OFFSET_INIT(task_struct_se, "task_struct", "se"); ++ MEMBER_OFFSET_INIT(sched_entity_run_node, "sched_entity", ++ "run_node"); ++ MEMBER_OFFSET_INIT(cfs_rq_rb_leftmost, "cfs_rq", "rb_leftmost"); ++ MEMBER_OFFSET_INIT(cfs_rq_nr_running, "cfs_rq", "nr_running"); ++ MEMBER_OFFSET_INIT(cfs_rq_tasks_timeline, "cfs_rq", ++ "tasks_timeline"); ++ MEMBER_OFFSET_INIT(rt_rq_active, "rt_rq", "active"); ++ MEMBER_OFFSET_INIT(task_struct_run_list, "task_struct", ++ "run_list"); ++ } + ++ if (!symbol_exists("per_cpu__runqueues")) ++ error(FATAL, "per_cpu__runqueues does not exist\n"); + +- if (symbol_exists("runqueues")) { +- runq = symbol_value("runqueues"); +- per_cpu = FALSE; +- } else if (symbol_exists("per_cpu__runqueues")) { +- runq = symbol_value("per_cpu__runqueues"); +- per_cpu = TRUE; +- } ++ runq = symbol_value("per_cpu__runqueues"); + + runqbuf = GETBUF(SIZE(runqueue)); ++ cfs_rq_buf = symbol_exists("per_cpu__init_cfs_rq") ? ++ GETBUF(SIZE(cfs_rq)) : NULL; + +- for (cpu = 0; cpu < kt->cpus; cpu++, runq += SIZE(runqueue)) { +- if (per_cpu) { +- if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { +- runq = symbol_value("per_cpu__runqueues") + +- kt->__per_cpu_offset[cpu]; +- } else +- runq = symbol_value("per_cpu__runqueues"); +- } ++ for (cpu = 0; cpu < kt->cpus; cpu++) { ++ if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { ++ runq = symbol_value("per_cpu__runqueues") + ++ kt->__per_cpu_offset[cpu]; ++ } else ++ runq = symbol_value("per_cpu__runqueues"); + +- fprintf(fp, "RUNQUEUES[%d]: %lx\n", cpu, runq); ++ fprintf(fp, "RUNQUEUES[%d]: %lx\n", cpu, runq); ++ readmem(runq, KVADDR, runqbuf, SIZE(runqueue), ++ "per-cpu rq", FAULT_ON_ERROR); ++ ++ if (cfs_rq_buf) { ++ /* ++ * Use default task group's cfs_rq on each cpu. ++ */ ++ if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { ++ cfs_rq = symbol_value("per_cpu__init_cfs_rq") + ++ kt->__per_cpu_offset[cpu]; ++ } else ++ cfs_rq = symbol_value("per_cpu__init_cfs_rq"); + +- readmem(runq, KVADDR, runqbuf, SIZE(runqueue), +- "runqueues array entry", FAULT_ON_ERROR); +- active = ULONG(runqbuf + OFFSET(runqueue_active)); +- expired = ULONG(runqbuf + OFFSET(runqueue_expired)); +- arrays = runq + OFFSET(runqueue_arrays); ++ readmem(cfs_rq, KVADDR, cfs_rq_buf, SIZE(cfs_rq), ++ "per-cpu cfs_rq", FAULT_ON_ERROR); ++ leftmost = ULONG(cfs_rq_buf + OFFSET(cfs_rq_rb_leftmost)); ++ tasks_timeline = ULONG(cfs_rq_buf + ++ OFFSET(cfs_rq_tasks_timeline)); ++ nr_running = LONG(cfs_rq_buf + OFFSET(rq_nr_running)); ++ cfs_rq_nr_running = ULONG(cfs_rq_buf + ++ OFFSET(cfs_rq_nr_running)); ++ root = (struct rb_root *)(cfs_rq + ++ OFFSET(cfs_rq_tasks_timeline)); ++ } else { ++ leftmost = ULONG(runqbuf + OFFSET(rq_cfs) + ++ OFFSET(cfs_rq_rb_leftmost)); ++ tasks_timeline = ULONG(runqbuf + OFFSET(rq_cfs) + ++ OFFSET(cfs_rq_tasks_timeline)); ++ nr_running = LONG(runqbuf + OFFSET(rq_nr_running)); ++ cfs_rq_nr_running = ULONG(runqbuf + OFFSET(rq_cfs) + ++ OFFSET(cfs_rq_nr_running)); ++ root = (struct rb_root *)(runq + OFFSET(rq_cfs) + ++ OFFSET(cfs_rq_tasks_timeline)); ++ } ++ ++ dump_RT_prio_array(nr_running != cfs_rq_nr_running, ++ runq + OFFSET(rq_rt) + OFFSET(rt_rq_active), ++ &runqbuf[OFFSET(rq_rt) + OFFSET(rt_rq_active)]); + +- console("active: %lx\n", active); +- console("expired: %lx\n", expired); +- console("arrays: %lx\n", arrays); ++ fprintf(fp, " CFS RB_ROOT: %lx\n", (ulong)root); + +- offset = active == arrays ? OFFSET(runqueue_arrays) : +- OFFSET(runqueue_arrays) + SIZE(prio_array); +- offset = active - runq; +- dump_prio_array(RUNQ_ACTIVE, active, &runqbuf[offset]); ++ if (!leftmost) ++ continue; + +- offset = expired == arrays ? OFFSET(runqueue_arrays) : +- OFFSET(runqueue_arrays) + SIZE(prio_array); +- offset = expired - runq; +- dump_prio_array(RUNQ_EXPIRED, expired, &runqbuf[offset]); ++ for (node = rb_first(root); node; node = rb_next(node)) { ++ tc = task_to_context((ulong)node - OFFSET(task_struct_se) - ++ OFFSET(sched_entity_run_node)); ++ if (!tc) ++ continue; ++ INDENT(2); ++ print_task_header(fp, tc, FALSE); ++ } + } ++ ++ FREEBUF(runqbuf); ++ if (cfs_rq_buf) ++ FREEBUF(cfs_rq_buf); + } + + static void +-dump_prio_array(int which, ulong k_prio_array, char *u_prio_array) ++dump_RT_prio_array(int active, ulong k_prio_array, char *u_prio_array) + { +- int i, c, cnt, qheads, nr_active; ++ int i, c, cnt, qheads; + ulong offset, kvaddr, uvaddr; + ulong list_head[2]; + struct list_data list_data, *ld; + struct task_context *tc; + ulong *tlist; + +- qheads = (i = ARRAY_LENGTH(prio_array_queue)) ? +- i : get_array_length("prio_array.queue", NULL, SIZE(list_head)); +- +- console("dump_prio_array[%d]: %lx %lx\n", +- which, k_prio_array, (ulong)u_prio_array); ++ fprintf(fp, " RT PRIO_ARRAY: %lx\n", k_prio_array); + +- nr_active = INT(u_prio_array + OFFSET(prio_array_nr_active)); +- console("nr_active: %d\n", nr_active); ++ if (!active) ++ return; + +- fprintf(fp, " %s PRIO_ARRAY: %lx\n", +- which == RUNQ_ACTIVE ? "ACTIVE" : "EXPIRED", k_prio_array); ++ qheads = (i = ARRAY_LENGTH(prio_array_queue)) ? ++ i : get_array_length("prio_array.queue", NULL, SIZE(list_head)); + + ld = &list_data; + +- for (i = 0; i < 140; i++) { ++ for (i = 0; i < qheads; i++) { + offset = OFFSET(prio_array_queue) + (i * SIZE(list_head)); + kvaddr = k_prio_array + offset; + uvaddr = (ulong)u_prio_array + offset; +@@ -5143,9 +6894,6 @@ + if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr)) + continue; + +- console("[%d] %lx => %lx-%lx ", i, kvaddr, list_head[0], +- list_head[1]); +- + fprintf(fp, " [%3d] ", i); + + BZERO(ld, sizeof(struct list_data)); +@@ -5155,8 +6903,7 @@ + hq_open(); + cnt = do_list(ld); + hq_close(); +- console("%d entries\n", cnt); +- tlist = (ulong *)GETBUF((cnt) * sizeof(ulong)); ++ tlist = (ulong *)GETBUF((cnt) * sizeof(ulong)); + cnt = retrieve_list(tlist, cnt); + for (c = 0; c < cnt; c++) { + if (!(tc = task_to_context(tlist[c]))) +@@ -5174,6 +6921,9 @@ + #define _NSIG_BPW machdep->bits + #define _NSIG_WORDS (_NSIG / _NSIG_BPW) + ++#undef SIGRTMIN ++#define SIGRTMIN 32 ++ + static struct signame { + char *name; + char *altname; +@@ -5209,23 +6959,56 @@ + /* 28 */ {"SIGWINCH", NULL}, + /* 29 */ {"SIGIO", "SIGPOLL"}, + /* 30 */ {"SIGPWR", NULL}, +- /* 31 */ {"SIGSYS", NULL}, ++ /* 31 */ {"SIGSYS", "SIGUNUSED"}, + {NULL, NULL}, /* Real time signals start here. */ + }; + ++static int ++sigrt_minmax(int *min, int *max) ++{ ++ int sigrtmax, j; ++ ++ sigrtmax = THIS_KERNEL_VERSION < LINUX(2,5,0) ? ++ _NSIG - 1 : _NSIG; ++ ++ if (min && max) { ++ j = sigrtmax-SIGRTMIN-1; ++ *max = j / 2; ++ *min = j - *max; ++ } ++ ++ return sigrtmax; ++} ++ + static void + signame_list(void) + { +- int i; ++ int i, sigrtmax, j, min, max; + +- for (i = 0; i < _NSIG; i++) { +- if (!signame[i].name) +- continue; ++ sigrtmax = sigrt_minmax(&min, &max); ++ j = 1; ++ ++ for (i = 1; i <= sigrtmax; i++) { ++ if ((i == SIGRTMIN) || (i == sigrtmax)) { ++ fprintf(fp, "[%d] %s", i, ++ (i== SIGRTMIN) ? "SIGRTMIN" : "SIGRTMAX"); ++ } else if (i > SIGRTMIN) { ++ if (j <= min){ ++ fprintf(fp, "[%d] %s%d", i , "SIGRTMIN+", j); ++ j++; ++ } else if (max >= 1) { ++ fprintf(fp, "[%d] %s%d", i , "SIGRTMAX-",max); ++ max--; ++ } ++ } else { ++ if (!signame[i].name) ++ continue; + +- fprintf(fp, "%s[%d] %s", i < 10 ? " " : "", +- i, signame[i].name); +- if (signame[i].altname) +- fprintf(fp, "/%s", signame[i].altname); ++ fprintf(fp, "%s[%d] %s", i < 10 ? " " : "", ++ i, signame[i].name); ++ if (signame[i].altname) ++ fprintf(fp, "/%s", signame[i].altname); ++ } + fprintf(fp, "\n"); + } + } +@@ -5236,8 +7019,7 @@ + static void + translate_sigset(ulonglong sigset) + { +- int i, c, bit, len; +- ulonglong mask, sig; ++ int sigrtmax, min, max, i, j, c, len; + char buf[BUFSIZE]; + + if (!sigset) { +@@ -5246,21 +7028,42 @@ + } + + len = 0; ++ sigrtmax= sigrt_minmax(&min, &max); ++ j = 1; ++ ++ for (i = 1, c = 0; i <= sigrtmax; i++) { ++ if (sigset & (ulonglong)1) { ++ if (i == SIGRTMIN || i == sigrtmax) ++ sprintf(buf, "%s%s", c++ ? " " : "", ++ (i==SIGRTMIN) ? "SIGRTMIN" : "SIGRTMAX"); ++ else if (i > SIGRTMIN) { ++ if (j <= min) ++ sprintf(buf, "%s%s%d", ++ c++ ? " " : "", "SIGRTMIN+", j); ++ else if (max >= 1) ++ sprintf(buf, "%s%s%d", ++ c++ ? " " : "", "SIGRTMAX-", max); ++ } else ++ sprintf(buf, "%s%s", c++ ? " " : "", ++ signame[i].name); + +- for (i = c = 0; i < (_NSIG/2); i++) { +- mask = (ulong)(1) << i; +- if ((sig = (sigset & mask))) { +- bit = ffs((int)sig); +- sprintf(buf, "%s%s", c++ ? " " : "", +- signame[bit].name); + if ((len + strlen(buf)) > 80) { + shift_string_left(buf, 1); + fprintf(fp, "\n"); + len = 0; + } ++ + len += strlen(buf); + fprintf(fp, buf); + } ++ ++ sigset >>= 1; ++ if (i > SIGRTMIN) { ++ if (j <= min) ++ j++; ++ else if (max >= 1) ++ max--; ++ } + } + fprintf(fp, "\n"); + } +@@ -5290,13 +7093,14 @@ + struct task_context *tc; + ulong *tasklist; + char *siglist; ++ int thread_group = FALSE; + + tasklist = (ulong *)GETBUF((MAXARGS+NR_CPUS)*sizeof(ulong)); + ref = (struct reference *)GETBUF(sizeof(struct reference)); + siglist = GETBUF(BUFSIZE); + ref->str = siglist; + +- while ((c = getopt(argcnt, args, "lR:s:")) != EOF) { ++ while ((c = getopt(argcnt, args, "lR:s:g")) != EOF) { + switch(c) + { + case 's': +@@ -5314,6 +7118,10 @@ + signame_list(); + return; + ++ case 'g': ++ pc->curcmd_flags |= TASK_SPECIFIED; ++ thread_group = TRUE; ++ break; + default: + argerrs++; + break; +@@ -5360,10 +7168,65 @@ + tasklist[tcnt++] = CURRENT_TASK(); + + for (c = 0; c < tcnt; c++) { +- do_sig(tasklist[c], 0, strlen(ref->str) ? ref : NULL); +- fprintf(fp, "\n"); ++ if (thread_group) ++ do_sig_thread_group(tasklist[c]); ++ else { ++ do_sig(tasklist[c], 0, strlen(ref->str) ? ref : NULL); ++ fprintf(fp, "\n"); ++ } ++ } ++ ++} ++ ++ ++/* ++ * Do the work for the "sig -g" command option, coming from sig or foreach. ++ */ ++static void ++do_sig_thread_group(ulong task) ++{ ++ int i; ++ int cnt; ++ struct task_context *tc; ++ ulong tgid; ++ ++ tc = task_to_context(task); ++ tgid = task_tgid(task); ++ ++ if (tc->pid != tgid) { ++ if (pc->curcmd_flags & TASK_SPECIFIED) { ++ if (!(tc = tgid_to_context(tgid))) ++ return; ++ task = tc->task; ++ } else ++ return; + } + ++ if ((tc->pid == 0) && (pc->curcmd_flags & IDLE_TASK_SHOWN)) ++ return; ++ ++ print_task_header(fp, tc, 0); ++ dump_signal_data(tc, THREAD_GROUP_LEVEL); ++ fprintf(fp, "\n "); ++ print_task_header(fp, tc, 0); ++ dump_signal_data(tc, TASK_LEVEL|TASK_INDENT); ++ ++ tc = FIRST_CONTEXT(); ++ for (i = cnt = 0; i < RUNNING_TASKS(); i++, tc++) { ++ if (tc->task == task) ++ continue; ++ ++ if (task_tgid(tc->task) == tgid) { ++ fprintf(fp, "\n "); ++ print_task_header(fp, tc, 0); ++ dump_signal_data(tc, TASK_LEVEL|TASK_INDENT); ++ cnt++; ++ if (tc->pid == 0) ++ pc->curcmd_flags |= IDLE_TASK_SHOWN; ++ } ++ } ++ ++ fprintf(fp, "\n"); + } + + /* +@@ -5381,7 +7244,7 @@ + else { + if (!(flags & FOREACH_TASK)) + print_task_header(fp, tc, 0); +- dump_signal_data(tc); ++ dump_signal_data(tc, TASK_LEVEL|THREAD_GROUP_LEVEL); + } + } + +@@ -5401,40 +7264,34 @@ + * Dump all signal-handling data for a task. + */ + static void +-dump_signal_data(struct task_context *tc) ++dump_signal_data(struct task_context *tc, ulong flags) + { +- int i, others, use_sighand; +- int translate, sig, sigpending; ++ int i, sigrtmax, others, use_sighand; ++ int translate, sigpending; + uint ti_flags; + ulonglong sigset, blocked, mask; +- ulong signal_struct, kaddr, handler, flags, sigqueue, next; ++ ulong signal_struct, kaddr, handler, sa_flags, sigqueue; + ulong sighand_struct; + long size; + char *signal_buf, *uaddr; ++ ulong shared_pending, signal; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + char buf4[BUFSIZE]; + +- sigset = task_signal(tc->task); ++ if (VALID_STRUCT(sigqueue) && !VALID_MEMBER(sigqueue_next)) { ++ MEMBER_OFFSET_INIT(sigqueue_next, "sigqueue", "next"); ++ MEMBER_OFFSET_INIT(sigqueue_list, "sigqueue", "list"); ++ MEMBER_OFFSET_INIT(sigqueue_info, "sigqueue", "info"); ++ } else if (!VALID_MEMBER(signal_queue_next)) { ++ MEMBER_OFFSET_INIT(signal_queue_next, "signal_queue", "next"); ++ MEMBER_OFFSET_INIT(signal_queue_info, "signal_queue", "info"); ++ } ++ ++ sigset = task_signal(tc->task, 0); + if (!tt->last_task_read) + return; +- blocked = task_blocked(tc->task); +- +- if (VALID_MEMBER(task_struct_sigpending)) +- sigpending = INT(tt->task_struct + +- OFFSET(task_struct_sigpending)); +- else if (VALID_MEMBER(thread_info_flags)) { +- fill_thread_info(tc->thread_info); +- ti_flags = UINT(tt->thread_info + OFFSET(thread_info_flags)); +- sigpending = ti_flags & (1<task_struct + +@@ -5443,143 +7300,259 @@ + signal_struct = ULONG(tt->task_struct + + OFFSET(task_struct_signal)); + +- fprintf(fp, "SIGNAL_STRUCT: %lx ", signal_struct); +- + size = MAX(SIZE(signal_struct), VALID_SIZE(signal_queue) ? + SIZE(signal_queue) : SIZE(sigqueue)); + if (VALID_SIZE(sighand_struct)) + size = MAX(size, SIZE(sighand_struct)); + signal_buf = GETBUF(size); + +- readmem(signal_struct, KVADDR, signal_buf, +- SIZE(signal_struct), "signal_struct buffer", +- FAULT_ON_ERROR); +- fprintf(fp, "COUNT: %d\n", +- INT(signal_buf + OFFSET(signal_struct_count))); +- +- fprintf(fp, " SIG %s %s %s %s\n", +- mkstring(buf1, VADDR_PRLEN == 8 ? 9 : VADDR_PRLEN, +- CENTER, "SIGACTION"), ++ if (signal_struct) ++ readmem(signal_struct, KVADDR, signal_buf, ++ SIZE(signal_struct), "signal_struct buffer", ++ FAULT_ON_ERROR); ++ ++ /* ++ * Signal dispositions (thread group level). ++ */ ++ if (flags & THREAD_GROUP_LEVEL) { ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ fprintf(fp, "SIGNAL_STRUCT: %lx ", signal_struct); ++ if (!signal_struct) { ++ fprintf(fp, "\n"); ++ return; ++ } ++ fprintf(fp, "COUNT: %d\n", ++ INT(signal_buf + OFFSET(signal_struct_count))); ++ ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ fprintf(fp, " SIG %s %s %s %s\n", ++ mkstring(buf1, VADDR_PRLEN == 8 ? 9 : VADDR_PRLEN, ++ CENTER, "SIGACTION"), + mkstring(buf2, UVADDR_PRLEN, RJUST, "HANDLER"), + mkstring(buf3, 16, CENTER, "MASK"), + mkstring(buf4, VADDR_PRLEN, LJUST, "FLAGS")); + +- if (VALID_MEMBER(task_struct_sighand)) { +- sighand_struct = ULONG(tt->task_struct + +- OFFSET(task_struct_sighand)); +- readmem(sighand_struct, KVADDR, signal_buf, +- SIZE(sighand_struct), "sighand_struct buffer", +- FAULT_ON_ERROR); +- use_sighand = TRUE; +- } else +- use_sighand = FALSE; +- +- for (i = 1; i < _NSIG; i++) { +- fprintf(fp, "%s[%d] ", i < 10 ? " " : "", i); +- +- if (use_sighand) { +- kaddr = sighand_struct + OFFSET(sighand_struct_action) + +- ((i-1) * SIZE(k_sigaction)); +- uaddr = signal_buf + OFFSET(sighand_struct_action) + +- ((i-1) * SIZE(k_sigaction)); +- } else { +- kaddr = signal_struct + OFFSET(signal_struct_action) + +- ((i-1) * SIZE(k_sigaction)); +- uaddr = signal_buf + OFFSET(signal_struct_action) + +- ((i-1) * SIZE(k_sigaction)); +- } ++ if (VALID_MEMBER(task_struct_sighand)) { ++ sighand_struct = ULONG(tt->task_struct + ++ OFFSET(task_struct_sighand)); ++ readmem(sighand_struct, KVADDR, signal_buf, ++ SIZE(sighand_struct), "sighand_struct buffer", ++ FAULT_ON_ERROR); ++ use_sighand = TRUE; ++ } else ++ use_sighand = FALSE; + +- handler = ULONG(uaddr + OFFSET(sigaction_sa_handler)); +- switch ((long)handler) +- { +- case -1: +- mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_ERR"); +- break; +- case 0: +- mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_DFL"); +- break; +- case 1: +- mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_IGN"); +- break; +- default: +- mkstring(buf1, UVADDR_PRLEN, RJUST|LONG_HEX, +- MKSTR(handler)); +- break; +- } ++ sigrtmax = sigrt_minmax(NULL, NULL); + +- mask = sigaction_mask((ulong)uaddr); +- flags = ULONG(uaddr + OFFSET(sigaction_sa_flags)); ++ for (i = 1; i <= sigrtmax; i++) { ++ if (flags & TASK_INDENT) ++ INDENT(2); + +- fprintf(fp, "%s%s %s %016llx %lx ", +- space(MINSPACE-1), +- mkstring(buf2,UVADDR_PRLEN,LJUST|LONG_HEX,MKSTR(kaddr)), +- buf1, +- mask, +- flags); +- +- if (flags) { +- others = 0; translate = 1; +- if (flags & SA_NOCLDSTOP) +- fprintf(fp, "%s%sSA_NOCLDSTOP", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); ++ fprintf(fp, "%s[%d] ", i < 10 ? " " : "", i); ++ ++ if (use_sighand) { ++ kaddr = sighand_struct + ++ OFFSET(sighand_struct_action) + ++ ((i-1) * SIZE(k_sigaction)); ++ uaddr = signal_buf + ++ OFFSET(sighand_struct_action) + ++ ((i-1) * SIZE(k_sigaction)); ++ } else { ++ kaddr = signal_struct + ++ OFFSET(signal_struct_action) + ++ ((i-1) * SIZE(k_sigaction)); ++ uaddr = signal_buf + ++ OFFSET(signal_struct_action) + ++ ((i-1) * SIZE(k_sigaction)); ++ } ++ ++ handler = ULONG(uaddr + OFFSET(sigaction_sa_handler)); ++ switch ((long)handler) ++ { ++ case -1: ++ mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_ERR"); ++ break; ++ case 0: ++ mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_DFL"); ++ break; ++ case 1: ++ mkstring(buf1, UVADDR_PRLEN, RJUST, "SIG_IGN"); ++ break; ++ default: ++ mkstring(buf1, UVADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(handler)); ++ break; ++ } ++ ++ mask = sigaction_mask((ulong)uaddr); ++ sa_flags = ULONG(uaddr + OFFSET(sigaction_sa_flags)); ++ ++ fprintf(fp, "%s%s %s %016llx %lx ", ++ space(MINSPACE-1), ++ mkstring(buf2, ++ UVADDR_PRLEN,LJUST|LONG_HEX,MKSTR(kaddr)), ++ buf1, ++ mask, ++ sa_flags); ++ ++ if (sa_flags) { ++ others = 0; translate = 1; ++ if (sa_flags & SA_NOCLDSTOP) ++ fprintf(fp, "%s%sSA_NOCLDSTOP", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); + #ifdef SA_RESTORER +- if (flags & SA_RESTORER) +- fprintf(fp, "%s%sSA_RESTORER", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); ++ if (sa_flags & SA_RESTORER) ++ fprintf(fp, "%s%sSA_RESTORER", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); + #endif + #ifdef SA_NOCLDWAIT +- if (flags & SA_NOCLDWAIT) +- fprintf(fp, "%s%sSA_NOCLDWAIT", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); ++ if (sa_flags & SA_NOCLDWAIT) ++ fprintf(fp, "%s%sSA_NOCLDWAIT", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); + #endif +- if (flags & SA_SIGINFO) +- fprintf(fp, "%s%sSA_SIGINFO", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); +- if (flags & SA_ONSTACK) +- fprintf(fp, "%s%sSA_ONSTACK", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); +- if (flags & SA_RESTART) +- fprintf(fp, "%s%sSA_RESTART", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); +- if (flags & SA_NODEFER) +- fprintf(fp, "%s%sSA_NODEFER", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); +- if (flags & SA_RESETHAND) +- fprintf(fp, "%s%sSA_RESETHAND", +- translate-- > 0 ? "(" : "", +- others++ ? "|" : ""); +- if (translate < 1) +- fprintf(fp, ")"); +- } +- +- fprintf(fp, "\n"); +- } +- +- if (VALID_MEMBER(task_struct_sigqueue)) +- sigqueue = ULONG(tt->task_struct + +- OFFSET(task_struct_sigqueue)); +- +- else if (VALID_MEMBER(task_struct_pending)) +- sigqueue = ULONG(tt->task_struct + +- OFFSET(task_struct_pending) + +- OFFSET_OPTION(sigpending_head, sigpending_list)); +- +- if (VALID_MEMBER(sigqueue_list) && empty_list(sigqueue)) +- sigqueue = 0; +- +- if (sigqueue) +- fprintf(fp, "SIGQUEUE: SIG %s\n", +- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SIGINFO")); +- else +- fprintf(fp, "SIGQUEUE: (empty)\n"); ++ if (sa_flags & SA_SIGINFO) ++ fprintf(fp, "%s%sSA_SIGINFO", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); ++ if (sa_flags & SA_ONSTACK) ++ fprintf(fp, "%s%sSA_ONSTACK", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); ++ if (sa_flags & SA_RESTART) ++ fprintf(fp, "%s%sSA_RESTART", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); ++ if (sa_flags & SA_NODEFER) ++ fprintf(fp, "%s%sSA_NODEFER", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); ++ if (sa_flags & SA_RESETHAND) ++ fprintf(fp, "%s%sSA_RESETHAND", ++ translate-- > 0 ? "(" : "", ++ others++ ? "|" : ""); ++ if (translate < 1) ++ fprintf(fp, ")"); ++ } ++ ++ fprintf(fp, "\n"); ++ } ++ } ++ ++ if (flags & TASK_LEVEL) { ++ /* ++ * Pending signals (task level). ++ */ ++ if (VALID_MEMBER(task_struct_sigpending)) ++ sigpending = INT(tt->task_struct + ++ OFFSET(task_struct_sigpending)); ++ else if (VALID_MEMBER(thread_info_flags)) { ++ fill_thread_info(tc->thread_info); ++ ti_flags = UINT(tt->thread_info + OFFSET(thread_info_flags)); ++ sigpending = ti_flags & (1<task); ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ fprintf(fp, " BLOCKED: %016llx\n", blocked); ++ ++ /* ++ * Pending queue (task level). ++ */ ++ ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ if (VALID_MEMBER(signal_struct_shared_pending)) { ++ fprintf(fp, "PRIVATE_PENDING\n"); ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ } ++ fprintf(fp, " SIGNAL: %016llx\n", sigset); ++ ++ if (VALID_MEMBER(task_struct_sigqueue)) ++ sigqueue = ULONG(tt->task_struct + ++ OFFSET(task_struct_sigqueue)); ++ ++ else if (VALID_MEMBER(task_struct_pending)) ++ sigqueue = ULONG(tt->task_struct + ++ OFFSET(task_struct_pending) + ++ OFFSET_OPTION(sigpending_head, ++ sigpending_list)); ++ ++ if (VALID_MEMBER(sigqueue_list) && empty_list(sigqueue)) ++ sigqueue = 0; ++ ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ if (sigqueue) { ++ fprintf(fp, " SIGQUEUE: SIG %s\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SIGINFO")); ++ sigqueue_list(sigqueue); ++ } else ++ fprintf(fp, " SIGQUEUE: (empty)\n"); ++ } ++ ++ /* ++ * Pending queue (thread group level). ++ */ ++ if ((flags & THREAD_GROUP_LEVEL) && ++ VALID_MEMBER(signal_struct_shared_pending)) { ++ ++ fprintf(fp, "SHARED_PENDING\n"); ++ shared_pending = signal_struct + OFFSET(signal_struct_shared_pending); ++ signal = shared_pending + OFFSET(sigpending_signal); ++ readmem(signal, KVADDR, signal_buf,SIZE(sigpending_signal), ++ "signal", FAULT_ON_ERROR); ++ sigset = task_signal(0, (ulong*)signal_buf); ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ fprintf(fp, " SIGNAL: %016llx\n", sigset); ++ sigqueue = (shared_pending + ++ OFFSET_OPTION(sigpending_head, sigpending_list) + ++ OFFSET(list_head_next)); ++ readmem(sigqueue,KVADDR, signal_buf, ++ SIZE(sigqueue), "sigqueue", FAULT_ON_ERROR); ++ sigqueue = ULONG(signal_buf); ++ ++ if (VALID_MEMBER(sigqueue_list) && empty_list(sigqueue)) ++ sigqueue = 0; ++ if (flags & TASK_INDENT) ++ INDENT(2); ++ if (sigqueue) { ++ fprintf(fp, " SIGQUEUE: SIG %s\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SIGINFO")); ++ sigqueue_list(sigqueue); ++ } else ++ fprintf(fp, " SIGQUEUE: (empty)\n"); ++ } ++ FREEBUF(signal_buf); ++} ++ ++/* ++ * Dump a pending signal queue (private/shared). ++ */ ++ ++static void sigqueue_list(ulong sigqueue) { ++ ulong sigqueue_save, next; ++ int sig; ++ char *signal_buf; ++ long size; ++ size = VALID_SIZE(signal_queue) ? SIZE(signal_queue) : SIZE(sigqueue); ++ signal_buf = GETBUF(size); + ++ sigqueue_save = sigqueue; + while (sigqueue) { + readmem(sigqueue, KVADDR, signal_buf, + SIZE_OPTION(signal_queue, sigqueue), +@@ -5597,14 +7570,17 @@ + OFFSET(siginfo_si_signo)); + } + +- fprintf(fp, " %3d %lx\n", ++ if (sigqueue_save == next) ++ break; ++ ++ fprintf(fp, " %3d %lx\n", + sig, sigqueue + + OFFSET_OPTION(signal_queue_info, sigqueue_info)); + + sigqueue = next; + } +- + FREEBUF(signal_buf); ++ + } + + /* +@@ -5614,12 +7590,13 @@ + */ + + static ulonglong +-task_signal(ulong task) ++task_signal(ulong task, ulong *signal) + { + ulonglong sigset; + ulong *sigset_ptr; + +- fill_task_struct(task); ++ if (task) { ++ fill_task_struct(task); + + if (!tt->last_task_read) + return 0; +@@ -5633,6 +7610,10 @@ + OFFSET(task_struct_signal)); + } else + return 0; ++ } else if (signal) { ++ sigset_ptr = signal; ++ } else ++ return 0; + + switch (_NSIG_WORDS) + { +--- crash/xen_hyper_dump_tables.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/xen_hyper_dump_tables.c 2008-12-02 10:37:11.000000000 -0500 +@@ -0,0 +1,950 @@ ++/* ++ * xen_hyper_dump_tables.c ++ * ++ * Portions Copyright (C) 2006-2007 Fujitsu Limited ++ * Portions Copyright (C) 2006-2007 VA Linux Systems Japan K.K. ++ * ++ * Authors: Itsuro Oda ++ * Fumihiko Kakuma ++ * ++ * This file is part of Xencrash. ++ * ++ * Xencrash 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 (version 2 of the License). ++ * ++ * Xencrash 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with Xencrash; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "defs.h" ++ ++#ifdef XEN_HYPERVISOR_ARCH ++#include "xen_hyper_defs.h" ++ ++static void xen_hyper_dump_xen_hyper_table(int verbose); ++static void xen_hyper_dump_xen_hyper_dumpinfo_table(int verbose); ++static void xen_hyper_dump_xen_hyper_domain_table(int verbose); ++static void xen_hyper_dump_xen_hyper_vcpu_table(int verbose); ++static void xen_hyper_dump_xen_hyper_pcpu_table(int verbose); ++static void xen_hyper_dump_xen_hyper_sched_table(int verbose); ++static void xen_hyper_dump_xen_hyper_size_table(char *spec, ulong makestruct); ++static void xen_hyper_dump_xen_hyper_offset_table(char *spec, ulong makestruct); ++ ++static void xen_hyper_dump_mem(void *mem, ulong len, int dsz); ++ ++/* ++ * Get help for a command, to dump an internal table, or the GNU public ++ * license copying/warranty information. ++ */ ++void ++xen_hyper_cmd_help(void) ++{ ++ int c; ++ int oflag; ++ ++ oflag = 0; ++ ++ while ((c = getopt(argcnt, args, ++ "aBbcDgHhM:mnOopszX:")) != EOF) { ++ switch(c) ++ { ++ case 'a': ++ dump_alias_data(); ++ return; ++ case 'b': ++ dump_shared_bufs(); ++ return; ++ case 'B': ++ dump_build_data(); ++ return; ++ case 'c': ++ dump_numargs_cache(); ++ return; ++ case 'n': ++ case 'D': ++ dumpfile_memory(DUMPFILE_MEM_DUMP); ++ return; ++ case 'g': ++ dump_gdb_data(); ++ return; ++ case 'H': ++ dump_hash_table(VERBOSE); ++ return; ++ case 'h': ++ dump_hash_table(!VERBOSE); ++ return; ++ case 'M': ++ dump_machdep_table(stol(optarg, FAULT_ON_ERROR, NULL)); ++ return; ++ case 'm': ++ dump_machdep_table(0); ++ return; ++ case 'O': ++ dump_offset_table(NULL, TRUE); ++ return; ++ case 'o': ++ oflag = TRUE; ++ break; ++ case 'p': ++ dump_program_context(); ++ return; ++ case 's': ++ dump_symbol_table(); ++ return; ++ case 'X': ++ if (strlen(optarg) != 3) { ++ argerrs++; ++ break; ++ } ++ if (!strncmp("Xen", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_table(VERBOSE); ++ else if (!strncmp("xen", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_table(!VERBOSE); ++ else if (!strncmp("Dmp", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_dumpinfo_table(VERBOSE); ++ else if (!strncmp("dmp", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_dumpinfo_table(!VERBOSE); ++ else if (!strncmp("Dom", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_domain_table(VERBOSE); ++ else if (!strncmp("dom", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_domain_table(!VERBOSE); ++ else if (!strncmp("Vcp", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_vcpu_table(VERBOSE); ++ else if (!strncmp("vcp", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_vcpu_table(!VERBOSE); ++ else if (!strncmp("Pcp", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_pcpu_table(VERBOSE); ++ else if (!strncmp("pcp", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_pcpu_table(!VERBOSE); ++ else if (!strncmp("Sch", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_sched_table(VERBOSE); ++ else if (!strncmp("sch", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_sched_table(!VERBOSE); ++ else if (!strncmp("siz", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_size_table(NULL, TRUE); ++ else if (!strncmp("ofs", optarg, strlen(optarg))) ++ xen_hyper_dump_xen_hyper_offset_table(NULL, TRUE); ++ else { ++ argerrs++; ++ break; ++ } ++ return; ++ case 'z': ++ fprintf(fp, "help options:\n"); ++ fprintf(fp, " -a - alias data\n"); ++ fprintf(fp, " -b - shared buffer data\n"); ++ fprintf(fp, " -B - build data\n"); ++ fprintf(fp, " -c - numargs cache\n"); ++ fprintf(fp, " -M machine specific\n"); ++ fprintf(fp, " -m - machdep_table\n"); ++ fprintf(fp, " -s - symbol table data\n"); ++ fprintf(fp, " -o - offset_table and size_table\n"); ++ fprintf(fp, " -p - program_context\n"); ++ fprintf(fp, " -h - hash_table data\n"); ++ fprintf(fp, " -H - hash_table data (verbose)\n"); ++ fprintf(fp, " -X Xen - xen table data (verbose)\n"); ++ fprintf(fp, " -X xen - xen table data\n"); ++ fprintf(fp, " -X Dmp - dumpinfo table data (verbose)\n"); ++ fprintf(fp, " -X dmp - dumpinfo table data\n"); ++ fprintf(fp, " -X Dom - domain table data (verbose)\n"); ++ fprintf(fp, " -X dom - domain table data\n"); ++ fprintf(fp, " -X Vcp - vcpu table data (verbose)\n"); ++ fprintf(fp, " -X vcp - vcpu table data\n"); ++ fprintf(fp, " -X Pcp - pcpu table data (verbose)\n"); ++ fprintf(fp, " -X pcp - pcpu table data\n"); ++ fprintf(fp, " -X Sch - schedule table data (verbose)\n"); ++ fprintf(fp, " -X sch - schedule table data\n"); ++ fprintf(fp, " -X siz - size table data\n"); ++ fprintf(fp, " -X ofs - offset table data\n"); ++ return; ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, COMPLETE_HELP); ++ ++ if (!args[optind]) { ++ if (oflag) ++ dump_offset_table(NULL, FALSE); ++ else ++ display_help_screen(""); ++ return; ++ } ++ ++ do { ++ if (oflag) ++ dump_offset_table(args[optind], FALSE); ++ else ++ cmd_usage(args[optind], COMPLETE_HELP); ++ optind++; ++ } while (args[optind]); ++} ++ ++/* ++ * "help -x xen" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_table(int verbose) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ uint cpuid; ++ int len, flag, i; ++ ++ len = 14; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "cpu_data_address: ", buf, flag, ++ (buf, "%lu\n", xht->cpu_data_address)); ++ XEN_HYPER_PRI(fp, len, "cpu_curr: ", buf, flag, ++ (buf, "%u\n", xht->cpu_curr)); ++ XEN_HYPER_PRI(fp, len, "max_cpus: ", buf, flag, ++ (buf, "%u\n", xht->max_cpus)); ++ XEN_HYPER_PRI(fp, len, "cores: ", buf, flag, ++ (buf, "%d\n", xht->cores)); ++ XEN_HYPER_PRI(fp, len, "pcpus: ", buf, flag, ++ (buf, "%d\n", xht->pcpus)); ++ XEN_HYPER_PRI(fp, len, "vcpus: ", buf, flag, ++ (buf, "%d\n", xht->vcpus)); ++ XEN_HYPER_PRI(fp, len, "domains: ", buf, flag, ++ (buf, "%d\n", xht->domains)); ++ XEN_HYPER_PRI(fp, len, "sys_pages: ", buf, flag, ++ (buf, "%lu\n", xht->sys_pages)); ++ XEN_HYPER_PRI(fp, len, "crashing_cpu: ", buf, flag, ++ (buf, "%d\n", xht->crashing_cpu)); ++ XEN_HYPER_PRI(fp, len, "crashing_vcc: ", buf, flag, ++ (buf, "%p\n", xht->crashing_vcc)); ++ XEN_HYPER_PRI(fp, len, "max_page: ", buf, flag, ++ (buf, "%lu\n", xht->max_page)); ++ XEN_HYPER_PRI(fp, len, "total_pages: ", buf, flag, ++ (buf, "%lu\n", xht->total_pages)); ++ XEN_HYPER_PRI(fp, len, "cpumask: ", buf, flag, ++ (buf, "%p\n", xht->cpumask)); ++ if (verbose && xht->cpumask) { ++ xen_hyper_dump_mem(xht->cpumask, ++ XEN_HYPER_SIZE(cpumask_t), sizeof(long)); ++ } ++ XEN_HYPER_PRI(fp, len, "cpu_idxs: ", buf, flag, ++ (buf, "%p\n", xht->cpu_idxs)); ++ if (verbose) { ++ for_cpu_indexes(i, cpuid) ++ fprintf(fp, "%03d : %d\n", i, cpuid); ++ } ++} ++ ++/* ++ * "help -x dmp" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_dumpinfo_table(int verbose) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ int len, flag; ++ ++ len = 25; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "note_ver: ", buf, flag, ++ (buf, "%u\n", xhdit->note_ver)); ++ XEN_HYPER_PRI(fp, len, "context_array: ", buf, flag, ++ (buf, "%p\n", xhdit->context_array)); ++ if (verbose && xhdit->context_array) { ++ xen_hyper_dump_mem((long *)xhdit->context_array, ++ sizeof(struct xen_hyper_dumpinfo_context) * ++ XEN_HYPER_MAX_CPUS(), sizeof(long)); ++ } ++ XEN_HYPER_PRI(fp, len, "context_xen_core_array: ", buf, flag, ++ (buf, "%p\n", xhdit->context_xen_core_array)); ++ if (verbose && xhdit->context_xen_core_array) { ++ xen_hyper_dump_mem((long *)xhdit->context_xen_core_array, ++ sizeof(struct xen_hyper_dumpinfo_context_xen_core) * ++ XEN_HYPER_MAX_CPUS(), sizeof(long)); ++ } ++ XEN_HYPER_PRI_CONST(fp, len, "context_xen_info: ", flag|XEN_HYPER_PRI_LF); ++ XEN_HYPER_PRI(fp, len, "note: ", buf, flag, ++ (buf, "%lx\n", xhdit->context_xen_info.note)); ++ XEN_HYPER_PRI(fp, len, "pcpu_id: ", buf, flag, ++ (buf, "%u\n", xhdit->context_xen_info.pcpu_id)); ++ XEN_HYPER_PRI(fp, len, "crash_xen_info_ptr: ", buf, flag, ++ (buf, "%p\n", xhdit->context_xen_info.crash_xen_info_ptr)); ++ XEN_HYPER_PRI(fp, len, "crash_note_core_array: ", buf, flag, ++ (buf, "%p\n", xhdit->crash_note_core_array)); ++ if (verbose && xhdit->crash_note_core_array) { ++ xen_hyper_dump_mem((long *)xhdit->crash_note_core_array, ++ xhdit->core_size * XEN_HYPER_NR_PCPUS(), ++ sizeof(long)); ++ } ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_core_array: ", buf, flag, ++ (buf, "%p\n", xhdit->crash_note_xen_core_array)); ++ if (verbose && xhdit->crash_note_xen_core_array) { ++ xen_hyper_dump_mem( ++ xhdit->crash_note_xen_core_array, ++ xhdit->xen_core_size * XEN_HYPER_NR_PCPUS(), ++ sizeof(long)); ++ } ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_info_ptr: ", buf, flag, ++ (buf, "%p\n", xhdit->crash_note_xen_info_ptr)); ++ if (verbose && xhdit->crash_note_xen_info_ptr) { ++ xen_hyper_dump_mem( ++ xhdit->crash_note_xen_info_ptr, ++ xhdit->xen_info_size, sizeof(long)); ++ } ++ XEN_HYPER_PRI(fp, len, "xen_info_cpu: ", buf, flag, ++ (buf, "%u\n", xhdit->xen_info_cpu)); ++ XEN_HYPER_PRI(fp, len, "note_size: ", buf, flag, ++ (buf, "%u\n", xhdit->note_size)); ++ XEN_HYPER_PRI(fp, len, "core_offset: ", buf, flag, ++ (buf, "%u\n", xhdit->core_offset)); ++ XEN_HYPER_PRI(fp, len, "core_size: ", buf, flag, ++ (buf, "%u\n", xhdit->core_size)); ++ XEN_HYPER_PRI(fp, len, "xen_core_offset: ", buf, flag, ++ (buf, "%u\n", xhdit->xen_core_offset)); ++ XEN_HYPER_PRI(fp, len, "xen_core_size: ", buf, flag, ++ (buf, "%u\n", xhdit->xen_core_size)); ++ XEN_HYPER_PRI(fp, len, "xen_info_offset: ", buf, flag, ++ (buf, "%u\n", xhdit->xen_info_offset)); ++ XEN_HYPER_PRI(fp, len, "xen_info_size: ", buf, flag, ++ (buf, "%u\n", xhdit->xen_info_size)); ++} ++ ++/* ++ * "help -x dom" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_domain_table(int verbose) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ struct xen_hyper_domain_context *dcca; ++ int len, flag, i; ++ ++ len = 22; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "context_array: ", buf, flag, ++ (buf, "%p\n", xhdt->context_array)); ++ if (verbose) { ++ char buf1[XEN_HYPER_CMD_BUFSIZE]; ++ int j; ++ for (i = 0, dcca = xhdt->context_array; ++ i < xhdt->context_array_cnt; i++, dcca++) { ++ snprintf(buf, XEN_HYPER_CMD_BUFSIZE, "context_array[%d]: ", i); ++ XEN_HYPER_PRI_CONST(fp, len, buf, flag|XEN_HYPER_PRI_LF); ++ XEN_HYPER_PRI(fp, len, "domain: ", buf, flag, ++ (buf, "%lx\n", dcca->domain)); ++ XEN_HYPER_PRI(fp, len, "domain_id: ", buf, flag, ++ (buf, "%d\n", dcca->domain_id)); ++ XEN_HYPER_PRI(fp, len, "tot_pages: ", buf, flag, ++ (buf, "%x\n", dcca->tot_pages)); ++ XEN_HYPER_PRI(fp, len, "max_pages: ", buf, flag, ++ (buf, "%x\n", dcca->max_pages)); ++ XEN_HYPER_PRI(fp, len, "xenheap_pages: ", buf, flag, ++ (buf, "%x\n", dcca->xenheap_pages)); ++ XEN_HYPER_PRI(fp, len, "shared_info: ", buf, flag, ++ (buf, "%lx\n", dcca->shared_info)); ++ XEN_HYPER_PRI(fp, len, "sched_priv: ", buf, flag, ++ (buf, "%lx\n", dcca->sched_priv)); ++ XEN_HYPER_PRI(fp, len, "next_in_list: ", buf, flag, ++ (buf, "%lx\n", dcca->next_in_list)); ++ XEN_HYPER_PRI(fp, len, "domain_flags: ", buf, flag, ++ (buf, "%lx\n", dcca->domain_flags)); ++ XEN_HYPER_PRI(fp, len, "evtchn: ", buf, flag, ++ (buf, "%lx\n", dcca->evtchn)); ++ XEN_HYPER_PRI(fp, len, "vcpu_cnt: ", buf, flag, ++ (buf, "%d\n", dcca->vcpu_cnt)); ++ for (j = 0; j < XEN_HYPER_MAX_VIRT_CPUS; j++) { ++ snprintf(buf1, XEN_HYPER_CMD_BUFSIZE, "vcpu[%d]: ", j); ++ XEN_HYPER_PRI(fp, len, buf1, buf, flag, ++ (buf, "%lx\n", dcca->vcpu[j])); ++ } ++ XEN_HYPER_PRI(fp, len, "vcpu_context_array: ", buf, flag, ++ (buf, "%p\n", dcca->vcpu_context_array)); ++ } ++ } ++ XEN_HYPER_PRI(fp, len, "context_array_cnt: ", buf, flag, ++ (buf, "%d\n", xhdt->context_array_cnt)); ++ XEN_HYPER_PRI(fp, len, "running_domains: ", buf, flag, ++ (buf, "%lu\n", xhdt->running_domains)); ++ XEN_HYPER_PRI(fp, len, "dom_io: ", buf, flag, ++ (buf, "%p\n", xhdt->dom_io)); ++ XEN_HYPER_PRI(fp, len, "dom_xen: ", buf, flag, ++ (buf, "%p\n", xhdt->dom_xen)); ++ XEN_HYPER_PRI(fp, len, "dom0: ", buf, flag, ++ (buf, "%p\n", xhdt->dom0)); ++ XEN_HYPER_PRI(fp, len, "idle_domain: ", buf, flag, ++ (buf, "%p\n", xhdt->idle_domain)); ++ XEN_HYPER_PRI(fp, len, "curr_domain: ", buf, flag, ++ (buf, "%p\n", xhdt->curr_domain)); ++ XEN_HYPER_PRI(fp, len, "last: ", buf, flag, ++ (buf, "%p\n", xhdt->last)); ++ XEN_HYPER_PRI(fp, len, "domain_struct: ", buf, flag, ++ (buf, "%p\n", xhdt->domain_struct)); ++ XEN_HYPER_PRI(fp, len, "domain_struct_verify: ", buf, flag, ++ (buf, "%p\n", xhdt->domain_struct_verify)); ++} ++ ++/* ++ * "help -x vcp" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_vcpu_table(int verbose) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ int len, flag; ++ ++ len = 25; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "vcpu_context_arrays: ", buf, flag, ++ (buf, "%p\n", xhvct->vcpu_context_arrays)); ++ XEN_HYPER_PRI(fp, len, "vcpu_context_arrays_cnt: ", buf, flag, ++ (buf, "%d\n", xhvct->vcpu_context_arrays_cnt)); ++ if (verbose) { ++ struct xen_hyper_vcpu_context_array *vcca; ++ struct xen_hyper_vcpu_context *vca; ++ int i, j; ++ ++ for (i = 0, vcca = xhvct->vcpu_context_arrays; ++ i < xhvct->vcpu_context_arrays_cnt; i++, vcca++) { ++ snprintf(buf, XEN_HYPER_CMD_BUFSIZE, "vcpu_context_arrays[%d]: ", i); ++ XEN_HYPER_PRI_CONST(fp, len, buf, flag|XEN_HYPER_PRI_LF); ++ if (vcca->context_array) { ++ XEN_HYPER_PRI(fp, len, "context_array: ", buf, flag, ++ (buf, "%p\n", vcca->context_array)); ++ } else { ++ XEN_HYPER_PRI(fp, len, "context_array: ", buf, flag, ++ (buf, "NULL\n")); ++ } ++ XEN_HYPER_PRI(fp, len, "context_array_cnt: ", buf, flag, ++ (buf, "%d\n", vcca->context_array_cnt)); ++ XEN_HYPER_PRI(fp, len, "context_array_valid: ", buf, flag, ++ (buf, "%d\n", vcca->context_array_valid)); ++ for (j = 0, vca = vcca->context_array; ++ j < vcca->context_array_cnt; j++, vca++) { ++ snprintf(buf, XEN_HYPER_CMD_BUFSIZE, "context_array[%d]: ", j); ++ XEN_HYPER_PRI_CONST(fp, len, buf, flag|XEN_HYPER_PRI_LF); ++ XEN_HYPER_PRI(fp, len, "vcpu: ", buf, flag, ++ (buf, "%lx\n", vca->vcpu)); ++ XEN_HYPER_PRI(fp, len, "vcpu_id: ", buf, flag, ++ (buf, "%d\n", vca->vcpu_id)); ++ XEN_HYPER_PRI(fp, len, "processor: ", buf, flag, ++ (buf, "%d\n", vca->processor)); ++ XEN_HYPER_PRI(fp, len, "vcpu_info: ", buf, flag, ++ (buf, "%lx\n", vca->vcpu_info)); ++ XEN_HYPER_PRI(fp, len, "domain: ", buf, flag, ++ (buf, "%lx\n", vca->domain)); ++ XEN_HYPER_PRI(fp, len, "next_in_list: ", buf, flag, ++ (buf, "%lx\n", vca->next_in_list)); ++ XEN_HYPER_PRI(fp, len, "sleep_tick: ", buf, flag, ++ (buf, "%lx\n", vca->sleep_tick)); ++ XEN_HYPER_PRI(fp, len, "sched_priv: ", buf, flag, ++ (buf, "%lx\n", vca->sched_priv)); ++ XEN_HYPER_PRI(fp, len, "state: ", buf, flag, ++ (buf, "%d\n", vca->state)); ++ XEN_HYPER_PRI(fp, len, "state_entry_time: ", buf, flag, ++ (buf, "%llux\n", (unsigned long long)(vca->state_entry_time))); ++ XEN_HYPER_PRI(fp, len, "runstate_guest: ", buf, flag, ++ (buf, "%lx\n", vca->runstate_guest)); ++ XEN_HYPER_PRI(fp, len, "vcpu_flags: ", buf, flag, ++ (buf, "%lx\n", vca->vcpu_flags)); ++ } ++ } ++ } ++ XEN_HYPER_PRI(fp, len, "idle_vcpu: ", buf, flag, ++ (buf, "%lx\n", xhvct->idle_vcpu)); ++ XEN_HYPER_PRI(fp, len, "idle_vcpu_context_array: ", buf, flag, ++ (buf, "%p\n", xhvct->idle_vcpu_context_array)); ++ XEN_HYPER_PRI(fp, len, "last: ", buf, flag, ++ (buf, "%p\n", xhvct->last)); ++ XEN_HYPER_PRI(fp, len, "vcpu_struct: ", buf, flag, ++ (buf, "%p\n", xhvct->vcpu_struct)); ++ XEN_HYPER_PRI(fp, len, "vcpu_struct_verify: ", buf, flag, ++ (buf, "%p\n", xhvct->vcpu_struct_verify)); ++} ++ ++/* ++ * "help -x pcp" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_pcpu_table(int verbose) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ struct xen_hyper_pcpu_context *pcca; ++ int len, flag, i; ++#ifdef X86_64 ++ uint64_t *ist_p; ++ int j; ++#endif ++ ++ len = 21; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "context_array: ", buf, flag, ++ (buf, "%p\n", xhpct->context_array)); ++ if (verbose) { ++ for (i = 0, pcca = xhpct->context_array; ++ i < XEN_HYPER_MAX_CPUS(); i++, pcca++) { ++ snprintf(buf, XEN_HYPER_CMD_BUFSIZE, "context_array %d: ", i); ++ XEN_HYPER_PRI_CONST(fp, len, buf, flag|XEN_HYPER_PRI_LF); ++ XEN_HYPER_PRI(fp, len, "pcpu: ", buf, flag, ++ (buf, "%lx\n", pcca->pcpu)); ++ XEN_HYPER_PRI(fp, len, "processor_id: ", buf, flag, ++ (buf, "%u\n", pcca->processor_id)); ++ XEN_HYPER_PRI(fp, len, "guest_cpu_user_regs: ", buf, flag, ++ (buf, "%lx\n", pcca->guest_cpu_user_regs)); ++ XEN_HYPER_PRI(fp, len, "current_vcpu: ", buf, flag, ++ (buf, "%lx\n", pcca->current_vcpu)); ++ XEN_HYPER_PRI(fp, len, "init_tss: ", buf, flag, ++ (buf, "%lx\n", pcca->init_tss)); ++#ifdef X86 ++ XEN_HYPER_PRI(fp, len, "sp.esp0: ", buf, flag, ++ (buf, "%x\n", pcca->sp.esp0)); ++#endif ++#ifdef X86_64 ++ XEN_HYPER_PRI(fp, len, "sp.rsp0: ", buf, flag, ++ (buf, "%lx\n", pcca->sp.rsp0)); ++ for (j = 0, ist_p = pcca->ist; ++ j < XEN_HYPER_TSS_IST_MAX; j++, ist_p++) { ++ XEN_HYPER_PRI(fp, len, "ist: ", buf, flag, ++ (buf, "%lx\n", *ist_p)); ++ } ++#endif ++ } ++ } ++ XEN_HYPER_PRI(fp, len, "last: ", buf, flag, ++ (buf, "%p\n", xhpct->last)); ++ XEN_HYPER_PRI(fp, len, "pcpu_struct: ", buf, flag, ++ (buf, "%p\n", xhpct->pcpu_struct)); ++} ++ ++/* ++ * "help -x sch" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_sched_table(int verbose) ++{ ++ struct xen_hyper_sched_context *schc; ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ int len, flag, i; ++ ++ len = 21; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "name: ", buf, flag, ++ (buf, "%s\n", xhscht->name)); ++ XEN_HYPER_PRI(fp, len, "opt_sched: ", buf, flag, ++ (buf, "%s\n", xhscht->opt_sched)); ++ XEN_HYPER_PRI(fp, len, "sched_id: ", buf, flag, ++ (buf, "%d\n", xhscht->sched_id)); ++ XEN_HYPER_PRI(fp, len, "scheduler: ", buf, flag, ++ (buf, "%lx\n", xhscht->scheduler)); ++ XEN_HYPER_PRI(fp, len, "scheduler_struct: ", buf, flag, ++ (buf, "%p\n", xhscht->scheduler_struct)); ++ XEN_HYPER_PRI(fp, len, "sched_context_array: ", buf, flag, ++ (buf, "%p\n", xhscht->sched_context_array)); ++ if (verbose) { ++ for (i = 0, schc = xhscht->sched_context_array; ++ i < xht->pcpus; i++, schc++) { ++ XEN_HYPER_PRI(fp, len, "sched_context_array[", buf, ++ flag, (buf, "%d]\n", i)); ++ XEN_HYPER_PRI(fp, len, "schedule_data: ", buf, flag, ++ (buf, "%lx\n", schc->schedule_data)); ++ XEN_HYPER_PRI(fp, len, "curr: ", buf, flag, ++ (buf, "%lx\n", schc->curr)); ++ XEN_HYPER_PRI(fp, len, "idle: ", buf, flag, ++ (buf, "%lx\n", schc->idle)); ++ XEN_HYPER_PRI(fp, len, "sched_priv: ", buf, flag, ++ (buf, "%lx\n", schc->sched_priv)); ++ XEN_HYPER_PRI(fp, len, "tick: ", buf, flag, ++ (buf, "%lx\n", schc->tick)); ++ } ++ } ++} ++ ++/* ++ * "help -x siz" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_size_table(char *spec, ulong makestruct) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ int len, flag; ++ ++ len = 23; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.ELF_Prstatus)); ++ XEN_HYPER_PRI(fp, len, "ELF_Signifo: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.ELF_Signifo)); ++ XEN_HYPER_PRI(fp, len, "ELF_Gregset: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.ELF_Gregset)); ++ XEN_HYPER_PRI(fp, len, "ELF_Timeval: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.ELF_Timeval)); ++ XEN_HYPER_PRI(fp, len, "arch_domain: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.arch_domain)); ++ XEN_HYPER_PRI(fp, len, "arch_shared_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.arch_shared_info)); ++ XEN_HYPER_PRI(fp, len, "cpu_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.cpu_info)); ++ XEN_HYPER_PRI(fp, len, "cpu_time: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.cpu_time)); ++ XEN_HYPER_PRI(fp, len, "cpu_user_regs: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.cpu_user_regs)); ++ XEN_HYPER_PRI(fp, len, "cpumask_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.cpumask_t)); ++ XEN_HYPER_PRI(fp, len, "cpuinfo_ia64: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.cpuinfo_ia64)); ++ XEN_HYPER_PRI(fp, len, "cpuinfo_x86: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.cpuinfo_x86)); ++ XEN_HYPER_PRI(fp, len, "crash_note_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.crash_note_t)); ++ XEN_HYPER_PRI(fp, len, "crash_note_core_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.crash_note_core_t)); ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.crash_note_xen_t)); ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_core_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.crash_note_xen_core_t)); ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_info_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.crash_note_xen_info_t)); ++ XEN_HYPER_PRI(fp, len, "crash_xen_core_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.crash_xen_core_t)); ++ XEN_HYPER_PRI(fp, len, "crash_xen_info_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.crash_xen_info_t)); ++ XEN_HYPER_PRI(fp, len, "domain: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.domain)); ++#ifdef IA64 ++ XEN_HYPER_PRI(fp, len, "mm_struct: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.mm_struct)); ++#endif ++ XEN_HYPER_PRI(fp, len, "note_buf_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.note_buf_t)); ++ XEN_HYPER_PRI(fp, len, "schedule_data: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.schedule_data)); ++ XEN_HYPER_PRI(fp, len, "scheduler: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.scheduler)); ++ XEN_HYPER_PRI(fp, len, "shared_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.shared_info)); ++ XEN_HYPER_PRI(fp, len, "timer: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.timer)); ++ XEN_HYPER_PRI(fp, len, "tss_struct: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.tss_struct)); ++ XEN_HYPER_PRI(fp, len, "vcpu: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.vcpu)); ++ XEN_HYPER_PRI(fp, len, "vcpu_runstate_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.vcpu_runstate_info)); ++ XEN_HYPER_PRI(fp, len, "xen_crash_xen_regs_t: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_size_table.xen_crash_xen_regs_t)); ++} ++ ++/* ++ * "help -x ofs" output ++ */ ++static void ++xen_hyper_dump_xen_hyper_offset_table(char *spec, ulong makestruct) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ int len, flag; ++ ++ len = 45; ++ flag = XEN_HYPER_PRI_R; ++ ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_info)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_cursig: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_cursig)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_sigpend: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_sigpend)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_sighold: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_sighold)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_pid: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_pid)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_ppid: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_ppid)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_pgrp: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_pgrp)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_sid: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_sid)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_stime: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_stime)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_cutime: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_cutime)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_cstime: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_cstime)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_reg: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_reg)); ++ XEN_HYPER_PRI(fp, len, "ELF_Prstatus_pr_fpvalid: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Prstatus_pr_fpvalid)); ++ XEN_HYPER_PRI(fp, len, "ELF_Timeval_tv_sec: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Timeval_tv_sec)); ++ XEN_HYPER_PRI(fp, len, "ELF_Timeval_tv_usec: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.ELF_Timeval_tv_usec)); ++ ++#ifdef IA64 ++ XEN_HYPER_PRI(fp, len, "arch_domain_mm: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.arch_domain_mm)); ++#endif ++ ++ XEN_HYPER_PRI(fp, len, "arch_shared_info_max_pfn: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.arch_shared_info_max_pfn)); ++ XEN_HYPER_PRI(fp, len, "arch_shared_info_pfn_to_mfn_frame_list_list: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.arch_shared_info_pfn_to_mfn_frame_list_list)); ++ XEN_HYPER_PRI(fp, len, "arch_shared_info_nmi_reason: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.arch_shared_info_nmi_reason)); ++ ++ XEN_HYPER_PRI(fp, len, "cpu_info_guest_cpu_user_regs: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_info_guest_cpu_user_regs)); ++ XEN_HYPER_PRI(fp, len, "cpu_info_processor_id: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_info_processor_id)); ++ XEN_HYPER_PRI(fp, len, "cpu_info_current_vcpu: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_info_current_vcpu)); ++ ++ XEN_HYPER_PRI(fp, len, "cpu_time_local_tsc_stamp: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_time_local_tsc_stamp)); ++ XEN_HYPER_PRI(fp, len, "cpu_time_stime_local_stamp: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_time_stime_local_stamp)); ++ XEN_HYPER_PRI(fp, len, "cpu_time_stime_master_stamp: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_time_stime_master_stamp)); ++ XEN_HYPER_PRI(fp, len, "cpu_time_tsc_scale: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_time_tsc_scale)); ++ XEN_HYPER_PRI(fp, len, "cpu_time_calibration_timer: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.cpu_time_calibration_timer)); ++ ++ XEN_HYPER_PRI(fp, len, "crash_note_t_core: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_t_core)); ++ XEN_HYPER_PRI(fp, len, "crash_note_t_xen: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_t_xen)); ++ XEN_HYPER_PRI(fp, len, "crash_note_t_xen_regs: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_t_xen_regs)); ++ XEN_HYPER_PRI(fp, len, "crash_note_t_xen_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_t_xen_info)); ++ ++ XEN_HYPER_PRI(fp, len, "crash_note_core_t_note: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_core_t_note)); ++ XEN_HYPER_PRI(fp, len, "crash_note_core_t_desc: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_core_t_desc)); ++ ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_t_note: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_xen_t_note)); ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_t_desc: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_xen_t_desc)); ++ ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_core_t_note: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_xen_core_t_note)); ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_core_t_desc: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_xen_core_t_desc)); ++ ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_info_t_note: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_xen_info_t_note)); ++ XEN_HYPER_PRI(fp, len, "crash_note_xen_info_t_desc: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.crash_note_xen_info_t_desc)); ++ ++ XEN_HYPER_PRI(fp, len, "domain_page_list: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_page_list)); ++ XEN_HYPER_PRI(fp, len, "domain_xenpage_list: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_xenpage_list)); ++ XEN_HYPER_PRI(fp, len, "domain_domain_id: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_domain_id)); ++ XEN_HYPER_PRI(fp, len, "domain_tot_pages: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_tot_pages)); ++ XEN_HYPER_PRI(fp, len, "domain_max_pages: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_max_pages)); ++ XEN_HYPER_PRI(fp, len, "domain_xenheap_pages: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_xenheap_pages)); ++ XEN_HYPER_PRI(fp, len, "domain_shared_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_shared_info)); ++ XEN_HYPER_PRI(fp, len, "domain_sched_priv: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_sched_priv)); ++ XEN_HYPER_PRI(fp, len, "domain_next_in_list: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_next_in_list)); ++ XEN_HYPER_PRI(fp, len, "domain_domain_flags: ", buf, flag, ++ (buf, "%lx\n", xen_hyper_offset_table.domain_domain_flags)); ++ XEN_HYPER_PRI(fp, len, "domain_evtchn: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_evtchn)); ++ XEN_HYPER_PRI(fp, len, "domain_is_hvm: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_is_hvm)); ++ XEN_HYPER_PRI(fp, len, "domain_is_privileged: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_is_privileged)); ++ XEN_HYPER_PRI(fp, len, "domain_debugger_attached: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_debugger_attached)); ++ if (XEN_HYPER_VALID_MEMBER(domain_is_polling)) { ++ XEN_HYPER_PRI(fp, len, "domain_is_polling: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_is_polling)); ++ } ++ XEN_HYPER_PRI(fp, len, "domain_is_dying: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_is_dying)); ++ XEN_HYPER_PRI(fp, len, "domain_is_paused_by_controller: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_is_paused_by_controller)); ++ XEN_HYPER_PRI(fp, len, "domain_is_shutting_down: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_is_shutting_down)); ++ XEN_HYPER_PRI(fp, len, "domain_is_shut_down: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_is_shut_down)); ++ XEN_HYPER_PRI(fp, len, "domain_vcpu: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_vcpu)); ++ XEN_HYPER_PRI(fp, len, "domain_arch: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.domain_arch)); ++ ++#ifdef IA64 ++ XEN_HYPER_PRI(fp, len, "mm_struct_pgd: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.mm_struct_pgd)); ++#endif ++ ++ XEN_HYPER_PRI(fp, len, "schedule_data_schedule_lock: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.schedule_data_schedule_lock)); ++ XEN_HYPER_PRI(fp, len, "schedule_data_curr: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.schedule_data_curr)); ++ XEN_HYPER_PRI(fp, len, "schedule_data_idle: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.schedule_data_idle)); ++ XEN_HYPER_PRI(fp, len, "schedule_data_sched_priv: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.schedule_data_sched_priv)); ++ XEN_HYPER_PRI(fp, len, "schedule_data_s_timer: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.schedule_data_s_timer)); ++ XEN_HYPER_PRI(fp, len, "schedule_data_tick: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.schedule_data_tick)); ++ ++ XEN_HYPER_PRI(fp, len, "scheduler_name: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_name)); ++ XEN_HYPER_PRI(fp, len, "scheduler_opt_name: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_opt_name)); ++ XEN_HYPER_PRI(fp, len, "scheduler_sched_id: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_sched_id)); ++ XEN_HYPER_PRI(fp, len, "scheduler_init: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_init)); ++ XEN_HYPER_PRI(fp, len, "scheduler_tick: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_tick)); ++ XEN_HYPER_PRI(fp, len, "scheduler_init_vcpu: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_init_vcpu)); ++ XEN_HYPER_PRI(fp, len, "scheduler_destroy_domain: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_destroy_domain)); ++ XEN_HYPER_PRI(fp, len, "scheduler_sleep: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_sleep)); ++ XEN_HYPER_PRI(fp, len, "scheduler_wake: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_wake)); ++ XEN_HYPER_PRI(fp, len, "scheduler_set_affinity: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_set_affinity)); ++ XEN_HYPER_PRI(fp, len, "scheduler_do_schedule: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_do_schedule)); ++ XEN_HYPER_PRI(fp, len, "scheduler_adjust: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_adjust)); ++ XEN_HYPER_PRI(fp, len, "scheduler_dump_settings: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_dump_settings)); ++ XEN_HYPER_PRI(fp, len, "scheduler_dump_cpu_state: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.scheduler_dump_cpu_state)); ++ ++ XEN_HYPER_PRI(fp, len, "shared_info_vcpu_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.shared_info_vcpu_info)); ++ XEN_HYPER_PRI(fp, len, "shared_info_evtchn_pending: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.shared_info_evtchn_pending)); ++ XEN_HYPER_PRI(fp, len, "shared_info_evtchn_mask: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.shared_info_evtchn_mask)); ++ XEN_HYPER_PRI(fp, len, "shared_info_arch: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.shared_info_arch)); ++ ++ XEN_HYPER_PRI(fp, len, "timer_expires: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.timer_expires)); ++ XEN_HYPER_PRI(fp, len, "timer_cpu: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.timer_cpu)); ++ XEN_HYPER_PRI(fp, len, "timer_function: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.timer_function)); ++ XEN_HYPER_PRI(fp, len, "timer_data: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.timer_data)); ++ XEN_HYPER_PRI(fp, len, "timer_heap_offset: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.timer_heap_offset)); ++ XEN_HYPER_PRI(fp, len, "timer_killed: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.timer_killed)); ++ ++ XEN_HYPER_PRI(fp, len, "tss_struct_rsp0: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.tss_struct_rsp0)); ++ XEN_HYPER_PRI(fp, len, "tss_struct_esp0: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.tss_struct_esp0)); ++ ++ XEN_HYPER_PRI(fp, len, "vcpu_vcpu_id: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_vcpu_id)); ++ XEN_HYPER_PRI(fp, len, "vcpu_processor: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_processor)); ++ XEN_HYPER_PRI(fp, len, "vcpu_vcpu_info: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_vcpu_info)); ++ XEN_HYPER_PRI(fp, len, "vcpu_domain: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_domain)); ++ XEN_HYPER_PRI(fp, len, "vcpu_next_in_list: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_next_in_list)); ++ XEN_HYPER_PRI(fp, len, "vcpu_timer: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_timer)); ++ XEN_HYPER_PRI(fp, len, "vcpu_sleep_tick: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_sleep_tick)); ++ XEN_HYPER_PRI(fp, len, "vcpu_poll_timer: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_poll_timer)); ++ XEN_HYPER_PRI(fp, len, "vcpu_sched_priv: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_sched_priv)); ++ XEN_HYPER_PRI(fp, len, "vcpu_runstate: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_runstate)); ++ XEN_HYPER_PRI(fp, len, "vcpu_runstate_guest: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_runstate_guest)); ++ XEN_HYPER_PRI(fp, len, "vcpu_vcpu_flags: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_vcpu_flags)); ++ XEN_HYPER_PRI(fp, len, "vcpu_pause_count: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_pause_count)); ++ XEN_HYPER_PRI(fp, len, "vcpu_virq_to_evtchn: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_virq_to_evtchn)); ++ XEN_HYPER_PRI(fp, len, "vcpu_cpu_affinity: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_cpu_affinity)); ++ XEN_HYPER_PRI(fp, len, "vcpu_nmi_addr: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_nmi_addr)); ++ XEN_HYPER_PRI(fp, len, "vcpu_vcpu_dirty_cpumask: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_vcpu_dirty_cpumask)); ++ XEN_HYPER_PRI(fp, len, "vcpu_arch: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_arch)); ++ XEN_HYPER_PRI(fp, len, "vcpu_runstate_info_state: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_runstate_info_state)); ++ XEN_HYPER_PRI(fp, len, "vcpu_runstate_info_state_entry_time: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_runstate_info_state_entry_time)); ++ XEN_HYPER_PRI(fp, len, "vcpu_runstate_info_time: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_runstate_info_time)); ++#ifdef IA64 ++ XEN_HYPER_PRI(fp, len, "vcpu_thread_ksp: ", buf, flag, ++ (buf, "%ld\n", xen_hyper_offset_table.vcpu_thread_ksp)); ++#endif ++} ++ ++/* ++ * dump specified memory with specified size. ++ */ ++#define DSP_BYTE_SIZE 16 ++ ++static void ++xen_hyper_dump_mem(void *mem, ulong len, int dsz) ++{ ++ long i, max; ++ void *mem_w = mem; ++ ++ if (!len || ++ (dsz != SIZEOF_8BIT && dsz != SIZEOF_16BIT && ++ dsz != SIZEOF_32BIT && dsz != SIZEOF_64BIT)) ++ return; ++ max = len / dsz + (len % dsz ? 1 : 0); ++ for (i = 0; i < max; i++) { ++ if (i != 0 && !(i % (DSP_BYTE_SIZE / dsz))) ++ fprintf(fp, "\n"); ++ if (i == 0 || !(i % (DSP_BYTE_SIZE / dsz))) ++ fprintf(fp, "%p : ", mem_w); ++ if (dsz == SIZEOF_8BIT) ++ fprintf(fp, "%02x ", *(uint8_t *)mem_w); ++ else if (dsz == SIZEOF_16BIT) ++ fprintf(fp, "%04x ", *(uint16_t *)mem_w); ++ else if (dsz == SIZEOF_32BIT) ++ fprintf(fp, "%08x ", *(uint32_t *)mem_w); ++ else if (dsz == SIZEOF_64BIT) ++ fprintf(fp, "%016llx ", *(unsigned long long *)mem_w); ++ mem_w = (char *)mem_w + dsz; ++ } ++ fprintf(fp, "\n"); ++} ++#endif +--- crash/lkcd_dump_v8.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_dump_v8.h 2007-10-30 10:51:55.000000000 -0400 +@@ -235,4 +235,304 @@ + int stack_offset; + } lkcdinfo_t; + ++/* ++ * ++ * machine specific dump headers ++ * ++ */ ++ ++/* ++ * IA64 --------------------------------------------------------- ++ */ ++ ++#if defined(IA64) ++ ++#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ ++#define DUMP_ASM_VERSION_NUMBER 0x5 /* version number */ ++ ++ ++struct pt_regs { ++ /* The following registers are saved by SAVE_MIN: */ ++ unsigned long b6; /* scratch */ ++ unsigned long b7; /* scratch */ ++ ++ unsigned long ar_csd; /* used by cmp8xchg16 (scratch) */ ++ unsigned long ar_ssd; /* reserved for future use (scratch) */ ++ ++ unsigned long r8; /* scratch (return value register 0) */ ++ unsigned long r9; /* scratch (return value register 1) */ ++ unsigned long r10; /* scratch (return value register 2) */ ++ unsigned long r11; /* scratch (return value register 3) */ ++ ++ unsigned long cr_ipsr; /* interrupted task's psr */ ++ unsigned long cr_iip; /* interrupted task's instruction pointer */ ++ unsigned long cr_ifs; /* interrupted task's function state */ ++ ++ unsigned long ar_unat; /* interrupted task's NaT register (preserved) */ ++ unsigned long ar_pfs; /* prev function state */ ++ unsigned long ar_rsc; /* RSE configuration */ ++ /* The following two are valid only if cr_ipsr.cpl > 0: */ ++ unsigned long ar_rnat; /* RSE NaT */ ++ unsigned long ar_bspstore; /* RSE bspstore */ ++ ++ unsigned long pr; /* 64 predicate registers (1 bit each) */ ++ unsigned long b0; /* return pointer (bp) */ ++ unsigned long loadrs; /* size of dirty partition << 16 */ ++ ++ unsigned long r1; /* the gp pointer */ ++ unsigned long r12; /* interrupted task's memory stack pointer */ ++ unsigned long r13; /* thread pointer */ ++ ++ unsigned long ar_fpsr; /* floating point status (preserved) */ ++ unsigned long r15; /* scratch */ ++ ++ /* The remaining registers are NOT saved for system calls. */ ++ ++ unsigned long r14; /* scratch */ ++ unsigned long r2; /* scratch */ ++ unsigned long r3; /* scratch */ ++ ++ /* The following registers are saved by SAVE_REST: */ ++ unsigned long r16; /* scratch */ ++ unsigned long r17; /* scratch */ ++ unsigned long r18; /* scratch */ ++ unsigned long r19; /* scratch */ ++ unsigned long r20; /* scratch */ ++ unsigned long r21; /* scratch */ ++ unsigned long r22; /* scratch */ ++ unsigned long r23; /* scratch */ ++ unsigned long r24; /* scratch */ ++ unsigned long r25; /* scratch */ ++ unsigned long r26; /* scratch */ ++ unsigned long r27; /* scratch */ ++ unsigned long r28; /* scratch */ ++ unsigned long r29; /* scratch */ ++ unsigned long r30; /* scratch */ ++ unsigned long r31; /* scratch */ ++ ++ unsigned long ar_ccv; /* compare/exchange value (scratch) */ ++ ++ /* ++ * Floating point registers that the kernel considers scratch: ++ */ ++ struct ia64_fpreg f6; /* scratch */ ++ struct ia64_fpreg f7; /* scratch */ ++ struct ia64_fpreg f8; /* scratch */ ++ struct ia64_fpreg f9; /* scratch */ ++ struct ia64_fpreg f10; /* scratch */ ++ struct ia64_fpreg f11; /* scratch */ ++}; ++ ++ ++ ++/* ++ * Structure: dump_header_asm_t ++ * Function: This is the header for architecture-specific stuff. It ++ * follows right after the dump header. ++ * ++ */ ++typedef struct _dump_header_asm_s { ++ ++ /* the dump magic number -- unique to verify dump is valid */ ++ uint64_t dha_magic_number; ++ ++ /* the version number of this dump */ ++ uint32_t dha_version; ++ ++ /* the size of this header (in case we can't read it) */ ++ uint32_t dha_header_size; ++ ++ /* pointer to pt_regs, (OLD: (struct pt_regs *, NEW: (uint64_t)) */ ++ uint64_t dha_pt_regs; ++ ++ /* the dump registers */ ++ struct pt_regs dha_regs; ++ ++ /* the rnat register saved after flushrs */ ++ uint64_t dha_rnat; ++ ++ /* the pfs register saved after flushrs */ ++ uint64_t dha_pfs; ++ ++ /* the bspstore register saved after flushrs */ ++ uint64_t dha_bspstore; ++ ++ /* smp specific */ ++ uint32_t dha_smp_num_cpus; ++ uint32_t dha_dumping_cpu; ++ struct pt_regs dha_smp_regs[NR_CPUS]; ++ uint64_t dha_smp_current_task[NR_CPUS]; ++ uint64_t dha_stack[NR_CPUS]; ++ uint64_t dha_stack_ptr[NR_CPUS]; ++ ++ /* load address of kernel */ ++ uint64_t dha_kernel_addr; ++ ++} __attribute__((packed)) dump_header_asm_t; ++ ++struct dump_CPU_info_ia64 { ++ struct pt_regs dha_smp_regs; ++ uint64_t dha_smp_current_task; ++ uint64_t dha_stack; ++ uint64_t dha_stack_ptr; ++} __attribute__((packed)) dump_CPU_info_ia64_t; ++ ++typedef struct dump_CPU_info_ia64 dump_CPU_info_t; ++ ++/* ++ * i386 --------------------------------------------------------- ++ */ ++ ++#elif defined(X86) ++ ++#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ ++#define DUMP_ASM_VERSION_NUMBER 0x5 /* version number */ ++ ++ ++struct pt_regs { ++ long ebx; ++ long ecx; ++ long edx; ++ long esi; ++ long edi; ++ long ebp; ++ long eax; ++ int xds; ++ int xes; ++ long orig_eax; ++ long eip; ++ int xcs; ++ long eflags; ++ long esp; ++ int xss; ++}; ++ ++/* ++ * Structure: __dump_header_asm ++ * Function: This is the header for architecture-specific stuff. It ++ * follows right after the dump header. ++ */ ++typedef struct _dump_header_asm_s { ++ /* the dump magic number -- unique to verify dump is valid */ ++ uint64_t dha_magic_number; ++ ++ /* the version number of this dump */ ++ uint32_t dha_version; ++ ++ /* the size of this header (in case we can't read it) */ ++ uint32_t dha_header_size; ++ ++ /* the esp for i386 systems */ ++ uint32_t dha_esp; ++ ++ /* the eip for i386 systems */ ++ uint32_t dha_eip; ++ ++ /* the dump registers */ ++ struct pt_regs dha_regs; ++ ++ /* smp specific */ ++ uint32_t dha_smp_num_cpus; ++ uint32_t dha_dumping_cpu; ++ struct pt_regs dha_smp_regs[NR_CPUS]; ++ uint32_t dha_smp_current_task[NR_CPUS]; ++ uint32_t dha_stack[NR_CPUS]; ++ uint32_t dha_stack_ptr[NR_CPUS]; ++} __attribute__((packed)) dump_header_asm_t; ++ ++/* ++ * CPU specific part of dump_header_asm_t ++ */ ++typedef struct dump_CPU_info_s { ++ struct pt_regs dha_smp_regs; ++ uint64_t dha_smp_current_task; ++ uint64_t dha_stack; ++ uint64_t dha_stack_ptr; ++} __attribute__ ((packed)) dump_CPU_info_t; ++ ++ ++/* ++ * x86-64 --------------------------------------------------------- ++ */ ++ ++#elif defined(X86_64) ++ ++/* definitions */ ++#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ ++#define DUMP_ASM_VERSION_NUMBER 0x2 /* version number */ ++ ++ ++struct pt_regs { ++ unsigned long r15; ++ unsigned long r14; ++ unsigned long r13; ++ unsigned long r12; ++ unsigned long rbp; ++ unsigned long rbx; ++/* arguments: non interrupts/non tracing syscalls only save upto here*/ ++ unsigned long r11; ++ unsigned long r10; ++ unsigned long r9; ++ unsigned long r8; ++ unsigned long rax; ++ unsigned long rcx; ++ unsigned long rdx; ++ unsigned long rsi; ++ unsigned long rdi; ++ unsigned long orig_rax; ++/* end of arguments */ ++/* cpu exception frame or undefined */ ++ unsigned long rip; ++ unsigned long cs; ++ unsigned long eflags; ++ unsigned long rsp; ++ unsigned long ss; ++/* top of stack page */ ++}; ++ ++/* ++ * Structure: dump_header_asm_t ++ * Function: This is the header for architecture-specific stuff. It ++ * follows right after the dump header. ++ */ ++typedef struct _dump_header_asm_s { ++ ++ /* the dump magic number -- unique to verify dump is valid */ ++ uint64_t dha_magic_number; ++ ++ /* the version number of this dump */ ++ uint32_t dha_version; ++ ++ /* the size of this header (in case we can't read it) */ ++ uint32_t dha_header_size; ++ ++ /* the dump registers */ ++ struct pt_regs dha_regs; ++ ++ /* smp specific */ ++ uint32_t dha_smp_num_cpus; ++ int dha_dumping_cpu; ++ struct pt_regs dha_smp_regs[NR_CPUS]; ++ uint64_t dha_smp_current_task[NR_CPUS]; ++ uint64_t dha_stack[NR_CPUS]; ++ uint64_t dha_stack_ptr[NR_CPUS]; ++} __attribute__((packed)) dump_header_asm_t; ++ ++ ++/* ++ * CPU specific part of dump_header_asm_t ++ */ ++typedef struct dump_CPU_info_s { ++ struct pt_regs dha_smp_regs; ++ uint64_t dha_smp_current_task; ++ uint64_t dha_stack; ++ uint64_t dha_stack_ptr; ++} __attribute__ ((packed)) dump_CPU_info_t; ++ ++#else ++ ++#define HAVE_NO_DUMP_HEADER_ASM 1 ++ ++#endif ++ + #endif /* _DUMP_H */ +--- crash/unwind.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/unwind.c 2009-01-23 10:30:52.000000000 -0500 +@@ -6,8 +6,8 @@ + /* + * unwind.c + * +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. All rights reserved. + * + * Adapted from: + * +@@ -36,6 +36,7 @@ + /* #include can't include this -- it's changing over time! */ + + #include "defs.h" ++#include "xen_hyper_defs.h" + + typedef unsigned char u8; + typedef unsigned long long u64; +@@ -64,6 +65,8 @@ + struct bt_info *); + static int unw_switch_from_osinit_v2(struct unw_frame_info *, + struct bt_info *); ++static int unw_switch_from_osinit_v3(struct unw_frame_info *, ++ struct bt_info *, char *); + static unsigned long get_init_stack_ulong(unsigned long addr); + static void unw_init_frame_info(struct unw_frame_info *, + struct bt_info *, ulong); +@@ -1392,14 +1395,62 @@ + unwind_init_v3(void) + #endif + { ++ int len; + struct gnu_request request, *req; + + req = &request; + ++ if (LKCD_KERNTYPES()) { ++ if ((len = STRUCT_SIZE("unw")) == 0) { ++ error(WARNING, ++ "cannot determine unw.tables offset; no struct unw\n"); ++ machdep->flags |= UNW_OUT_OF_SYNC; ++ return; ++ } ++ machdep->machspec->unw_tables_offset = ++ MEMBER_OFFSET("unw", "tables"); ++ if (MEMBER_EXISTS("unw", "r0")) ++ machdep->flags |= UNW_R0; ++ /* ++ * no verification of save_order, sw_off, preg_index as ++ * we're purely depending on the structure definition. ++ */ ++ if (MEMBER_EXISTS("unw", "pt_regs_offsets")) { ++ machdep->machspec->unw_pt_regs_offsets = ++ MEMBER_OFFSET("unw", "pt_regs_offsets") - ++ machdep->machspec->unw_tables_offset; ++ machdep->machspec->unw_kernel_table_offset = ++ MEMBER_OFFSET("unw", "kernel_table") - ++ machdep->machspec->unw_tables_offset; ++ machdep->flags |= UNW_PTREGS; ++ } ++ if (!load_unw_table(CLEAR_SCRIPT_CACHE)) { ++ error(WARNING, ++ "unwind_init: cannot read kernel unw table\n"); ++ machdep->flags |= UNW_OUT_OF_SYNC; ++ } ++ machdep->machspec->unw = (void *)&unw; ++ /* fall to common structure size verifications */ ++ goto verify; ++ } ++ + if (get_symbol_type("unw", "tables", req) == TYPE_CODE_UNDEF) { +- error(WARNING, "cannot determine unw.tables offset\n"); +- machdep->flags |= UNW_OUT_OF_SYNC; +- } else { ++ /* ++ * KLUDGE ALERT: ++ * If unw.tables cannot be ascertained by gdb, try unw.save_order, ++ * given that it is the field just after unw.tables. ++ */ ++ if (get_symbol_type("unw", "save_order", req) == TYPE_CODE_UNDEF) { ++ error(WARNING, "cannot determine unw.tables offset\n"); ++ machdep->flags |= UNW_OUT_OF_SYNC; ++ } else ++ req->member_offset -= BITS_PER_BYTE * sizeof(void *); ++ ++ if (CRASHDEBUG(1)) ++ error(WARNING, "using unw.save_order to determine unw.tables\n"); ++ } ++ ++ if (!(machdep->flags & UNW_OUT_OF_SYNC)) { + machdep->machspec->unw_tables_offset = + req->member_offset/BITS_PER_BYTE; + +@@ -1433,7 +1484,7 @@ + + machdep->machspec->unw = (void *)&unw; + } +- ++verify: + verify_common_struct("unw_frame_info", sizeof(struct unw_frame_info)); + verify_common_struct("unw_table", sizeof(struct unw_table)); + verify_common_struct("unw_table_entry", sizeof(struct unw_table_entry)); +@@ -1658,8 +1709,13 @@ + unw_get_sp(info, &sp); + unw_get_bsp(info, &bsp); + +- if (ip < GATE_ADDR + PAGE_SIZE) +- break; ++ if (XEN_HYPER_MODE()) { ++ if (!IS_KVADDR(ip)) ++ break; ++ } else { ++ if (ip < GATE_ADDR + PAGE_SIZE) ++ break; ++ } + + if ((sm = value_search(ip, NULL))) + name = sm->name; +@@ -1720,11 +1776,29 @@ + * ia64_init_handler. + */ + if (STREQ(name, "ia64_init_handler")) { +- unw_switch_from_osinit_v2(info, bt); +- frame++; +- goto restart; ++ if (symbol_exists("ia64_mca_modify_original_stack")) { ++ /* ++ * 2.6.14 or later kernels no longer keep ++ * minstate info in pt_regs/switch_stack. ++ * unw_switch_from_osinit_v3() will try ++ * to find the interrupted task and restart ++ * backtrace itself. ++ */ ++ if (unw_switch_from_osinit_v3(info, bt, "INIT") == FALSE) ++ break; ++ } else { ++ if (unw_switch_from_osinit_v2(info, bt) == FALSE) ++ break; ++ frame++; ++ goto restart; ++ } + } + ++ if (STREQ(name, "ia64_mca_handler") && ++ symbol_exists("ia64_mca_modify_original_stack")) ++ if (unw_switch_from_osinit_v3(info, bt, "MCA") == FALSE) ++ break; ++ + frame++; + + } while (unw_unwind(info) >= 0); +@@ -1844,8 +1918,13 @@ + ulong sw; + + sw = SWITCH_STACK_ADDR(bt->task); +- if (!INSTACK(sw, bt) && !ia64_in_init_stack(sw)) +- return FALSE; ++ if (XEN_HYPER_MODE()) { ++ if (!INSTACK(sw, bt) && !ia64_in_mca_stack_hyper(sw, bt)) ++ return FALSE; ++ } else { ++ if (!INSTACK(sw, bt) && !ia64_in_init_stack(sw)) ++ return FALSE; ++ } + + unw_init_frame_info(info, bt, sw); + return TRUE; +@@ -1967,6 +2046,124 @@ + return TRUE; + } + ++/* CPL (current privilege level) is 2-bit field */ ++#define IA64_PSR_CPL0_BIT 32 ++#define IA64_PSR_CPL_MASK (3UL << IA64_PSR_CPL0_BIT) ++ ++static int ++user_mode(struct bt_info *bt, unsigned long pt) ++{ ++ unsigned long cr_ipsr; ++ ++ cr_ipsr = IA64_GET_STACK_ULONG(pt + offsetof(struct pt_regs, cr_ipsr)); ++ if (cr_ipsr & IA64_PSR_CPL_MASK) ++ return 1; ++ return 0; ++} ++ ++/* ++ * Cope with INIT/MCA stack for the kernel 2.6.14 or later ++ * ++ * Returns FALSE if no more unwinding is needed. ++ */ ++#define ALIGN16(x) ((x)&~15) ++static int ++unw_switch_from_osinit_v3(struct unw_frame_info *info, struct bt_info *bt, ++ char *type) ++{ ++ unsigned long pt, sw, pid; ++ int processor; ++ char *p, *q; ++ struct task_context *tc = NULL; ++ struct bt_info clone_bt; ++ ++ /* ++ * The structure of INIT/MCA stack ++ * ++ * +---------------------------+ <-------- IA64_STK_OFFSET ++ * | pt_regs | ++ * +---------------------------+ ++ * | switch_stack | ++ * +---------------------------+ ++ * | SAL/OS state | ++ * +---------------------------+ ++ * | 16 byte scratch area | ++ * +---------------------------+ <-------- SP at start of C handler ++ * | ..... | ++ * +---------------------------+ ++ * | RBS for MCA/INIT handler | ++ * +---------------------------+ ++ * | struct task for MCA/INIT | ++ * +---------------------------+ <-------- bt->task ++ */ ++ pt = ALIGN16(bt->task + IA64_STK_OFFSET - STRUCT_SIZE("pt_regs")); ++ sw = ALIGN16(pt - STRUCT_SIZE("switch_stack")); ++ ++ /* ++ * 1. Try to find interrupted task from comm ++ * ++ * comm format of INIT/MCA task: ++ * - " " ++ * - " " ++ * where "" is either "INIT" or "MCA". ++ * The latter form is chosen if PID is 0. ++ * ++ * See ia64_mca_modify_comm() in arch/ia64/kernel/mca.c ++ */ ++ if (!bt->tc || !bt->tc->comm) ++ goto find_exframe; ++ ++ if ((p = strstr(bt->tc->comm, type))) { ++ p += strlen(type); ++ if (*p != ' ') ++ goto find_exframe; ++ if ((q = strchr(++p, ' '))) { ++ /* " " */ ++ if (sscanf(++q, "%d", &processor) > 0) { ++ tc = pid_to_context(0); ++ while (tc) { ++ if (tc != bt->tc && ++ tc->processor == processor) ++ break; ++ tc = tc->tc_next; ++ } ++ } ++ } else if (sscanf(p, "%lu", &pid) > 0) ++ /* " " */ ++ tc = pid_to_context(pid); ++ } ++ ++ if (tc) { ++ /* Clone bt_info and do backtrace */ ++ clone_bt_info(bt, &clone_bt, tc); ++ if (!BT_REFERENCE_CHECK(&clone_bt)) { ++ fprintf(fp, "(%s) INTERRUPTED TASK\n", type); ++ print_task_header(fp, tc, 0); ++ } ++ if (!user_mode(bt, pt)) ++ back_trace(&clone_bt); ++ else if (!BT_REFERENCE_CHECK(bt)) { ++ fprintf(fp, " #0 [interrupted in user space]\n"); ++ /* at least show the incomplete exception frame */ ++ bt->flags |= BT_INCOMPLETE_USER_EFRAME; ++ ia64_exception_frame(pt, bt); ++ } ++ return FALSE; ++ } ++ ++ /* task matching with INIT/MCA task's comm is not found */ ++ ++find_exframe: ++ /* ++ * 2. If step 1 doesn't work, try best to find exception frame ++ */ ++ unw_init_from_interruption(info, bt, pt, sw); ++ if (!BT_REFERENCE_CHECK(bt)) ++ ia64_exception_frame(pt, bt); ++ ++ return TRUE; ++} ++ + static void + unw_init_frame_info (struct unw_frame_info *info, struct bt_info *bt, ulong sw) + { +--- crash/gdb-6.1/gdb/dwarf2read.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb-6.1/gdb/dwarf2read.c 2009-01-22 16:09:48.000000000 -0500 +@@ -7391,8 +7391,7 @@ + When the result is a register number, the global isreg flag is set, + otherwise it is cleared. + +- Note that stack[0] is unused except as a default error return. +- Note that stack overflow is not yet handled. */ ++ Note that stack[0] is unused except as a default error return. */ + + static CORE_ADDR + decode_locdesc (struct dwarf_block *blk, struct dwarf2_cu *cu) +@@ -7409,7 +7408,7 @@ + + i = 0; + stacki = 0; +- stack[stacki] = 0; ++ stack[++stacki] = 0; + isreg = 0; + + while (i < size) +@@ -7591,6 +7590,16 @@ + dwarf_stack_op_name (op)); + return (stack[stacki]); + } ++ /* Enforce maximum stack depth of size-1 to avoid ++stacki writing ++ outside of the allocated space. Also enforce minimum > 0. ++ -- wad@google.com 14 Aug 2006 */ ++ if (stacki >= sizeof (stack) / sizeof (*stack) - 1) ++ internal_error (__FILE__, __LINE__, ++ _("location description stack too deep: %d"), ++ stacki); ++ if (stacki <= 0) ++ internal_error (__FILE__, __LINE__, ++ _("location description stack too shallow")); + } + return (stack[stacki]); + } +--- crash/gdb-6.1/gdb/symfile.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb-6.1/gdb/symfile.c 2007-01-30 10:43:08.000000000 -0500 +@@ -3,7 +3,7 @@ + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Portions Copyright (C) 2001, 2002 Mission Critical Linux, Inc. +- Copyright (c) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. All rights reserved. + + Contributed by Cygnus Support, using pieces from other GDB modules. + +@@ -1678,7 +1678,11 @@ + to load the program. */ + sect_opts[section_index].name = ".text"; + sect_opts[section_index].value = arg; ++#ifdef CRASH_MERGE ++ if (++section_index >= num_sect_opts) ++#else + if (++section_index > num_sect_opts) ++#endif + { + num_sect_opts *= 2; + sect_opts = ((struct sect_opt *) +@@ -1714,7 +1718,11 @@ + { + sect_opts[section_index].value = arg; + expecting_sec_addr = 0; ++#ifdef CRASH_MERGE ++ if (++section_index >= num_sect_opts) ++#else + if (++section_index > num_sect_opts) ++#endif + { + num_sect_opts *= 2; + sect_opts = ((struct sect_opt *) +@@ -3510,6 +3518,13 @@ + bfd_byte * + symfile_relocate_debug_section (bfd *abfd, asection *sectp, bfd_byte *buf) + { ++#ifdef CRASH_MERGE ++ /* Executable files have all the relocations already resolved. ++ * Handle files linked with --emit-relocs. ++ * http://sources.redhat.com/ml/gdb/2006-08/msg00137.html */ ++ if ((abfd->flags & EXEC_P) != 0) ++ return NULL; ++#endif + /* We're only interested in debugging sections with relocation + information. */ + if ((sectp->flags & SEC_RELOC) == 0) +--- crash/gdb-6.1/gdb/main.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb-6.1/gdb/main.c 2009-01-22 15:33:12.000000000 -0500 +@@ -628,7 +628,7 @@ + + if (!inhibit_gdbinit) + { +- catch_command_errors (source_command, homeinit, 0, RETURN_MASK_ALL); ++ catch_command_errors (source_command, homeinit, -1, RETURN_MASK_ALL); + } + + /* Do stats; no need to do them elsewhere since we'll only +@@ -714,7 +714,7 @@ + || memcmp ((char *) &homebuf, (char *) &cwdbuf, sizeof (struct stat))) + if (!inhibit_gdbinit) + { +- catch_command_errors (source_command, gdbinit, 0, RETURN_MASK_ALL); ++ catch_command_errors (source_command, gdbinit, -1, RETURN_MASK_ALL); + } + + for (i = 0; i < ncmd; i++) +--- crash/gdb-6.1/gdb/symtab.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb-6.1/gdb/symtab.c 2007-01-30 10:43:08.000000000 -0500 +@@ -4,7 +4,7 @@ + 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + Portions Copyright (C) 2001, 2002 Mission Critical Linux, Inc. +- Copyright (c) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ Copyright (c) 2002, 2003, 2004, 2005, 2007 Red Hat, Inc. All rights reserved. + + This file is part of GDB. + +@@ -4523,14 +4523,54 @@ + struct symbol *sym; + struct expression *expr; + struct cleanup *old_chain; +- ++ int i; ++ int allsect = 0; ++ char *secname; ++ char buf[80]; ++ + gdb_current_load_module = lm = (struct load_module *)req->addr; + + req->name = lm->mod_namelist; + gdb_delete_symbol_file(req); + +- sprintf(req->buf, "add-symbol-file %s 0x%lx", lm->mod_namelist, +- lm->mod_text_start); ++ for (i = 0 ; i < lm->mod_sections; i++) { ++ if (STREQ(lm->mod_section_data[i].name, ".text") && ++ (lm->mod_section_data[i].flags & SEC_FOUND)) ++ allsect = 1; ++ } ++ ++ if (!allsect) { ++ sprintf(req->buf, "add-symbol-file %s 0x%lx", lm->mod_namelist, ++ lm->mod_text_start ? lm->mod_text_start : lm->mod_base); ++ if (lm->mod_data_start) { ++ sprintf(buf, " -s .data 0x%lx", lm->mod_data_start); ++ strcat(req->buf, buf); ++ } ++ if (lm->mod_bss_start) { ++ sprintf(buf, " -s .bss 0x%lx", lm->mod_bss_start); ++ strcat(req->buf, buf); ++ } ++ if (lm->mod_rodata_start) { ++ sprintf(buf, " -s .rodata 0x%lx", lm->mod_rodata_start); ++ strcat(req->buf, buf); ++ } ++ } else { ++ sprintf(req->buf, "add-symbol-file %s 0x%lx", lm->mod_namelist, ++ lm->mod_text_start); ++ for (i = 0; i < lm->mod_sections; i++) { ++ secname = lm->mod_section_data[i].name; ++ if ((lm->mod_section_data[i].flags & SEC_FOUND) && ++ !STREQ(secname, ".text")) { ++ sprintf(buf, " -s %s 0x%lx", secname, ++ lm->mod_section_data[i].offset + lm->mod_base); ++ strcat(req->buf, buf); ++ } ++ } ++ } ++ ++ if (gdb_CRASHDEBUG(1)) { ++ fprintf_filtered(gdb_stdout, "gdb_add_symbol_file: %s\n", req->buf); ++ } + + execute_command(req->buf, FALSE); + +--- crash/gdb-6.1/gdb/Makefile.in.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb-6.1/gdb/Makefile.in 2009-01-22 15:33:12.000000000 -0500 +@@ -2513,7 +2513,7 @@ + $(expression_h) $(frame_h) $(value_h) $(language_h) $(filenames_h) \ + $(objfiles_h) $(source_h) $(disasm_h) $(ui_out_h) $(top_h) \ + $(cli_decode_h) $(cli_script_h) $(cli_setshow_h) $(cli_cmds_h) \ +- $(tui_h) ++ $(tui_h) $(gdb_stat_h) + $(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/cli/cli-cmds.c + cli-decode.o: $(srcdir)/cli/cli-decode.c $(defs_h) $(symtab_h) \ + $(gdb_regex_h) $(gdb_string_h) $(ui_out_h) $(cli_cmds_h) \ +--- crash/gdb-6.1/gdb/ppc-linux-tdep.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb-6.1/gdb/ppc-linux-tdep.c 2005-07-14 11:08:17.000000000 -0400 +@@ -0,0 +1,1116 @@ ++/* Target-dependent code for GDB, the GNU debugger. ++ ++ Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, ++ 1997, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. ++ Copyright (c) 2004, 2005 Red Hat, Inc. All rights reserved. ++ ++ This file is part of GDB. ++ ++ 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. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. */ ++ ++#include "defs.h" ++#include "frame.h" ++#include "inferior.h" ++#include "symtab.h" ++#include "target.h" ++#include "gdbcore.h" ++#include "gdbcmd.h" ++#include "symfile.h" ++#include "objfiles.h" ++#include "regcache.h" ++#include "value.h" ++#include "osabi.h" ++ ++#include "solib-svr4.h" ++#include "ppc-tdep.h" ++ ++/* The following instructions are used in the signal trampoline code ++ on GNU/Linux PPC. The kernel used to use magic syscalls 0x6666 and ++ 0x7777 but now uses the sigreturn syscalls. We check for both. */ ++#define INSTR_LI_R0_0x6666 0x38006666 ++#define INSTR_LI_R0_0x7777 0x38007777 ++#define INSTR_LI_R0_NR_sigreturn 0x38000077 ++#define INSTR_LI_R0_NR_rt_sigreturn 0x380000AC ++ ++#define INSTR_SC 0x44000002 ++ ++/* Since the *-tdep.c files are platform independent (i.e, they may be ++ used to build cross platform debuggers), we can't include system ++ headers. Therefore, details concerning the sigcontext structure ++ must be painstakingly rerecorded. What's worse, if these details ++ ever change in the header files, they'll have to be changed here ++ as well. */ ++ ++/* __SIGNAL_FRAMESIZE from */ ++#define PPC_LINUX_SIGNAL_FRAMESIZE 64 ++ ++/* From , offsetof(struct sigcontext_struct, regs) == 0x1c */ ++#define PPC_LINUX_REGS_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x1c) ++ ++/* From , ++ offsetof(struct sigcontext_struct, handler) == 0x14 */ ++#define PPC_LINUX_HANDLER_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x14) ++ ++/* From , values for PT_NIP, PT_R1, and PT_LNK */ ++#define PPC_LINUX_PT_R0 0 ++#define PPC_LINUX_PT_R1 1 ++#define PPC_LINUX_PT_R2 2 ++#define PPC_LINUX_PT_R3 3 ++#define PPC_LINUX_PT_R4 4 ++#define PPC_LINUX_PT_R5 5 ++#define PPC_LINUX_PT_R6 6 ++#define PPC_LINUX_PT_R7 7 ++#define PPC_LINUX_PT_R8 8 ++#define PPC_LINUX_PT_R9 9 ++#define PPC_LINUX_PT_R10 10 ++#define PPC_LINUX_PT_R11 11 ++#define PPC_LINUX_PT_R12 12 ++#define PPC_LINUX_PT_R13 13 ++#define PPC_LINUX_PT_R14 14 ++#define PPC_LINUX_PT_R15 15 ++#define PPC_LINUX_PT_R16 16 ++#define PPC_LINUX_PT_R17 17 ++#define PPC_LINUX_PT_R18 18 ++#define PPC_LINUX_PT_R19 19 ++#define PPC_LINUX_PT_R20 20 ++#define PPC_LINUX_PT_R21 21 ++#define PPC_LINUX_PT_R22 22 ++#define PPC_LINUX_PT_R23 23 ++#define PPC_LINUX_PT_R24 24 ++#define PPC_LINUX_PT_R25 25 ++#define PPC_LINUX_PT_R26 26 ++#define PPC_LINUX_PT_R27 27 ++#define PPC_LINUX_PT_R28 28 ++#define PPC_LINUX_PT_R29 29 ++#define PPC_LINUX_PT_R30 30 ++#define PPC_LINUX_PT_R31 31 ++#define PPC_LINUX_PT_NIP 32 ++#define PPC_LINUX_PT_MSR 33 ++#define PPC_LINUX_PT_CTR 35 ++#define PPC_LINUX_PT_LNK 36 ++#define PPC_LINUX_PT_XER 37 ++#define PPC_LINUX_PT_CCR 38 ++#define PPC_LINUX_PT_MQ 39 ++#define PPC_LINUX_PT_FPR0 48 /* each FP reg occupies 2 slots in this space */ ++#define PPC_LINUX_PT_FPR31 (PPC_LINUX_PT_FPR0 + 2*31) ++#define PPC_LINUX_PT_FPSCR (PPC_LINUX_PT_FPR0 + 2*32 + 1) ++ ++static int ppc_linux_at_sigtramp_return_path (CORE_ADDR pc); ++ ++/* Determine if pc is in a signal trampoline... ++ ++ Ha! That's not what this does at all. wait_for_inferior in ++ infrun.c calls PC_IN_SIGTRAMP in order to detect entry into a ++ signal trampoline just after delivery of a signal. But on ++ GNU/Linux, signal trampolines are used for the return path only. ++ The kernel sets things up so that the signal handler is called ++ directly. ++ ++ If we use in_sigtramp2() in place of in_sigtramp() (see below) ++ we'll (often) end up with stop_pc in the trampoline and prev_pc in ++ the (now exited) handler. The code there will cause a temporary ++ breakpoint to be set on prev_pc which is not very likely to get hit ++ again. ++ ++ If this is confusing, think of it this way... the code in ++ wait_for_inferior() needs to be able to detect entry into a signal ++ trampoline just after a signal is delivered, not after the handler ++ has been run. ++ ++ So, we define in_sigtramp() below to return 1 if the following is ++ true: ++ ++ 1) The previous frame is a real signal trampoline. ++ ++ - and - ++ ++ 2) pc is at the first or second instruction of the corresponding ++ handler. ++ ++ Why the second instruction? It seems that wait_for_inferior() ++ never sees the first instruction when single stepping. When a ++ signal is delivered while stepping, the next instruction that ++ would've been stepped over isn't, instead a signal is delivered and ++ the first instruction of the handler is stepped over instead. That ++ puts us on the second instruction. (I added the test for the ++ first instruction long after the fact, just in case the observed ++ behavior is ever fixed.) ++ ++ PC_IN_SIGTRAMP is called from blockframe.c as well in order to set ++ the frame's type (if a SIGTRAMP_FRAME). Because of our strange ++ definition of in_sigtramp below, we can't rely on the frame's type ++ getting set correctly from within blockframe.c. This is why we ++ take pains to set it in init_extra_frame_info(). ++ ++ NOTE: cagney/2002-11-10: I suspect the real problem here is that ++ the get_prev_frame() only initializes the frame's type after the ++ call to INIT_FRAME_INFO. get_prev_frame() should be fixed, this ++ code shouldn't be working its way around a bug :-(. */ ++ ++int ++ppc_linux_in_sigtramp (CORE_ADDR pc, char *func_name) ++{ ++ CORE_ADDR lr; ++ CORE_ADDR sp; ++ CORE_ADDR tramp_sp; ++ char buf[4]; ++ CORE_ADDR handler; ++ ++ lr = read_register (gdbarch_tdep (current_gdbarch)->ppc_lr_regnum); ++ if (!ppc_linux_at_sigtramp_return_path (lr)) ++ return 0; ++ ++ sp = read_register (SP_REGNUM); ++ ++ if (target_read_memory (sp, buf, sizeof (buf)) != 0) ++ return 0; ++ ++ tramp_sp = extract_unsigned_integer (buf, 4); ++ ++ if (target_read_memory (tramp_sp + PPC_LINUX_HANDLER_PTR_OFFSET, buf, ++ sizeof (buf)) != 0) ++ return 0; ++ ++ handler = extract_unsigned_integer (buf, 4); ++ ++ return (pc == handler || pc == handler + 4); ++} ++ ++static int ++insn_is_sigreturn (unsigned long pcinsn) ++{ ++ switch(pcinsn) ++ { ++ case INSTR_LI_R0_0x6666: ++ case INSTR_LI_R0_0x7777: ++ case INSTR_LI_R0_NR_sigreturn: ++ case INSTR_LI_R0_NR_rt_sigreturn: ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++/* ++ * The signal handler trampoline is on the stack and consists of exactly ++ * two instructions. The easiest and most accurate way of determining ++ * whether the pc is in one of these trampolines is by inspecting the ++ * instructions. It'd be faster though if we could find a way to do this ++ * via some simple address comparisons. ++ */ ++static int ++ppc_linux_at_sigtramp_return_path (CORE_ADDR pc) ++{ ++ char buf[12]; ++ unsigned long pcinsn; ++ if (target_read_memory (pc - 4, buf, sizeof (buf)) != 0) ++ return 0; ++ ++ /* extract the instruction at the pc */ ++ pcinsn = extract_unsigned_integer (buf + 4, 4); ++ ++ return ( ++ (insn_is_sigreturn (pcinsn) ++ && extract_unsigned_integer (buf + 8, 4) == INSTR_SC) ++ || ++ (pcinsn == INSTR_SC ++ && insn_is_sigreturn (extract_unsigned_integer (buf, 4)))); ++} ++ ++static CORE_ADDR ++ppc_linux_skip_trampoline_code (CORE_ADDR pc) ++{ ++ char buf[4]; ++ struct obj_section *sect; ++ struct objfile *objfile; ++ unsigned long insn; ++ CORE_ADDR plt_start = 0; ++ CORE_ADDR symtab = 0; ++ CORE_ADDR strtab = 0; ++ int num_slots = -1; ++ int reloc_index = -1; ++ CORE_ADDR plt_table; ++ CORE_ADDR reloc; ++ CORE_ADDR sym; ++ long symidx; ++ char symname[1024]; ++ struct minimal_symbol *msymbol; ++ ++ /* Find the section pc is in; return if not in .plt */ ++ sect = find_pc_section (pc); ++ if (!sect || strcmp (sect->the_bfd_section->name, ".plt") != 0) ++ return 0; ++ ++ objfile = sect->objfile; ++ ++ /* Pick up the instruction at pc. It had better be of the ++ form ++ li r11, IDX ++ ++ where IDX is an index into the plt_table. */ ++ ++ if (target_read_memory (pc, buf, 4) != 0) ++ return 0; ++ insn = extract_unsigned_integer (buf, 4); ++ ++ if ((insn & 0xffff0000) != 0x39600000 /* li r11, VAL */ ) ++ return 0; ++ ++ reloc_index = (insn << 16) >> 16; ++ ++ /* Find the objfile that pc is in and obtain the information ++ necessary for finding the symbol name. */ ++ for (sect = objfile->sections; sect < objfile->sections_end; ++sect) ++ { ++ const char *secname = sect->the_bfd_section->name; ++ if (strcmp (secname, ".plt") == 0) ++ plt_start = sect->addr; ++ else if (strcmp (secname, ".rela.plt") == 0) ++ num_slots = ((int) sect->endaddr - (int) sect->addr) / 12; ++ else if (strcmp (secname, ".dynsym") == 0) ++ symtab = sect->addr; ++ else if (strcmp (secname, ".dynstr") == 0) ++ strtab = sect->addr; ++ } ++ ++ /* Make sure we have all the information we need. */ ++ if (plt_start == 0 || num_slots == -1 || symtab == 0 || strtab == 0) ++ return 0; ++ ++ /* Compute the value of the plt table */ ++ plt_table = plt_start + 72 + 8 * num_slots; ++ ++ /* Get address of the relocation entry (Elf32_Rela) */ ++ if (target_read_memory (plt_table + reloc_index, buf, 4) != 0) ++ return 0; ++ reloc = extract_unsigned_integer (buf, 4); ++ ++ sect = find_pc_section (reloc); ++ if (!sect) ++ return 0; ++ ++ if (strcmp (sect->the_bfd_section->name, ".text") == 0) ++ return reloc; ++ ++ /* Now get the r_info field which is the relocation type and symbol ++ index. */ ++ if (target_read_memory (reloc + 4, buf, 4) != 0) ++ return 0; ++ symidx = extract_unsigned_integer (buf, 4); ++ ++ /* Shift out the relocation type leaving just the symbol index */ ++ /* symidx = ELF32_R_SYM(symidx); */ ++ symidx = symidx >> 8; ++ ++ /* compute the address of the symbol */ ++ sym = symtab + symidx * 4; ++ ++ /* Fetch the string table index */ ++ if (target_read_memory (sym, buf, 4) != 0) ++ return 0; ++ symidx = extract_unsigned_integer (buf, 4); ++ ++ /* Fetch the string; we don't know how long it is. Is it possible ++ that the following will fail because we're trying to fetch too ++ much? */ ++ if (target_read_memory (strtab + symidx, symname, sizeof (symname)) != 0) ++ return 0; ++ ++ /* This might not work right if we have multiple symbols with the ++ same name; the only way to really get it right is to perform ++ the same sort of lookup as the dynamic linker. */ ++ msymbol = lookup_minimal_symbol_text (symname, NULL); ++ if (!msymbol) ++ return 0; ++ ++ return SYMBOL_VALUE_ADDRESS (msymbol); ++} ++ ++/* The rs6000 version of FRAME_SAVED_PC will almost work for us. The ++ signal handler details are different, so we'll handle those here ++ and call the rs6000 version to do the rest. */ ++CORE_ADDR ++ppc_linux_frame_saved_pc (struct frame_info *fi) ++{ ++ if ((get_frame_type (fi) == SIGTRAMP_FRAME)) ++ { ++ CORE_ADDR regs_addr = ++ read_memory_integer (get_frame_base (fi) ++ + PPC_LINUX_REGS_PTR_OFFSET, 4); ++ /* return the NIP in the regs array */ ++ return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_NIP, 4); ++ } ++ else if (get_next_frame (fi) ++ && (get_frame_type (get_next_frame (fi)) == SIGTRAMP_FRAME)) ++ { ++ CORE_ADDR regs_addr = ++ read_memory_integer (get_frame_base (get_next_frame (fi)) ++ + PPC_LINUX_REGS_PTR_OFFSET, 4); ++ /* return LNK in the regs array */ ++ return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_LNK, 4); ++ } ++ else ++ return rs6000_frame_saved_pc (fi); ++} ++ ++void ++ppc_linux_init_extra_frame_info (int fromleaf, struct frame_info *fi) ++{ ++ rs6000_init_extra_frame_info (fromleaf, fi); ++ ++ if (get_next_frame (fi) != 0) ++ { ++ /* We're called from get_prev_frame_info; check to see if ++ this is a signal frame by looking to see if the pc points ++ at trampoline code */ ++ if (ppc_linux_at_sigtramp_return_path (get_frame_pc (fi))) ++ deprecated_set_frame_type (fi, SIGTRAMP_FRAME); ++ else ++ /* FIXME: cagney/2002-11-10: Is this double bogus? What ++ happens if the frame has previously been marked as a dummy? */ ++ deprecated_set_frame_type (fi, NORMAL_FRAME); ++ } ++} ++ ++int ++ppc_linux_frameless_function_invocation (struct frame_info *fi) ++{ ++ /* We'll find the wrong thing if we let ++ rs6000_frameless_function_invocation () search for a signal trampoline */ ++ if (ppc_linux_at_sigtramp_return_path (get_frame_pc (fi))) ++ return 0; ++ else ++ return rs6000_frameless_function_invocation (fi); ++} ++ ++void ++ppc_linux_frame_init_saved_regs (struct frame_info *fi) ++{ ++ if ((get_frame_type (fi) == SIGTRAMP_FRAME)) ++ { ++ CORE_ADDR regs_addr; ++ int i; ++ if (deprecated_get_frame_saved_regs (fi)) ++ return; ++ ++ frame_saved_regs_zalloc (fi); ++ ++ regs_addr = ++ read_memory_integer (get_frame_base (fi) ++ + PPC_LINUX_REGS_PTR_OFFSET, 4); ++ deprecated_get_frame_saved_regs (fi)[PC_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_NIP; ++ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_ps_regnum] = ++ regs_addr + 4 * PPC_LINUX_PT_MSR; ++ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_cr_regnum] = ++ regs_addr + 4 * PPC_LINUX_PT_CCR; ++ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_lr_regnum] = ++ regs_addr + 4 * PPC_LINUX_PT_LNK; ++ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_ctr_regnum] = ++ regs_addr + 4 * PPC_LINUX_PT_CTR; ++ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_xer_regnum] = ++ regs_addr + 4 * PPC_LINUX_PT_XER; ++ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_mq_regnum] = ++ regs_addr + 4 * PPC_LINUX_PT_MQ; ++ for (i = 0; i < 32; i++) ++ deprecated_get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_gp0_regnum + i] = ++ regs_addr + 4 * PPC_LINUX_PT_R0 + 4 * i; ++ for (i = 0; i < 32; i++) ++ deprecated_get_frame_saved_regs (fi)[FP0_REGNUM + i] = regs_addr + 4 * PPC_LINUX_PT_FPR0 + 8 * i; ++ } ++ else ++ rs6000_frame_init_saved_regs (fi); ++} ++ ++CORE_ADDR ++ppc_linux_frame_chain (struct frame_info *thisframe) ++{ ++ /* Kernel properly constructs the frame chain for the handler */ ++ if ((get_frame_type (thisframe) == SIGTRAMP_FRAME)) ++ return read_memory_integer (get_frame_base (thisframe), 4); ++ else ++ return rs6000_frame_chain (thisframe); ++} ++ ++/* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint ++ in much the same fashion as memory_remove_breakpoint in mem-break.c, ++ but is careful not to write back the previous contents if the code ++ in question has changed in between inserting the breakpoint and ++ removing it. ++ ++ Here is the problem that we're trying to solve... ++ ++ Once upon a time, before introducing this function to remove ++ breakpoints from the inferior, setting a breakpoint on a shared ++ library function prior to running the program would not work ++ properly. In order to understand the problem, it is first ++ necessary to understand a little bit about dynamic linking on ++ this platform. ++ ++ A call to a shared library function is accomplished via a bl ++ (branch-and-link) instruction whose branch target is an entry ++ in the procedure linkage table (PLT). The PLT in the object ++ file is uninitialized. To gdb, prior to running the program, the ++ entries in the PLT are all zeros. ++ ++ Once the program starts running, the shared libraries are loaded ++ and the procedure linkage table is initialized, but the entries in ++ the table are not (necessarily) resolved. Once a function is ++ actually called, the code in the PLT is hit and the function is ++ resolved. In order to better illustrate this, an example is in ++ order; the following example is from the gdb testsuite. ++ ++ We start the program shmain. ++ ++ [kev@arroyo testsuite]$ ../gdb gdb.base/shmain ++ [...] ++ ++ We place two breakpoints, one on shr1 and the other on main. ++ ++ (gdb) b shr1 ++ Breakpoint 1 at 0x100409d4 ++ (gdb) b main ++ Breakpoint 2 at 0x100006a0: file gdb.base/shmain.c, line 44. ++ ++ Examine the instruction (and the immediatly following instruction) ++ upon which the breakpoint was placed. Note that the PLT entry ++ for shr1 contains zeros. ++ ++ (gdb) x/2i 0x100409d4 ++ 0x100409d4 : .long 0x0 ++ 0x100409d8 : .long 0x0 ++ ++ Now run 'til main. ++ ++ (gdb) r ++ Starting program: gdb.base/shmain ++ Breakpoint 1 at 0xffaf790: file gdb.base/shr1.c, line 19. ++ ++ Breakpoint 2, main () ++ at gdb.base/shmain.c:44 ++ 44 g = 1; ++ ++ Examine the PLT again. Note that the loading of the shared ++ library has initialized the PLT to code which loads a constant ++ (which I think is an index into the GOT) into r11 and then ++ branchs a short distance to the code which actually does the ++ resolving. ++ ++ (gdb) x/2i 0x100409d4 ++ 0x100409d4 : li r11,4 ++ 0x100409d8 : b 0x10040984 ++ (gdb) c ++ Continuing. ++ ++ Breakpoint 1, shr1 (x=1) ++ at gdb.base/shr1.c:19 ++ 19 l = 1; ++ ++ Now we've hit the breakpoint at shr1. (The breakpoint was ++ reset from the PLT entry to the actual shr1 function after the ++ shared library was loaded.) Note that the PLT entry has been ++ resolved to contain a branch that takes us directly to shr1. ++ (The real one, not the PLT entry.) ++ ++ (gdb) x/2i 0x100409d4 ++ 0x100409d4 : b 0xffaf76c ++ 0x100409d8 : b 0x10040984 ++ ++ The thing to note here is that the PLT entry for shr1 has been ++ changed twice. ++ ++ Now the problem should be obvious. GDB places a breakpoint (a ++ trap instruction) on the zero value of the PLT entry for shr1. ++ Later on, after the shared library had been loaded and the PLT ++ initialized, GDB gets a signal indicating this fact and attempts ++ (as it always does when it stops) to remove all the breakpoints. ++ ++ The breakpoint removal was causing the former contents (a zero ++ word) to be written back to the now initialized PLT entry thus ++ destroying a portion of the initialization that had occurred only a ++ short time ago. When execution continued, the zero word would be ++ executed as an instruction an an illegal instruction trap was ++ generated instead. (0 is not a legal instruction.) ++ ++ The fix for this problem was fairly straightforward. The function ++ memory_remove_breakpoint from mem-break.c was copied to this file, ++ modified slightly, and renamed to ppc_linux_memory_remove_breakpoint. ++ In tm-linux.h, MEMORY_REMOVE_BREAKPOINT is defined to call this new ++ function. ++ ++ The differences between ppc_linux_memory_remove_breakpoint () and ++ memory_remove_breakpoint () are minor. All that the former does ++ that the latter does not is check to make sure that the breakpoint ++ location actually contains a breakpoint (trap instruction) prior ++ to attempting to write back the old contents. If it does contain ++ a trap instruction, we allow the old contents to be written back. ++ Otherwise, we silently do nothing. ++ ++ The big question is whether memory_remove_breakpoint () should be ++ changed to have the same functionality. The downside is that more ++ traffic is generated for remote targets since we'll have an extra ++ fetch of a memory word each time a breakpoint is removed. ++ ++ For the time being, we'll leave this self-modifying-code-friendly ++ version in ppc-linux-tdep.c, but it ought to be migrated somewhere ++ else in the event that some other platform has similar needs with ++ regard to removing breakpoints in some potentially self modifying ++ code. */ ++int ++ppc_linux_memory_remove_breakpoint (CORE_ADDR addr, char *contents_cache) ++{ ++ const unsigned char *bp; ++ int val; ++ int bplen; ++ char old_contents[BREAKPOINT_MAX]; ++ ++ /* Determine appropriate breakpoint contents and size for this address. */ ++ bp = BREAKPOINT_FROM_PC (&addr, &bplen); ++ if (bp == NULL) ++ error ("Software breakpoints not implemented for this target."); ++ ++ val = target_read_memory (addr, old_contents, bplen); ++ ++ /* If our breakpoint is no longer at the address, this means that the ++ program modified the code on us, so it is wrong to put back the ++ old value */ ++ if (val == 0 && memcmp (bp, old_contents, bplen) == 0) ++ val = target_write_memory (addr, contents_cache, bplen); ++ ++ return val; ++} ++ ++/* For historic reasons, PPC 32 GNU/Linux follows PowerOpen rather ++ than the 32 bit SYSV R4 ABI structure return convention - all ++ structures, no matter their size, are put in memory. Vectors, ++ which were added later, do get returned in a register though. */ ++ ++static enum return_value_convention ++ppc_linux_return_value (struct gdbarch *gdbarch, struct type *valtype, ++ struct regcache *regcache, void *readbuf, ++ const void *writebuf) ++{ ++ if ((TYPE_CODE (valtype) == TYPE_CODE_STRUCT ++ || TYPE_CODE (valtype) == TYPE_CODE_UNION) ++ && !((TYPE_LENGTH (valtype) == 16 || TYPE_LENGTH (valtype) == 8) ++ && TYPE_VECTOR (valtype))) ++ return RETURN_VALUE_STRUCT_CONVENTION; ++ else ++ return ppc_sysv_abi_return_value (gdbarch, valtype, regcache, readbuf, ++ writebuf); ++} ++ ++/* Fetch (and possibly build) an appropriate link_map_offsets ++ structure for GNU/Linux PPC targets using the struct offsets ++ defined in link.h (but without actual reference to that file). ++ ++ This makes it possible to access GNU/Linux PPC shared libraries ++ from a GDB that was not built on an GNU/Linux PPC host (for cross ++ debugging). */ ++ ++struct link_map_offsets * ++ppc_linux_svr4_fetch_link_map_offsets (void) ++{ ++ static struct link_map_offsets lmo; ++ static struct link_map_offsets *lmp = NULL; ++ ++ if (lmp == NULL) ++ { ++ lmp = &lmo; ++ ++ lmo.r_debug_size = 8; /* The actual size is 20 bytes, but ++ this is all we need. */ ++ lmo.r_map_offset = 4; ++ lmo.r_map_size = 4; ++ ++ lmo.link_map_size = 20; /* The actual size is 560 bytes, but ++ this is all we need. */ ++ lmo.l_addr_offset = 0; ++ lmo.l_addr_size = 4; ++ ++ lmo.l_name_offset = 4; ++ lmo.l_name_size = 4; ++ ++ lmo.l_next_offset = 12; ++ lmo.l_next_size = 4; ++ ++ lmo.l_prev_offset = 16; ++ lmo.l_prev_size = 4; ++ } ++ ++ return lmp; ++} ++ ++ ++/* Macros for matching instructions. Note that, since all the ++ operands are masked off before they're or-ed into the instruction, ++ you can use -1 to make masks. */ ++ ++#define insn_d(opcd, rts, ra, d) \ ++ ((((opcd) & 0x3f) << 26) \ ++ | (((rts) & 0x1f) << 21) \ ++ | (((ra) & 0x1f) << 16) \ ++ | ((d) & 0xffff)) ++ ++#define insn_ds(opcd, rts, ra, d, xo) \ ++ ((((opcd) & 0x3f) << 26) \ ++ | (((rts) & 0x1f) << 21) \ ++ | (((ra) & 0x1f) << 16) \ ++ | ((d) & 0xfffc) \ ++ | ((xo) & 0x3)) ++ ++#define insn_xfx(opcd, rts, spr, xo) \ ++ ((((opcd) & 0x3f) << 26) \ ++ | (((rts) & 0x1f) << 21) \ ++ | (((spr) & 0x1f) << 16) \ ++ | (((spr) & 0x3e0) << 6) \ ++ | (((xo) & 0x3ff) << 1)) ++ ++/* Read a PPC instruction from memory. PPC instructions are always ++ big-endian, no matter what endianness the program is running in, so ++ we can't use read_memory_integer or one of its friends here. */ ++static unsigned int ++read_insn (CORE_ADDR pc) ++{ ++ unsigned char buf[4]; ++ ++ read_memory (pc, buf, 4); ++ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; ++} ++ ++ ++/* An instruction to match. */ ++struct insn_pattern ++{ ++ unsigned int mask; /* mask the insn with this... */ ++ unsigned int data; /* ...and see if it matches this. */ ++ int optional; /* If non-zero, this insn may be absent. */ ++}; ++ ++/* Return non-zero if the instructions at PC match the series ++ described in PATTERN, or zero otherwise. PATTERN is an array of ++ 'struct insn_pattern' objects, terminated by an entry whose mask is ++ zero. ++ ++ When the match is successful, fill INSN[i] with what PATTERN[i] ++ matched. If PATTERN[i] is optional, and the instruction wasn't ++ present, set INSN[i] to 0 (which is not a valid PPC instruction). ++ INSN should have as many elements as PATTERN. Note that, if ++ PATTERN contains optional instructions which aren't present in ++ memory, then INSN will have holes, so INSN[i] isn't necessarily the ++ i'th instruction in memory. */ ++static int ++insns_match_pattern (CORE_ADDR pc, ++ struct insn_pattern *pattern, ++ unsigned int *insn) ++{ ++ int i; ++ ++ for (i = 0; pattern[i].mask; i++) ++ { ++ insn[i] = read_insn (pc); ++ if ((insn[i] & pattern[i].mask) == pattern[i].data) ++ pc += 4; ++ else if (pattern[i].optional) ++ insn[i] = 0; ++ else ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++/* Return the 'd' field of the d-form instruction INSN, properly ++ sign-extended. */ ++static CORE_ADDR ++insn_d_field (unsigned int insn) ++{ ++ return ((((CORE_ADDR) insn & 0xffff) ^ 0x8000) - 0x8000); ++} ++ ++ ++/* Return the 'ds' field of the ds-form instruction INSN, with the two ++ zero bits concatenated at the right, and properly ++ sign-extended. */ ++static CORE_ADDR ++insn_ds_field (unsigned int insn) ++{ ++ return ((((CORE_ADDR) insn & 0xfffc) ^ 0x8000) - 0x8000); ++} ++ ++ ++/* If DESC is the address of a 64-bit PowerPC GNU/Linux function ++ descriptor, return the descriptor's entry point. */ ++static CORE_ADDR ++ppc64_desc_entry_point (CORE_ADDR desc) ++{ ++ /* The first word of the descriptor is the entry point. */ ++ return (CORE_ADDR) read_memory_unsigned_integer (desc, 8); ++} ++ ++ ++/* Pattern for the standard linkage function. These are built by ++ build_plt_stub in elf64-ppc.c, whose GLINK argument is always ++ zero. */ ++static struct insn_pattern ppc64_standard_linkage[] = ++ { ++ /* addis r12, r2, */ ++ { insn_d (-1, -1, -1, 0), insn_d (15, 12, 2, 0), 0 }, ++ ++ /* std r2, 40(r1) */ ++ { -1, insn_ds (62, 2, 1, 40, 0), 0 }, ++ ++ /* ld r11, (r12) */ ++ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 }, ++ ++ /* addis r12, r12, 1 */ ++ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 2, 1), 1 }, ++ ++ /* ld r2, (r12) */ ++ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 12, 0, 0), 0 }, ++ ++ /* addis r12, r12, 1 */ ++ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 2, 1), 1 }, ++ ++ /* mtctr r11 */ ++ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467), ++ 0 }, ++ ++ /* ld r11, (r12) */ ++ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 }, ++ ++ /* bctr */ ++ { -1, 0x4e800420, 0 }, ++ ++ { 0, 0, 0 } ++ }; ++#define PPC64_STANDARD_LINKAGE_LEN \ ++ (sizeof (ppc64_standard_linkage) / sizeof (ppc64_standard_linkage[0])) ++ ++ ++/* Recognize a 64-bit PowerPC GNU/Linux linkage function --- what GDB ++ calls a "solib trampoline". */ ++static int ++ppc64_in_solib_call_trampoline (CORE_ADDR pc, char *name) ++{ ++ /* Detecting solib call trampolines on PPC64 GNU/Linux is a pain. ++ ++ It's not specifically solib call trampolines that are the issue. ++ Any call from one function to another function that uses a ++ different TOC requires a trampoline, to save the caller's TOC ++ pointer and then load the callee's TOC. An executable or shared ++ library may have more than one TOC, so even intra-object calls ++ may require a trampoline. Since executable and shared libraries ++ will all have their own distinct TOCs, every inter-object call is ++ also an inter-TOC call, and requires a trampoline --- so "solib ++ call trampolines" are just a special case. ++ ++ The 64-bit PowerPC GNU/Linux ABI calls these call trampolines ++ "linkage functions". Since they need to be near the functions ++ that call them, they all appear in .text, not in any special ++ section. The .plt section just contains an array of function ++ descriptors, from which the linkage functions load the callee's ++ entry point, TOC value, and environment pointer. So ++ in_plt_section is useless. The linkage functions don't have any ++ special linker symbols to name them, either. ++ ++ The only way I can see to recognize them is to actually look at ++ their code. They're generated by ppc_build_one_stub and some ++ other functions in bfd/elf64-ppc.c, so that should show us all ++ the instruction sequences we need to recognize. */ ++ unsigned int insn[PPC64_STANDARD_LINKAGE_LEN]; ++ ++ return insns_match_pattern (pc, ppc64_standard_linkage, insn); ++} ++ ++ ++/* When the dynamic linker is doing lazy symbol resolution, the first ++ call to a function in another object will go like this: ++ ++ - The user's function calls the linkage function: ++ ++ 100007c4: 4b ff fc d5 bl 10000498 ++ 100007c8: e8 41 00 28 ld r2,40(r1) ++ ++ - The linkage function loads the entry point (and other stuff) from ++ the function descriptor in the PLT, and jumps to it: ++ ++ 10000498: 3d 82 00 00 addis r12,r2,0 ++ 1000049c: f8 41 00 28 std r2,40(r1) ++ 100004a0: e9 6c 80 98 ld r11,-32616(r12) ++ 100004a4: e8 4c 80 a0 ld r2,-32608(r12) ++ 100004a8: 7d 69 03 a6 mtctr r11 ++ 100004ac: e9 6c 80 a8 ld r11,-32600(r12) ++ 100004b0: 4e 80 04 20 bctr ++ ++ - But since this is the first time that PLT entry has been used, it ++ sends control to its glink entry. That loads the number of the ++ PLT entry and jumps to the common glink0 code: ++ ++ 10000c98: 38 00 00 00 li r0,0 ++ 10000c9c: 4b ff ff dc b 10000c78 ++ ++ - The common glink0 code then transfers control to the dynamic ++ linker's fixup code: ++ ++ 10000c78: e8 41 00 28 ld r2,40(r1) ++ 10000c7c: 3d 82 00 00 addis r12,r2,0 ++ 10000c80: e9 6c 80 80 ld r11,-32640(r12) ++ 10000c84: e8 4c 80 88 ld r2,-32632(r12) ++ 10000c88: 7d 69 03 a6 mtctr r11 ++ 10000c8c: e9 6c 80 90 ld r11,-32624(r12) ++ 10000c90: 4e 80 04 20 bctr ++ ++ Eventually, this code will figure out how to skip all of this, ++ including the dynamic linker. At the moment, we just get through ++ the linkage function. */ ++ ++/* If the current thread is about to execute a series of instructions ++ at PC matching the ppc64_standard_linkage pattern, and INSN is the result ++ from that pattern match, return the code address to which the ++ standard linkage function will send them. (This doesn't deal with ++ dynamic linker lazy symbol resolution stubs.) */ ++static CORE_ADDR ++ppc64_standard_linkage_target (CORE_ADDR pc, unsigned int *insn) ++{ ++ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); ++ ++ /* The address of the function descriptor this linkage function ++ references. */ ++ CORE_ADDR desc ++ = ((CORE_ADDR) read_register (tdep->ppc_gp0_regnum + 2) ++ + (insn_d_field (insn[0]) << 16) ++ + insn_ds_field (insn[2])); ++ ++ /* The first word of the descriptor is the entry point. Return that. */ ++ return ppc64_desc_entry_point (desc); ++} ++ ++ ++/* Given that we've begun executing a call trampoline at PC, return ++ the entry point of the function the trampoline will go to. */ ++static CORE_ADDR ++ppc64_skip_trampoline_code (CORE_ADDR pc) ++{ ++ unsigned int ppc64_standard_linkage_insn[PPC64_STANDARD_LINKAGE_LEN]; ++ ++ if (insns_match_pattern (pc, ppc64_standard_linkage, ++ ppc64_standard_linkage_insn)) ++ return ppc64_standard_linkage_target (pc, ppc64_standard_linkage_insn); ++ else ++ return 0; ++} ++ ++ ++/* Support for CONVERT_FROM_FUNC_PTR_ADDR (ARCH, ADDR, TARG) on PPC64 ++ GNU/Linux. ++ ++ Usually a function pointer's representation is simply the address ++ of the function. On GNU/Linux on the 64-bit PowerPC however, a ++ function pointer is represented by a pointer to a TOC entry. This ++ TOC entry contains three words, the first word is the address of ++ the function, the second word is the TOC pointer (r2), and the ++ third word is the static chain value. Throughout GDB it is ++ currently assumed that a function pointer contains the address of ++ the function, which is not easy to fix. In addition, the ++ conversion of a function address to a function pointer would ++ require allocation of a TOC entry in the inferior's memory space, ++ with all its drawbacks. To be able to call C++ virtual methods in ++ the inferior (which are called via function pointers), ++ find_function_addr uses this function to get the function address ++ from a function pointer. */ ++ ++/* If ADDR points at what is clearly a function descriptor, transform ++ it into the address of the corresponding function. Be ++ conservative, otherwize GDB will do the transformation on any ++ random addresses such as occures when there is no symbol table. */ ++ ++static CORE_ADDR ++ppc64_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch, ++ CORE_ADDR addr, ++ struct target_ops *targ) ++{ ++ struct section_table *s = target_section_by_addr (targ, addr); ++ ++ /* Check if ADDR points to a function descriptor. */ ++ if (s && strcmp (s->the_bfd_section->name, ".opd") == 0) ++ return get_target_memory_unsigned (targ, addr, 8); ++ ++ return addr; ++} ++ ++#ifdef CRASH_MERGE ++enum { ++ PPC_ELF_NGREG = 48, ++ PPC_ELF_NFPREG = 33, ++ PPC_ELF_NVRREG = 33 ++}; ++ ++enum { ++ ELF_GREGSET_SIZE = (PPC_ELF_NGREG * 4), ++ ELF_FPREGSET_SIZE = (PPC_ELF_NFPREG * 8) ++}; ++#else ++enum { ++ ELF_NGREG = 48, ++ ELF_NFPREG = 33, ++ ELF_NVRREG = 33 ++}; ++ ++enum { ++ ELF_GREGSET_SIZE = (ELF_NGREG * 4), ++ ELF_FPREGSET_SIZE = (ELF_NFPREG * 8) ++}; ++#endif ++ ++void ++ppc_linux_supply_gregset (char *buf) ++{ ++ int regi; ++ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); ++ ++ for (regi = 0; regi < 32; regi++) ++ supply_register (regi, buf + 4 * regi); ++ ++ supply_register (PC_REGNUM, buf + 4 * PPC_LINUX_PT_NIP); ++ supply_register (tdep->ppc_lr_regnum, buf + 4 * PPC_LINUX_PT_LNK); ++ supply_register (tdep->ppc_cr_regnum, buf + 4 * PPC_LINUX_PT_CCR); ++ supply_register (tdep->ppc_xer_regnum, buf + 4 * PPC_LINUX_PT_XER); ++ supply_register (tdep->ppc_ctr_regnum, buf + 4 * PPC_LINUX_PT_CTR); ++ if (tdep->ppc_mq_regnum != -1) ++ supply_register (tdep->ppc_mq_regnum, buf + 4 * PPC_LINUX_PT_MQ); ++ supply_register (tdep->ppc_ps_regnum, buf + 4 * PPC_LINUX_PT_MSR); ++} ++ ++void ++ppc_linux_supply_fpregset (char *buf) ++{ ++ int regi; ++ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); ++ ++ for (regi = 0; regi < 32; regi++) ++ supply_register (FP0_REGNUM + regi, buf + 8 * regi); ++ ++ /* The FPSCR is stored in the low order word of the last doubleword in the ++ fpregset. */ ++ supply_register (tdep->ppc_fpscr_regnum, buf + 8 * 32 + 4); ++} ++ ++/* ++ Use a local version of this function to get the correct types for regsets. ++*/ ++ ++static void ++fetch_core_registers (char *core_reg_sect, ++ unsigned core_reg_size, ++ int which, ++ CORE_ADDR reg_addr) ++{ ++ if (which == 0) ++ { ++ if (core_reg_size == ELF_GREGSET_SIZE) ++ ppc_linux_supply_gregset (core_reg_sect); ++ else ++ warning ("wrong size gregset struct in core file"); ++ } ++ else if (which == 2) ++ { ++ if (core_reg_size == ELF_FPREGSET_SIZE) ++ ppc_linux_supply_fpregset (core_reg_sect); ++ else ++ warning ("wrong size fpregset struct in core file"); ++ } ++} ++ ++/* Register that we are able to handle ELF file formats using standard ++ procfs "regset" structures. */ ++ ++static struct core_fns ppc_linux_regset_core_fns = ++{ ++ bfd_target_elf_flavour, /* core_flavour */ ++ default_check_format, /* check_format */ ++ default_core_sniffer, /* core_sniffer */ ++ fetch_core_registers, /* core_read_registers */ ++ NULL /* next */ ++}; ++ ++static void ++ppc_linux_init_abi (struct gdbarch_info info, ++ struct gdbarch *gdbarch) ++{ ++ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); ++ ++ if (tdep->wordsize == 4) ++ { ++ /* Until November 2001, gcc did not comply with the 32 bit SysV ++ R4 ABI requirement that structures less than or equal to 8 ++ bytes should be returned in registers. Instead GCC was using ++ the the AIX/PowerOpen ABI - everything returned in memory ++ (well ignoring vectors that is). When this was corrected, it ++ wasn't fixed for GNU/Linux native platform. Use the ++ PowerOpen struct convention. */ ++ set_gdbarch_return_value (gdbarch, ppc_linux_return_value); ++ ++ /* Note: kevinb/2002-04-12: See note in rs6000_gdbarch_init regarding ++ *_push_arguments(). The same remarks hold for the methods below. */ ++ set_gdbarch_deprecated_frameless_function_invocation (gdbarch, ppc_linux_frameless_function_invocation); ++ set_gdbarch_deprecated_frame_chain (gdbarch, ppc_linux_frame_chain); ++ set_gdbarch_deprecated_frame_saved_pc (gdbarch, ppc_linux_frame_saved_pc); ++ ++ set_gdbarch_deprecated_frame_init_saved_regs (gdbarch, ++ ppc_linux_frame_init_saved_regs); ++ set_gdbarch_deprecated_init_extra_frame_info (gdbarch, ++ ppc_linux_init_extra_frame_info); ++ ++ set_gdbarch_memory_remove_breakpoint (gdbarch, ++ ppc_linux_memory_remove_breakpoint); ++ /* Shared library handling. */ ++ set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section); ++ set_gdbarch_skip_trampoline_code (gdbarch, ++ ppc_linux_skip_trampoline_code); ++ set_solib_svr4_fetch_link_map_offsets ++ (gdbarch, ppc_linux_svr4_fetch_link_map_offsets); ++ } ++ ++ if (tdep->wordsize == 8) ++ { ++ /* Handle PPC64 GNU/Linux function pointers (which are really ++ function descriptors). */ ++ set_gdbarch_convert_from_func_ptr_addr ++ (gdbarch, ppc64_linux_convert_from_func_ptr_addr); ++ ++ set_gdbarch_in_solib_call_trampoline ++ (gdbarch, ppc64_in_solib_call_trampoline); ++ set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code); ++ ++ /* PPC64 malloc's entry-point is called ".malloc". */ ++ set_gdbarch_name_of_malloc (gdbarch, ".malloc"); ++ } ++} ++ ++void ++_initialize_ppc_linux_tdep (void) ++{ ++ /* Register for all sub-familes of the POWER/PowerPC: 32-bit and ++ 64-bit PowerPC, and the older rs6k. */ ++ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc, GDB_OSABI_LINUX, ++ ppc_linux_init_abi); ++ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc64, GDB_OSABI_LINUX, ++ ppc_linux_init_abi); ++ gdbarch_register_osabi (bfd_arch_rs6000, bfd_mach_rs6k, GDB_OSABI_LINUX, ++ ppc_linux_init_abi); ++ add_core_fns (&ppc_linux_regset_core_fns); ++} +--- crash/s390_dump.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/s390_dump.c 2006-10-11 09:14:35.000000000 -0400 +@@ -1,8 +1,8 @@ + /* s390_dump.c - core analysis suite + * + * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. All rights reserved. + * Copyright (C) 2005 Michael Holzheu, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify +@@ -16,7 +16,7 @@ + * GNU General Public License for more details. + */ + #include "defs.h" +-#include ++//#include + #include "ibm_common.h" + + static FILE * s390_file; +@@ -69,10 +69,13 @@ + return WRITE_ERROR; + } + ++#define S390_PAGE_SHIFT 12 ++#define S390_PAGE_SIZE (1UL << S390_PAGE_SHIFT) ++ + uint + s390_page_size(void) + { +- return PAGE_SIZE; ++ return S390_PAGE_SIZE; + } + + int +--- crash/gdb-6.1.patch.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/gdb-6.1.patch 2009-01-22 16:26:30.000000000 -0500 +@@ -0,0 +1,319 @@ ++--- gdb-6.1.orig/bfd/coff-alpha.c +++++ gdb-6.1/bfd/coff-alpha.c ++@@ -1455,7 +1455,7 @@ alpha_relocate_section (output_bfd, info ++ amt = sizeof (struct ecoff_section_tdata); ++ lita_sec_data = ((struct ecoff_section_tdata *) ++ bfd_zalloc (input_bfd, amt)); ++- ecoff_section_data (input_bfd, lita_sec) = lita_sec_data; +++ lita_sec->used_by_bfd = lita_sec_data; ++ } ++ ++ if (lita_sec_data->gp != 0) ++--- gdb-6.1.orig/sim/ppc/debug.c +++++ gdb-6.1/sim/ppc/debug.c ++@@ -28,6 +28,7 @@ ++ #ifdef HAVE_STDLIB_H ++ #include ++ #endif +++#include ++ ++ int ppc_trace[nr_trace_options]; ++ ++--- gdb-6.1.orig/gdb/remote.c +++++ gdb-6.1/gdb/remote.c ++@@ -3445,7 +3445,7 @@ remote_store_registers (int regnum) ++ { ++ int i; ++ regs = alloca (rs->sizeof_g_packet); ++- memset (regs, rs->sizeof_g_packet, 0); +++ memset (regs, 0, rs->sizeof_g_packet); ++ for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++) ++ { ++ struct packet_reg *r = &rs->regs[i]; ++--- gdb-6.1.orig/gdb/std-regs.c +++++ gdb-6.1/gdb/std-regs.c ++@@ -61,7 +61,7 @@ value_of_builtin_frame_reg (struct frame ++ val = allocate_value (builtin_type_frame_reg); ++ VALUE_LVAL (val) = not_lval; ++ buf = VALUE_CONTENTS_RAW (val); ++- memset (buf, TYPE_LENGTH (VALUE_TYPE (val)), 0); +++ memset (buf, 0, TYPE_LENGTH (VALUE_TYPE (val))); ++ /* frame.base. */ ++ if (frame != NULL) ++ ADDRESS_TO_POINTER (builtin_type_void_data_ptr, buf, ++@@ -87,7 +87,7 @@ value_of_builtin_frame_fp_reg (struct fr ++ struct value *val = allocate_value (builtin_type_void_data_ptr); ++ char *buf = VALUE_CONTENTS_RAW (val); ++ if (frame == NULL) ++- memset (buf, TYPE_LENGTH (VALUE_TYPE (val)), 0); +++ memset (buf, 0, TYPE_LENGTH (VALUE_TYPE (val))); ++ else ++ ADDRESS_TO_POINTER (builtin_type_void_data_ptr, buf, ++ get_frame_base_address (frame)); ++@@ -105,7 +105,7 @@ value_of_builtin_frame_pc_reg (struct fr ++ struct value *val = allocate_value (builtin_type_void_data_ptr); ++ char *buf = VALUE_CONTENTS_RAW (val); ++ if (frame == NULL) ++- memset (buf, TYPE_LENGTH (VALUE_TYPE (val)), 0); +++ memset (buf, 0, TYPE_LENGTH (VALUE_TYPE (val))); ++ else ++ ADDRESS_TO_POINTER (builtin_type_void_data_ptr, buf, ++ get_frame_pc (frame)); ++--- gdb-6.1.orig/gdb/dwarf2-frame.c +++++ gdb-6.1/gdb/dwarf2-frame.c ++@@ -1353,7 +1353,9 @@ decode_frame_entry_1 (struct comp_unit * ++ else if (*augmentation == 'P') ++ { ++ /* Skip. */ ++- buf += size_of_encoded_value (*buf++); +++// buf += size_of_encoded_value (*buf++); +++ buf += size_of_encoded_value(*buf); +++ buf++; ++ augmentation++; ++ } ++ ++--- gdb-6.1/opcodes/i386-dis.c.orig +++++ gdb-6.1/opcodes/i386-dis.c ++@@ -2092,6 +2092,10 @@ print_insn (bfd_vma pc, disassemble_info ++ dp = &dis386_twobyte[*++codep]; ++ need_modrm = twobyte_has_modrm[*codep]; ++ uses_SSE_prefix = twobyte_uses_SSE_prefix[*codep]; +++ if (dp->name && strcmp(dp->name, "ud2a") == 0) { +++ extern int kernel_BUG_encoding_bytes(void); +++ codep += kernel_BUG_encoding_bytes(); +++ } ++ } ++ else ++ { ++--- gdb-6.1/gdb/amd64-linux-nat.c.orig 2008-02-19 08:59:33.000000000 -0500 +++++ gdb-6.1/gdb/amd64-linux-nat.c 2008-02-19 09:27:23.000000000 -0500 ++@@ -28,6 +28,7 @@ ++ ++ #include "gdb_assert.h" ++ #include "gdb_string.h" +++typedef unsigned int u32; ++ #include ++ #include ++ #include ++--- gdb-6.1/gdb/cli/cli-cmds.c.orig +++++ gdb-6.1/gdb/cli/cli-cmds.c ++@@ -37,6 +37,7 @@ ++ #include "objfiles.h" ++ #include "source.h" ++ #include "disasm.h" +++#include "gdb_stat.h" ++ ++ #include "ui-out.h" ++ ++@@ -54,6 +55,8 @@ ++ #define GDBINIT_FILENAME ".gdbinit" ++ #endif ++ +++#include +++ ++ /* Prototypes for local command functions */ ++ ++ static void complete_command (char *, int); ++@@ -441,12 +444,30 @@ source_command (char *args, int from_tty ++ stream = fopen (file, FOPEN_RT); ++ if (!stream) ++ { ++- if (from_tty) +++ if (from_tty > 0) ++ perror_with_name (file); ++ else ++ return; ++ } ++ +++ if (from_tty == -1) +++ { +++ struct stat statbuf; +++ int fd = fileno (stream); +++ if (fstat (fd, &statbuf) < 0) +++ { +++ perror_with_name (file); +++ fclose (stream); +++ return; +++ } +++ if (statbuf.st_uid != getuid () || (statbuf.st_mode & S_IWOTH)) +++ { +++ extern void untrusted_file(FILE *, char *); untrusted_file(NULL, file); +++ fclose (stream); +++ return; +++ } +++ } +++ ++ script_from_file (stream, file); ++ ++ do_cleanups (old_cleanups); ++--- gdb-6.1/gdb/dwarfread.c.orig +++++ gdb-6.1/gdb/dwarfread.c ++@@ -2138,9 +2138,7 @@ decode_line_numbers (char *linetable) ++ ++ NOTES ++ ++- Note that stack[0] is unused except as a default error return. ++- Note that stack overflow is not yet handled. ++- */ +++ Note that stack[0] is unused except as a default error return. */ ++ ++ static int ++ locval (struct dieinfo *dip) ++@@ -2160,7 +2158,7 @@ locval (struct dieinfo *dip) ++ loc += nbytes; ++ end = loc + locsize; ++ stacki = 0; ++- stack[stacki] = 0; +++ stack[++stacki] = 0; ++ dip->isreg = 0; ++ dip->offreg = 0; ++ dip->optimized_out = 1; ++@@ -2224,6 +2222,16 @@ locval (struct dieinfo *dip) ++ stacki--; ++ break; ++ } +++ /* Enforce maximum stack depth of size-1 to avoid ++stacki writing +++ outside of the allocated space. Also enforce minimum > 0. +++ -- wad@google.com 14 Aug 2006 */ +++ if (stacki >= sizeof (stack) / sizeof (*stack) - 1) +++ internal_error (__FILE__, __LINE__, +++ _("location description stack too deep: %d"), +++ stacki); +++ if (stacki <= 0) +++ internal_error (__FILE__, __LINE__, +++ _("location description stack too shallow")); ++ } ++ return (stack[stacki]); ++ } ++--- gdb-6.1/bfd/elfcode.h.orig +++++ gdb-6.1/bfd/elfcode.h ++@@ -33,7 +33,7 @@ Foundation, Inc., 59 Temple Place - Suit ++ /* Problems and other issues to resolve. ++ ++ (1) BFD expects there to be some fixed number of "sections" in ++- the object file. I.E. there is a "section_count" variable in the +++ the object file. I.E. there is a "section_count" variable in the ++ bfd structure which contains the number of sections. However, ELF ++ supports multiple "views" of a file. In particular, with current ++ implementations, executable files typically have two tables, a ++@@ -629,8 +629,13 @@ elf_object_p (bfd *abfd) ++ ++ if (i_ehdrp->e_shoff != 0) ++ { +++ bfd_signed_vma where = i_ehdrp->e_shoff; +++ +++ if (where != (file_ptr) where) +++ goto got_wrong_format_error; +++ ++ /* Seek to the section header table in the file. */ ++- if (bfd_seek (abfd, (file_ptr) i_ehdrp->e_shoff, SEEK_SET) != 0) +++ if (bfd_seek (abfd, (file_ptr) where, SEEK_SET) != 0) ++ goto got_no_match; ++ ++ /* Read the first section header at index 0, and convert to internal ++@@ -642,11 +647,46 @@ elf_object_p (bfd *abfd) ++ /* If the section count is zero, the actual count is in the first ++ section header. */ ++ if (i_ehdrp->e_shnum == SHN_UNDEF) ++- i_ehdrp->e_shnum = i_shdr.sh_size; +++ { +++ i_ehdrp->e_shnum = i_shdr.sh_size; +++ if (i_ehdrp->e_shnum != i_shdr.sh_size +++ || i_ehdrp->e_shnum == 0) +++ goto got_wrong_format_error; +++ } ++ ++ /* And similarly for the string table index. */ ++ if (i_ehdrp->e_shstrndx == SHN_XINDEX) ++- i_ehdrp->e_shstrndx = i_shdr.sh_link; +++ { +++ i_ehdrp->e_shstrndx = i_shdr.sh_link; +++ if (i_ehdrp->e_shstrndx != i_shdr.sh_link) +++ goto got_wrong_format_error; +++ } +++ +++ /* Sanity check that we can read all of the section headers. +++ It ought to be good enough to just read the last one. */ +++ if (i_ehdrp->e_shnum != 1) +++ { +++ /* Check that we don't have a totally silly number of sections. */ +++ if (i_ehdrp->e_shnum > (unsigned int) -1 / sizeof (x_shdr) +++ || i_ehdrp->e_shnum > (unsigned int) -1 / sizeof (i_shdr)) +++ goto got_wrong_format_error; +++ +++ where += (i_ehdrp->e_shnum - 1) * sizeof (x_shdr); +++ if (where != (file_ptr) where) +++ goto got_wrong_format_error; +++ if ((bfd_size_type) where <= i_ehdrp->e_shoff) +++ goto got_wrong_format_error; +++ +++ if (bfd_seek (abfd, (file_ptr) where, SEEK_SET) != 0) +++ goto got_no_match; +++ if (bfd_bread (&x_shdr, sizeof x_shdr, abfd) != sizeof (x_shdr)) +++ goto got_no_match; +++ +++ /* Back to where we were. */ +++ where = i_ehdrp->e_shoff + sizeof (x_shdr); +++ if (bfd_seek (abfd, (file_ptr) where, SEEK_SET) != 0) +++ goto got_no_match; +++ } ++ } ++ ++ /* Allocate space for a copy of the section header table in ++@@ -690,6 +730,20 @@ elf_object_p (bfd *abfd) ++ goto got_no_match; ++ elf_swap_shdr_in (abfd, &x_shdr, i_shdrp + shindex); ++ +++ /* Sanity check sh_link and sh_info. */ +++ if (i_shdrp[shindex].sh_link >= num_sec +++ || (i_shdrp[shindex].sh_link >= SHN_LORESERVE +++ && i_shdrp[shindex].sh_link <= SHN_HIRESERVE)) +++ goto got_wrong_format_error; +++ +++ if (((i_shdrp[shindex].sh_flags & SHF_INFO_LINK) +++ || i_shdrp[shindex].sh_type == SHT_RELA +++ || i_shdrp[shindex].sh_type == SHT_REL) +++ && (i_shdrp[shindex].sh_info >= num_sec +++ || (i_shdrp[shindex].sh_info >= SHN_LORESERVE +++ && i_shdrp[shindex].sh_info <= SHN_HIRESERVE))) +++ goto got_wrong_format_error; +++ ++ /* If the section is loaded, but not page aligned, clear ++ D_PAGED. */ ++ if (i_shdrp[shindex].sh_size != 0 ++@@ -708,6 +762,17 @@ elf_object_p (bfd *abfd) ++ goto got_no_match; ++ } ++ +++ /* A further sanity check. */ +++ if (i_ehdrp->e_shnum != 0) +++ { +++ if (i_ehdrp->e_shstrndx >= elf_numsections (abfd) +++ || (i_ehdrp->e_shstrndx >= SHN_LORESERVE +++ && i_ehdrp->e_shstrndx <= SHN_HIRESERVE)) +++ goto got_wrong_format_error; +++ } +++ else if (i_ehdrp->e_shstrndx != 0) +++ goto got_wrong_format_error; +++ ++ /* Read in the program headers. */ ++ if (i_ehdrp->e_phnum == 0) ++ elf_tdata (abfd)->phdr = NULL; ++@@ -1071,7 +1136,7 @@ elf_slurp_symbol_table (bfd *abfd, asymb ++ symcount); ++ ++ /* Slurp in the symbols without the version information, ++- since that is more helpful than just quitting. */ +++ since that is more helpful than just quitting. */ ++ verhdr = NULL; ++ } ++ ++@@ -1138,7 +1203,7 @@ elf_slurp_symbol_table (bfd *abfd, asymb ++ sym->symbol.section = bfd_abs_section_ptr; ++ ++ /* If this is a relocatable file, then the symbol value is ++- already section relative. */ +++ already section relative. */ ++ if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0) ++ sym->symbol.value -= sym->symbol.section->vma; ++ +--- crash/kernel.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/kernel.c 2009-02-04 16:53:05.000000000 -0500 +@@ -1,8 +1,8 @@ + /* kernel.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -16,11 +16,12 @@ + */ + + #include "defs.h" ++#include "xen_hyper_defs.h" + #include + + static void do_module_cmd(ulong, char *, ulong, char *, char *); + static char *find_module_objfile(char *, char *, char *); +-static char *get_uptime(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); +@@ -42,330 +43,589 @@ + 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 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); + + + /* + * Gather a few kernel basics. + */ + void +-kernel_init(int when) ++kernel_init() + { +- int i; +- char *p1, *p2, buf[BUFSIZE];; ++ int i, c; ++ char *p1, *p2, buf[BUFSIZE]; + struct syment *sp1, *sp2; ++ char *rqstruct; ++ char *irq_desc_type_name; + + if (pc->flags & KERNEL_DEBUG_QUERY) + return; + +- switch (when) +- { +- case PRE_GDB: +- 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 = symbol_value("_end"); ++ 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 = symbol_value("_end"); + +- 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') && (sp2->type == 'A') && +- (sp2->value > sp1->value)) +- kt->flags |= SMP|PER_CPU_OFF; ++ /* ++ * 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 (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; + +- get_symbol_data("xtime", sizeof(struct timespec), &kt->date); ++ get_symbol_data("xtime", sizeof(struct timespec), &kt->date); + +- if (pc->flags & GET_TIMESTAMP) { +- fprintf(fp, "%s\n\n", +- strip_linefeeds(ctime(&kt->date.tv_sec))); +- clean_exit(0); +- } ++ if (pc->flags & GET_TIMESTAMP) { ++ fprintf(fp, "%s\n\n", ++ strip_linefeeds(ctime(&kt->date.tv_sec))); ++ clean_exit(0); ++ } + +- readmem(symbol_value("system_utsname"), KVADDR, &kt->utsname, +- sizeof(struct new_utsname), "system_utsname", +- FAULT_ON_ERROR); +- strncpy(buf, kt->utsname.release, MIN(strlen(kt->utsname.release), 65)); +- if (ascii_string(kt->utsname.release)) { +- 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); +- } +- break; ++ 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"); + +- case POST_GDB: +- if (symbol_exists("__per_cpu_offset")) { ++ strncpy(buf, kt->utsname.release, MIN(strlen(kt->utsname.release), 65)); ++ if (ascii_string(kt->utsname.release)) { ++ 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]); ++ } 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 <= NR_CPUS ? i : NR_CPUS), +- &kt->__per_cpu_offset[0]); +- kt->flags |= PER_CPU_OFF; +- } +- MEMBER_OFFSET_INIT(runqueue_cpu, "runqueue", "cpu"); +- if (VALID_MEMBER(runqueue_cpu)) { +- 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 (!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, "runqueue", "idle"); +- MEMBER_OFFSET_INIT(runqueue_curr, "runqueue", "curr"); +- } +- MEMBER_OFFSET_INIT(runqueue_active, "runqueue", "active"); +- MEMBER_OFFSET_INIT(runqueue_expired, "runqueue", "expired"); +- MEMBER_OFFSET_INIT(runqueue_arrays, "runqueue", "arrays"); +- MEMBER_OFFSET_INIT(prio_array_queue, "prio_array", "queue"); +- MEMBER_OFFSET_INIT(prio_array_nr_active, "prio_array", +- "nr_active"); +- STRUCT_SIZE_INIT(runqueue, "runqueue"); +- STRUCT_SIZE_INIT(prio_array, "prio_array"); +- +- /* +- * 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(); +- } ++ 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; ++ } ++ if (STRUCT_EXISTS("runqueue")) ++ rqstruct = "runqueue"; ++ else if (STRUCT_EXISTS("rq")) ++ rqstruct = "rq"; + +- if ((kt->flags & SMP) && ACTIVE() && (kt->cpus == 1) && +- (kt->flags & PER_CPU_OFF)) ++ 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(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"); ++ ++ /* ++ * 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->cpus > NR_CPUS) { +- error(WARNING, +- "calculated number of cpus (%d) greater than compiled-in NR_CPUS (%d)\n", +- kt->cpus, NR_CPUS); +- error(FATAL, "recompile crash with larger NR_CPUS\n"); +- } +- +- STRUCT_SIZE_INIT(spinlock_t, "spinlock_t"); +- verify_spinlock(); +- +- 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"); +- +- MEMBER_OFFSET_INIT(irq_desc_t_status, "irq_desc_t", "status"); +- MEMBER_OFFSET_INIT(irq_desc_t_handler, "irq_desc_t", "handler"); +- MEMBER_OFFSET_INIT(irq_desc_t_action, "irq_desc_t", "action"); +- MEMBER_OFFSET_INIT(irq_desc_t_depth, "irq_desc_t", "depth"); +- MEMBER_OFFSET_INIT(hw_interrupt_type_typename, ++ 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"); ++ } ++ ++ STRUCT_SIZE_INIT(spinlock_t, "spinlock_t"); ++ verify_spinlock(); ++ ++ 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); ++ 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 ++ 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"); ++ 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_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, ++ MEMBER_OFFSET_INIT(hw_interrupt_type_ack, + "hw_interrupt_type", "ack"); +- MEMBER_OFFSET_INIT(hw_interrupt_type_end, ++ MEMBER_OFFSET_INIT(hw_interrupt_type_end, + "hw_interrupt_type", "end"); + MEMBER_OFFSET_INIT(hw_interrupt_type_set_affinity, + "hw_interrupt_type", "set_affinity"); +- 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"); +- +- STRUCT_SIZE_INIT(irq_desc_t, "irq_desc_t"); +- +- 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"); +- } +- +- 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_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"); +- } ++ } 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"); ++ ++ STRUCT_SIZE_INIT(irq_cpustat_t, "irq_cpustat_t"); ++ MEMBER_OFFSET_INIT(irq_cpustat_t___softirq_active, ++ "irq_cpustat_t", "__softirq_active"); ++ MEMBER_OFFSET_INIT(irq_cpustat_t___softirq_mask, ++ "irq_cpustat_t", "__softirq_mask"); ++ ++ STRUCT_SIZE_INIT(timer_list, "timer_list"); ++ MEMBER_OFFSET_INIT(timer_list_list, "timer_list", "list"); ++ MEMBER_OFFSET_INIT(timer_list_next, "timer_list", "next"); ++ MEMBER_OFFSET_INIT(timer_list_entry, "timer_list", "entry"); ++ MEMBER_OFFSET_INIT(timer_list_expires, "timer_list", "expires"); ++ MEMBER_OFFSET_INIT(timer_list_function, "timer_list", "function"); ++ STRUCT_SIZE_INIT(timer_vec_root, "timer_vec_root"); ++ if (VALID_STRUCT(timer_vec_root)) ++ MEMBER_OFFSET_INIT(timer_vec_root_vec, ++ "timer_vec_root", "vec"); ++ STRUCT_SIZE_INIT(timer_vec, "timer_vec"); ++ if (VALID_STRUCT(timer_vec)) ++ MEMBER_OFFSET_INIT(timer_vec_vec, "timer_vec", "vec"); ++ ++ STRUCT_SIZE_INIT(tvec_root_s, "tvec_root_s"); ++ if (VALID_STRUCT(tvec_root_s)) { ++ STRUCT_SIZE_INIT(tvec_t_base_s, "tvec_t_base_s"); ++ MEMBER_OFFSET_INIT(tvec_t_base_s_tv1, ++ "tvec_t_base_s", "tv1"); ++ MEMBER_OFFSET_INIT(tvec_root_s_vec, ++ "tvec_root_s", "vec"); ++ STRUCT_SIZE_INIT(tvec_s, "tvec_s"); ++ MEMBER_OFFSET_INIT(tvec_s_vec, "tvec_s", "vec"); ++ } else { ++ STRUCT_SIZE_INIT(tvec_root_s, "tvec_root"); ++ if (VALID_STRUCT(tvec_root_s)) { ++ STRUCT_SIZE_INIT(tvec_t_base_s, "tvec_base"); ++ MEMBER_OFFSET_INIT(tvec_t_base_s_tv1, ++ "tvec_base", "tv1"); ++ MEMBER_OFFSET_INIT(tvec_root_s_vec, ++ "tvec_root", "vec"); ++ STRUCT_SIZE_INIT(tvec_s, "tvec"); ++ MEMBER_OFFSET_INIT(tvec_s_vec, "tvec", "vec"); ++ } ++ } ++ STRUCT_SIZE_INIT(__wait_queue, "__wait_queue"); ++ if (VALID_STRUCT(__wait_queue)) { ++ if (MEMBER_EXISTS("__wait_queue", "task")) ++ MEMBER_OFFSET_INIT(__wait_queue_task, ++ "__wait_queue", "task"); ++ else ++ MEMBER_OFFSET_INIT(__wait_queue_task, ++ "__wait_queue", "private"); ++ MEMBER_OFFSET_INIT(__wait_queue_head_task_list, ++ "__wait_queue_head", "task_list"); ++ MEMBER_OFFSET_INIT(__wait_queue_task_list, ++ "__wait_queue", "task_list"); ++ } else { ++ STRUCT_SIZE_INIT(wait_queue, "wait_queue"); ++ if (VALID_STRUCT(wait_queue)) { ++ MEMBER_OFFSET_INIT(wait_queue_task, ++ "wait_queue", "task"); ++ MEMBER_OFFSET_INIT(wait_queue_next, ++ "wait_queue", "next"); + } ++ } + +- STRUCT_SIZE_INIT(pt_regs, "pt_regs"); +- STRUCT_SIZE_INIT(softirq_state, "softirq_state"); +- STRUCT_SIZE_INIT(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(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"); ++ STRUCT_SIZE_INIT(pt_regs, "pt_regs"); ++ STRUCT_SIZE_INIT(softirq_state, "softirq_state"); ++ 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(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; +- } ++ if (!(kt->flags & NO_KALLSYMS)) ++ kt->flags |= KALLSYMS_V1; ++ } + +- MEMBER_OFFSET_INIT(module_num_symtab, "module", "num_symtab"); ++ 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 (VALID_MEMBER(module_num_symtab)) { ++ MEMBER_OFFSET_INIT(module_symtab, "module", "symtab"); ++ MEMBER_OFFSET_INIT(module_strtab, "module", "strtab"); + +- if (!(kt->flags & NO_KALLSYMS)) +- kt->flags |= KALLSYMS_V2; ++ if (!(kt->flags & 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; ++ } ++ ++ BUG_bytes_init(); ++} ++ ++/* ++ * 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; ++ struct mapinfo { ++ ulong cpu_flag; ++ char *name; ++ } mapinfo[] = { ++ { POSSIBLE, "cpu_possible_map" }, ++ { PRESENT, "cpu_present_map" }, ++ { ONLINE, "cpu_online_map" }, ++ }; ++ ++ if ((len = STRUCT_SIZE("cpumask_t")) < 0) ++ len = sizeof(ulong); ++ ++ buf = GETBUF(len); ++ ++ for (m = 0; m < sizeof(mapinfo)/sizeof(struct mapinfo); m++) { ++ if (!kernel_symbol_exists(mapinfo[m].name)) ++ continue; ++ ++ if (!readmem(symbol_value(mapinfo[m].name), KVADDR, buf, len, ++ mapinfo[m].name, RETURN_ON_ERROR)) { ++ error(WARNING, "cannot read %s\n", mapinfo[m].name); ++ continue; + } +- break; ++ ++ 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; ++ kt->cpu_flags[cpu] |= mapinfo[m].cpu_flag; ++ } ++ } ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "%s: ", mapinfo[m].name); ++ for (i = 0; i < NR_CPUS; i++) { ++ if (kt->cpu_flags[i] & mapinfo[m].cpu_flag) ++ fprintf(fp, "%d ", i); ++ } ++ fprintf(fp, "\n"); ++ } ++ ++ } ++ ++ FREEBUF(buf); ++} ++ ++/* ++ * Determine whether a cpu is in one of the cpu masks. ++ */ ++int ++in_cpu_map(int map, int cpu) ++{ ++ if (cpu >= (kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS)) { ++ error(INFO, "in_cpu_map: invalid cpu: %d\n", cpu); ++ return FALSE; ++ } ++ ++ switch (map) ++ { ++ case POSSIBLE: ++ if (!kernel_symbol_exists("cpu_possible_map")) { ++ error(INFO, "cpu_possible_map does not exist\n"); ++ return FALSE; ++ } ++ return (kt->cpu_flags[cpu] & POSSIBLE); ++ ++ case PRESENT: ++ if (!kernel_symbol_exists("cpu_present_map")) { ++ error(INFO, "cpu_present_map does not exist\n"); ++ return FALSE; ++ } ++ return (kt->cpu_flags[cpu] & PRESENT); ++ ++ case ONLINE: ++ if (!kernel_symbol_exists("cpu_online_map")) { ++ error(INFO, "cpu_online_map does not exist\n"); ++ return FALSE; ++ } ++ return (kt->cpu_flags[cpu] & ONLINE); + } ++ ++ 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 +@@ -377,7 +637,7 @@ + { + char buf[BUFSIZE]; + ulong linux_banner; +- int argc; ++ int argc, len; + char *arglist[MAXARGS]; + char *p1, *p2; + struct syment *sp; +@@ -389,7 +649,7 @@ + + if (!(sp = symbol_search("linux_banner"))) + error(FATAL, "linux_banner symbol does not exist?\n"); +- else if (sp->type == 'R') ++ else if ((sp->type == 'R') || (sp->type == 'r')) + linux_banner = symbol_value("linux_banner"); + else + get_symbol_data("linux_banner", sizeof(ulong), &linux_banner); +@@ -405,9 +665,10 @@ + error(WARNING, "cannot read linux_banner string\n"); + + if (ACTIVE()) { +- if (strlen(kt->proc_version) && !STREQ(buf, kt->proc_version)) { ++ len = strlen(kt->proc_version); ++ if ((len > 0) && (strncmp(buf, kt->proc_version, len) != 0)) { + if (CRASHDEBUG(1)) { +- fprintf(fp, "/proc/version:\n%s", ++ fprintf(fp, "/proc/version:\n%s\n", + kt->proc_version); + fprintf(fp, "linux_banner:\n%s\n", buf); + } +@@ -422,7 +683,7 @@ + fprintf(fp, "linux_banner:\n%s\n", buf); + goto bad_match; + } +- strcpy(kt->proc_version, buf); ++ strcpy(kt->proc_version, strip_linefeeds(buf)); + } + + verify_namelist(); +@@ -471,6 +732,9 @@ + } + } + ++ if (CRASHDEBUG(1)) ++ gdb_readnow_warning(); ++ + return; + + bad_match: +@@ -614,6 +878,10 @@ + 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; + +@@ -633,7 +901,7 @@ + if (!strstr(buffer, "Linux version 2.")) + continue; + +- if (STREQ(buffer, kt->proc_version)) { ++ if (strstr(buffer, kt->proc_version)) { + found = TRUE; + break; + } +@@ -680,7 +948,7 @@ + if (found) { + if (CRASHDEBUG(1)) { + fprintf(fp, "verify_namelist:\n"); +- fprintf(fp, "/proc/version:\n%s", kt->proc_version); ++ fprintf(fp, "/proc/version:\n%s\n", kt->proc_version); + fprintf(fp, "utsname version: %s\n", + kt->utsname.version); + fprintf(fp, "%s:\n%s\n", namelist, buffer); +@@ -689,8 +957,12 @@ + } + + if (CRASHDEBUG(1)) { ++ error(WARNING, ++ "\ncannot find matching kernel version in %s file:\n\n", ++ namelist); ++ + fprintf(fp, "verify_namelist:\n"); +- fprintf(fp, "/proc/version:\n%s", kt->proc_version); ++ fprintf(fp, "/proc/version:\n%s\n", kt->proc_version); + fprintf(fp, "utsname version: %s\n", kt->utsname.version); + fprintf(fp, "%s:\n%s\n", namelist, buffer2); + } +@@ -740,7 +1012,7 @@ + { + int c; + int do_load_module_filter, do_machdep_filter, reverse; +- int unfiltered, user_mode, count_entered; ++ int unfiltered, user_mode, count_entered, bug_bytes_entered; + ulong curaddr; + ulong revtarget; + ulong count; +@@ -754,7 +1026,16 @@ + char buf4[BUFSIZE]; + char buf5[BUFSIZE]; + +- reverse = count_entered = FALSE; ++ if ((argcnt == 2) && STREQ(args[1], "-b")) { ++ fprintf(fp, "encoded bytes being skipped after ud2a: "); ++ if (kt->BUG_bytes < 0) ++ fprintf(fp, "undetermined\n"); ++ else ++ fprintf(fp, "%d\n", kt->BUG_bytes); ++ return; ++ } ++ ++ reverse = count_entered = bug_bytes_entered = FALSE; + sp = NULL; + unfiltered = user_mode = do_machdep_filter = do_load_module_filter = 0; + +@@ -763,7 +1044,7 @@ + req->flags |= GNU_FROM_TTY_OFF|GNU_RETURN_ON_ERROR; + req->count = 1; + +- while ((c = getopt(argcnt, args, "ulrx")) != EOF) { ++ while ((c = getopt(argcnt, args, "ulrxb:B:")) != EOF) { + switch(c) + { + case 'x': +@@ -786,6 +1067,12 @@ + BZERO(buf4, BUFSIZE); + break; + ++ case 'B': ++ case 'b': ++ kt->BUG_bytes = atoi(optarg); ++ bug_bytes_entered = TRUE; ++ break; ++ + default: + argerrs++; + break; +@@ -846,7 +1133,7 @@ + if (user_mode) { + sprintf(buf1, "x/%ldi 0x%lx", + req->count ? req->count : 1, req->addr); +- pc->cmdgenspec = pc->cmdgencur; ++ pc->curcmd_flags |= MEMTYPE_UVADDR; + gdb_pass_through(buf1, NULL, 0); + return; + } +@@ -962,7 +1249,9 @@ + close_tmpfile(); + } + } +- else cmd_usage(pc->curcmd, SYNOPSIS); ++ else if (bug_bytes_entered) ++ return; ++ else cmd_usage(pc->curcmd, SYNOPSIS); + + if (!reverse) { + FREEBUF(req->buf); +@@ -1053,6 +1342,185 @@ + FREEBUF(req); + } + ++/* ++ * x86 and x86_64 kernels may have file/line-number encoding ++ * asm()'d in just after the "ud2a" instruction, which confuses ++ * the disassembler and the x86 backtracer. Determine the ++ * number of bytes to skip. ++ */ ++static void ++BUG_bytes_init(void) ++{ ++ if (machine_type("X86")) ++ kt->BUG_bytes = BUG_x86(); ++ else if (machine_type("X86_64")) ++ kt->BUG_bytes = BUG_x86_64(); ++} ++ ++static int ++BUG_x86(void) ++{ ++ struct syment *sp, *spn; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ ulong vaddr, fileptr; ++ int found; ++ ++ /* ++ * Prior to 2.4.19, a call to do_BUG() preceded ++ * the standalone ud2a instruction. ++ */ ++ if (THIS_KERNEL_VERSION < LINUX(2,4,19)) ++ return 0; ++ ++ /* ++ * 2.6.20 introduced __bug_table support for i386, ++ * but even if CONFIG_DEBUG_BUGVERBOSE is not configured, ++ * the ud2a stands alone. ++ */ ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,20)) ++ return 0; ++ ++ /* ++ * For previous kernel versions, it may depend upon ++ * whether CONFIG_DEBUG_BUGVERBOSE was configured: ++ * ++ * #ifdef CONFIG_DEBUG_BUGVERBOSE ++ * #define BUG() \ ++ * __asm__ __volatile__( "ud2\n" \ ++ * "\t.word %c0\n" \ ++ * "\t.long %c1\n" \ ++ * : : "i" (__LINE__), "i" (__FILE__)) ++ * #else ++ * #define BUG() __asm__ __volatile__("ud2\n") ++ * #endif ++ * ++ * But that's not necessarily true, since there are ++ * pre-2.6.11 versions that force it like so: ++ * ++ * #if 1 /- Set to zero for a slightly smaller kernel -/ ++ * #define BUG() \ ++ * __asm__ __volatile__( "ud2\n" \ ++ * "\t.word %c0\n" \ ++ * "\t.long %c1\n" \ ++ * : : "i" (__LINE__), "i" (__FILE__)) ++ * #else ++ * #define BUG() __asm__ __volatile__("ud2\n") ++ * #endif ++ */ ++ ++ /* ++ * This works if in-kernel config data is available. ++ */ ++ if ((THIS_KERNEL_VERSION >= LINUX(2,6,11)) && ++ (kt->flags & BUGVERBOSE_OFF)) ++ return 0; ++ ++ /* ++ * At this point, it's a pretty safe bet that it's configured, ++ * but to be sure, disassemble a known BUG() caller and ++ * verify that the encoding is there. ++ */ ++ ++#define X86_BUG_BYTES (6) /* sizeof(short) + sizeof(pointer) */ ++ ++ if (!(sp = symbol_search("do_exit")) || ++ !(spn = next_symbol(NULL, sp))) ++ return X86_BUG_BYTES; ++ ++ sprintf(buf1, "x/%ldi 0x%lx", spn->value - sp->value, sp->value); ++ ++ found = FALSE; ++ 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(arglist[0], RETURN_ON_ERROR, 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 that includes +@@ -1094,7 +1562,8 @@ + } + + #define FRAMESIZE_DEBUG_MESSAGE \ +-"usage: bt -F [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 usage: bt -F [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 -F [clear|dump|validate] [-I rip]\n If rip: \"validate\" will verbosely recalculate the framesize.\n If !rip: \"clear\" will clear the framesize cache.\n \"dump\" will dump the current framesize cache entries.\n" ++ + + /* + * Display a kernel stack backtrace. Arguments may be any number pid or task +@@ -1108,18 +1577,25 @@ + * -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) \ +- BCOPY(&bt_setup, bt, sizeof(struct bt_info)); \ ++ clone_bt_info(&bt_setup, bt, (TC)); \ + if (refptr) { \ + BZERO(&reference, sizeof(struct reference)); \ + bt->ref = &reference; \ + bt->ref->str = refptr; \ +- } \ +- bt->tc = (TC); \ +- bt->task = ((TC)->task); \ +- bt->stackbase = GET_STACKBASE((TC)->task); \ +- bt->stacktop = GET_STACKTOP((TC)->task); \ +- bt->stackbuf = NULL; ++ } + + void + cmd_bt(void) +@@ -1140,17 +1616,45 @@ + bt = &bt_info; + BZERO(bt, sizeof(struct bt_info)); + +- while ((c = getopt(argcnt, args, "fF:I:S:aloreEgstd:R:")) != EOF) { +- switch(c) ++ if (kt->flags & USE_OLD_BT) ++ bt->flags |= BT_OLD_BACK_TRACE; ++ ++ while ((c = getopt(argcnt, args, "fF:I:S:aloreEgstTd:R:O")) != EOF) { ++ switch (c) + { + case 'f': + bt->flags |= BT_FULL; + break; + + case 'o': ++ if (XEN_HYPER_MODE()) ++ option_not_supported(c); + bt->flags |= BT_OLD_BACK_TRACE; + break; + ++ case 'O': ++ if (!(machine_type("X86") || machine_type("X86_64")) || ++ XEN_HYPER_MODE()) ++ option_not_supported(c); ++ else if (kt->flags & USE_OLD_BT) { ++ /* ++ * Make this setting idempotent across the use of ++ * $HOME/.crashrc, ./.crashrc, and "-i input" files. ++ * If we've been here before during initialization, ++ * leave it alone. ++ */ ++ if (pc->flags & INIT_IFILE) { ++ error(INFO, "use old bt method by default (already set)\n"); ++ return; ++ } ++ kt->flags &= ~USE_OLD_BT; ++ error(INFO, "use new bt method by default\n"); ++ } else { ++ kt->flags |= USE_OLD_BT; ++ error(INFO, "use old bt method by default\n"); ++ } ++ return; ++ + case 'R': + if (refptr) + error(INFO, "only one -R option allowed\n"); +@@ -1166,11 +1670,15 @@ + 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; + +@@ -1217,6 +1725,9 @@ + } 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; +@@ -1241,6 +1752,8 @@ + bt->flags |= BT_SYMBOLIC_ARGS; + break; + ++ case 'T': ++ bt->flags |= BT_TEXT_SYMBOLS_ALL; + case 't': + bt->flags |= BT_TEXT_SYMBOLS; + break; +@@ -1286,12 +1799,50 @@ + 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 (active) { + if (ACTIVE()) + error(FATAL, + "-a option not supported on a live system\n"); + + for (c = 0; c < NR_CPUS; c++) { ++ if (setjmp(pc->foreach_loop_env)) { ++ free_all_bufs(); ++ continue; ++ } ++ pc->flags |= IN_FOREACH; ++ + if ((tc = task_to_context(tt->panic_threads[c]))) { + BT_SETUP(tc); + if (!BT_REFERENCE_CHECK(bt)) +@@ -1299,6 +1850,7 @@ + back_trace(bt); + } + } ++ pc->flags &= ~IN_FOREACH; + + return; + } +@@ -1350,9 +1902,10 @@ + char buf[BUFSIZE]; + + if (bt->flags & BT_TEXT_SYMBOLS) { +- fprintf(fp, "%sSTART: %s at %lx\n", +- space(VADDR_PRLEN > 8 ? 14 : 6), +- closest_symbol(eip), eip); ++ if (!(bt->flags & BT_TEXT_SYMBOLS_ALL)) ++ fprintf(fp, "%sSTART: %s at %lx\n", ++ space(VADDR_PRLEN > 8 ? 14 : 6), ++ closest_symbol(eip), eip); + } + + if (bt->hp) +@@ -1435,6 +1988,9 @@ + 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)); + } +@@ -1461,20 +2017,26 @@ + if (bt->hp) { + if (bt->hp->esp && !INSTACK(bt->hp->esp, bt)) + error(INFO, +- "invalid stack address for this task: %lx\n", +- bt->hp->esp); ++ "invalid 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); + +- } else if (NETDUMP_DUMPFILE()) ++ } 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 (LKCD_DUMPFILE()) + get_lkcd_regs(bt, &eip, &esp); ++ else if (XENDUMP_DUMPFILE()) ++ get_xendump_regs(bt, &eip, &esp); + else + machdep->get_stack_frame(bt, &eip, &esp); + +@@ -1486,6 +2048,13 @@ + 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; +@@ -1510,8 +2079,13 @@ + { + case BT_HARDIRQ: + btloc.hp->eip = symbol_value("do_IRQ"); +- btloc.hp->esp = ULONG(bt->stackbuf + +- SIZE(irq_ctx) - (sizeof(unsigned int)*2)); ++ 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, "--- ---\n"); + break; + +@@ -1538,7 +2112,7 @@ + BCOPY(bt, &btsave, sizeof(struct bt_info)); + + if (CRASHDEBUG(4)) +- dump_bt_info(bt); ++ dump_bt_info(bt, "back_trace"); + + machdep->back_trace(bt); + +@@ -1546,7 +2120,15 @@ + 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); +@@ -1562,6 +2144,8 @@ + restore_stack(struct bt_info *bt) + { + ulonglong type; ++ struct syment *sp; ++ ulong retvaddr; + + bt->instptr = bt->stkptr = 0; + type = 0; +@@ -1569,14 +2153,30 @@ + switch (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)) + { + case BT_HARDIRQ: +- bt->instptr = symbol_value("do_IRQ"); +- bt->stkptr = ULONG(bt->stackbuf + +- SIZE(irq_ctx) - (sizeof(unsigned int)*2)); ++ 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: +- bt->instptr = symbol_value("do_softirq"); ++ retvaddr = ULONG(bt->stackbuf + ++ SIZE(irq_ctx) - sizeof(char *)); ++ if ((sp = value_search(retvaddr, NULL)) && ++ STREQ(sp->name, "do_softirq")) ++ bt->instptr = retvaddr; ++ else ++ bt->instptr = symbol_value("do_softirq"); + bt->stkptr = ULONG(bt->stackbuf + + OFFSET(thread_info_previous_esp)); + type = BT_SOFTIRQ; +@@ -1635,7 +2235,8 @@ + if ((p1 = strstr(buf, ":"))) { + esp = eip = 0; + *p1 = NULLCHAR; +- if ((esp = htol(buf, RETURN_ON_ERROR, NULL)) != BADADDR) ++ if (((esp = htol(buf, RETURN_ON_ERROR, NULL)) != BADADDR) ++ && INSTACK(esp, bt)) + eip = GET_STACK_ULONG(esp); + if (esp && eip) { + hooks[cnt].esp = esp; +@@ -1660,12 +2261,14 @@ + * Debug routine most likely useful from above in back_trace() + */ + void +-dump_bt_info(struct bt_info *bt) ++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); +@@ -1700,6 +2303,11 @@ + 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++){ +@@ -1721,6 +2329,25 @@ + *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 + +@@ -1837,8 +2464,8 @@ + return; + } + +- if (IS_VMALLOC_ADDR(list.next) && +- IS_VMALLOC_ADDR(list.prev)) { ++ 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; +@@ -1873,14 +2500,17 @@ + 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(7)) ++ if (CRASHDEBUG(3)) + fprintf(fp, "module: %lx\n", mod); + + if (!readmem(mod, KVADDR, modbuf, SIZE(module), + "module struct", RETURN_ON_ERROR|QUIET)) { + error(WARNING, +- "cannot access vmalloc'd module memory\n\n"); ++ "%scannot access vmalloc'd module memory\n\n", ++ DUMPFILE() ? "\n" : ""); + kt->mods_installed = 0; + kt->flags |= NO_MODULE_ACCESS; + FREEBUF(modbuf); +@@ -1914,7 +2544,8 @@ + kallsymsbuf, SIZE(kallsyms_header), + "kallsyms_header", RETURN_ON_ERROR|QUIET)) { + error(WARNING, +- "cannot access module kallsyms_header\n"); ++ "%scannot access module kallsyms_header\n", ++ DUMPFILE() ? "\n" : ""); + } else { + nsyms = UINT(kallsymsbuf + + OFFSET(kallsyms_header_symbols)); +@@ -1924,7 +2555,10 @@ + break; + + case KALLSYMS_V2: +- numksyms = ULONG(modbuf + OFFSET(module_num_symtab)); ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) ++ numksyms = UINT(modbuf + OFFSET(module_num_symtab)); ++ else ++ numksyms = ULONG(modbuf + OFFSET(module_num_symtab)); + total += numksyms; + break; + } +@@ -1947,6 +2581,8 @@ + store_module_symbols_v2(total, kt->mods_installed); + break; + } ++ ++ please_wait_done(); + } + + +@@ -2035,8 +2671,12 @@ + case KMOD_V2: + module_name = modbuf + + OFFSET(module_name); +- mod_size = LONG(modbuf + +- OFFSET(module_core_size)); ++ 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 +@@ -2112,7 +2752,7 @@ + address = 0; + flag = LIST_MODULE_HDR; + +- while ((c = getopt(argcnt, args, "rd:Ds:St:")) != EOF) { ++ while ((c = getopt(argcnt, args, "rd:Ds:St:o")) != EOF) { + switch(c) + { + case 'r': +@@ -2145,6 +2785,19 @@ + 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 't': + if (is_directory(optarg)) + tree = optarg; +@@ -2459,7 +3112,7 @@ + + + static char * +-find_module_objfile(char *modref, char *filename, char *tree) ++module_objfile_search(char *modref, char *filename, char *tree) + { + char buf[BUFSIZE]; + char file[BUFSIZE]; +@@ -2477,16 +3130,20 @@ + strcpy(file, filename); + #ifdef MODULES_IN_CWD + else { +- sprintf(file, "%s.o", modref); +- 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; +- } ++ 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 +@@ -2505,6 +3162,8 @@ + 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))) +@@ -2578,7 +3237,7 @@ + retbuf = search_directory_tree(dir, file); + + if (!retbuf) { +- sprintf(dir, "/lib/modules/%s", kt->utsname.release); ++ sprintf(dir, "/lib/modules/%s/updates", kt->utsname.release); + if (!(retbuf = search_directory_tree(dir, file))) { + switch (kt->flags & (KMOD_V1|KMOD_V2)) + { +@@ -2589,13 +3248,68 @@ + } + } + +- return retbuf; +-} +- +- +-/* +- * Unlink any temporary remote module object files. +- */ ++ if (!retbuf) { ++ sprintf(dir, "/lib/modules/%s", kt->utsname.release); ++ if (!(retbuf = search_directory_tree(dir, file))) { ++ switch (kt->flags & (KMOD_V1|KMOD_V2)) ++ { ++ case KMOD_V2: ++ sprintf(file, "%s.ko", modref); ++ retbuf = search_directory_tree(dir, file); ++ } ++ } ++ } ++ ++ return retbuf; ++} ++ ++/* ++ * First look for a module based upon its reference name. ++ * If that fails, try replacing any underscores in the ++ * reference name with a dash. ++ * If that fails, because of intermingled dashes and underscores, ++ * try a regex expression. ++ * ++ * Example: module name "dm_mod" comes from "dm-mod.ko" objfile ++ * module name "dm_region_hash" comes from "dm-region_hash.ko" objfile ++ */ ++static char * ++find_module_objfile(char *modref, char *filename, char *tree) ++{ ++ char * retbuf; ++ char tmpref[BUFSIZE]; ++ int i, c; ++ ++ retbuf = module_objfile_search(modref, filename, tree); ++ ++ if (!retbuf) { ++ strncpy(tmpref, modref, BUFSIZE); ++ for (c = 0; c < BUFSIZE && tmpref[c]; c++) ++ if (tmpref[c] == '_') ++ tmpref[c] = '-'; ++ retbuf = module_objfile_search(tmpref, filename, tree); ++ } ++ ++ if (!retbuf && (count_chars(modref, '_') > 1)) { ++ for (i = c = 0; modref[i]; i++) { ++ if (modref[i] == '_') { ++ tmpref[c++] = '['; ++ tmpref[c++] = '_'; ++ tmpref[c++] = '-'; ++ tmpref[c++] = ']'; ++ } else ++ tmpref[c++] = modref[i]; ++ } ++ tmpref[c] = NULLCHAR; ++ retbuf = module_objfile_search(tmpref, filename, tree); ++ } ++ ++ return retbuf; ++} ++ ++/* ++ * Unlink any temporary remote module object files. ++ */ + void + unlink_module(struct load_module *load_module) + { +@@ -2651,7 +3365,7 @@ + dump_log(int msg_level) + { + int i; +- ulong log_buf, log_start, logged_chars; ++ ulong log_buf, logged_chars; + char *buf; + char last; + ulong index; +@@ -2678,13 +3392,16 @@ + + buf = GETBUF(log_buf_len); + log_wrap = FALSE; +- get_symbol_data("log_start", sizeof(ulong), &log_start); + get_symbol_data("logged_chars", sizeof(ulong), &logged_chars); + readmem(log_buf, KVADDR, buf, + log_buf_len, "log_buf contents", FAULT_ON_ERROR); + +- log_start &= log_buf_len-1; +- index = (logged_chars < log_buf_len) ? 0 : log_start; ++ if (logged_chars < log_buf_len) { ++ index = 0; ++ } else { ++ get_symbol_data("log_end", sizeof(ulong), &index); ++ index &= log_buf_len-1; ++ } + + if ((logged_chars < log_buf_len) && (index == 0) && (buf[index] == '<')) + loglevel = TRUE; +@@ -2787,6 +3504,8 @@ + 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++; +@@ -2867,6 +3586,9 @@ + if (NETDUMP_DUMPFILE() && is_partial_netdump()) + fprintf(fp, " [PARTIAL DUMP]"); + ++ if (DISKDUMP_DUMPFILE() && is_partial_diskdump()) ++ fprintf(fp, " [PARTIAL DUMP]"); ++ + fprintf(fp, "\n"); + } + +@@ -2876,7 +3598,7 @@ + get_symbol_data("xtime", sizeof(struct timespec), &kt->date); + fprintf(fp, " DATE: %s\n", + strip_linefeeds(ctime(&kt->date.tv_sec))); +- fprintf(fp, " UPTIME: %s\n", get_uptime(buf)); ++ 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); +@@ -2891,10 +3613,17 @@ + #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"); ++ 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, +@@ -2952,28 +3681,42 @@ + /* + * Calculate and return the uptime. + */ +- +-static char * +-get_uptime(char *buf) ++char * ++get_uptime(char *buf, ulonglong *j64p) + { +- ulong jiffies; +- +- get_symbol_data("jiffies", sizeof(long), &jiffies); ++ ulong jiffies, tmp1, tmp2; ++ ulonglong jiffies_64, wrapped; + +- if ((machine_type("S390") || machine_type("S390X")) && +- (THIS_KERNEL_VERSION >= LINUX(2,6,0))) +- jiffies -= ((unsigned long)(unsigned int)(-300*machdep->hz)); +- else if (symbol_exists("jiffies_64") && BITS64() && +- (((ulonglong)jiffies & 0xffffffff00000000ULL) == +- 0x100000000ULL)) +- jiffies &= 0xffffffff; +- +- convert_time((ulonglong)jiffies, buf); ++ 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) +@@ -3048,9 +3791,9 @@ + struct syment *sp, *spn; + long size; + #ifdef S390X +- unsigned int *sct, *sys_call_table, addr; ++ unsigned int *sct, *sys_call_table, sys_ni_syscall, addr; + #else +- ulong *sys_call_table, *sct, addr; ++ ulong *sys_call_table, *sct, sys_ni_syscall, addr; + #endif + if (GDB_PATCHED()) + error(INFO, "line numbers are not available\n"); +@@ -3068,6 +3811,8 @@ + 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(); + +@@ -3080,13 +3825,17 @@ + "%3x " : "%3d ", i); + fprintf(fp, + "invalid sys_call_table entry: %lx (%s)\n", +- *sct, value_to_symstr(*sct, buf1, 0)); ++ (unsigned long)*sct, ++ value_to_symstr(*sct, buf1, 0)); + } + continue; + } + + fprintf(fp, (output_radix == 16) ? "%3x " : "%3d ", i); +- fprintf(fp, "%-26s ", scp); ++ 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 +@@ -3181,16 +3930,16 @@ + * "help -k" output + */ + void +-dump_kernel_table(void) ++dump_kernel_table(int verbose) + { +- int i; ++ int i, j, more, nr_cpus; + struct new_utsname *uts; + int others; + + others = 0; + uts = &kt->utsname; + +- fprintf(fp, " flags: %lx (", kt->flags); ++ 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) +@@ -3225,6 +3974,32 @@ + fprintf(fp, "%sKMOD_V2", others++ ? "|" : ""); + if (kt->flags & KALLSYMS_V2) + fprintf(fp, "%sKALLSYMS_V2", others++ ? "|" : ""); ++ if (kt->flags & USE_OLD_BT) ++ fprintf(fp, "%sUSE_OLD_BT", others++ ? "|" : ""); ++ if (kt->flags & ARCH_XEN) ++ fprintf(fp, "%sARCH_XEN", others++ ? "|" : ""); ++ if (kt->flags & ARCH_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++ ? "|" : ""); + fprintf(fp, ")\n"); + fprintf(fp, " stext: %lx\n", kt->stext); + fprintf(fp, " etext: %lx\n", kt->etext); +@@ -3234,8 +4009,10 @@ + 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); + if (kt->display_bh == display_bh_1) + fprintf(fp, " display_bh: display_bh_1()\n"); + else if (kt->display_bh == display_bh_2) +@@ -3263,21 +4040,141 @@ + 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\n", kt->relocate); + fprintf(fp, " runq_siblings: %d\n", kt->runq_siblings); + fprintf(fp, " __rq_idx[NR_CPUS]: "); +- for (i = 0; i < NR_CPUS; i++) ++ 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++) ++ 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++) ++ for (i = 0; i < nr_cpus; i++) { + fprintf(fp, "%s%.*lx ", (i % 4) == 0 ? "\n " : "", + LONG_PRLEN, kt->__per_cpu_offset[i]); +- fprintf(fp, "\n cpu_flags[NR_CPUS]:"); +- for (i = 0; i < NR_CPUS; i++) ++ if ((i % 4) == 0) { ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (kt->__per_cpu_offset[j]) ++ more = TRUE; ++ } ++ } ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } ++ ++ } ++ fprintf(fp, "\n cpu_flags[NR_CPUS]: "); ++ for (i = 0; i < nr_cpus; i++) { ++ if (!(kt->cpu_flags)) { ++ fprintf(fp, "(unused)\n"); ++ goto no_cpu_flags; ++ } + fprintf(fp, "%lx ", kt->cpu_flags[i]); ++ for (j = i, more = FALSE; j < nr_cpus; j++) { ++ if (kt->cpu_flags[j]) ++ more = TRUE; ++ } ++ if (!more) { ++ fprintf(fp, "..."); ++ break; ++ } ++ } + fprintf(fp, "\n"); ++ fprintf(fp, " cpu_possible_map: "); ++ if (kernel_symbol_exists("cpu_possible_map")) { ++ for (i = 0; i < nr_cpus; i++) { ++ if (kt->cpu_flags[i] & POSSIBLE) ++ fprintf(fp, "%d ", i); ++ } ++ fprintf(fp, "\n"); ++ } else ++ fprintf(fp, "(does not exist)\n"); ++ fprintf(fp, " cpu_present_map: "); ++ if (kernel_symbol_exists("cpu_present_map")) { ++ for (i = 0; i < nr_cpus; i++) { ++ if (kt->cpu_flags[i] & PRESENT) ++ fprintf(fp, "%d ", i); ++ } ++ fprintf(fp, "\n"); ++ } else ++ fprintf(fp, "(does not exist)\n"); ++ fprintf(fp, " cpu_online_map: "); ++ if (kernel_symbol_exists("cpu_online_map")) { ++ for (i = 0; i < nr_cpus; i++) { ++ if (kt->cpu_flags[i] & ONLINE) ++ fprintf(fp, "%d ", i); ++ } ++ fprintf(fp, "\n"); ++ } else ++ fprintf(fp, "(does not exist)\n"); ++no_cpu_flags: ++ 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 start: %lx end: %lx (%ld mfns)\n", ++ i, kt->p2m_mapping_cache[i].mapping, ++ 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"); + } + + /* +@@ -3314,7 +4211,7 @@ + if (machine_type("S390") || machine_type("S390X")) + command_not_supported(); + +- while ((c = getopt(argcnt, args, "db")) != EOF) { ++ while ((c = getopt(argcnt, args, "dbu")) != EOF) { + switch(c) + { + case 'd': +@@ -3344,6 +4241,17 @@ + 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; ++ + default: + argerrs++; + break; +@@ -3362,6 +4270,8 @@ + return; + } + ++ pc->curcmd_flags &= ~IRQ_IN_USE; ++ + while (args[optind]) { + i = dtoi(args[optind], FAULT_ON_ERROR, NULL); + if (i >= nr_irqs) +@@ -3402,13 +4312,22 @@ + + readmem(irq_desc_addr + OFFSET(irq_desc_t_status), KVADDR, &status, + sizeof(int), "irq_desc entry", FAULT_ON_ERROR); +- readmem(irq_desc_addr + OFFSET(irq_desc_t_handler), KVADDR, &handler, +- sizeof(long), "irq_desc entry", 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 entry", ++ 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 entry", ++ FAULT_ON_ERROR); + readmem(irq_desc_addr + OFFSET(irq_desc_t_action), KVADDR, &action, + sizeof(long), "irq_desc entry", FAULT_ON_ERROR); + readmem(irq_desc_addr + OFFSET(irq_desc_t_depth), KVADDR, &depth, + sizeof(int), "irq_desc entry", FAULT_ON_ERROR); + ++ if (!action && (handler == (ulong)pc->curcmd_private)) ++ return; ++ + fprintf(fp, " IRQ: %d\n", irq); + fprintf(fp, " STATUS: %x %s", status, status ? "(" : ""); + others = 0; +@@ -3441,19 +4360,30 @@ + } else + fprintf(fp, "%lx\n", handler); + +- if (handler) { +- readmem(handler+OFFSET(hw_interrupt_type_typename), KVADDR, +- &tmp1, sizeof(void *), +- "hw_interrupt_type typename", FAULT_ON_ERROR); ++ 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"); + +- readmem(handler+OFFSET(hw_interrupt_type_startup), KVADDR, +- &tmp1, sizeof(void *), +- "hw_interrupt_type startup", FAULT_ON_ERROR); ++ 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)); +@@ -3464,9 +4394,15 @@ + value_to_symstr(tmp2, buf, 0)); + fprintf(fp, "\n"); + +- readmem(handler+OFFSET(hw_interrupt_type_shutdown), KVADDR, +- &tmp1, sizeof(void *), +- "hw_interrupt_type shutdown", FAULT_ON_ERROR); ++ 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)); +@@ -3494,9 +4430,14 @@ + fprintf(fp, "\n"); + } + +- readmem(handler+OFFSET(hw_interrupt_type_enable), KVADDR, +- &tmp1, sizeof(void *), +- "hw_interrupt_type enable", FAULT_ON_ERROR); ++ 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)); +@@ -3507,9 +4448,14 @@ + value_to_symstr(tmp2, buf, 0)); + fprintf(fp, "\n"); + +- readmem(handler+OFFSET(hw_interrupt_type_disable), KVADDR, +- &tmp1, sizeof(void *), +- "hw_interrupt_type disable", FAULT_ON_ERROR); ++ 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)); +@@ -3534,6 +4480,84 @@ + 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)) { +@@ -3550,6 +4574,20 @@ + 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)) { +@@ -3567,6 +4605,66 @@ + 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"); + } + } + +@@ -4146,7 +5244,7 @@ + } + + /* +- * 2.6 per-cpu timers, using "per_cpu__tvec_bases". XXX ++ * 2.6 per-cpu timers, using "per_cpu__tvec_bases". + */ + + static void +@@ -4169,8 +5267,20 @@ + */ + 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; + +@@ -4220,8 +5330,12 @@ + else + tvec_bases = symbol_value("per_cpu__tvec_bases"); + +- fprintf(fp, "TVEC_BASES[%d]: %lx\n", cpu, +- tvec_bases + SIZE(tvec_t_base_s)); ++ 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")); +@@ -4320,6 +5434,11 @@ + else + tvec_bases = symbol_value("per_cpu__tvec_bases"); + ++ 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); +@@ -4475,9 +5594,16 @@ + ld->start = vec[i]; + ld->list_head_offset = offset; + ld->end = vec_kvaddr; ++ ld->flags = RETURN_ON_LIST_ERROR; + + hq_open(); +- timer_cnt = do_list(ld); ++ 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)); +@@ -4708,21 +5834,627 @@ + machdep->last_pgd_read = 0; + machdep->last_pmd_read = 0; + machdep->last_ptbl_read = 0; ++ if (machdep->clear_machdep_cache) ++ machdep->clear_machdep_cache(); + } + } + + /* +- * For kernels containing cpu_online_map, count the bits. ++ * If it exists, return the number of cpus in the cpu_online_map. + */ + int + get_cpus_online() + { +- ulong cpu_online_map; ++ int i, len, online; ++ struct gnu_request req; ++ char *buf; ++ ulong *maskptr; + + if (!symbol_exists("cpu_online_map")) + return 0; + +- get_symbol_data("cpu_online_map", sizeof(ulong), &cpu_online_map); ++ if (LKCD_KERNTYPES()) { ++ if ((len = STRUCT_SIZE("cpumask_t")) < 0) ++ error(FATAL, "cannot determine type cpumask_t\n"); ++ } else ++ len = get_symbol_type("cpu_online_map", NULL, &req) == ++ TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; ++ buf = GETBUF(len); + +- return count_bits_long(cpu_online_map); ++ online = 0; ++ ++ if (readmem(symbol_value("cpu_online_map"), 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); ++ ++ FREEBUF(buf); ++ if (CRASHDEBUG(1)) ++ error(INFO, "get_cpus_online: online: %d\n", online); ++ } ++ ++ return online; ++} ++ ++/* ++ * If it exists, return the number of cpus in the cpu_present_map. ++ */ ++int ++get_cpus_present() ++{ ++ int i, len, present; ++ struct gnu_request req; ++ char *buf; ++ ulong *maskptr; ++ ++ if (!symbol_exists("cpu_present_map")) ++ return 0; ++ ++ if (LKCD_KERNTYPES()) { ++ if ((len = STRUCT_SIZE("cpumask_t")) < 0) ++ error(FATAL, "cannot determine type cpumask_t\n"); ++ } else ++ len = get_symbol_type("cpu_present_map", NULL, &req) == ++ TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; ++ buf = GETBUF(len); ++ ++ present = 0; ++ ++ if (readmem(symbol_value("cpu_present_map"), 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); ++ ++ FREEBUF(buf); ++ if (CRASHDEBUG(1)) ++ error(INFO, "get_cpus_present: present: %d\n", present); ++ } ++ ++ return present; ++} ++ ++/* ++ * If it exists, return the number of cpus in the cpu_possible_map. ++ */ ++int ++get_cpus_possible() ++{ ++ int i, len, possible; ++ struct gnu_request req; ++ char *buf; ++ ulong *maskptr; ++ ++ if (!symbol_exists("cpu_possible_map")) ++ return 0; ++ ++ if (LKCD_KERNTYPES()) { ++ if ((len = STRUCT_SIZE("cpumask_t")) < 0) ++ error(FATAL, "cannot determine type cpumask_t\n"); ++ } else ++ len = get_symbol_type("cpu_possible_map", NULL, &req) == ++ TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; ++ buf = GETBUF(len); ++ ++ possible = 0; ++ ++ if (readmem(symbol_value("cpu_possible_map"), 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); ++ ++ FREEBUF(buf); ++ if (CRASHDEBUG(1)) ++ error(INFO, "get_cpus_possible: possible: %d\n", ++ possible); ++ } ++ ++ return possible; ++} ++ ++/* ++ * 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)) ++ error(INFO, ++ "xen_machine_to_pseudo_PAE: 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 mapping, kmfn, pfn, p, i, c; ++ ulong start, end; ++ ulong *mp; ++ ++ mp = (ulong *)kt->m2p_page; ++ mapping = kt->phys_to_machine_mapping; ++ ++ /* ++ * Check the FIFO cache first. ++ */ ++ for (c = 0; c < P2M_MAPPING_CACHE; c++) { ++ if (kt->p2m_mapping_cache[c].mapping && ++ ((mfn >= kt->p2m_mapping_cache[c].start) && ++ (mfn <= kt->p2m_mapping_cache[c].end))) { ++ ++ if (kt->p2m_mapping_cache[c].mapping != kt->last_mapping_read) { ++ if (!readmem(kt->p2m_mapping_cache[c].mapping, KVADDR, ++ mp, PAGESIZE(), "phys_to_machine_mapping page (cached)", ++ RETURN_ON_ERROR)) ++ error(FATAL, "cannot access " ++ "phys_to_machine_mapping page\n"); ++ else ++ kt->last_mapping_read = kt->p2m_mapping_cache[c].mapping; ++ } else ++ kt->p2m_page_cache_hits++; ++ ++ for (i = 0; i < XEN_PFNS_PER_PAGE; i++) { ++ kmfn = (*(mp+i)) & ~XEN_FOREIGN_FRAME; ++ if (kmfn == mfn) { ++ p = P2M_MAPPING_TO_PAGE_INDEX(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; ++ } ++ } ++ ++ /* ++ * The machine address was not cached, so search from the ++ * beginning of the phys_to_machine_mapping array, caching ++ * only the found machine address. ++ */ ++ 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); ++} ++ ++/* ++ * 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; ++} ++ ++ ++ ++/* ++ * Read the relevant IKCONFIG (In Kernel Config) data if available. ++ */ ++ ++static char *ikconfig[] = { ++ "CONFIG_NR_CPUS", ++ "CONFIG_PGTABLE_4", ++ "CONFIG_HZ", ++ "CONFIG_DEBUG_BUGVERBOSE", ++ 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"); ++ 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; ++ ++ 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; ++ 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); ++ } ++ } ++ } ++ } ++ } 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; ++ } + } +--- crash/unwind_x86_32_64.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/unwind_x86_32_64.c 2006-11-15 14:58:11.000000000 -0500 +@@ -0,0 +1,1220 @@ ++#if defined(X86_64) ++/* ++ * Support for genarating DWARF CFI based backtraces. ++ * Borrowed heavily from the kernel's implementation of unwinding using the ++ * DWARF CFI written by Jan Beulich ++ */ ++ ++#ifdef X86_64 ++#include "unwind_x86_64.h" ++#endif ++#ifdef X86 ++#include "unwind_x86.h" ++#endif ++ ++#include "defs.h" ++ ++#define MAX_STACK_DEPTH 8 ++ ++static struct local_unwind_table { ++ struct { ++ unsigned long pc; ++ unsigned long range; ++ } core, init; ++ void *address; ++ unsigned long size; ++} *local_unwind_tables, default_unwind_table; ++ ++static int gather_in_memory_unwind_tables(void); ++static int populate_local_tables(ulong, char *); ++static int unwind_tables_cnt = 0; ++static struct local_unwind_table *find_table(unsigned long); ++static void dump_local_unwind_tables(void); ++ ++static const struct { ++ unsigned offs:BITS_PER_LONG / 2; ++ unsigned width:BITS_PER_LONG / 2; ++} reg_info[] = { ++ UNW_REGISTER_INFO ++}; ++ ++#undef PTREGS_INFO ++#undef EXTRA_INFO ++ ++#ifndef REG_INVALID ++#define REG_INVALID(r) (reg_info[r].width == 0) ++#endif ++ ++#define DW_CFA_nop 0x00 ++#define DW_CFA_set_loc 0x01 ++#define DW_CFA_advance_loc1 0x02 ++#define DW_CFA_advance_loc2 0x03 ++#define DW_CFA_advance_loc4 0x04 ++#define DW_CFA_offset_extended 0x05 ++#define DW_CFA_restore_extended 0x06 ++#define DW_CFA_undefined 0x07 ++#define DW_CFA_same_value 0x08 ++#define DW_CFA_register 0x09 ++#define DW_CFA_remember_state 0x0a ++#define DW_CFA_restore_state 0x0b ++#define DW_CFA_def_cfa 0x0c ++#define DW_CFA_def_cfa_register 0x0d ++#define DW_CFA_def_cfa_offset 0x0e ++#define DW_CFA_def_cfa_expression 0x0f ++#define DW_CFA_expression 0x10 ++#define DW_CFA_offset_extended_sf 0x11 ++#define DW_CFA_def_cfa_sf 0x12 ++#define DW_CFA_def_cfa_offset_sf 0x13 ++#define DW_CFA_val_offset 0x14 ++#define DW_CFA_val_offset_sf 0x15 ++#define DW_CFA_val_expression 0x16 ++#define DW_CFA_lo_user 0x1c ++#define DW_CFA_GNU_window_save 0x2d ++#define DW_CFA_GNU_args_size 0x2e ++#define DW_CFA_GNU_negative_offset_extended 0x2f ++#define DW_CFA_hi_user 0x3f ++ ++#define DW_EH_PE_FORM 0x07 ++#define DW_EH_PE_native 0x00 ++#define DW_EH_PE_leb128 0x01 ++#define DW_EH_PE_data2 0x02 ++#define DW_EH_PE_data4 0x03 ++#define DW_EH_PE_data8 0x04 ++#define DW_EH_PE_signed 0x08 ++#define DW_EH_PE_ADJUST 0x70 ++#define DW_EH_PE_abs 0x00 ++#define DW_EH_PE_pcrel 0x10 ++#define DW_EH_PE_textrel 0x20 ++#define DW_EH_PE_datarel 0x30 ++#define DW_EH_PE_funcrel 0x40 ++#define DW_EH_PE_aligned 0x50 ++#define DW_EH_PE_indirect 0x80 ++#define DW_EH_PE_omit 0xff ++ ++#define min(x,y) ({ \ ++ typeof(x) _x = (x); \ ++ typeof(y) _y = (y); \ ++ (void) (&_x == &_y); \ ++ _x < _y ? _x : _y; }) ++ ++#define max(x,y) ({ \ ++ typeof(x) _x = (x); \ ++ typeof(y) _y = (y); \ ++ (void) (&_x == &_y); \ ++ _x > _y ? _x : _y; }) ++#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1)) ++ ++typedef unsigned long uleb128_t; ++typedef signed long sleb128_t; ++ ++struct unwind_item { ++ enum item_location { ++ Nowhere, ++ Memory, ++ Register, ++ Value ++ } where; ++ uleb128_t value; ++}; ++ ++struct unwind_state { ++ uleb128_t loc, org; ++ const u8 *cieStart, *cieEnd; ++ uleb128_t codeAlign; ++ sleb128_t dataAlign; ++ struct cfa { ++ uleb128_t reg, offs; ++ } cfa; ++ struct unwind_item regs[ARRAY_SIZE(reg_info)]; ++ unsigned stackDepth:8; ++ unsigned version:8; ++ const u8 *label; ++ const u8 *stack[MAX_STACK_DEPTH]; ++}; ++ ++static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; ++ ++static uleb128_t get_uleb128(const u8 **pcur, const u8 *end) ++{ ++ const u8 *cur = *pcur; ++ uleb128_t value; ++ unsigned shift; ++ ++ for (shift = 0, value = 0; cur < end; shift += 7) { ++ if (shift + 7 > 8 * sizeof(value) ++ && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { ++ cur = end + 1; ++ break; ++ } ++ value |= (uleb128_t)(*cur & 0x7f) << shift; ++ if (!(*cur++ & 0x80)) ++ break; ++ } ++ *pcur = cur; ++ ++ return value; ++} ++ ++static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) ++{ ++ const u8 *cur = *pcur; ++ sleb128_t value; ++ unsigned shift; ++ ++ for (shift = 0, value = 0; cur < end; shift += 7) { ++ if (shift + 7 > 8 * sizeof(value) ++ && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { ++ cur = end + 1; ++ break; ++ } ++ value |= (sleb128_t)(*cur & 0x7f) << shift; ++ if (!(*cur & 0x80)) { ++ value |= -(*cur++ & 0x40) << shift; ++ break; ++ } ++ } ++ *pcur = cur; ++ ++ return value; ++} ++ ++static unsigned long read_pointer(const u8 **pLoc, ++ const void *end, ++ signed ptrType) ++{ ++ unsigned long value = 0; ++ union { ++ const u8 *p8; ++ const u16 *p16u; ++ const s16 *p16s; ++ const u32 *p32u; ++ const s32 *p32s; ++ const unsigned long *pul; ++ } ptr; ++ ++ if (ptrType < 0 || ptrType == DW_EH_PE_omit) ++ return 0; ++ ptr.p8 = *pLoc; ++ switch(ptrType & DW_EH_PE_FORM) { ++ case DW_EH_PE_data2: ++ if (end < (const void *)(ptr.p16u + 1)) ++ return 0; ++ if(ptrType & DW_EH_PE_signed) ++ value = get_unaligned(ptr.p16s++); ++ else ++ value = get_unaligned(ptr.p16u++); ++ break; ++ case DW_EH_PE_data4: ++#ifdef CONFIG_64BIT ++ if (end < (const void *)(ptr.p32u + 1)) ++ return 0; ++ if(ptrType & DW_EH_PE_signed) ++ value = get_unaligned(ptr.p32s++); ++ else ++ value = get_unaligned(ptr.p32u++); ++ break; ++ case DW_EH_PE_data8: ++ BUILD_BUG_ON(sizeof(u64) != sizeof(value)); ++#else ++ BUILD_BUG_ON(sizeof(u32) != sizeof(value)); ++#endif ++ case DW_EH_PE_native: ++ if (end < (const void *)(ptr.pul + 1)) ++ return 0; ++ value = get_unaligned(ptr.pul++); ++ break; ++ case DW_EH_PE_leb128: ++ BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value)); ++ value = ptrType & DW_EH_PE_signed ++ ? get_sleb128(&ptr.p8, end) ++ : get_uleb128(&ptr.p8, end); ++ if ((const void *)ptr.p8 > end) ++ return 0; ++ break; ++ default: ++ return 0; ++ } ++ switch(ptrType & DW_EH_PE_ADJUST) { ++ case DW_EH_PE_abs: ++ break; ++ case DW_EH_PE_pcrel: ++ value += (unsigned long)*pLoc; ++ break; ++ default: ++ return 0; ++ } ++ ++/* TBD ++ if ((ptrType & DW_EH_PE_indirect) ++ && __get_user(value, (unsigned long *)value)) ++ return 0; ++*/ ++ *pLoc = ptr.p8; ++ ++ return value; ++} ++ ++static signed fde_pointer_type(const u32 *cie) ++{ ++ const u8 *ptr = (const u8 *)(cie + 2); ++ unsigned version = *ptr; ++ ++ if (version != 1) ++ return -1; /* unsupported */ ++ if (*++ptr) { ++ const char *aug; ++ const u8 *end = (const u8 *)(cie + 1) + *cie; ++ uleb128_t len; ++ ++ /* check if augmentation size is first (and thus present) */ ++ if (*ptr != 'z') ++ return -1; ++ /* check if augmentation string is nul-terminated */ ++ if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) ++ return -1; ++ ++ptr; /* skip terminator */ ++ get_uleb128(&ptr, end); /* skip code alignment */ ++ get_sleb128(&ptr, end); /* skip data alignment */ ++ /* skip return address column */ ++ version <= 1 ? (void)++ptr : (void)get_uleb128(&ptr, end); ++ len = get_uleb128(&ptr, end); /* augmentation length */ ++ if (ptr + len < ptr || ptr + len > end) ++ return -1; ++ end = ptr + len; ++ while (*++aug) { ++ if (ptr >= end) ++ return -1; ++ switch(*aug) { ++ case 'L': ++ ++ptr; ++ break; ++ case 'P': { ++ signed ptrType = *ptr++; ++ ++ if (!read_pointer(&ptr, end, ptrType) || ptr > end) ++ return -1; ++ } ++ break; ++ case 'R': ++ return *ptr; ++ default: ++ return -1; ++ } ++ } ++ } ++ return DW_EH_PE_native|DW_EH_PE_abs; ++} ++ ++static int advance_loc(unsigned long delta, struct unwind_state *state) ++{ ++ state->loc += delta * state->codeAlign; ++ ++ return delta > 0; ++} ++ ++static void set_rule(uleb128_t reg, ++ enum item_location where, ++ uleb128_t value, ++ struct unwind_state *state) ++{ ++ if (reg < ARRAY_SIZE(state->regs)) { ++ state->regs[reg].where = where; ++ state->regs[reg].value = value; ++ } ++} ++ ++static int processCFI(const u8 *start, ++ const u8 *end, ++ unsigned long targetLoc, ++ signed ptrType, ++ struct unwind_state *state) ++{ ++ union { ++ const u8 *p8; ++ const u16 *p16; ++ const u32 *p32; ++ } ptr; ++ int result = 1; ++ ++ if (start != state->cieStart) { ++ state->loc = state->org; ++ result = processCFI(state->cieStart, state->cieEnd, 0, ptrType, state); ++ if (targetLoc == 0 && state->label == NULL) ++ return result; ++ } ++ for (ptr.p8 = start; result && ptr.p8 < end; ) { ++ switch(*ptr.p8 >> 6) { ++ uleb128_t value; ++ ++ case 0: ++ switch(*ptr.p8++) { ++ case DW_CFA_nop: ++ break; ++ case DW_CFA_set_loc: ++ if ((state->loc = read_pointer(&ptr.p8, end, ++ ptrType)) == 0) ++ result = 0; ++ break; ++ case DW_CFA_advance_loc1: ++ result = ptr.p8 < end && advance_loc(*ptr.p8++, state); ++ break; ++ case DW_CFA_advance_loc2: ++ result = ptr.p8 <= end + 2 ++ && advance_loc(*ptr.p16++, state); ++ break; ++ case DW_CFA_advance_loc4: ++ result = ptr.p8 <= end + 4 ++ && advance_loc(*ptr.p32++, state); ++ break; ++ case DW_CFA_offset_extended: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Memory, ++ get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_val_offset: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Value, ++ get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_offset_extended_sf: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Memory, ++ get_sleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_val_offset_sf: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Value, ++ get_sleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_restore_extended: ++ case DW_CFA_undefined: ++ case DW_CFA_same_value: ++ set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, state); ++ break; ++ case DW_CFA_register: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Register, ++ get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_remember_state: ++ if (ptr.p8 == state->label) { ++ state->label = NULL; ++ return 1; ++ } ++ if (state->stackDepth >= MAX_STACK_DEPTH) ++ return 0; ++ state->stack[state->stackDepth++] = ptr.p8; ++ break; ++ case DW_CFA_restore_state: ++ if (state->stackDepth) { ++ const uleb128_t loc = state->loc; ++ const u8 *label = state->label; ++ ++ state->label = state->stack[state->stackDepth - 1]; ++ memcpy(&state->cfa, &badCFA, sizeof(state->cfa)); ++ memset(state->regs, 0, sizeof(state->regs)); ++ state->stackDepth = 0; ++ result = processCFI(start, end, 0, ptrType, state); ++ state->loc = loc; ++ state->label = label; ++ } else ++ return 0; ++ break; ++ case DW_CFA_def_cfa: ++ state->cfa.reg = get_uleb128(&ptr.p8, end); ++ /*nobreak*/ ++ case DW_CFA_def_cfa_offset: ++ state->cfa.offs = get_uleb128(&ptr.p8, end); ++ break; ++ case DW_CFA_def_cfa_sf: ++ state->cfa.reg = get_uleb128(&ptr.p8, end); ++ /*nobreak*/ ++ case DW_CFA_def_cfa_offset_sf: ++ state->cfa.offs = get_sleb128(&ptr.p8, end) ++ * state->dataAlign; ++ break; ++ case DW_CFA_def_cfa_register: ++ state->cfa.reg = get_uleb128(&ptr.p8, end); ++ break; ++ /*todo case DW_CFA_def_cfa_expression: */ ++ /*todo case DW_CFA_expression: */ ++ /*todo case DW_CFA_val_expression: */ ++ case DW_CFA_GNU_args_size: ++ get_uleb128(&ptr.p8, end); ++ break; ++ case DW_CFA_GNU_negative_offset_extended: ++ value = get_uleb128(&ptr.p8, end); ++ set_rule(value, Memory, (uleb128_t)0 - ++ get_uleb128(&ptr.p8, end), state); ++ break; ++ case DW_CFA_GNU_window_save: ++ default: ++ result = 0; ++ break; ++ } ++ break; ++ case 1: ++ result = advance_loc(*ptr.p8++ & 0x3f, state); ++ break; ++ case 2: ++ value = *ptr.p8++ & 0x3f; ++ set_rule(value, Memory, get_uleb128(&ptr.p8, end), ++ state); ++ break; ++ case 3: ++ set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); ++ break; ++ } ++ if (ptr.p8 > end) ++ result = 0; ++ if (result && targetLoc != 0 && targetLoc < state->loc) ++ return 1; ++ } ++ ++ return result ++ && ptr.p8 == end ++ && (targetLoc == 0 ++ || (/*todo While in theory this should apply, gcc in practice omits ++ everything past the function prolog, and hence the location ++ never reaches the end of the function. ++ targetLoc < state->loc &&*/ state->label == NULL)); ++} ++ ++ ++/* Unwind to previous to frame. Returns 0 if successful, negative ++ * number in case of an error. */ ++int ++unwind(struct unwind_frame_info *frame) ++{ ++#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) ++ const u32 *fde = NULL, *cie = NULL; ++ const u8 *ptr = NULL, *end = NULL; ++ unsigned long startLoc = 0, endLoc = 0, cfa; ++ unsigned i; ++ signed ptrType = -1; ++ uleb128_t retAddrReg = 0; ++// struct unwind_table *table; ++ void *unwind_table; ++ struct local_unwind_table *table; ++ struct unwind_state state; ++ u64 reg_ptr = 0; ++ ++ ++ if (UNW_PC(frame) == 0) ++ return -EINVAL; ++ ++ if ((table = find_table(UNW_PC(frame)))) { ++// unsigned long tableSize = unwind_table_size; ++ unsigned long tableSize = table->size; ++ ++ unwind_table = table->address; ++ ++ for (fde = unwind_table; ++ tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; ++ tableSize -= sizeof(*fde) + *fde, ++ fde += 1 + *fde / sizeof(*fde)) { ++ if (!*fde || (*fde & (sizeof(*fde) - 1))) ++ break; ++ if (!fde[1]) ++ continue; /* this is a CIE */ ++ if ((fde[1] & (sizeof(*fde) - 1)) ++ || fde[1] > (unsigned long)(fde + 1) ++ - (unsigned long)unwind_table) ++ continue; /* this is not a valid FDE */ ++ cie = fde + 1 - fde[1] / sizeof(*fde); ++ if (*cie <= sizeof(*cie) + 4 ++ || *cie >= fde[1] - sizeof(*fde) ++ || (*cie & (sizeof(*cie) - 1)) ++ || cie[1] ++ || (ptrType = fde_pointer_type(cie)) < 0) { ++ cie = NULL; /* this is not a (valid) CIE */ ++ continue; ++ } ++ ptr = (const u8 *)(fde + 2); ++ startLoc = read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ ptrType); ++ endLoc = startLoc ++ + read_pointer(&ptr, ++ (const u8 *)(fde + 1) + *fde, ++ ptrType & DW_EH_PE_indirect ++ ? ptrType ++ : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed)); ++ if (UNW_PC(frame) >= startLoc && UNW_PC(frame) < endLoc) ++ break; ++ cie = NULL; ++ } ++ } ++ if (cie != NULL) { ++ memset(&state, 0, sizeof(state)); ++ state.cieEnd = ptr; /* keep here temporarily */ ++ ptr = (const u8 *)(cie + 2); ++ end = (const u8 *)(cie + 1) + *cie; ++ if ((state.version = *ptr) != 1) ++ cie = NULL; /* unsupported version */ ++ else if (*++ptr) { ++ /* check if augmentation size is first (and thus present) */ ++ if (*ptr == 'z') { ++ /* check for ignorable (or already handled) ++ * nul-terminated augmentation string */ ++ while (++ptr < end && *ptr) ++ if (strchr("LPR", *ptr) == NULL) ++ break; ++ } ++ if (ptr >= end || *ptr) ++ cie = NULL; ++ } ++ ++ptr; ++ } ++ if (cie != NULL) { ++ /* get code aligment factor */ ++ state.codeAlign = get_uleb128(&ptr, end); ++ /* get data aligment factor */ ++ state.dataAlign = get_sleb128(&ptr, end); ++ if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) ++ cie = NULL; ++ else { ++ retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); ++ /* skip augmentation */ ++ if (((const char *)(cie + 2))[1] == 'z') ++ ptr += get_uleb128(&ptr, end); ++ if (ptr > end ++ || retAddrReg >= ARRAY_SIZE(reg_info) ++ || REG_INVALID(retAddrReg) ++ || reg_info[retAddrReg].width != sizeof(unsigned long)) ++ cie = NULL; ++ } ++ } ++ if (cie != NULL) { ++ state.cieStart = ptr; ++ ptr = state.cieEnd; ++ state.cieEnd = end; ++ end = (const u8 *)(fde + 1) + *fde; ++ /* skip augmentation */ ++ if (((const char *)(cie + 2))[1] == 'z') { ++ uleb128_t augSize = get_uleb128(&ptr, end); ++ ++ if ((ptr += augSize) > end) ++ fde = NULL; ++ } ++ } ++ if (cie == NULL || fde == NULL) ++ return -ENXIO; ++ ++ state.org = startLoc; ++ memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); ++ /* process instructions */ ++ if (!processCFI(ptr, end, UNW_PC(frame), ptrType, &state) ++ || state.loc > endLoc ++ || state.regs[retAddrReg].where == Nowhere ++ || state.cfa.reg >= ARRAY_SIZE(reg_info) ++ || reg_info[state.cfa.reg].width != sizeof(unsigned long) ++ || state.cfa.offs % sizeof(unsigned long)) { ++ return -EIO; ++ } ++ /* update frame */ ++ cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; ++ startLoc = min((unsigned long)UNW_SP(frame), cfa); ++ endLoc = max((unsigned long)UNW_SP(frame), cfa); ++ if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { ++ startLoc = min(STACK_LIMIT(cfa), cfa); ++ endLoc = max(STACK_LIMIT(cfa), cfa); ++ } ++#ifndef CONFIG_64BIT ++# define CASES CASE(8); CASE(16); CASE(32) ++#else ++# define CASES CASE(8); CASE(16); CASE(32); CASE(64) ++#endif ++ for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { ++ if (REG_INVALID(i)) { ++ if (state.regs[i].where == Nowhere) ++ continue; ++ return -EIO; ++ } ++ switch(state.regs[i].where) { ++ default: ++ break; ++ case Register: ++ if (state.regs[i].value >= ARRAY_SIZE(reg_info) ++ || REG_INVALID(state.regs[i].value) ++ || reg_info[i].width > reg_info[state.regs[i].value].width){ ++ return -EIO; ++ } ++ switch(reg_info[state.regs[i].value].width) { ++#define CASE(n) \ ++ case sizeof(u##n): \ ++ state.regs[i].value = FRAME_REG(state.regs[i].value, \ ++ const u##n); \ ++ break ++ CASES; ++#undef CASE ++ default: ++ return -EIO; ++ } ++ break; ++ } ++ } ++ for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { ++ if (REG_INVALID(i)) ++ continue; ++ switch(state.regs[i].where) { ++ case Nowhere: ++ if (reg_info[i].width != sizeof(UNW_SP(frame)) ++ || &FRAME_REG(i, __typeof__(UNW_SP(frame))) ++ != &UNW_SP(frame)) ++ continue; ++ UNW_SP(frame) = cfa; ++ break; ++ case Register: ++ switch(reg_info[i].width) { ++#define CASE(n) case sizeof(u##n): \ ++ FRAME_REG(i, u##n) = state.regs[i].value; \ ++ break ++ CASES; ++#undef CASE ++ default: ++ return -EIO; ++ } ++ break; ++ case Value: ++ if (reg_info[i].width != sizeof(unsigned long)){ ++ return -EIO;} ++ FRAME_REG(i, unsigned long) = cfa + state.regs[i].value ++ * state.dataAlign; ++ break; ++ case Memory: { ++ unsigned long addr = cfa + state.regs[i].value ++ * state.dataAlign; ++ if ((state.regs[i].value * state.dataAlign) ++ % sizeof(unsigned long) ++ || addr < startLoc ++ || addr + sizeof(unsigned long) < addr ++ || addr + sizeof(unsigned long) > endLoc){ ++ return -EIO;} ++ switch(reg_info[i].width) { ++#define CASE(n) case sizeof(u##n): \ ++ readmem(addr, KVADDR, ®_ptr,sizeof(u##n), "register", RETURN_ON_ERROR|QUIET); \ ++ FRAME_REG(i, u##n) = (u##n)reg_ptr;\ ++ break ++ CASES; ++#undef CASE ++ default: ++ return -EIO; ++ } ++ } ++ break; ++ } ++ } ++ return 0; ++#undef CASES ++#undef FRAME_REG ++} ++ ++/* ++ * Initialize the unwind table(s) in the best-case order: ++ * ++ * 1. Use the in-memory kernel and module unwind tables. ++ * 2. Use the in-memory kernel-only .eh_frame data. (possible?) ++ * 3. Use the kernel-only .eh_frame data from the vmlinux file. ++ */ ++void ++init_unwind_table(void) ++{ ++ ulong unwind_table_size; ++ void *unwind_table; ++ ++ kt->flags &= ~DWARF_UNWIND; ++ ++ if (gather_in_memory_unwind_tables()) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "init_unwind_table: DWARF_UNWIND_MEMORY (%d tables)\n", ++ unwind_tables_cnt); ++ ++ kt->flags |= DWARF_UNWIND_MEMORY; ++ if (unwind_tables_cnt > 1) ++ kt->flags |= DWARF_UNWIND_MODULES; ++ if (!(kt->flags & NO_DWARF_UNWIND)) ++ kt->flags |= DWARF_UNWIND; ++ ++ return; ++ } ++ ++ if (symbol_exists("__start_unwind") && ++ symbol_exists("__end_unwind")) { ++ unwind_table_size = symbol_value("__end_unwind") - ++ symbol_value("__start_unwind"); ++ ++ if (!(unwind_table = malloc(unwind_table_size))) { ++ error(WARNING, "cannot malloc unwind table space\n"); ++ goto try_eh_frame; ++ } ++ ++ if (!readmem(symbol_value("__start_unwind"), KVADDR, unwind_table, ++ unwind_table_size, "unwind table", RETURN_ON_ERROR)) { ++ error(WARNING, "cannot read unwind table data\n"); ++ free(unwind_table); ++ goto try_eh_frame; ++ } ++ ++ kt->flags |= DWARF_UNWIND_MEMORY; ++ if (!(kt->flags & NO_DWARF_UNWIND)) ++ kt->flags |= DWARF_UNWIND; ++ ++ default_unwind_table.size = unwind_table_size; ++ default_unwind_table.address = unwind_table; ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "init_unwind_table: DWARF_UNWIND_MEMORY\n"); ++ ++ return; ++ } ++ ++try_eh_frame: ++ ++ if (st->dwarf_eh_frame_size) { ++ int fd; ++ ++ unwind_table_size = st->dwarf_eh_frame_size; ++ ++ if (!(unwind_table = malloc(unwind_table_size))) { ++ error(WARNING, "cannot malloc unwind table space\n"); ++ return; ++ } ++ ++ if ((fd = open(pc->namelist, O_RDONLY)) < 0) { ++ error(WARNING, "cannot open %s for .eh_frame data\n", ++ pc->namelist); ++ free(unwind_table); ++ return; ++ } ++ ++ lseek(fd, st->dwarf_eh_frame_file_offset, SEEK_SET); ++ ++ if (read(fd, unwind_table, st->dwarf_eh_frame_size) != ++ st->dwarf_eh_frame_size) { ++ error(WARNING, "cannot read .eh_frame data from %s\n", ++ pc->namelist); ++ free(unwind_table); ++ return; ++ } ++ ++ close(fd); ++ ++ default_unwind_table.size = unwind_table_size; ++ default_unwind_table.address = unwind_table; ++ ++ kt->flags |= DWARF_UNWIND_EH_FRAME; ++ if (!(kt->flags & NO_DWARF_UNWIND)) ++ kt->flags |= DWARF_UNWIND; ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "init_unwind_table: DWARF_UNWIND_EH_FRAME\n"); ++ ++ return; ++ } ++} ++ ++/* ++ * Find the appropriate kernel-only "root_table" unwind_table, ++ * and pass it to populate_local_tables() to do the heavy lifting. ++ */ ++static int ++gather_in_memory_unwind_tables(void) ++{ ++ int i, cnt, found; ++ struct syment *sp, *root_tables[10]; ++ char *root_table_buf; ++ char buf[BUFSIZE]; ++ ulong name; ++ ++ STRUCT_SIZE_INIT(unwind_table, "unwind_table"); ++ MEMBER_OFFSET_INIT(unwind_table_core, "unwind_table", "core"); ++ MEMBER_OFFSET_INIT(unwind_table_init, "unwind_table", "init"); ++ MEMBER_OFFSET_INIT(unwind_table_address, "unwind_table", "address"); ++ MEMBER_OFFSET_INIT(unwind_table_size, "unwind_table", "size"); ++ MEMBER_OFFSET_INIT(unwind_table_link, "unwind_table", "link"); ++ MEMBER_OFFSET_INIT(unwind_table_name, "unwind_table", "name"); ++ ++ if (INVALID_SIZE(unwind_table) || ++ INVALID_MEMBER(unwind_table_core) || ++ INVALID_MEMBER(unwind_table_init) || ++ INVALID_MEMBER(unwind_table_address) || ++ INVALID_MEMBER(unwind_table_size) || ++ INVALID_MEMBER(unwind_table_link) || ++ INVALID_MEMBER(unwind_table_name)) { ++ if (CRASHDEBUG(1)) ++ error(NOTE, ++ "unwind_table structure has changed, or does not exist in this kernel\n"); ++ return 0; ++ } ++ ++ /* ++ * Unfortunately there are two kernel root_table symbols. ++ */ ++ if (!(cnt = get_syment_array("root_table", root_tables, 10))) ++ return 0; ++ ++ root_table_buf = GETBUF(SIZE(unwind_table)); ++ for (i = found = 0; i < cnt; i++) { ++ sp = root_tables[i]; ++ if (!readmem(sp->value, KVADDR, root_table_buf, ++ SIZE(unwind_table), "root unwind_table", ++ RETURN_ON_ERROR|QUIET)) ++ goto gather_failed; ++ ++ name = ULONG(root_table_buf + OFFSET(unwind_table_name)); ++ if (read_string(name, buf, strlen("kernel")+1) && ++ STREQ("kernel", buf)) { ++ found++; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "root_table name: %lx [%s]\n", ++ name, buf); ++ break; ++ } ++ } ++ ++ if (!found) ++ goto gather_failed; ++ ++ cnt = populate_local_tables(sp->value, root_table_buf); ++ ++ FREEBUF(root_table_buf); ++ return cnt; ++ ++gather_failed: ++ ++ FREEBUF(root_table_buf); ++ return 0; ++} ++ ++/* ++ * Transfer the relevant data from the kernel and module unwind_table ++ * structures to the local_unwind_table structures. ++ */ ++static int ++populate_local_tables(ulong root, char *buf) ++{ ++ struct list_data list_data, *ld; ++ int i, cnt; ++ ulong *table_list; ++ ulong vaddr; ++ struct local_unwind_table *tp; ++ ++ ld = &list_data; ++ BZERO(ld, sizeof(struct list_data)); ++ ld->start = root; ++ ld->member_offset = OFFSET(unwind_table_link); ++ if (CRASHDEBUG(1)) ++ ld->flags |= VERBOSE; ++ ++ hq_open(); ++ cnt = do_list(ld); ++ table_list = (ulong *)GETBUF(cnt * sizeof(ulong)); ++ cnt = retrieve_list(table_list, cnt); ++ hq_close(); ++ ++ if (!(local_unwind_tables = ++ malloc(sizeof(struct local_unwind_table) * cnt))) { ++ error(WARNING, "cannot malloc unwind_table space (%d tables)\n", ++ cnt); ++ FREEBUF(table_list); ++ return 0; ++ } ++ ++ for (i = 0; i < cnt; i++, tp++) { ++ ++ if (!readmem(table_list[i], KVADDR, buf, ++ SIZE(unwind_table), "unwind_table", ++ RETURN_ON_ERROR|QUIET)) { ++ error(WARNING, "cannot read unwind_table\n"); ++ goto failed; ++ } ++ ++ tp = &local_unwind_tables[i]; ++ ++ /* ++ * Copy the required table info for find_table(). ++ */ ++ BCOPY(buf + OFFSET(unwind_table_core), ++ (char *)&tp->core.pc, sizeof(ulong)*2); ++ BCOPY(buf + OFFSET(unwind_table_init), ++ (char *)&tp->init.pc, sizeof(ulong)*2); ++ BCOPY(buf + OFFSET(unwind_table_size), ++ (char *)&tp->size, sizeof(ulong)); ++ ++ /* ++ * Then read the DWARF CFI data. ++ */ ++ vaddr = ULONG(buf + OFFSET(unwind_table_address)); ++ ++ if (!(tp->address = malloc(tp->size))) { ++ error(WARNING, "cannot malloc unwind_table space\n"); ++ goto failed; ++ break; ++ } ++ if (!readmem(vaddr, KVADDR, tp->address, ++ tp->size, "DWARF CFI data", RETURN_ON_ERROR|QUIET)) { ++ error(WARNING, "cannot read unwind_table data\n"); ++ goto failed; ++ } ++ } ++ ++ unwind_tables_cnt = cnt; ++ ++ if (CRASHDEBUG(7)) ++ dump_local_unwind_tables(); ++ ++failed: ++ ++ FREEBUF(table_list); ++ return unwind_tables_cnt; ++} ++ ++/* ++ * Find the unwind_table containing a pc. ++ */ ++static struct local_unwind_table * ++find_table(unsigned long pc) ++{ ++ int i; ++ struct local_unwind_table *tp, *table; ++ ++ table = &default_unwind_table; ++ ++ for (i = 0; i < unwind_tables_cnt; i++, tp++) { ++ tp = &local_unwind_tables[i]; ++ if ((pc >= tp->core.pc ++ && pc < tp->core.pc + tp->core.range) ++ || (pc >= tp->init.pc ++ && pc < tp->init.pc + tp->init.range)) { ++ table = tp; ++ break; ++ } ++ } ++ ++ return table; ++} ++ ++static void ++dump_local_unwind_tables(void) ++{ ++ int i, others; ++ struct local_unwind_table *tp; ++ ++ others = 0; ++ fprintf(fp, "DWARF flags: ("); ++ 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++ ? "|" : ""); ++ fprintf(fp, ")\n\n"); ++ ++ fprintf(fp, "default_unwind_table:\n"); ++ fprintf(fp, " address: %lx\n", ++ (ulong)default_unwind_table.address); ++ fprintf(fp, " size: %ld\n\n", ++ (ulong)default_unwind_table.size); ++ ++ fprintf(fp, "local_unwind_tables[%d]:\n", unwind_tables_cnt); ++ for (i = 0; i < unwind_tables_cnt; i++, tp++) { ++ tp = &local_unwind_tables[i]; ++ fprintf(fp, "[%d]\n", i); ++ fprintf(fp, " core: pc: %lx\n", tp->core.pc); ++ fprintf(fp, " range: %ld\n", tp->core.range); ++ fprintf(fp, " init: pc: %lx\n", tp->init.pc); ++ fprintf(fp, " range: %ld\n", tp->init.range); ++ fprintf(fp, " address: %lx\n", (ulong)tp->address); ++ fprintf(fp, " size: %ld\n", tp->size); ++ } ++} ++ ++ ++int ++dwarf_backtrace(struct bt_info *bt, int level, ulong stacktop) ++{ ++ unsigned long bp, offset; ++ struct syment *sp; ++ char *name; ++ struct unwind_frame_info *frame; ++ ++ frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info)); ++// frame->regs.rsp = bt->stkptr; ++// frame->regs.rip = bt->instptr; ++ UNW_SP(frame) = bt->stkptr; ++ UNW_PC(frame) = bt->instptr; ++ ++ /* read rbp from stack for non active tasks */ ++ if (!(bt->flags & BT_DUMPFILE_SEARCH) && !bt->bptr) { ++// readmem(frame->regs.rsp, KVADDR, &bp, ++ readmem(UNW_SP(frame), KVADDR, &bp, ++ sizeof(unsigned long), "reading bp", FAULT_ON_ERROR); ++ frame->regs.rbp = bp; /* fixme for x86 */ ++ } ++ ++ sp = value_search(UNW_PC(frame), &offset); ++ if (!sp) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", ++ UNW_PC(frame)); ++ goto bailout; ++ } ++ ++ /* ++ * If offset is zero, it means we have crossed over to the next ++ * function. Recalculate by adjusting the text address ++ */ ++ if (!offset) { ++ sp = value_search(UNW_PC(frame) - 1, &offset); ++ if (!sp) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "unwind: cannot find symbol for PC: %lx\n", ++ UNW_PC(frame)-1); ++ goto bailout; ++ } ++ } ++ ++ ++ ++ name = sp->name; ++ fprintf(fp, " #%d [%016lx] %s at %016lx \n", level, UNW_SP(frame), name, UNW_PC(frame)); ++ ++ if (CRASHDEBUG(2)) ++ fprintf(fp, " < SP: %lx PC: %lx FP: %lx >\n", UNW_SP(frame), ++ UNW_PC(frame), frame->regs.rbp); ++ ++ while ((UNW_SP(frame) < stacktop) ++ && !unwind(frame) && UNW_PC(frame)) { ++ /* To prevent rip pushed on IRQ stack being reported both ++ * both on the IRQ and process stacks ++ */ ++ if ((bt->flags & BT_IRQSTACK) && (UNW_SP(frame) >= stacktop - 16)) ++ break; ++ level++; ++ sp = value_search(UNW_PC(frame), &offset); ++ if (!sp) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "unwind: cannot find symbol for PC: %lx\n", ++ UNW_PC(frame)); ++ break; ++ } ++ ++ /* ++ * If offset is zero, it means we have crossed over to the next ++ * function. Recalculate by adjusting the text address ++ */ ++ if (!offset) { ++ sp = value_search(UNW_PC(frame) - 1, &offset); ++ if (!sp) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "unwind: cannot find symbol for PC: %lx\n", ++ UNW_PC(frame)-1); ++ goto bailout; ++ } ++ } ++ name = sp->name; ++ fprintf(fp, "%s#%d [%016lx] %s at %016lx \n", level < 10 ? " " : "", ++ level, UNW_SP(frame), name, UNW_PC(frame)); ++ ++ if (CRASHDEBUG(2)) ++ fprintf(fp, " < SP: %lx PC: %lx FP: %lx >\n", UNW_SP(frame), ++ UNW_PC(frame), frame->regs.rbp); ++ } ++ ++bailout: ++ FREEBUF(frame); ++ return ++level; ++} ++ ++int ++dwarf_print_stack_entry(struct bt_info *bt, int level) ++{ ++ unsigned long offset; ++ struct syment *sp; ++ char *name; ++ struct unwind_frame_info *frame; ++ ++ frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info)); ++ UNW_SP(frame) = bt->stkptr; ++ UNW_PC(frame) = bt->instptr; ++ ++ sp = value_search(UNW_PC(frame), &offset); ++ if (!sp) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", ++ UNW_PC(frame)); ++ goto bailout; ++ } ++ ++ /* ++ * If offset is zero, it means we have crossed over to the next ++ * function. Recalculate by adjusting the text address ++ */ ++ if (!offset) { ++ sp = value_search(UNW_PC(frame) - 1, &offset); ++ if (!sp) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "unwind: cannot find symbol for PC: %lx\n", ++ UNW_PC(frame)-1); ++ goto bailout; ++ } ++ } ++ name = sp->name; ++ fprintf(fp, " #%d [%016lx] %s at %016lx \n", level, UNW_SP(frame), name, UNW_PC(frame)); ++ ++bailout: ++ FREEBUF(frame); ++ return level; ++} ++ ++void ++dwarf_debug(struct bt_info *bt) ++{ ++ struct unwind_frame_info *frame; ++ ulong bp; ++ ++ if (!bt->hp->eip) { ++ dump_local_unwind_tables(); ++ return; ++ } ++ ++ if (!(kt->flags & DWARF_UNWIND_CAPABLE)) { ++ error(INFO, "not DWARF capable\n"); ++ return; ++ } ++ ++ frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info)); ++ ++ /* ++ * XXX: This only works for the first PC/SP pair seen in a normal ++ * backtrace, so it's not particularly helpful. Ideally it should ++ * be capable to take any PC/SP pair in a stack, but it appears to ++ * related to the rbp value. ++ */ ++ ++ UNW_PC(frame) = bt->hp->eip; ++ UNW_SP(frame) = bt->hp->esp; ++ ++ readmem(UNW_SP(frame), KVADDR, &bp, ++ sizeof(unsigned long), "reading bp", FAULT_ON_ERROR); ++ frame->regs.rbp = bp; /* fixme for x86 */ ++ ++ unwind(frame); ++ ++ fprintf(fp, "frame size: %lx (%lx)\n", ++ (ulong)UNW_SP(frame), (ulong)UNW_SP(frame) - bt->hp->esp); ++ ++ FREEBUF(frame); ++} ++ ++ ++#endif +--- crash/lkcd_vmdump_v2_v3.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_vmdump_v2_v3.h 2008-02-20 12:12:46.000000000 -0500 +@@ -1,8 +1,8 @@ + /* lkcd_vmdump_v2_v3.h - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 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 +@@ -51,6 +51,7 @@ + #define _ASM_VMDUMP_H + + /* necessary header files */ ++typedef unsigned int u32; + #include /* for pt_regs */ + + /* definitions */ +@@ -81,7 +82,11 @@ + uint32_t dha_eip; + + /* the dump registers */ ++#ifndef S390 ++#ifndef S390X + struct pt_regs dha_regs; ++#endif ++#endif + + } dump_header_asm_t; + +@@ -97,6 +102,7 @@ + */ + + #ifndef IA64 ++typedef unsigned int u32; + #include /* for pt_regs */ + #endif + +--- crash/defs.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/defs.h 2009-02-05 10:43:34.000000000 -0500 +@@ -1,8 +1,8 @@ + /* defs.h - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002 Silicon Graphics, Inc. + * + * This program is free software; you can redistribute it and/or modify +@@ -46,6 +46,7 @@ + #include + #include + #include ++#include /* backtrace() */ + + #define BASELEVEL_REVISION "4.0" + +@@ -54,12 +55,16 @@ + + #define TRUE (1) + #define FALSE (0) ++#define STR(x) #x ++#ifndef offsetof ++# define offsetof(TYPE, MEMBER) ((ulong)&((TYPE *)0)->MEMBER) ++#endif + + #ifdef X86 +-#define NR_CPUS (32) ++#define NR_CPUS (256) + #endif + #ifdef X86_64 +-#define NR_CPUS (32) ++#define NR_CPUS (512) + #endif + #ifdef ALPHA + #define NR_CPUS (64) +@@ -68,10 +73,10 @@ + #define NR_CPUS (32) + #endif + #ifdef IA64 +-#define NR_CPUS (512) ++#define NR_CPUS (4096) + #endif + #ifdef PPC64 +-#define NR_CPUS (128) ++#define NR_CPUS (1024) + #endif + #ifdef S390 + #define NR_CPUS (64) +@@ -88,8 +93,10 @@ + + #define HIST_BLKSIZE (4096) + +-#define STREQ(A, B) (A && B && (strcmp((char *)(A), (char *)(B)) == 0)) +-#define STRNEQ(A, B) (A && B && \ ++static inline int string_exists(char *s) { return (s ? TRUE : FALSE); } ++#define STREQ(A, B) (string_exists((char *)A) && string_exists((char *)B) && \ ++ (strcmp((char *)(A), (char *)(B)) == 0)) ++#define STRNEQ(A, B) (string_exists((char *)A) && string_exists((char *)B) && \ + (strncmp((char *)(A), (char *)(B), strlen((char *)(B))) == 0)) + #define BZERO(S, N) (memset(S, NULLCHAR, N)) + #define BCOPY(S, D, C) (memcpy(D, S, C)) +@@ -98,6 +105,7 @@ + #define LASTCHAR(s) (s[strlen(s)-1]) + #define FIRSTCHAR(s) (s[0]) + #define QUOTED_STRING(s) ((FIRSTCHAR(s) == '"') && (LASTCHAR(s) == '"')) ++#define PATHEQ(A, B) ((A) && (B) && (pathcmp((char *)(A), (char *)(B)) == 0)) + + #ifdef roundup + #undef roundup +@@ -106,6 +114,8 @@ + + typedef uint64_t physaddr_t; + ++#define PADDR_NOT_AVAILABLE (0x1ULL) ++ + typedef unsigned long long int ulonglong; + struct number_option { + ulong num; +@@ -155,8 +165,8 @@ + #define UNLINK_MODULES (0x1000000000ULL) + #define S390D (0x2000000000ULL) + #define REM_S390D (0x4000000000ULL) +-#define PC_UNUSED_1 (0x8000000000ULL) +-#define PC_UNUSED_2 (0x10000000000ULL) ++#define SYSRQ (0x8000000000ULL) ++#define KDUMP (0x10000000000ULL) + #define NETDUMP (0x20000000000ULL) + #define REM_NETDUMP (0x40000000000ULL) + #define SYSMAP (0x80000000000ULL) +@@ -169,11 +179,20 @@ + #define VERSION_QUERY (0x4000000000000ULL) + #define READNOW (0x8000000000000ULL) + #define NOCRASHRC (0x10000000000000ULL) ++#define INIT_IFILE (0x20000000000000ULL) ++#define XENDUMP (0x40000000000000ULL) ++#define XEN_HYPER (0x80000000000000ULL) ++#define XEN_CORE (0x100000000000000ULL) ++#define PLEASE_WAIT (0x200000000000000ULL) ++#define IFILE_ERROR (0x400000000000000ULL) ++#define KERNTYPES (0x800000000000000ULL) ++#define MINIMAL_MODE (0x1000000000000000ULL) ++#define CRASHBUILTIN (0x2000000000000000ULL) + + #define ACTIVE() (pc->flags & LIVE_SYSTEM) + #define DUMPFILE() (!(pc->flags & LIVE_SYSTEM)) +-#define MEMORY_SOURCES (NETDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP) +-#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|MCLXCD|LKCD|S390D) ++#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP|XENDUMP|CRASHBUILTIN) ++#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D|XENDUMP) + #define REMOTE() (pc->flags & REMOTE_DAEMON) + #define REMOTE_ACTIVE() (pc->flags & REM_LIVE_SYSTEM) + #define REMOTE_DUMPFILE() \ +@@ -182,16 +201,35 @@ + #define LKCD_DUMPFILE() (pc->flags & (LKCD|REM_LKCD)) + #define NETDUMP_DUMPFILE() (pc->flags & (NETDUMP|REM_NETDUMP)) + #define DISKDUMP_DUMPFILE() (pc->flags & DISKDUMP) ++#define KDUMP_DUMPFILE() (pc->flags & KDUMP) ++#define XENDUMP_DUMPFILE() (pc->flags & XENDUMP) ++#define XEN_HYPER_MODE() (pc->flags & XEN_HYPER) ++#define SYSRQ_TASK(X) ((pc->flags & SYSRQ) && is_task_active(X)) ++#define XEN_CORE_DUMPFILE() (pc->flags & XEN_CORE) ++#define LKCD_KERNTYPES() (pc->flags & KERNTYPES) + + #define NETDUMP_LOCAL (0x1) /* netdump_data flags */ + #define NETDUMP_REMOTE (0x2) +-#define NETDUMP_VALID() (nd->flags & (NETDUMP_LOCAL|NETDUMP_REMOTE)) ++#define VMCORE_VALID() (nd->flags & (NETDUMP_LOCAL|NETDUMP_REMOTE|KDUMP_LOCAL)) + #define NETDUMP_ELF32 (0x4) + #define NETDUMP_ELF64 (0x8) + #define PARTIAL_DUMP (0x10) /* netdump or diskdump */ ++#define KDUMP_ELF32 (0x20) ++#define KDUMP_ELF64 (0x40) ++#define KDUMP_LOCAL (0x80) ++ ++#define DUMPFILE_FORMAT(flags) ((flags) & \ ++ (NETDUMP_ELF32|NETDUMP_ELF64|KDUMP_ELF32|KDUMP_ELF64)) ++ ++#define DISKDUMP_LOCAL (0x1) ++#define KDUMP_CMPRS_LOCAL (0x2) ++#define ERROR_EXCLUDED (0x4) ++#define ZERO_EXCLUDED (0x8) ++#define DISKDUMP_VALID() (dd->flags & DISKDUMP_LOCAL) ++#define KDUMP_CMPRS_VALID() (dd->flags & KDUMP_CMPRS_LOCAL) + +-#define DISKDUMP_LOCAL (0x1) +-#define DISKDUMP_VALID() (dd->flags & DISKDUMP_LOCAL) ++#define XENDUMP_LOCAL (0x1) ++#define XENDUMP_VALID() (xd->flags & XENDUMP_LOCAL) + + #define CRASHDEBUG(x) (pc->debug >= (x)) + +@@ -210,6 +248,7 @@ + #define SEEK_ERROR (-1) + #define READ_ERROR (-2) + #define WRITE_ERROR (-3) ++#define PAGE_EXCLUDED (-4) + + #define RESTART() (longjmp(pc->main_loop_env, 1)) + #define RESUME_FOREACH() (longjmp(pc->foreach_loop_env, 1)) +@@ -319,15 +358,28 @@ + #define SCROLL_NONE 0 + #define SCROLL_LESS 1 + #define SCROLL_MORE 2 ++#define SCROLL_CRASHPAGER 3 + ulong redirect; /* per-cmd origin and output flags */ + pid_t stdpipe_pid; /* per-cmd standard output pipe's pid */ + pid_t pipe_pid; /* per-cmd output pipe's pid */ + pid_t pipe_shell_pid; /* per-cmd output pipe's shell pid */ + char pipe_command[BUFSIZE]; /* pipe command line */ ++ struct command_table_entry *cmd_table; /* linux/xen command table */ + char *curcmd; /* currently-executing command */ + char *lastcmd; /* previously-executed command */ + ulong cmdgencur; /* current command generation number */ +- ulong cmdgenspec; /* specified command generation num */ ++ ulong curcmd_flags; /* general purpose per-command flag */ ++#define XEN_MACHINE_ADDR (0x1) ++#define REPEAT (0x2) ++#define IDLE_TASK_SHOWN (0x4) ++#define TASK_SPECIFIED (0x8) ++#define MEMTYPE_UVADDR (0x10) ++#define MEMTYPE_FILEADDR (0x20) ++#define HEADER_PRINTED (0x40) ++#define BAD_INSTRUCTION (0x80) ++#define UD2A_INSTRUCTION (0x100) ++#define IRQ_IN_USE (0x200) ++ ulonglong curcmd_private; /* general purpose per-command info */ + int cur_gdb_cmd; /* current gdb command */ + int last_gdb_cmd; /* previously-executed gdb command */ + int sigint_cnt; /* number of ignored SIGINTs */ +@@ -347,11 +399,11 @@ + struct extension_table *curext; /* extension being loaded */ + int (*readmem)(int, void *, int, ulong, physaddr_t); /* memory access */ + int (*writemem)(int, void *, int, ulong, physaddr_t);/* memory access */ ++ ulong ifile_in_progress; /* original xxx_IFILE flags */ ++ off_t ifile_offset; /* current offset into input file */ ++ char *runtime_ifile_cmd; /* runtime command using input file */ + }; + +-#define UNIQUE_COMMAND(s) \ +- (STREQ(pc->curcmd, s) && (pc->cmdgencur == pc->cmdgenspec)) +- + #define READMEM pc->readmem + + typedef void (*cmd_func_t)(void); +@@ -365,6 +417,7 @@ + + #define REFRESH_TASK_TABLE (0x1) /* command_table_entry flags */ + #define HIDDEN_COMMAND (0x2) ++#define CLEANUP (0x4) /* for extensions only */ + + /* + * A linked list of extension table structures keeps track of the current +@@ -407,9 +460,36 @@ + #define KALLSYMS_V2 (0x2000) + #define TVEC_BASES_V2 (0x4000) + #define GCC_3_3_3 (0x8000) ++#define USE_OLD_BT (0x10000) ++#define ARCH_XEN (0x20000) ++#define NO_IKCONFIG (0x40000) ++#define DWARF_UNWIND (0x80000) ++#define NO_DWARF_UNWIND (0x100000) ++#define DWARF_UNWIND_MEMORY (0x200000) ++#define DWARF_UNWIND_EH_FRAME (0x400000) ++#define DWARF_UNWIND_CAPABLE (DWARF_UNWIND_MEMORY|DWARF_UNWIND_EH_FRAME) ++#define DWARF_UNWIND_MODULES (0x800000) ++#define BUGVERBOSE_OFF (0x1000000) ++#define RELOC_SET (0x2000000) ++#define RELOC_FORCE (0x4000000) ++#define ARCH_OPENVZ (0x8000000) ++#define ARCH_PVOPS (0x10000000) + + #define GCC_VERSION_DEPRECATED (GCC_3_2|GCC_3_2_3|GCC_2_96|GCC_3_3_2|GCC_3_3_3) + ++#define XEN() (kt->flags & ARCH_XEN) ++#define OPENVZ() (kt->flags & ARCH_OPENVZ) ++#define PVOPS() (kt->flags & ARCH_PVOPS) ++ ++#define XEN_MACHINE_TO_MFN(m) ((ulonglong)(m) >> PAGESHIFT()) ++#define XEN_PFN_TO_PSEUDO(p) ((ulonglong)(p) << PAGESHIFT()) ++ ++#define XEN_MFN_NOT_FOUND (~0UL) ++#define XEN_PFNS_PER_PAGE (PAGESIZE()/sizeof(ulong)) ++#define XEN_FOREIGN_FRAME (1UL << (BITS()-1)) ++ ++#define XEN_MACHADDR_NOT_FOUND (~0ULL) ++ + struct kernel_table { /* kernel data */ + ulong flags; + ulong stext; +@@ -420,6 +500,7 @@ + ulong init_end; + ulong end; + int cpus; ++ char *cpus_override; + void (*display_bh)(void); + ulong module_list; + ulong kernel_module; +@@ -430,11 +511,39 @@ + uint kernel_version[3]; + uint gcc_version[3]; + int runq_siblings; +- long __rq_idx[NR_CPUS]; +- long __cpu_idx[NR_CPUS]; ++ int kernel_NR_CPUS; + long __per_cpu_offset[NR_CPUS]; +- long cpu_flags[NR_CPUS]; +-#define NMI 0x1 ++ long *__rq_idx; ++ long *__cpu_idx; ++ ulong *cpu_flags; ++#define POSSIBLE (0x1) ++#define PRESENT (0x2) ++#define ONLINE (0x4) ++#define NMI (0x8) ++ int BUG_bytes; ++ ulong xen_flags; ++#define WRITABLE_PAGE_TABLES (0x1) ++#define SHADOW_PAGE_TABLES (0x2) ++#define CANONICAL_PAGE_TABLES (0x4) ++#define XEN_SUSPEND (0x8) ++ char *m2p_page; ++ ulong phys_to_machine_mapping; ++ ulong p2m_table_size; ++#define P2M_MAPPING_CACHE (512) ++ struct p2m_mapping_cache { ++ ulong mapping; ++ ulong start; ++ ulong end; ++ } p2m_mapping_cache[P2M_MAPPING_CACHE]; ++#define P2M_MAPPING_TO_PAGE_INDEX(c) \ ++ (((kt->p2m_mapping_cache[c].mapping - kt->phys_to_machine_mapping)/PAGESIZE()) \ ++ * XEN_PFNS_PER_PAGE) ++ ulong last_mapping_read; ++ ulong p2m_cache_index; ++ ulong p2m_pages_searched; ++ ulong p2m_mfn_cache_hits; ++ ulong p2m_page_cache_hits; ++ ulong relocate; + }; + + /* +@@ -493,14 +602,14 @@ + ulong retries; + ulong panicmsg; + int panic_processor; +- ulong idle_threads[NR_CPUS]; +- ulong panic_threads[NR_CPUS]; +- ulong panic_ksp[NR_CPUS]; +- ulong active_set[NR_CPUS]; +- ulong hardirq_ctx[NR_CPUS]; +- ulong hardirq_tasks[NR_CPUS]; +- ulong softirq_ctx[NR_CPUS]; +- ulong softirq_tasks[NR_CPUS]; ++ ulong *idle_threads; ++ ulong *panic_threads; ++ ulong *active_set; ++ ulong *panic_ksp; ++ ulong *hardirq_ctx; ++ ulong *hardirq_tasks; ++ ulong *softirq_ctx; ++ ulong *softirq_tasks; + ulong panic_task; + ulong this_task; + int pidhash_len; +@@ -511,6 +620,7 @@ + char *task_struct; + char *thread_info; + char *mm_struct; ++ ulong init_pid_ns; + }; + + #define TASK_INIT_DONE (0x1) +@@ -527,6 +637,7 @@ + #define IRQSTACKS (0x800) + #define TIMESPEC (0x1000) + #define NO_TIMESPEC (0x2000) ++#define ACTIVE_ONLY (0x4000) + + #define TASK_SLUSH (20) + +@@ -578,6 +689,7 @@ + ulonglong flags; + ulong instptr; + ulong stkptr; ++ ulong bptr; + ulong stackbase; + ulong stacktop; + char *stackbuf; +@@ -602,6 +714,8 @@ + (void *)(&bt->stackbuf[(ulong)STACK_OFFSET_TYPE(OFF)]), (size_t)(SZ)) + + struct machine_specific; /* uniquely defined below each machine's area */ ++struct xendump_data; ++struct xen_kdump_data; + + struct machdep_table { + ulong flags; +@@ -645,14 +759,24 @@ + char **file; + } *line_number_hooks; + ulong last_pgd_read; ++ ulong last_pud_read; + ulong last_pmd_read; + ulong last_ptbl_read; + char *pgd; ++ char *pud; + char *pmd; + char *ptbl; + int ptrs_per_pgd; + char *cmdline_arg; + struct machine_specific *machspec; ++ ulong section_size_bits; ++ ulong max_physmem_bits; ++ ulong sections_per_root; ++ int (*xendump_p2m_create)(struct xendump_data *); ++ ulong (*xendump_panic_task)(struct xendump_data *); ++ void (*get_xendump_regs)(struct xendump_data *, struct bt_info *, ulong *, ulong *); ++ void (*clear_machdep_cache)(void); ++ int (*xen_kdump_p2m_create)(struct xen_kdump_data *); + }; + + /* +@@ -660,19 +784,25 @@ + * as defined in their processor-specific files below. (see KSYMS_START defs). + */ + #define HWRESET (0x80000000) +-#define SYSRQ (0x40000000) +-#define OMIT_FRAME_PTR (0x20000000) +-#define FRAMESIZE_DEBUG (0x10000000) +-#define MACHDEP_BT_TEXT (0x8000000) +-#define DEVMEMRD (0x4000000) +-#define INIT (0x2000000) +-#define SYSRQ_TASK(X) ((machdep->flags & SYSRQ) && is_task_active(X)) ++#define OMIT_FRAME_PTR (0x40000000) ++#define FRAMESIZE_DEBUG (0x20000000) ++#define MACHDEP_BT_TEXT (0x10000000) ++#define DEVMEMRD (0x8000000) ++#define INIT (0x4000000) ++#define VM_4_LEVEL (0x2000000) ++#define MCA (0x1000000) ++#define PAE (0x800000) + + extern struct machdep_table *machdep; + ++#ifndef HZ ++#define HZ sysconf(_SC_CLK_TCK) ++#endif ++ + #define IS_LAST_PGD_READ(pgd) ((ulong)(pgd) == machdep->last_pgd_read) + #define IS_LAST_PMD_READ(pmd) ((ulong)(pmd) == machdep->last_pmd_read) + #define IS_LAST_PTBL_READ(ptbl) ((ulong)(ptbl) == machdep->last_ptbl_read) ++#define IS_LAST_PUD_READ(pud) ((ulong)(pud) == machdep->last_pud_read) + + #define FILL_PGD(PGD, TYPE, SIZE) \ + if (!IS_LAST_PGD_READ(PGD)) { \ +@@ -681,6 +811,13 @@ + machdep->last_pgd_read = (ulong)(PGD); \ + } + ++#define FILL_PUD(PUD, TYPE, SIZE) \ ++ if (!IS_LAST_PUD_READ(PUD)) { \ ++ readmem((ulonglong)((ulong)(PUD)), TYPE, machdep->pud, \ ++ SIZE, "pud page", FAULT_ON_ERROR); \ ++ machdep->last_pud_read = (ulong)(PUD); \ ++ } ++ + #define FILL_PMD(PMD, TYPE, SIZE) \ + if (!IS_LAST_PMD_READ(PMD)) { \ + readmem((ulonglong)(PMD), TYPE, machdep->pmd, \ +@@ -695,10 +832,12 @@ + machdep->last_ptbl_read = (ulong)(PTBL); \ + } + ++#define SETUP_ENV (0) + #define PRE_SYMTAB (1) + #define PRE_GDB (2) + #define POST_GDB (3) + #define POST_INIT (4) ++#define POST_VM (5) + + #define FOREACH_BT (1) + #define FOREACH_VM (2) +@@ -737,6 +876,7 @@ + #define FOREACH_c_FLAG (0x40000) + #define FOREACH_f_FLAG (0x80000) + #define FOREACH_o_FLAG (0x100000) ++#define FOREACH_T_FLAG (0x200000) + + struct foreach_data { + ulong flags; +@@ -810,10 +950,15 @@ + long task_struct_last_run; + long task_struct_timestamp; + long task_struct_thread_info; ++ long task_struct_nsproxy; ++ long task_struct_rlim; + long thread_info_task; + long thread_info_cpu; + long thread_info_previous_esp; + long thread_info_flags; ++ long nsproxy_mnt_ns; ++ long mnt_namespace_root; ++ long mnt_namespace_list; + long pid_link_pid; + long pid_hash_chain; + long hlist_node_next; +@@ -830,6 +975,8 @@ + long tms_tms_stime; + long signal_struct_count; + long signal_struct_action; ++ long signal_struct_shared_pending; ++ long signal_struct_rlim; + long k_sigaction_sa; + long sigaction_sa_handler; + long sigaction_sa_flags; +@@ -875,8 +1022,14 @@ + long mm_struct_mmap; + long mm_struct_pgd; + long mm_struct_rss; ++ long mm_struct_anon_rss; ++ long mm_struct_file_rss; + long mm_struct_total_vm; + long mm_struct_start_code; ++ long mm_struct_arg_start; ++ long mm_struct_arg_end; ++ long mm_struct_env_start; ++ long mm_struct_env_end; + long vm_area_struct_vm_mm; + long vm_area_struct_vm_next; + long vm_area_struct_vm_end; +@@ -948,6 +1101,7 @@ + long block_device_bd_disk; + long irq_desc_t_status; + long irq_desc_t_handler; ++ long irq_desc_t_chip; + long irq_desc_t_action; + long irq_desc_t_depth; + long irqdesc_action; +@@ -968,8 +1122,28 @@ + long hw_interrupt_type_ack; + long hw_interrupt_type_end; + long hw_interrupt_type_set_affinity; ++ long irq_chip_typename; ++ long irq_chip_startup; ++ long irq_chip_shutdown; ++ long irq_chip_enable; ++ long irq_chip_disable; ++ long irq_chip_ack; ++ long irq_chip_end; ++ long irq_chip_set_affinity; ++ long irq_chip_mask; ++ long irq_chip_mask_ack; ++ long irq_chip_unmask; ++ long irq_chip_eoi; ++ long irq_chip_retrigger; ++ long irq_chip_set_type; ++ long irq_chip_set_wake; + long irq_cpustat_t___softirq_active; + long irq_cpustat_t___softirq_mask; ++ long fdtable_max_fds; ++ long fdtable_max_fdset; ++ long fdtable_open_fds; ++ long fdtable_fd; ++ long files_struct_fdt; + long files_struct_max_fds; + long files_struct_max_fdset; + long files_struct_open_fds; +@@ -978,6 +1152,9 @@ + long file_f_dentry; + long file_f_vfsmnt; + long file_f_count; ++ long file_f_path; ++ long path_mnt; ++ long path_dentry; + long fs_struct_root; + long fs_struct_pwd; + long fs_struct_rootmnt; +@@ -1067,6 +1244,8 @@ + long net_device_type; + long net_device_addr_len; + long net_device_ip_ptr; ++ long net_device_dev_list; ++ long net_dev_base_head; + long device_next; + long device_name; + long device_type; +@@ -1088,6 +1267,8 @@ + long inet_opt_dport; + long inet_opt_sport; + long inet_opt_num; ++ long ipv6_pinfo_rcv_saddr; ++ long ipv6_pinfo_daddr; + long timer_list_list; + long timer_list_next; + long timer_list_entry; +@@ -1123,6 +1304,7 @@ + long zone_struct_name; + long zone_struct_size; + long zone_struct_memsize; ++ long zone_struct_zone_start_pfn; + long zone_struct_zone_start_paddr; + long zone_struct_zone_start_mapnr; + long zone_struct_zone_mem_map; +@@ -1143,6 +1325,7 @@ + long zone_pages_min; + long zone_pages_low; + long zone_pages_high; ++ long zone_vm_stat; + long neighbour_next; + long neighbour_primary_key; + long neighbour_ha; +@@ -1210,7 +1393,72 @@ + long x8664_pda_irqstackptr; + long x8664_pda_level4_pgt; + long x8664_pda_cpunumber; ++ long x8664_pda_me; + long tss_struct_ist; ++ long mem_section_section_mem_map; ++ long vcpu_guest_context_user_regs; ++ long cpu_user_regs_eip; ++ long cpu_user_regs_esp; ++ long cpu_user_regs_rip; ++ long cpu_user_regs_rsp; ++ long unwind_table_core; ++ long unwind_table_init; ++ long unwind_table_address; ++ long unwind_table_size; ++ long unwind_table_link; ++ long unwind_table_name; ++ long rq_cfs; ++ long rq_rt; ++ long rq_nr_running; ++ long cfs_rq_rb_leftmost; ++ long cfs_rq_nr_running; ++ long cfs_rq_tasks_timeline; ++ long task_struct_se; ++ long sched_entity_run_node; ++ long rt_rq_active; ++ long kmem_cache_size; ++ long kmem_cache_objsize; ++ long kmem_cache_offset; ++ long kmem_cache_order; ++ long kmem_cache_local_node; ++ long kmem_cache_objects; ++ long kmem_cache_inuse; ++ long kmem_cache_align; ++ long kmem_cache_name; ++ long kmem_cache_list; ++ long kmem_cache_node; ++ long kmem_cache_cpu_slab; ++ long page_inuse; ++/* long page_offset; use "old" page->offset */ ++ long page_slab; ++ long page_first_page; ++ long page_freelist; ++ long kmem_cache_node_nr_partial; ++ long kmem_cache_node_nr_slabs; ++ long kmem_cache_node_partial; ++ long kmem_cache_node_full; ++ long pid_numbers; ++ long upid_nr; ++ long upid_ns; ++ long upid_pid_chain; ++ long pid_tasks; ++ long kmem_cache_cpu_freelist; ++ long kmem_cache_cpu_page; ++ long kmem_cache_cpu_node; ++ long kmem_cache_flags; ++ long zone_nr_active; ++ long zone_nr_inactive; ++ long zone_all_unreclaimable; ++ long zone_present_pages; ++ long zone_flags; ++ long zone_pages_scanned; ++ long pcpu_info_vcpu; ++ long pcpu_info_idle; ++ long vcpu_struct_rq; ++ long task_struct_sched_info; ++ long sched_info_last_arrival; ++ long page_objects; ++ long kmem_cache_oo; + }; + + struct size_table { /* stash of commonly-used sizes */ +@@ -1239,6 +1487,7 @@ + long umode_t; + long dentry; + long files_struct; ++ long fdtable; + long fs_struct; + long file; + long inode; +@@ -1264,6 +1513,7 @@ + long net_device; + long sock; + long signal_struct; ++ long sigpending_signal; + long signal_queue; + long sighand_struct; + long sigqueue; +@@ -1292,15 +1542,28 @@ + long address_space; + long char_device_struct; + long inet_sock; ++ long in6_addr; + long socket; + long spinlock_t; + long radix_tree_root; + long radix_tree_node; + long x8664_pda; ++ long ppc64_paca; + long gate_struct; + long tss_struct; + long task_struct_start_time; + long cputime_t; ++ long mem_section; ++ long pid_link; ++ long unwind_table; ++ long rlimit; ++ long kmem_cache; ++ long kmem_cache_node; ++ long upid; ++ long kmem_cache_cpu; ++ long cfs_rq; ++ long pcpu_info; ++ long vcpu_struct; + }; + + struct array_table { +@@ -1327,6 +1590,9 @@ + int free_area_DIMENSION; + int prio_array_queue; + int height_to_maxindex; ++ int pid_hash; ++ int kmem_cache_node; ++ int kmem_cache_cpu_slab; + }; + + /* +@@ -1342,7 +1608,12 @@ + #define MEMBER_OFFSET(X,Y) datatype_info((X), (Y), NULL) + #define MEMBER_EXISTS(X,Y) (datatype_info((X), (Y), NULL) >= 0) + #define MEMBER_SIZE_REQUEST ((struct datatype_member *)(-1)) ++#define MEMBER_TYPE_REQUEST ((struct datatype_member *)(-3)) + #define MEMBER_SIZE(X,Y) datatype_info((X), (Y), MEMBER_SIZE_REQUEST) ++#define MEMBER_TYPE(X,Y) datatype_info((X), (Y), MEMBER_TYPE_REQUEST) ++ ++#define ANON_MEMBER_OFFSET_REQUEST ((struct datatype_member *)(-2)) ++#define ANON_MEMBER_OFFSET(X,Y) datatype_info((X), (Y), ANON_MEMBER_OFFSET_REQUEST) + + /* + * The following set of macros can only be used with pre-intialized fields +@@ -1365,7 +1636,9 @@ + #define MEMBER_OFFSET_INIT(X, Y, Z) (ASSIGN_OFFSET(X) = MEMBER_OFFSET(Y, Z)) + #define STRUCT_SIZE_INIT(X, Y) (ASSIGN_SIZE(X) = STRUCT_SIZE(Y)) + #define ARRAY_LENGTH_INIT(A, B, C, D, E) ((A) = get_array_length(C, D, E)) ++#define ARRAY_LENGTH_INIT_ALT(A, B, C, D, E) ((A) = get_array_length_alt(B, C, D, E)) + #define MEMBER_SIZE_INIT(X, Y, Z) (ASSIGN_SIZE(X) = MEMBER_SIZE(Y, Z)) ++#define ANON_MEMBER_OFFSET_INIT(X, Y, Z) (ASSIGN_OFFSET(X) = ANON_MEMBER_OFFSET(Y, Z)) + + /* + * For use with non-debug kernels. +@@ -1389,6 +1662,7 @@ + #define ULONGLONG(ADDR) *((ulonglong *)((char *)(ADDR))) + #define ULONG_PTR(ADDR) *((ulong **)((char *)(ADDR))) + #define USHORT(ADDR) *((ushort *)((char *)(ADDR))) ++#define SHORT(ADDR) *((short *)((char *)(ADDR))) + #define VOID_PTR(ADDR) *((void **)((char *)(ADDR))) + + struct node_table { +@@ -1396,6 +1670,7 @@ + ulong pgdat; + ulong mem_map; + ulong size; ++ ulong present; + ulonglong start_paddr; + ulong start_mapnr; + }; +@@ -1420,8 +1695,10 @@ + ulong kmem_max_limit; + ulong kmem_max_cpus; + ulong kmem_cache_count; ++ ulong kmem_cache_len_nodes; + ulong PG_reserved; + ulong PG_slab; ++ ulong PG_head_tail_mask; + int kmem_cache_namelen; + ulong page_hash_table; + int page_hash_table_len; +@@ -1441,17 +1718,42 @@ + ulong cached_vma_hits[VMA_CACHE]; + int vma_cache_index; + ulong vma_cache_fills; +-}; +- +-#define NODES (0x1) +-#define ZONES (0x2) +-#define PERCPU_KMALLOC_V1 (0x4) +-#define COMMON_VADDR (0x8) +-#define KMEM_CACHE_INIT (0x10) +-#define V_MEM_MAP (0x20) +-#define PERCPU_KMALLOC_V2 (0x40) +-#define KMEM_CACHE_UNAVAIL (0x80) +-#define DISCONTIGMEM (0x100) ++ void *mem_sec; ++ char *mem_section; ++ int ZONE_HIGHMEM; ++ ulong *node_online_map; ++ int node_online_map_len; ++ int nr_vm_stat_items; ++ char **vm_stat_items; ++ int cpu_slab_type; ++ int nr_vm_event_items; ++ char **vm_event_items; ++}; ++ ++#define NODES (0x1) ++#define ZONES (0x2) ++#define PERCPU_KMALLOC_V1 (0x4) ++#define COMMON_VADDR (0x8) ++#define KMEM_CACHE_INIT (0x10) ++#define V_MEM_MAP (0x20) ++#define PERCPU_KMALLOC_V2 (0x40) ++#define KMEM_CACHE_UNAVAIL (0x80) ++#define FLATMEM (0x100) ++#define DISCONTIGMEM (0x200) ++#define SPARSEMEM (0x400) ++#define SPARSEMEM_EX (0x800) ++#define PERCPU_KMALLOC_V2_NODES (0x1000) ++#define KMEM_CACHE_DELAY (0x2000) ++#define NODES_ONLINE (0x4000) ++#define VM_STAT (0x8000) ++#define KMALLOC_SLUB (0x10000) ++#define CONFIG_NUMA (0x20000) ++#define VM_EVENT (0x40000) ++ ++#define IS_FLATMEM() (vt->flags & FLATMEM) ++#define IS_DISCONTIGMEM() (vt->flags & DISCONTIGMEM) ++#define IS_SPARSEMEM() (vt->flags & SPARSEMEM) ++#define IS_SPARSEMEM_EX() (vt->flags & SPARSEMEM_EX) + + #define COMMON_VADDR_SPACE() (vt->flags & COMMON_VADDR) + #define PADDR_PRLEN (vt->paddr_prlen) +@@ -1478,7 +1780,8 @@ + long list_head_offset; + ulong end; + ulong searchfor; +- char *structname; ++ char **structname; ++ int structname_args; + char *header; + }; + #define LIST_OFFSET_ENTERED (VERBOSE << 1) +@@ -1503,18 +1806,18 @@ + char argbuf[1]; + }; + +-static inline void +-save_return_address(ulong *retaddr) +-{ +- retaddr[0] = (ulong) __builtin_return_address(0); +-#if defined(X86) || defined(PPC) || defined(X86_64) || defined(PPC64) +- if (__builtin_frame_address(1)) +- retaddr[1] = (ulong) __builtin_return_address(1); +- if (__builtin_frame_address(2)) +- retaddr[2] = (ulong) __builtin_return_address(2); +- if (__builtin_frame_address(3)) +- retaddr[3] = (ulong) __builtin_return_address(3); +-#endif ++#define NUMBER_STACKFRAMES 4 ++ ++#define SAVE_RETURN_ADDRESS(retaddr) \ ++{ \ ++ int i; \ ++ int saved_stacks; \ ++ \ ++ saved_stacks = backtrace((void **)retaddr, NUMBER_STACKFRAMES); \ ++ \ ++ /* explicitely zero out the invalid addresses */ \ ++ for (i = saved_stacks; i < NUMBER_STACKFRAMES; i++) \ ++ retaddr[i] = 0; \ + } + + #endif /* !GDB_COMMON */ +@@ -1584,8 +1887,11 @@ + int mods_installed; + struct load_module *current; + struct load_module *load_modules; ++ off_t dwarf_eh_frame_file_offset; ++ ulong dwarf_eh_frame_size; + }; + ++/* flags for st */ + #define KERNEL_SYMS (0x1) + #define MODULE_SYMS (0x2) + #define LOAD_MODULE_SYMS (0x4) +@@ -1596,6 +1902,8 @@ + #define NO_SEC_CONTENTS (0x40) + #define FORCE_DEBUGINFO (0x80) + #define CRC_MATCHES (0x100) ++#define ADD_SYMBOL_FILE (0x200) ++#define USE_OLD_ADD_SYM (0x400) + + #endif /* !GDB_COMMON */ + +@@ -1611,6 +1919,8 @@ + #define MOD_KALLSYMS (0x8) + #define MOD_INITRD (0x10) + ++#define SEC_FOUND (0x10000) ++ + struct mod_section_data { + #if defined(GDB_6_1) + struct bfd_section *section; +@@ -1659,6 +1969,8 @@ + #define KVADDR (0x1) + #define UVADDR (0x2) + #define PHYSADDR (0x4) ++#define XENMACHADDR (0x8) ++#define FILEADDR (0x10) + #define AMBIGUOUS (~0) + + #define USE_USER_PGD (UVADDR << 2) +@@ -1680,6 +1992,33 @@ + #define VIRTPAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask) + #define PHYSPAGEBASE(X) (((physaddr_t)(X)) & (physaddr_t)machdep->pagemask) + ++/* ++ * Sparse memory stuff ++ * These must follow the definitions in the kernel mmzone.h ++ */ ++#define SECTION_SIZE_BITS() (machdep->section_size_bits) ++#define MAX_PHYSMEM_BITS() (machdep->max_physmem_bits) ++#define SECTIONS_SHIFT() (MAX_PHYSMEM_BITS() - SECTION_SIZE_BITS()) ++#define PA_SECTION_SHIFT() (SECTION_SIZE_BITS()) ++#define PFN_SECTION_SHIFT() (SECTION_SIZE_BITS() - PAGESHIFT()) ++#define NR_MEM_SECTIONS() (1UL << SECTIONS_SHIFT()) ++#define PAGES_PER_SECTION() (1UL << PFN_SECTION_SHIFT()) ++#define PAGE_SECTION_MASK() (~(PAGES_PER_SECTION()-1)) ++ ++#define pfn_to_section_nr(pfn) ((pfn) >> PFN_SECTION_SHIFT()) ++#define section_nr_to_pfn(sec) ((sec) << PFN_SECTION_SHIFT()) ++ ++#define SECTIONS_PER_ROOT() (machdep->sections_per_root) ++ ++/* CONFIG_SPARSEMEM_EXTREME */ ++#define _SECTIONS_PER_ROOT_EXTREME() (PAGESIZE() / SIZE(mem_section)) ++/* !CONFIG_SPARSEMEM_EXTREME */ ++#define _SECTIONS_PER_ROOT() (1) ++ ++#define SECTION_NR_TO_ROOT(sec) ((sec) / SECTIONS_PER_ROOT()) ++#define NR_SECTION_ROOTS() (NR_MEM_SECTIONS() / SECTIONS_PER_ROOT()) ++#define SECTION_ROOT_MASK() (SECTIONS_PER_ROOT() - 1) ++ + /* + * Machine specific stuff + */ +@@ -1689,8 +2028,8 @@ + #define MACHINE_TYPE "X86" + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) + #define VTOP(X) ((unsigned long)(X)-(machdep->kvbase)) +-#define IS_VMALLOC_ADDR(X) ((ulong)(X) >= vt->vmalloc_start) +-#define KVBASE_MASK (0x1fffff) ++#define IS_VMALLOC_ADDR(X) (vt->vmalloc_start && (ulong)(X) >= vt->vmalloc_start) ++#define KVBASE_MASK (0x1ffffff) + + #define PGDIR_SHIFT_2LEVEL (22) + #define PTRS_PER_PTE_2LEVEL (1024) +@@ -1721,25 +2060,94 @@ + + #define SWP_TYPE(entry) (((entry) >> 1) & 0x3f) + #define SWP_OFFSET(entry) ((entry) >> 8) ++#define __swp_type_PAE(entry) (((entry) >> 32) & 0x1f) ++#define __swp_type_nonPAE(entry) (((entry) >> 1) & 0x1f) ++#define __swp_offset_PAE(entry) (((entry) >> 32) >> 5) ++#define __swp_offset_nonPAE(entry) ((entry) >> 8) ++#define __swp_type(entry) (machdep->flags & PAE ? \ ++ __swp_type_PAE(entry) : __swp_type_nonPAE(entry)) ++#define __swp_offset(entry) (machdep->flags & PAE ? \ ++ __swp_offset_PAE(entry) : __swp_offset_nonPAE(entry)) + + #define TIF_SIGPENDING (2) + ++// CONFIG_X86_PAE ++#define _SECTION_SIZE_BITS_PAE_ORIG 30 ++#define _SECTION_SIZE_BITS_PAE_2_6_26 29 ++#define _MAX_PHYSMEM_BITS_PAE 36 ++ ++// !CONFIG_X86_PAE ++#define _SECTION_SIZE_BITS 26 ++#define _MAX_PHYSMEM_BITS 32 ++ ++#define IS_LAST_PMD_READ_PAE(pmd) ((ulong)(pmd) == machdep->machspec->last_pmd_read_PAE) ++#define IS_LAST_PTBL_READ_PAE(ptbl) ((ulong)(ptbl) == machdep->machspec->last_ptbl_read_PAE) ++ ++#define FILL_PMD_PAE(PMD, TYPE, SIZE) \ ++ if (!IS_LAST_PMD_READ_PAE(PMD)) { \ ++ readmem((ulonglong)(PMD), TYPE, machdep->pmd, \ ++ SIZE, "pmd page", FAULT_ON_ERROR); \ ++ machdep->machspec->last_pmd_read_PAE = (ulonglong)(PMD); \ ++ } ++ ++#define FILL_PTBL_PAE(PTBL, TYPE, SIZE) \ ++ if (!IS_LAST_PTBL_READ_PAE(PTBL)) { \ ++ readmem((ulonglong)(PTBL), TYPE, machdep->ptbl, \ ++ SIZE, "page table", FAULT_ON_ERROR); \ ++ machdep->machspec->last_ptbl_read_PAE = (ulonglong)(PTBL); \ ++ } ++ + #endif /* X86 */ + + #ifdef X86_64 + #define _64BIT_ + #define MACHINE_TYPE "X86_64" + +-#define USERSPACE_TOP 0x0000008000000000 +-#define __START_KERNEL_map 0xffffffff80000000 +-#define PAGE_OFFSET 0x0000010000000000 +- +-#define VMALLOC_START 0xffffff0000000000 +-#define VMALLOC_END 0xffffff7fffffffff +-#define MODULES_VADDR 0xffffffffa0000000 +-#define MODULES_END 0xffffffffafffffff ++#define USERSPACE_TOP (machdep->machspec->userspace_top) ++#define PAGE_OFFSET (machdep->machspec->page_offset) ++#define VMALLOC_START (machdep->machspec->vmalloc_start_addr) ++#define VMALLOC_END (machdep->machspec->vmalloc_end) ++#define VMEMMAP_VADDR (machdep->machspec->vmemmap_vaddr) ++#define VMEMMAP_END (machdep->machspec->vmemmap_end) ++#define MODULES_VADDR (machdep->machspec->modules_vaddr) ++#define MODULES_END (machdep->machspec->modules_end) ++ ++#define __START_KERNEL_map 0xffffffff80000000UL + #define MODULES_LEN (MODULES_END - MODULES_VADDR) + ++#define USERSPACE_TOP_ORIG 0x0000008000000000 ++#define PAGE_OFFSET_ORIG 0x0000010000000000 ++#define VMALLOC_START_ADDR_ORIG 0xffffff0000000000 ++#define VMALLOC_END_ORIG 0xffffff7fffffffff ++#define MODULES_VADDR_ORIG 0xffffffffa0000000 ++#define MODULES_END_ORIG 0xffffffffafffffff ++ ++#define USERSPACE_TOP_2_6_11 0x0000800000000000 ++#define PAGE_OFFSET_2_6_11 0xffff810000000000 ++#define VMALLOC_START_ADDR_2_6_11 0xffffc20000000000 ++#define VMALLOC_END_2_6_11 0xffffe1ffffffffff ++#define MODULES_VADDR_2_6_11 0xffffffff88000000 ++#define MODULES_END_2_6_11 0xfffffffffff00000 ++ ++#define VMEMMAP_VADDR_2_6_24 0xffffe20000000000 ++#define VMEMMAP_END_2_6_24 0xffffe2ffffffffff ++ ++#define PAGE_OFFSET_2_6_27 0xffff880000000000 ++ ++#define USERSPACE_TOP_XEN 0x0000800000000000 ++#define PAGE_OFFSET_XEN 0xffff880000000000 ++#define VMALLOC_START_ADDR_XEN 0xffffc20000000000 ++#define VMALLOC_END_XEN 0xffffe1ffffffffff ++#define MODULES_VADDR_XEN 0xffffffff88000000 ++#define MODULES_END_XEN 0xfffffffffff00000 ++ ++#define USERSPACE_TOP_XEN_RHEL4 0x0000008000000000 ++#define PAGE_OFFSET_XEN_RHEL4 0xffffff8000000000 ++#define VMALLOC_START_ADDR_XEN_RHEL4 0xffffff0000000000 ++#define VMALLOC_END_XEN_RHEL4 0xffffff7fffffffff ++#define MODULES_VADDR_XEN_RHEL4 0xffffffffa0000000 ++#define MODULES_END_XEN_RHEL4 0xffffffffafffffff ++ + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) + #define VTOP(X) x86_64_VTOP((ulong)(X)) + #define IS_VMALLOC_ADDR(X) x86_64_IS_VMALLOC_ADDR((ulong)(X)) +@@ -1757,12 +2165,37 @@ + #define pmd_index(address) (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1)) + #define pte_index(address) (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) + ++#define IS_LAST_PML4_READ(pml4) ((ulong)(pml4) == machdep->machspec->last_pml4_read) ++ + #define FILL_PML4() { \ + if (!(pc->flags & RUNTIME) || ACTIVE()) \ +- readmem(vt->kernel_pgd[0], KVADDR, machdep->machspec->pml4, \ ++ if (!IS_LAST_PML4_READ(vt->kernel_pgd[0])) \ ++ readmem(vt->kernel_pgd[0], KVADDR, machdep->machspec->pml4, \ + PAGESIZE(), "init_level4_pgt", FAULT_ON_ERROR); \ ++ machdep->machspec->last_pml4_read = (ulong)(vt->kernel_pgd[0]); \ + } + ++#define FILL_PML4_HYPER() { \ ++ if (!machdep->machspec->last_pml4_read) { \ ++ unsigned long idle_pg_table = \ ++ symbol_exists("idle_pg_table_4") ? symbol_value("idle_pg_table_4") : \ ++ symbol_value("idle_pg_table"); \ ++ readmem(idle_pg_table, KVADDR, \ ++ machdep->machspec->pml4, PAGESIZE(), "idle_pg_table", \ ++ FAULT_ON_ERROR); \ ++ machdep->machspec->last_pml4_read = idle_pg_table; \ ++ }\ ++} ++ ++#define IS_LAST_UPML_READ(pml) ((ulong)(pml) == machdep->machspec->last_upml_read) ++ ++#define FILL_UPML(PML, TYPE, SIZE) \ ++ if (!IS_LAST_UPML_READ(PML)) { \ ++ readmem((ulonglong)((ulong)(PML)), TYPE, machdep->machspec->upml, \ ++ SIZE, "pml page", FAULT_ON_ERROR); \ ++ machdep->machspec->last_upml_read = (ulong)(PML); \ ++ } ++ + /* + * PHYSICAL_PAGE_MASK changed (enlarged) between 2.4 and 2.6, so + * for safety, use the 2.6 values to generate it. +@@ -1791,11 +2224,33 @@ + + #define SWP_TYPE(entry) (((entry) >> 1) & 0x3f) + #define SWP_OFFSET(entry) ((entry) >> 8) ++#define __swp_type(entry) SWP_TYPE(entry) ++#define __swp_offset(entry) SWP_OFFSET(entry) + + #define TIF_SIGPENDING (2) + + #define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask) + ++#define _CPU_PDA_READ2(CPU, BUFFER) \ ++ ((readmem(symbol_value("_cpu_pda"), \ ++ KVADDR, &cpu_pda_addr, sizeof(unsigned long), \ ++ "_cpu_pda addr", FAULT_ON_ERROR)) && \ ++ (readmem(cpu_pda_addr + ((CPU) * sizeof(void *)), \ ++ KVADDR, &cpu_pda_addr, sizeof(unsigned long), \ ++ "_cpu_pda addr", FAULT_ON_ERROR)) && \ ++ (cpu_pda_addr) && \ ++ (readmem(cpu_pda_addr, KVADDR, (BUFFER), SIZE(x8664_pda), \ ++ "cpu_pda entry", FAULT_ON_ERROR))) ++ ++#define _CPU_PDA_READ(CPU, BUFFER) \ ++ ((STRNEQ("_cpu_pda", closest_symbol((symbol_value("_cpu_pda") + \ ++ ((CPU) * sizeof(unsigned long)))))) && \ ++ (readmem(symbol_value("_cpu_pda") + ((CPU) * sizeof(void *)), \ ++ KVADDR, &cpu_pda_addr, sizeof(unsigned long), \ ++ "_cpu_pda addr", FAULT_ON_ERROR)) && \ ++ (readmem(cpu_pda_addr, KVADDR, (BUFFER), SIZE(x8664_pda), \ ++ "cpu_pda entry", FAULT_ON_ERROR))) ++ + #define CPU_PDA_READ(CPU, BUFFER) \ + (STRNEQ("cpu_pda", closest_symbol((symbol_value("cpu_pda") + \ + ((CPU) * SIZE(x8664_pda))))) && \ +@@ -1806,6 +2261,9 @@ + #define VALID_LEVEL4_PGT_ADDR(X) \ + (((X) == VIRTPAGEBASE(X)) && IS_KVADDR(X) && !IS_VMALLOC_ADDR(X)) + ++#define _SECTION_SIZE_BITS 27 ++#define _MAX_PHYSMEM_BITS 40 ++ + #endif /* X86_64 */ + + #ifdef ALPHA +@@ -1816,7 +2274,7 @@ + + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) + #define VTOP(X) ((unsigned long)(X)-(machdep->kvbase)) +-#define IS_VMALLOC_ADDR(X) ((ulong)(X) >= vt->vmalloc_start) ++#define IS_VMALLOC_ADDR(X) (vt->vmalloc_start && (ulong)(X) >= vt->vmalloc_start) + #define KSEG_BASE_48_BIT (0xffff800000000000) + #define KSEG_BASE (0xfffffc0000000000) + #define _PFN_MASK (0xFFFFFFFF00000000) +@@ -1848,6 +2306,8 @@ + + #define SWP_TYPE(entry) (((entry) >> 32) & 0xff) + #define SWP_OFFSET(entry) ((entry) >> 40) ++#define __swp_type(entry) SWP_TYPE(entry) ++#define __swp_offset(entry) SWP_OFFSET(entry) + + #define TIF_SIGPENDING (2) + +@@ -1861,7 +2321,7 @@ + + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) + #define VTOP(X) ((unsigned long)(X)-(machdep->kvbase)) +-#define IS_VMALLOC_ADDR(X) ((ulong)(X) >= vt->vmalloc_start) ++#define IS_VMALLOC_ADDR(X) (vt->vmalloc_start && (ulong)(X) >= vt->vmalloc_start) + + #define PGDIR_SHIFT (22) + #define PTRS_PER_PTE (1024) +@@ -1881,9 +2341,14 @@ + + #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f) + #define SWP_OFFSET(entry) ((entry) >> 8) ++#define __swp_type(entry) SWP_TYPE(entry) ++#define __swp_offset(entry) SWP_OFFSET(entry) + + #define TIF_SIGPENDING (2) + ++#define _SECTION_SIZE_BITS 24 ++#define _MAX_PHYSMEM_BITS 44 ++ + #endif /* PPC */ + + #ifdef IA64 +@@ -1908,6 +2373,9 @@ + #define KERNEL_UNCACHED_BASE ((ulong)KERNEL_UNCACHED_REGION << REGION_SHIFT) + #define KERNEL_CACHED_BASE ((ulong)KERNEL_CACHED_REGION << REGION_SHIFT) + ++#define _SECTION_SIZE_BITS 30 ++#define _MAX_PHYSMEM_BITS 50 ++ + /* + * As of 2.6, these are no longer straight forward. + */ +@@ -1917,16 +2385,57 @@ + + #define SWITCH_STACK_ADDR(X) (ia64_get_switch_stack((ulong)(X))) + +-#define PGDIR_SHIFT (PAGESHIFT() + 2*(PAGESHIFT()-3)) +-#define PMD_SHIFT (PAGESHIFT() + (PAGESHIFT()-3)) +-#define PTRS_PER_PGD (((ulong)(1)) << (PAGESHIFT()-3)) +-#define PTRS_PER_PMD (((ulong)(1)) << (PAGESHIFT()-3)) +-#define PTRS_PER_PTE (((ulong)(1)) << (PAGESHIFT()-3)) +-#define PTRS_PER_PAGE (((ulong)(1)) << (PAGESHIFT()-3)) + #define __IA64_UL(x) ((unsigned long)(x)) + #define IA64_MAX_PHYS_BITS (50) /* max # of phys address bits (architected) */ + + /* ++ * How many pointers will a page table level hold expressed in shift ++ */ ++#define PTRS_PER_PTD_SHIFT (PAGESHIFT()-3) ++ ++/* ++ * Definitions for fourth level: ++ */ ++#define PTRS_PER_PTE (__IA64_UL(1) << (PTRS_PER_PTD_SHIFT)) ++ ++/* ++ * Definitions for third level: ++ * ++ * PMD_SHIFT determines the size of the area a third-level page table ++ * can map. ++ */ ++#define PMD_SHIFT (PAGESHIFT() + (PTRS_PER_PTD_SHIFT)) ++#define PMD_SIZE (1UL << PMD_SHIFT) ++#define PMD_MASK (~(PMD_SIZE-1)) ++#define PTRS_PER_PMD (1UL << (PTRS_PER_PTD_SHIFT)) ++ ++/* ++ * PUD_SHIFT determines the size of the area a second-level page table ++ * can map ++ */ ++#define PUD_SHIFT (PMD_SHIFT + (PTRS_PER_PTD_SHIFT)) ++#define PUD_SIZE (1UL << PUD_SHIFT) ++#define PUD_MASK (~(PUD_SIZE-1)) ++#define PTRS_PER_PUD (1UL << (PTRS_PER_PTD_SHIFT)) ++ ++/* ++ * Definitions for first level: ++ * ++ * PGDIR_SHIFT determines what a first-level page table entry can map. ++ */ ++ ++#define PGDIR_SHIFT_4L (PUD_SHIFT + (PTRS_PER_PTD_SHIFT)) ++#define PGDIR_SHIFT_3L (PMD_SHIFT + (PTRS_PER_PTD_SHIFT)) ++/* Turns out 4L & 3L PGDIR_SHIFT are the same (for now) */ ++#define PGDIR_SHIFT PGDIR_SHIFT_4L ++#define PGDIR_SIZE (__IA64_UL(1) << PGDIR_SHIFT) ++#define PGDIR_MASK (~(PGDIR_SIZE-1)) ++#define PTRS_PER_PGD_SHIFT PTRS_PER_PTD_SHIFT ++#define PTRS_PER_PGD (1UL << PTRS_PER_PGD_SHIFT) ++#define USER_PTRS_PER_PGD (5*PTRS_PER_PGD/8) /* regions 0-4 are user regions */ ++#define FIRST_USER_ADDRESS 0 ++ ++/* + * First, define the various bits in a PTE. Note that the PTE format + * matches the VHPT short format, the firt doubleword of the VHPD long + * format, and the first doubleword of the TLB insertion format. +@@ -1978,6 +2487,7 @@ + #define __DIRTY_BITS _PAGE_ED | __DIRTY_BITS_NO_ED + + #define EFI_PAGE_SHIFT (12) ++ + /* + * NOTE: #include'ing creates too many compiler problems, so + * this stuff is hardwired here; it's probably etched in stone somewhere. +@@ -2020,6 +2530,8 @@ + + #define SWP_TYPE(entry) (((entry) >> 1) & 0xff) + #define SWP_OFFSET(entry) ((entry) >> 9) ++#define __swp_type(entry) ((entry >> 2) & 0x7f) ++#define __swp_offset(entry) ((entry << 1) >> 10) + + #define TIF_SIGPENDING (1) + +@@ -2038,11 +2550,14 @@ + #define _64BIT_ + #define MACHINE_TYPE "PPC64" + ++#define PPC64_64K_PAGE_SIZE 65536 ++#define PPC64_STACK_SIZE 16384 ++ + #define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask) + + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) + #define VTOP(X) ((unsigned long)(X)-(machdep->kvbase)) +-#define IS_VMALLOC_ADDR(X) ((ulong)(X) >= vt->vmalloc_start) ++#define IS_VMALLOC_ADDR(X) (vt->vmalloc_start && (ulong)(X) >= vt->vmalloc_start) + #define KERNELBASE machdep->pageoffset + + #define PGDIR_SHIFT (machdep->pageshift + (machdep->pageshift -3) + (machdep->pageshift - 2)) +@@ -2067,6 +2582,33 @@ + #define PGD_OFFSET(vaddr) ((vaddr >> PGDIR_SHIFT) & 0x7ff) + #define PMD_OFFSET(vaddr) ((vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) + ++/* 4-level page table support */ ++ ++/* 4K pagesize */ ++#define PTE_INDEX_SIZE_L4_4K 9 ++#define PMD_INDEX_SIZE_L4_4K 7 ++#define PUD_INDEX_SIZE_L4_4K 7 ++#define PGD_INDEX_SIZE_L4_4K 9 ++#define PTE_SHIFT_L4_4K 17 ++#define PMD_MASKED_BITS_4K 0 ++ ++/* 64K pagesize */ ++#define PTE_INDEX_SIZE_L4_64K 12 ++#define PMD_INDEX_SIZE_L4_64K 12 ++#define PUD_INDEX_SIZE_L4_64K 0 ++#define PGD_INDEX_SIZE_L4_64K 4 ++#define PTE_SHIFT_L4_64K_V1 32 ++#define PTE_SHIFT_L4_64K_V2 30 ++#define PMD_MASKED_BITS_64K 0x1ff ++ ++#define L4_OFFSET(vaddr) ((vaddr >> (machdep->machspec->l4_shift)) & 0x1ff) ++ ++#define PGD_OFFSET_L4(vaddr) \ ++ ((vaddr >> (machdep->machspec->l3_shift)) & (machdep->machspec->ptrs_per_l3 - 1)) ++ ++#define PMD_OFFSET_L4(vaddr) \ ++ ((vaddr >> (machdep->machspec->l2_shift)) & (machdep->machspec->ptrs_per_l2 - 1)) ++ + #define _PAGE_PRESENT 0x001UL /* software: pte contains a translation */ + #define _PAGE_USER 0x002UL /* matches one of the PP bits */ + #define _PAGE_RW 0x004UL /* software: user write access allowed */ +@@ -2080,6 +2622,8 @@ + + #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f) + #define SWP_OFFSET(entry) ((entry) >> 8) ++#define __swp_type(entry) SWP_TYPE(entry) ++#define __swp_offset(entry) SWP_OFFSET(entry) + + #define MSR_PR_LG 14 /* Problem State / Privilege Level */ + /* Used to find the user or kernel-mode frame*/ +@@ -2087,6 +2631,9 @@ + #define STACK_FRAME_OVERHEAD 112 + #define EXCP_FRAME_MARKER 0x7265677368657265 + ++#define _SECTION_SIZE_BITS 24 ++#define _MAX_PHYSMEM_BITS 44 ++ + #endif /* PPC64 */ + + #ifdef S390 +@@ -2095,7 +2642,7 @@ + + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) + #define VTOP(X) ((unsigned long)(X)-(machdep->kvbase)) +-#define IS_VMALLOC_ADDR(X) s390_IS_VMALLOC_ADDR(X) ++#define IS_VMALLOC_ADDR(X) (vt->vmalloc_start && (ulong)(X) >= vt->vmalloc_start) + + #define PTRS_PER_PTE 1024 + #define PTRS_PER_PMD 1 +@@ -2105,9 +2652,14 @@ + #define SWP_TYPE(entry) (((entry) >> 2) & 0x1f) + #define SWP_OFFSET(entry) ((((entry) >> 11) & 0xfffffffe) | \ + (((entry) >> 7) & 0x1)) ++#define __swp_type(entry) SWP_TYPE(entry) ++#define __swp_offset(entry) SWP_OFFSET(entry) + + #define TIF_SIGPENDING (2) + ++#define _SECTION_SIZE_BITS 25 ++#define _MAX_PHYSMEM_BITS 31 ++ + #endif /* S390 */ + + #ifdef S390X +@@ -2116,7 +2668,7 @@ + + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) + #define VTOP(X) ((unsigned long)(X)-(machdep->kvbase)) +-#define IS_VMALLOC_ADDR(X) ((ulong)(X) >= vt->vmalloc_start) ++#define IS_VMALLOC_ADDR(X) (vt->vmalloc_start && (ulong)(X) >= vt->vmalloc_start) + #define PTRS_PER_PTE 512 + #define PTRS_PER_PMD 1024 + #define PTRS_PER_PGD 2048 +@@ -2125,15 +2677,22 @@ + #define SWP_TYPE(entry) (((entry) >> 2) & 0x1f) + #define SWP_OFFSET(entry) ((((entry) >> 11) & 0xfffffffffffffffe) | \ + (((entry) >> 7) & 0x1)) ++#define __swp_type(entry) SWP_TYPE(entry) ++#define __swp_offset(entry) SWP_OFFSET(entry) + + #define TIF_SIGPENDING (2) + ++#define _SECTION_SIZE_BITS 28 ++#define _MAX_PHYSMEM_BITS 42 ++ + #endif /* S390X */ + + #ifdef PLATFORM + + #define SWP_TYPE(entry) (error("PLATFORM_SWP_TYPE: TBD\n")) + #define SWP_OFFSET(entry) (error("PLATFORM_SWP_OFFSET: TBD\n")) ++#define __swp_type(entry) SWP_TYPE(entry) ++#define __swp_offset(entry) SWP_OFFSET(entry) + + #endif /* PLATFORM */ + +@@ -2185,7 +2744,10 @@ + #define BADVAL ((ulong)(-1)) + #define UNUSED (-1) + ++#define UNINITIALIZED (BADVAL) ++ + #define BITS_PER_BYTE (8) ++#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long)) + + /* + * precision lengths for fprintf +@@ -2199,9 +2761,10 @@ + + #define MINSPACE (-100) + +-#define SYNOPSIS (0x1) +-#define COMPLETE_HELP (0x2) +-#define PIPE_TO_LESS (0x4) ++#define SYNOPSIS (0x1) ++#define COMPLETE_HELP (0x2) ++#define PIPE_TO_SCROLL (0x4) ++#define MUST_HELP (0x8) + + #define LEFT_JUSTIFY (1) + #define RIGHT_JUSTIFY (2) +@@ -2214,6 +2777,7 @@ + #define INT_DEC (0x20) + #define INT_HEX (0x40) + #define LONGLONG_HEX (0x80) ++#define ZERO_FILL (0x100) + + #define INIT_TIME (1) + #define RUN_TIME (2) +@@ -2419,17 +2983,22 @@ + /* + * ps command options. + */ +-#define PS_BY_PID (0x1) +-#define PS_BY_TASK (0x2) +-#define PS_BY_CMD (0x4) +-#define PS_SHOW_ALL (0x8) +-#define PS_PPID_LIST (0x10) +-#define PS_CHILD_LIST (0x20) +-#define PS_KERNEL (0x40) +-#define PS_USER (0x80) +-#define PS_TIMES (0x100) +-#define PS_KSTACKP (0x200) +-#define PS_LAST_RUN (0x400) ++#define PS_BY_PID (0x1) ++#define PS_BY_TASK (0x2) ++#define PS_BY_CMD (0x4) ++#define PS_SHOW_ALL (0x8) ++#define PS_PPID_LIST (0x10) ++#define PS_CHILD_LIST (0x20) ++#define PS_KERNEL (0x40) ++#define PS_USER (0x80) ++#define PS_TIMES (0x100) ++#define PS_KSTACKP (0x200) ++#define PS_LAST_RUN (0x400) ++#define PS_ARGV_ENVP (0x800) ++#define PS_TGID_LIST (0x1000) ++#define PS_RLIMIT (0x2000) ++ ++#define PS_EXCLUSIVE (PS_TGID_LIST|PS_ARGV_ENVP|PS_TIMES|PS_CHILD_LIST|PS_PPID_LIST|PS_LAST_RUN|PS_RLIMIT) + + #define MAX_PS_ARGS (100) /* maximum command-line specific requests */ + +@@ -2461,7 +3030,7 @@ + extern struct program_context program_context, *pc; + extern struct task_table task_table, *tt; + extern struct kernel_table kernel_table, *kt; +-extern struct command_table_entry base_command_table[]; ++extern struct command_table_entry linux_command_table[]; + extern char *args[MAXARGS]; + extern int argcnt; + extern int argerrs; +@@ -2534,6 +3103,9 @@ + void cmd_gdb(void); /* gdb_interface.c */ + void cmd_net(void); /* net.c */ + void cmd_extend(void); /* extensions.c */ ++#if defined(S390) || defined(S390X) ++void cmd_s390dbf(void); ++#endif + + /* + * main.c +@@ -2571,6 +3143,7 @@ + #define machdep_init(X) ppc64_init(X) + #endif + int clean_exit(int); ++int untrusted_file(FILE *, char *); + + /* + * cmdline.c +@@ -2591,6 +3164,9 @@ + int interruptible(void); + int received_SIGINT(void); + void debug_redirect(char *); ++int CRASHPAGER_valid(void); ++char *setup_scroll_command(void); ++int minimal_functions(char *); + + /* + * tools.c +@@ -2658,6 +3234,7 @@ + int hq_open(void); + int hq_close(void); + int hq_enter(ulong); ++int hq_entry_exists(ulong); + long get_embedded(void); + void dump_embedded(char *); + char *ordinal(ulong, char *); +@@ -2683,9 +3260,16 @@ + int clean_arg(void); + int empty_list(ulong); + int machine_type(char *); ++int machine_type_mismatch(char *, char *, char *, ulong); + void command_not_supported(void); + void option_not_supported(int); +- ++void please_wait(char *); ++void please_wait_done(void); ++int pathcmp(char *, char *); ++int calculate(char *, ulong *, ulonglong *, ulong); ++int endian_mismatch(char *, char, ulong); ++uint16_t swap16(uint16_t, int); ++uint32_t swap32(uint32_t, int); + + /* + * symbols.c +@@ -2721,9 +3305,11 @@ + struct syment *next_symbol(char *, struct syment *); + struct syment *prev_symbol(char *, struct syment *); + void get_symbol_data(char *, long, void *); ++int try_get_symbol_data(char *, long, void *); + char *value_to_symstr(ulong, char *, ulong); + char *value_symbol(ulong); + ulong symbol_value(char *); ++ulong symbol_value_module(char *, char *); + int symbol_exists(char *s); + int kernel_symbol_exists(char *s); + int get_syment_array(char *, struct syment **, int); +@@ -2738,9 +3324,12 @@ + void dump_struct_table(ulong); + void dump_offset_table(char *, ulong); + int is_elf_file(char *); ++int is_kernel(char *); ++int file_elf_version(char *); + int is_system_map(char *); + int select_namelist(char *); + int get_array_length(char *, int *, long); ++int get_array_length_alt(char *, char *, int *, long); + int builtin_array_length(char *, int, int *); + char *get_line_number(ulong, char *, int); + char *get_build_directory(char *); +@@ -2768,6 +3357,7 @@ + long OFFSET_option(long, long, char *, char *, int, char *, char *); + long SIZE_option(long, long, char *, char *, int, char *, char *); + void dump_trace(ulong *); ++int enumerator_value(char *, long *); + + /* + * memory.c +@@ -2807,6 +3397,7 @@ + char *swap_location(ulonglong, char *); + void clear_swap_info_cache(void); + uint memory_page_size(void); ++void force_page_size(char *); + ulong first_vmalloc_address(void); + int l1_cache_size(void); + int dumpfile_memory(int); +@@ -2838,6 +3429,7 @@ + void open_files_dump(ulong, int, struct reference *); + void get_pathname(ulong, char *, int, int, ulong); + ulong file_to_dentry(ulong); ++ulong file_to_vfsmnt(ulong); + void nlm_files_dump(void); + int get_proc_version(void); + int file_checksum(char *, long *); +@@ -2874,6 +3466,7 @@ + void help_init(void); + void cmd_usage(char *, int); + void display_version(void); ++void display_help_screen(char *); + #ifdef X86 + #define dump_machdep_table(X) x86_dump_machdep_table(X) + #endif +@@ -2945,6 +3538,9 @@ + extern char *help_waitq[]; + extern char *help_whatis[]; + extern char *help_wr[]; ++#if defined(S390) || defined(S390X) ++extern char *help_s390dbf[]; ++#endif + + /* + * task.c +@@ -2962,10 +3558,13 @@ + ulong task_flags(ulong); + ulong task_state(ulong); + ulong task_mm(ulong, int); ++ulong task_tgid(ulong); + ulonglong task_last_run(ulong); ++ulong vaddr_in_task_struct(ulong); + int comm_exists(char *); + struct task_context *task_to_context(ulong); + struct task_context *pid_to_context(ulong); ++struct task_context *tgid_to_context(ulong); + ulong stkptr_to_task(ulong); + ulong task_to_thread_info(ulong); + ulong task_to_stackbase(ulong); +@@ -3005,11 +3604,17 @@ + */ + void register_extension(struct command_table_entry *); + void dump_extension_table(int); ++void load_extension(char *); ++void unload_extension(char *); ++/* Hooks for sial */ ++unsigned long get_curtask(void); ++char *crash_global_cmd(void); ++struct command_table_entry *crash_cmd_table(void); + + /* + * kernel.c + */ +-void kernel_init(int); ++void kernel_init(void); + void module_init(void); + void verify_version(void); + void verify_spinlock(void); +@@ -3019,14 +3624,21 @@ + int is_system_call(char *, ulong); + void generic_dump_irq(int); + int generic_dis_filter(ulong, char *); ++int kernel_BUG_encoding_bytes(void); + void display_sys_stats(void); +-void dump_kernel_table(void); +-void dump_bt_info(struct bt_info *); ++char *get_uptime(char *, ulonglong *); ++void clone_bt_info(struct bt_info *, struct bt_info *, struct task_context *); ++void dump_kernel_table(int); ++void dump_bt_info(struct bt_info *, char *where); + void dump_log(int); + void set_cpu(int); + void clear_machdep_cache(void); + struct stack_hook *gather_text_list(struct bt_info *); + int get_cpus_online(void); ++int get_cpus_present(void); ++int get_cpus_possible(void); ++int in_cpu_map(int, int); ++void paravirt_init(void); + void print_stack_text_syms(struct bt_info *, ulong, ulong); + void back_trace(struct bt_info *); + #define BT_RAW (0x1ULL) +@@ -3039,11 +3651,13 @@ + #define BT_EXCEPTION_FRAME (0x80ULL) + #define BT_LINE_NUMBERS (0x100ULL) + #define BT_USER_EFRAME (0x200ULL) ++#define BT_INCOMPLETE_USER_EFRAME (BT_USER_EFRAME) + #define BT_SAVE_LASTSP (0x400ULL) + #define BT_FROM_EXCEPTION (0x800ULL) + #define BT_FROM_CALLFRAME (0x1000ULL) + #define BT_EFRAME_SEARCH (0x2000ULL) + #define BT_SPECULATE (0x4000ULL) ++#define BT_FRAMESIZE_DISABLE (BT_SPECULATE) + #define BT_RESCHEDULE (0x8000ULL) + #define BT_SCHEDULE (BT_RESCHEDULE) + #define BT_RET_FROM_SMP_FORK (0x10000ULL) +@@ -3069,6 +3683,8 @@ + #define BT_DUMPFILE_SEARCH (0x800000000ULL) + #define BT_EFRAME_SEARCH2 (0x1000000000ULL) + #define BT_START (0x2000000000ULL) ++#define BT_TEXT_SYMBOLS_ALL (0x4000000000ULL) ++#define BT_XEN_STOP_THIS_CPU (0x8000000000ULL) + + #define BT_REF_HEXVAL (0x1) + #define BT_REF_SYMBOL (0x2) +@@ -3101,6 +3717,17 @@ + #define TYPE_S390D (REMOTE_VERBOSE << 6) + #define TYPE_NETDUMP (REMOTE_VERBOSE << 7) + ++ulonglong xen_m2p(ulonglong); ++ ++void read_in_kernel_config(int); ++ ++#define IKCFG_INIT (0) ++#define IKCFG_READ (1) ++ ++#define MAGIC_START "IKCFG_ST" ++#define MAGIC_END "IKCFG_ED" ++#define MAGIC_SIZE (sizeof(MAGIC_START) - 1) ++ + /* + * dev.c + */ +@@ -3129,7 +3756,6 @@ + void x86_display_idt_table(void); + #define display_idt_table() x86_display_idt_table() + #define KSYMS_START (0x1) +-#define PAE (0x2) + void x86_dump_eframe_common(struct bt_info *bt, ulong *, int); + char *x86_function_called_by(ulong); + struct syment *x86_jmp_error_code(ulong); +@@ -3140,6 +3766,8 @@ + ulong entry_tramp_start; + ulong entry_tramp_end; + physaddr_t entry_tramp_start_phys; ++ ulonglong last_pmd_read_PAE; ++ ulonglong last_ptbl_read_PAE; + }; + + struct syment *x86_is_entry_tramp_address(ulong, ulong *); +@@ -3194,19 +3822,54 @@ + #define NMI_STACK 2 /* ebase[] offset to NMI exception stack */ + + struct machine_specific { ++ ulong userspace_top; ++ ulong page_offset; ++ ulong vmalloc_start_addr; ++ ulong vmalloc_end; ++ ulong vmemmap_vaddr; ++ ulong vmemmap_end; ++ ulong modules_vaddr; ++ ulong modules_end; ++ ulong phys_base; + char *pml4; ++ char *upml; ++ ulong last_upml_read; ++ ulong last_pml4_read; + char *irqstack; ++ ulong irq_eframe_link; + struct x86_64_pt_regs_offsets pto; + struct x86_64_stkinfo stkinfo; + }; + + #define KSYMS_START (0x1) + #define PT_REGS_INIT (0x2) ++#define VM_ORIG (0x4) ++#define VM_2_6_11 (0x8) ++#define VM_XEN (0x10) ++#define NO_TSS (0x20) ++#define SCHED_TEXT (0x40) ++#define PHYS_BASE (0x80) ++#define VM_XEN_RHEL4 (0x100) ++#define VMEMMAP (0x200) ++ ++#define VM_FLAGS (VM_ORIG|VM_2_6_11|VM_XEN|VM_XEN_RHEL4) + + #define _2MB_PAGE_MASK (~((MEGABYTES(2))-1)) ++ ++#endif ++ ++#if defined(X86) || defined(X86_64) ++ ++/* ++ * unwind_x86_32_64.c ++ */ ++void init_unwind_table(void); ++int dwarf_backtrace(struct bt_info *, int, ulong); ++void dwarf_debug(struct bt_info *); ++int dwarf_print_stack_entry(struct bt_info *, int); ++ + #endif + +-void x86_64_backtrace_notice(ulong); + + /* + * ppc64.c +@@ -3240,13 +3903,42 @@ + ulong hwintrstack[NR_CPUS]; + char *hwstackbuf; + uint hwstacksize; +-}; ++ char *level4; ++ ulong last_level4_read; ++ ++ uint l4_index_size; ++ uint l3_index_size; ++ uint l2_index_size; ++ uint l1_index_size; ++ ++ uint ptrs_per_l3; ++ uint ptrs_per_l2; ++ uint ptrs_per_l1; ++ ++ uint l4_shift; ++ uint l3_shift; ++ uint l2_shift; ++ uint l1_shift; ++ ++ uint pte_shift; ++ uint l2_masked_bits; ++}; ++ ++#define IS_LAST_L4_READ(l4) ((ulong)(l4) == machdep->machspec->last_level4_read) ++ ++#define FILL_L4(L4, TYPE, SIZE) \ ++ if (!IS_LAST_L4_READ(L4)) { \ ++ readmem((ulonglong)((ulong)(L4)), TYPE, machdep->machspec->level4, \ ++ SIZE, "level4 page", FAULT_ON_ERROR); \ ++ machdep->machspec->last_level4_read = (ulong)(L4); \ ++ } + + void ppc64_init(int); + void ppc64_dump_machdep_table(ulong); + #define display_idt_table() \ + error(FATAL, "-d option is not applicable to PowerPC architecture\n") + #define KSYMS_START (0x1) ++#define VM_ORIG (0x2) + #endif + + /* +@@ -3258,15 +3950,27 @@ + #define display_idt_table() \ + error(FATAL, "-d option is not applicable to PowerPC architecture\n") + #define KSYMS_START (0x1) ++/* This should match PPC_FEATURE_BOOKE from include/asm-powerpc/cputable.h */ ++#define CPU_BOOKE (0x00008000) + #endif + + /* + * lkcd_fix_mem.c + */ + ++struct _dump_header_asm_s; ++struct _dump_header_s; + ulong get_lkcd_switch_stack(ulong); +-int fix_addr_v8(int); ++int fix_addr_v8(struct _dump_header_asm_s *); ++int lkcd_dump_init_v8_arch(struct _dump_header_s *dh); + int fix_addr_v7(int); ++int get_lkcd_regs_for_cpu_arch(int cpu, ulong *eip, ulong *esp); ++int lkcd_get_kernel_start_v8(ulong *addr); ++ ++/* ++ * lkcd_v8.c ++ */ ++int get_lkcd_regs_for_cpu_v8(struct bt_info *bt, ulong *eip, ulong *esp); + + /* + * ia64.c +@@ -3283,6 +3987,8 @@ + #define display_idt_table() \ + error(FATAL, "-d option TBD on ia64 architecture\n"); + int ia64_in_init_stack(ulong addr); ++int ia64_in_mca_stack_hyper(ulong addr, struct bt_info *bt); ++physaddr_t ia64_xen_kdump_p2m(struct xen_kdump_data *xkd, physaddr_t pseudo); + + #define OLD_UNWIND (0x1) /* CONFIG_IA64_NEW_UNWIND not turned on */ + #define NEW_UNWIND (0x2) /* CONFIG_IA64_NEW_UNWIND turned on */ +@@ -3396,10 +4102,30 @@ + int netdump_init(char *, FILE *); + ulong get_netdump_panic_task(void); + ulong get_netdump_switch_stack(ulong); +-int netdump_memory_dump(FILE *); + FILE *set_netdump_fp(FILE *); ++int netdump_memory_dump(FILE *); + void get_netdump_regs(struct bt_info *, ulong *, ulong *); + int is_partial_netdump(void); ++void get_netdump_regs_x86(struct bt_info *, ulong *, ulong *); ++void get_netdump_regs_x86_64(struct bt_info *, ulong *, ulong *); ++struct vmcore_data; ++struct vmcore_data *get_kdump_vmcore_data(void); ++int read_kdump(int, void *, int, ulong, physaddr_t); ++int write_kdump(int, void *, int, ulong, physaddr_t); ++int is_kdump(char *, ulong); ++int kdump_init(char *, FILE *); ++ulong get_kdump_panic_task(void); ++uint kdump_page_size(void); ++int kdump_free_memory(void); ++int kdump_memory_used(void); ++int kdump_memory_dump(FILE *); ++void get_kdump_regs(struct bt_info *, ulong *, ulong *); ++void xen_kdump_p2m_mfn(char *); ++int is_sadump_xen(void); ++void set_xen_phys_start(char *); ++ulong xen_phys_start(void); ++int xen_major_version(void); ++int xen_minor_version(void); + + /* + * diskdump.c +@@ -3416,6 +4142,28 @@ + int diskdump_memory_dump(FILE *); + FILE *set_diskdump_fp(FILE *); + void get_diskdump_regs(struct bt_info *, ulong *, ulong *); ++int diskdump_phys_base(unsigned long *); ++ulong *diskdump_flags; ++int is_partial_diskdump(void); ++ ++/* ++ * xendump.c ++ */ ++int is_xendump(char *); ++int read_xendump(int, void *, int, ulong, physaddr_t); ++int write_xendump(int, void *, int, ulong, physaddr_t); ++uint xendump_page_size(void); ++int xendump_free_memory(void); ++int xendump_memory_used(void); ++int xendump_init(char *, FILE *); ++int xendump_memory_dump(FILE *); ++ulong get_xendump_panic_task(void); ++void get_xendump_regs(struct bt_info *, ulong *, ulong *); ++char *xc_core_mfn_to_page(ulong, char *); ++int xc_core_mfn_to_page_index(ulong); ++void xendump_panic_hook(char *); ++int read_xendump_hyper(int, void *, int, ulong, physaddr_t); ++struct xendump_data *get_xendump_data(void); + + /* + * net.c +@@ -3493,6 +4241,8 @@ + void lkcd_dumpfile_complaint(uint32_t, uint32_t, int); + int set_mb_benchmark(ulong); + ulonglong fix_lkcd_address(ulonglong); ++int lkcd_get_kernel_start(ulong *addr); ++int get_lkcd_regs_for_cpu(struct bt_info *bt, ulong *eip, ulong *esp); + + /* + * lkcd_v1.c +@@ -3560,6 +4310,7 @@ + #define LKCD_DUMP_V7 (0x7) /* DUMP_VERSION_NUMBER */ + #define LKCD_DUMP_V8 (0x8) /* DUMP_VERSION_NUMBER */ + #define LKCD_DUMP_V9 (0x9) /* DUMP_VERSION_NUMBER */ ++#define LKCD_DUMP_V10 (0xa) /* DUMP_VERSION_NUMBER */ + + #define LKCD_DUMP_VERSION_NUMBER_MASK (0xf) + #define LKCD_DUMP_RAW (0x1) /* DUMP_[DH_]RAW */ +@@ -3764,8 +4515,8 @@ + extern int prettyprint_structs; + extern int prettyprint_arrays; + extern int repeat_count_threshold; +-extern int repeat_count_threshold; + extern unsigned int print_max; ++extern int stop_print_at_null; + + /* + * gdb/utils.c +@@ -3814,4 +4565,8 @@ + extern int have_partial_symbols(void); + extern int have_full_symbols(void); + ++#if defined(X86) || defined(X86_64) || defined(IA64) ++#define XEN_HYPERVISOR_ARCH ++#endif ++ + #endif /* !GDB_COMMON */ +--- crash/netdump.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/netdump.c 2009-01-15 16:17:17.000000000 -0500 +@@ -1,7 +1,7 @@ + /* netdump.c + * +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. +@@ -13,38 +13,14 @@ + * Author: David Anderson + */ + ++#define _LARGEFILE64_SOURCE 1 /* stat64() */ ++ + #include "defs.h" + #include "netdump.h" + +-struct pt_load_segment { +- off_t file_offset; +- physaddr_t phys_start; +- physaddr_t phys_end; +-}; +- +-struct netdump_data { +- ulong flags; +- int ndfd; +- FILE *ofp; +- uint header_size; +- char *netdump_header; +- uint num_pt_load_segments; +- struct pt_load_segment *pt_load_segments; +- Elf32_Ehdr *elf32; +- Elf32_Phdr *notes32; +- Elf32_Phdr *load32; +- Elf64_Ehdr *elf64; +- Elf64_Phdr *notes64; +- Elf64_Phdr *load64; +- void *nt_prstatus; +- void *nt_prpsinfo; +- void *nt_taskstruct; +- ulong task_struct; +- ulong switch_stack; +-}; +- +-static struct netdump_data netdump_data = { 0 }; +-static struct netdump_data *nd = &netdump_data; ++static struct vmcore_data vmcore_data = { 0 }; ++static struct vmcore_data *nd = &vmcore_data; ++static struct xen_kdump_data xen_kdump_data = { 0 }; + static void netdump_print(char *, ...); + static void dump_Elf32_Ehdr(Elf32_Ehdr *); + static void dump_Elf32_Phdr(Elf32_Phdr *, int); +@@ -52,31 +28,41 @@ + static void dump_Elf64_Ehdr(Elf64_Ehdr *); + static void dump_Elf64_Phdr(Elf64_Phdr *, int); + static size_t dump_Elf64_Nhdr(Elf64_Off offset, int); +-static void get_netdump_regs_x86(struct bt_info *, ulong *, ulong *); +-static void get_netdump_regs_x86_64(struct bt_info *, ulong *, ulong *); + static void get_netdump_regs_ppc64(struct bt_info *, ulong *, ulong *); ++static physaddr_t xen_kdump_p2m(physaddr_t); ++static void check_dumpfile_size(char *); + + #define ELFSTORE 1 + #define ELFREAD 0 +- ++ ++#define MIN_PAGE_SIZE (4096) ++ ++/* ++ * PPC64 and IA64 architectures have configurable page sizes, ++ * which can differ from the host machine's page size. ++ */ ++#define READ_PAGESIZE_FROM_VMCOREINFO() \ ++ (machine_type("IA64") || machine_type("PPC64")) ++ + /* +- * Determine whether a file is a netdump creation, and if TRUE, +- * initialize the netdump_data structure. ++ * Determine whether a file is a netdump/diskdump/kdump creation, ++ * and if TRUE, initialize the vmcore_data structure. + */ + int +-is_netdump(char *file, ulong source) ++is_netdump(char *file, ulong source_query) + { +- int i; +- int fd; ++ int i, fd, swap; + Elf32_Ehdr *elf32; + Elf32_Phdr *load32; + Elf64_Ehdr *elf64; + Elf64_Phdr *load64; +- char header[MIN_NETDUMP_ELF_HEADER_SIZE]; ++ char eheader[MIN_NETDUMP_ELF_HEADER_SIZE]; + char buf[BUFSIZE]; + size_t size, len, tot; + Elf32_Off offset32; + Elf64_Off offset64; ++ ulong tmp_flags; ++ char *tmp_elf_header; + + if ((fd = open(file, O_RDWR)) < 0) { + if ((fd = open(file, O_RDONLY)) < 0) { +@@ -87,7 +73,7 @@ + } + + size = MIN_NETDUMP_ELF_HEADER_SIZE; +- if (read(fd, header, size) != size) { ++ if (read(fd, eheader, size) != size) { + sprintf(buf, "%s: read", file); + perror(buf); + goto bailout; +@@ -99,89 +85,163 @@ + goto bailout; + } + +- elf32 = (Elf32_Ehdr *)&header[0]; +- elf64 = (Elf64_Ehdr *)&header[0]; ++ tmp_flags = 0; ++ elf32 = (Elf32_Ehdr *)&eheader[0]; ++ elf64 = (Elf64_Ehdr *)&eheader[0]; + + /* +- * Verify the ELF header ++ * Verify the ELF header, and determine the dumpfile format. ++ * ++ * For now, kdump vmcores differ from netdump/diskdump like so: ++ * ++ * 1. The first kdump PT_LOAD segment is packed just after ++ * the ELF header, whereas netdump/diskdump page-align ++ * the first PT_LOAD segment. ++ * 2. Each kdump PT_LOAD segment has a p_align field of zero, ++ * whereas netdump/diskdump have their p_align fields set ++ * to the system page-size. ++ * ++ * If either kdump difference is seen, presume kdump -- this ++ * is obviously subject to change. + */ +- if (STRNEQ(elf32->e_ident, ELFMAG) && +- (elf32->e_ident[EI_CLASS] == ELFCLASS32) && +- (elf32->e_ident[EI_DATA] == ELFDATA2LSB) && +- (elf32->e_ident[EI_VERSION] == EV_CURRENT) && +- (elf32->e_type == ET_CORE) && +- (elf32->e_version == EV_CURRENT) && +- (elf32->e_phnum >= 2)) { +- switch (elf32->e_machine) ++ ++ if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT) ++ goto bailout; ++ ++ swap = (((eheader[EI_DATA] == ELFDATA2LSB) && ++ (__BYTE_ORDER == __BIG_ENDIAN)) || ++ ((eheader[EI_DATA] == ELFDATA2MSB) && ++ (__BYTE_ORDER == __LITTLE_ENDIAN))); ++ ++ if ((elf32->e_ident[EI_CLASS] == ELFCLASS32) && ++ (swap16(elf32->e_type, swap) == ET_CORE) && ++ (swap32(elf32->e_version, swap) == EV_CURRENT) && ++ (swap16(elf32->e_phnum, swap) >= 2)) { ++ switch (swap16(elf32->e_machine, swap)) + { + case EM_386: +- if (machine_type("X86")) +- break; ++ if (machine_type_mismatch(file, "X86", NULL, ++ source_query)) ++ goto bailout; ++ break; ++ + default: +- goto bailout; ++ if (machine_type_mismatch(file, "(unknown)", NULL, ++ source_query)) ++ goto bailout; + } +- nd->flags |= NETDUMP_ELF32; ++ ++ if (endian_mismatch(file, elf32->e_ident[EI_DATA], ++ source_query)) ++ goto bailout; ++ + load32 = (Elf32_Phdr *) +- &header[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)]; ++ &eheader[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)]; + size = (size_t)load32->p_offset; +- } else if (STRNEQ(elf64->e_ident, ELFMAG) && +- (elf64->e_ident[EI_CLASS] == ELFCLASS64) && +- (elf64->e_ident[EI_VERSION] == EV_CURRENT) && +- (elf64->e_type == ET_CORE) && +- (elf64->e_version == EV_CURRENT) && +- (elf64->e_phnum >= 2)) { +- switch (elf64->e_machine) ++ ++ if ((load32->p_offset & (MIN_PAGE_SIZE-1)) && ++ (load32->p_align == 0)) ++ tmp_flags |= KDUMP_ELF32; ++ else ++ tmp_flags |= NETDUMP_ELF32; ++ } else if ((elf64->e_ident[EI_CLASS] == ELFCLASS64) && ++ (swap16(elf64->e_type, swap) == ET_CORE) && ++ (swap32(elf64->e_version, swap) == EV_CURRENT) && ++ (swap16(elf64->e_phnum, swap) >= 2)) { ++ switch (swap16(elf64->e_machine, swap)) + { + case EM_IA_64: +- if ((elf64->e_ident[EI_DATA] == ELFDATA2LSB) && +- machine_type("IA64")) +- break; +- else ++ if (machine_type_mismatch(file, "IA64", NULL, ++ source_query)) + goto bailout; ++ break; + + case EM_PPC64: +- if ((elf64->e_ident[EI_DATA] == ELFDATA2MSB) && +- machine_type("PPC64")) +- break; +- else ++ if (machine_type_mismatch(file, "PPC64", NULL, ++ source_query)) + goto bailout; ++ break; + + case EM_X86_64: +- if ((elf64->e_ident[EI_DATA] == ELFDATA2LSB) && +- machine_type("X86_64")) +- break; +- else ++ if (machine_type_mismatch(file, "X86_64", NULL, ++ source_query)) + goto bailout; ++ break; ++ ++ case EM_386: ++ if (machine_type_mismatch(file, "X86", NULL, ++ source_query)) ++ goto bailout; ++ break; + + default: +- goto bailout; ++ if (machine_type_mismatch(file, "(unknown)", NULL, ++ source_query)) ++ goto bailout; + } +- nd->flags |= NETDUMP_ELF64; ++ ++ if (endian_mismatch(file, elf64->e_ident[EI_DATA], ++ source_query)) ++ goto bailout; ++ + load64 = (Elf64_Phdr *) +- &header[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)]; ++ &eheader[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)]; + size = (size_t)load64->p_offset; +- } else ++ if ((load64->p_offset & (MIN_PAGE_SIZE-1)) && ++ (load64->p_align == 0)) ++ tmp_flags |= KDUMP_ELF64; ++ else ++ tmp_flags |= NETDUMP_ELF64; ++ } else { ++ if (CRASHDEBUG(2)) ++ error(INFO, "%s: not a %s ELF dumpfile\n", ++ file, source_query == NETDUMP_LOCAL ? ++ "netdump" : "kdump"); ++ ++ + goto bailout; ++ } ++ ++ switch (DUMPFILE_FORMAT(tmp_flags)) ++ { ++ case NETDUMP_ELF32: ++ case NETDUMP_ELF64: ++ if (source_query & (NETDUMP_LOCAL|NETDUMP_REMOTE)) ++ break; ++ else ++ goto bailout; ++ ++ case KDUMP_ELF32: ++ case KDUMP_ELF64: ++ if (source_query & KDUMP_LOCAL) ++ break; ++ else ++ goto bailout; ++ } + +- if ((nd->netdump_header = (char *)malloc(size)) == NULL) { +- fprintf(stderr, "cannot malloc netdump header buffer\n"); ++ if ((tmp_elf_header = (char *)malloc(size)) == NULL) { ++ fprintf(stderr, "cannot malloc ELF header buffer\n"); + clean_exit(1); + } + +- if (read(fd, nd->netdump_header, size) != size) { ++ if (read(fd, tmp_elf_header, size) != size) { + sprintf(buf, "%s: read", file); + perror(buf); ++ free(tmp_elf_header); + goto bailout; + } + + nd->ndfd = fd; +- nd->flags |= source; ++ nd->elf_header = tmp_elf_header; ++ nd->flags = tmp_flags; ++ nd->flags |= source_query; + +- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64)) ++ switch (DUMPFILE_FORMAT(nd->flags)) + { + case NETDUMP_ELF32: ++ case KDUMP_ELF32: + nd->header_size = load32->p_offset; +- nd->elf32 = (Elf32_Ehdr *)&nd->netdump_header[0]; ++ nd->elf32 = (Elf32_Ehdr *)&nd->elf_header[0]; + nd->num_pt_load_segments = nd->elf32->e_phnum - 1; + if ((nd->pt_load_segments = (struct pt_load_segment *) + malloc(sizeof(struct pt_load_segment) * +@@ -190,9 +250,11 @@ + clean_exit(1); + } + nd->notes32 = (Elf32_Phdr *) +- &nd->netdump_header[sizeof(Elf32_Ehdr)]; ++ &nd->elf_header[sizeof(Elf32_Ehdr)]; + nd->load32 = (Elf32_Phdr *) +- &nd->netdump_header[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)]; ++ &nd->elf_header[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)]; ++ if (DUMPFILE_FORMAT(nd->flags) == NETDUMP_ELF32) ++ nd->page_size = (uint)nd->load32->p_align; + dump_Elf32_Ehdr(nd->elf32); + dump_Elf32_Phdr(nd->notes32, ELFREAD); + for (i = 0; i < nd->num_pt_load_segments; i++) +@@ -205,8 +267,9 @@ + break; + + case NETDUMP_ELF64: ++ case KDUMP_ELF64: + nd->header_size = load64->p_offset; +- nd->elf64 = (Elf64_Ehdr *)&nd->netdump_header[0]; ++ nd->elf64 = (Elf64_Ehdr *)&nd->elf_header[0]; + nd->num_pt_load_segments = nd->elf64->e_phnum - 1; + if ((nd->pt_load_segments = (struct pt_load_segment *) + malloc(sizeof(struct pt_load_segment) * +@@ -215,9 +278,11 @@ + clean_exit(1); + } + nd->notes64 = (Elf64_Phdr *) +- &nd->netdump_header[sizeof(Elf64_Ehdr)]; ++ &nd->elf_header[sizeof(Elf64_Ehdr)]; + nd->load64 = (Elf64_Phdr *) +- &nd->netdump_header[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)]; ++ &nd->elf_header[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)]; ++ if (DUMPFILE_FORMAT(nd->flags) == NETDUMP_ELF64) ++ nd->page_size = (uint)nd->load64->p_align; + dump_Elf64_Ehdr(nd->elf64); + dump_Elf64_Phdr(nd->notes64, ELFREAD); + for (i = 0; i < nd->num_pt_load_segments; i++) +@@ -230,6 +295,9 @@ + break; + } + ++ if (CRASHDEBUG(1)) ++ netdump_memory_dump(fp); ++ + return nd->header_size; + + bailout: +@@ -238,15 +306,97 @@ + } + + /* ++ * Return the e_version number of an ELF file ++ * (or -1 if its not readable ELF file) ++ */ ++int ++file_elf_version(char *file) ++{ ++ int fd, size; ++ Elf32_Ehdr *elf32; ++ Elf64_Ehdr *elf64; ++ char header[MIN_NETDUMP_ELF_HEADER_SIZE]; ++ char buf[BUFSIZE]; ++ ++ if ((fd = open(file, O_RDONLY)) < 0) { ++ sprintf(buf, "%s: open", file); ++ perror(buf); ++ return -1; ++ } ++ ++ size = MIN_NETDUMP_ELF_HEADER_SIZE; ++ if (read(fd, header, size) != size) { ++ sprintf(buf, "%s: read", file); ++ perror(buf); ++ close(fd); ++ return -1; ++ } ++ close(fd); ++ ++ elf32 = (Elf32_Ehdr *)&header[0]; ++ elf64 = (Elf64_Ehdr *)&header[0]; ++ ++ if (STRNEQ(elf32->e_ident, ELFMAG) && ++ (elf32->e_ident[EI_CLASS] == ELFCLASS32) && ++ (elf32->e_ident[EI_DATA] == ELFDATA2LSB) && ++ (elf32->e_ident[EI_VERSION] == EV_CURRENT)) { ++ return (elf32->e_version); ++ } else if (STRNEQ(elf64->e_ident, ELFMAG) && ++ (elf64->e_ident[EI_CLASS] == ELFCLASS64) && ++ (elf64->e_ident[EI_VERSION] == EV_CURRENT)) { ++ return (elf64->e_version); ++ } ++ ++ return -1; ++} ++ ++/* ++ * Check whether any PT_LOAD segment goes beyond the file size. ++ */ ++static void ++check_dumpfile_size(char *file) ++{ ++ int i; ++ struct stat64 stat; ++ struct pt_load_segment *pls; ++ uint64_t segment_end; ++ ++ if (stat64(file, &stat) < 0) ++ return; ++ ++ for (i = 0; i < nd->num_pt_load_segments; i++) { ++ pls = &nd->pt_load_segments[i]; ++ ++ segment_end = pls->file_offset + ++ (pls->phys_end - pls->phys_start); ++ ++ if (segment_end > stat.st_size) { ++ error(WARNING, "%s: may be truncated or incomplete\n" ++ " PT_LOAD p_offset: %lld\n" ++ " p_filesz: %lld\n" ++ " bytes required: %lld\n" ++ " dumpfile size: %lld\n\n", ++ file, pls->file_offset, ++ pls->phys_end - pls->phys_start, ++ segment_end, stat.st_size); ++ return; ++ } ++ } ++} ++ ++/* + * Perform any post-dumpfile determination stuff here. + */ + int + netdump_init(char *unused, FILE *fptr) + { +- if (!NETDUMP_VALID()) ++ if (!VMCORE_VALID()) + return FALSE; + + nd->ofp = fptr; ++ ++ check_dumpfile_size(pc->dumpfile); ++ + return TRUE; + } + +@@ -263,19 +413,19 @@ + /* + * The Elf32_Phdr has 32-bit fields for p_paddr, p_filesz and + * p_memsz, so for now, multiple PT_LOAD segment support is +- * restricted to 64-bit machines. Until a "standard" becomes +- * available in the future that deals with physical memory +- * segments that start at greater then 4GB, or memory segments +- * sizes that are greater than 4GB (kexec?), then this feature +- * is restricted to 64-bit machines. ++ * restricted to 64-bit machines for netdump/diskdump vmcores. ++ * However, kexec/kdump has introduced the optional use of a ++ * 64-bit ELF header for 32-bit processors. + */ +- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64)) ++ switch (DUMPFILE_FORMAT(nd->flags)) + { + case NETDUMP_ELF32: + offset = (off_t)paddr + (off_t)nd->header_size; + break; + + case NETDUMP_ELF64: ++ case KDUMP_ELF32: ++ case KDUMP_ELF64: + if (nd->num_pt_load_segments == 1) { + offset = (off_t)paddr + (off_t)nd->header_size; + break; +@@ -289,6 +439,11 @@ + pls->file_offset; + break; + } ++ if (pls->zero_fill && (paddr >= pls->phys_end) && ++ (paddr < pls->zero_fill)) { ++ memset(bufptr, 0, cnt); ++ return cnt; ++ } + } + + if (!offset) +@@ -302,24 +457,57 @@ + + if (read(nd->ndfd, bufptr, cnt) != cnt) + return READ_ERROR; ++ + return cnt; + } + + /* +- * Write to a netdump-created dumpfile. ++ * Write to a netdump-created dumpfile. Note that cmd_wr() does not ++ * allow writes to dumpfiles, so you can't get here from there. ++ * But, if it would ever be helpful, here it is... + */ + int + write_netdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) + { + off_t offset; ++ struct pt_load_segment *pls; ++ int i; + +- offset = (off_t)paddr + (off_t)nd->header_size; ++ switch (DUMPFILE_FORMAT(nd->flags)) ++ { ++ case NETDUMP_ELF32: ++ offset = (off_t)paddr + (off_t)nd->header_size; ++ break; ++ ++ case NETDUMP_ELF64: ++ case KDUMP_ELF32: ++ case KDUMP_ELF64: ++ if (nd->num_pt_load_segments == 1) { ++ offset = (off_t)paddr + (off_t)nd->header_size; ++ break; ++ } ++ ++ for (i = offset = 0; i < nd->num_pt_load_segments; i++) { ++ pls = &nd->pt_load_segments[i]; ++ if ((paddr >= pls->phys_start) && ++ (paddr < pls->phys_end)) { ++ offset = (off_t)(paddr - pls->phys_start) + ++ pls->file_offset; ++ break; ++ } ++ } ++ ++ if (!offset) ++ return READ_ERROR; ++ ++ break; ++ } + +- if (lseek(nd->ndfd, offset, SEEK_SET) != offset) ++ if (lseek(nd->ndfd, offset, SEEK_SET) == -1) + return SEEK_ERROR; + + if (write(nd->ndfd, bufptr, cnt) != cnt) +- return WRITE_ERROR; ++ return READ_ERROR; + + return cnt; + } +@@ -330,7 +518,7 @@ + FILE * + set_netdump_fp(FILE *fp) + { +- if (!NETDUMP_VALID()) ++ if (!VMCORE_VALID()) + return NULL; + + nd->ofp = fp; +@@ -346,7 +534,7 @@ + char buf[BUFSIZE]; + va_list ap; + +- if (!fmt || !strlen(fmt) || !NETDUMP_VALID()) ++ if (!fmt || !strlen(fmt) || !VMCORE_VALID()) + return; + + va_start(ap, fmt); +@@ -362,33 +550,21 @@ + uint + netdump_page_size(void) + { +- uint pagesz; +- +- if (!NETDUMP_VALID()) ++ if (!VMCORE_VALID()) + return 0; + +- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64)) +- { +- case NETDUMP_ELF32: +- pagesz = (uint)nd->load32->p_align; +- break; +- case NETDUMP_ELF64: +- pagesz = (uint)nd->load64->p_align; +- break; +- } +- +- return pagesz; ++ return nd->page_size; + } + + int + netdump_free_memory(void) + { +- return (NETDUMP_VALID() ? 0 : 0); ++ return (VMCORE_VALID() ? 0 : 0); + } + + int netdump_memory_used(void) + { +- return (NETDUMP_VALID() ? 0 : 0); ++ return (VMCORE_VALID() ? 0 : 0); + } + + /* +@@ -414,21 +590,57 @@ + #ifdef DAEMON + return nd->task_struct; + #else +- int i; ++ int i, crashing_cpu; + size_t len; + char *user_regs; + ulong ebp, esp, task; + +- if (!NETDUMP_VALID() || !get_active_set()) +- return NO_TASK; ++ if (!VMCORE_VALID() || !get_active_set()) ++ goto panic_task_undetermined; + +- if (nd->task_struct) ++ if (nd->task_struct) { ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "get_netdump_panic_task: NT_TASKSTRUCT: %lx\n", ++ nd->task_struct); + return nd->task_struct; ++ } ++ ++ switch (DUMPFILE_FORMAT(nd->flags)) ++ { ++ case NETDUMP_ELF32: ++ case NETDUMP_ELF64: ++ crashing_cpu = -1; ++ break; ++ ++ case KDUMP_ELF32: ++ case KDUMP_ELF64: ++ crashing_cpu = -1; ++ if (symbol_exists("crashing_cpu")) { ++ get_symbol_data("crashing_cpu", sizeof(int), &i); ++ if ((i >= 0) && (i < nd->num_prstatus_notes)) { ++ crashing_cpu = i; ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "get_netdump_panic_task: crashing_cpu: %d\n", ++ crashing_cpu); ++ } ++ } ++ ++ if ((nd->num_prstatus_notes > 1) && (crashing_cpu == -1)) ++ goto panic_task_undetermined; ++ break; ++ } ++ ++ if (nd->elf32 && (nd->elf32->e_machine == EM_386)) { ++ Elf32_Nhdr *note32; ++ ++ if ((nd->num_prstatus_notes > 1) && (crashing_cpu != -1)) ++ note32 = (Elf32_Nhdr *) ++ nd->nt_prstatus_percpu[crashing_cpu]; ++ else ++ note32 = (Elf32_Nhdr *)nd->nt_prstatus; + +- if (nd->elf32 && nd->elf32->e_machine == EM_386) { +- Elf32_Nhdr *note32 = (Elf32_Nhdr *) +- ((char *)nd->elf32 + nd->notes32->p_offset); +- + len = sizeof(Elf32_Nhdr); + len = roundup(len + note32->n_namesz, 4); + len = roundup(len + note32->n_descsz, 4); +@@ -437,14 +649,15 @@ + - SIZE(user_regs_struct) - sizeof(int); + ebp = ULONG(user_regs + OFFSET(user_regs_struct_ebp)); + esp = ULONG(user_regs + OFFSET(user_regs_struct_esp)); ++check_ebp_esp: + if (CRASHDEBUG(1)) +- fprintf(fp, +- "get_netdump_panic_task: esp: %lx ebp: %lx\n", ++ error(INFO, ++ "get_netdump_panic_task: NT_PRSTATUS esp: %lx ebp: %lx\n", + esp, ebp); + if (IS_KVADDR(esp)) { + task = stkptr_to_task(esp); + if (CRASHDEBUG(1)) +- fprintf(fp, ++ error(INFO, + "get_netdump_panic_task: esp: %lx -> task: %lx\n", + esp, task); + for (i = 0; task && (i < NR_CPUS); i++) { +@@ -455,7 +668,7 @@ + if (IS_KVADDR(ebp)) { + task = stkptr_to_task(ebp); + if (CRASHDEBUG(1)) +- fprintf(fp, ++ error(INFO, + "get_netdump_panic_task: ebp: %lx -> task: %lx\n", + ebp, task); + for (i = 0; task && (i < NR_CPUS); i++) { +@@ -464,25 +677,37 @@ + } + } + } else if (nd->elf64) { +- Elf64_Nhdr *note64 = (Elf64_Nhdr *) +- ((char *)nd->elf64 + nd->notes64->p_offset); +- ++ Elf64_Nhdr *note64; ++ ++ if ((nd->num_prstatus_notes > 1) && (crashing_cpu != -1)) ++ note64 = (Elf64_Nhdr *) ++ nd->nt_prstatus_percpu[crashing_cpu]; ++ else ++ note64 = (Elf64_Nhdr *)nd->nt_prstatus; ++ + len = sizeof(Elf64_Nhdr); + len = roundup(len + note64->n_namesz, 4); + user_regs = (char *)((char *)note64 + len + + MEMBER_OFFSET("elf_prstatus", "pr_reg")); ++ ++ if (nd->elf64->e_machine == EM_386) { ++ ebp = ULONG(user_regs + OFFSET(user_regs_struct_ebp)); ++ esp = ULONG(user_regs + OFFSET(user_regs_struct_esp)); ++ goto check_ebp_esp; ++ } ++ + if (nd->elf64->e_machine == EM_PPC64) { + /* + * Get the GPR1 register value. + */ + esp = *(ulong *)((char *)user_regs + 8); + if (CRASHDEBUG(1)) +- fprintf(fp, +- "get_netdump_panic_task: esp: %lx\n", esp); ++ error(INFO, ++ "get_netdump_panic_task: NT_PRSTATUS esp: %lx\n", esp); + if (IS_KVADDR(esp)) { + task = stkptr_to_task(esp); + if (CRASHDEBUG(1)) +- fprintf(fp, ++ error(INFO, + "get_netdump_panic_task: esp: %lx -> task: %lx\n", + esp, task); + for (i = 0; task && (i < NR_CPUS); i++) { +@@ -493,8 +718,10 @@ + } + } + ++panic_task_undetermined: ++ + if (CRASHDEBUG(1)) +- fprintf(fp, "get_netdump_panic_task: returning NO_TASK\n"); ++ error(INFO, "get_netdump_panic_task: failed\n"); + + return NO_TASK; + #endif +@@ -512,7 +739,7 @@ + return nd->switch_stack; + return 0; + #else +- if (!NETDUMP_VALID() || !get_active_set()) ++ if (!VMCORE_VALID() || !get_active_set()) + return 0; + + if (nd->task_struct == task) +@@ -525,33 +752,75 @@ + int + netdump_memory_dump(FILE *fp) + { +- int i, others; ++ int i, others, wrap, flen; + size_t len, tot; + FILE *fpsave; + Elf32_Off offset32; + Elf32_Off offset64; + struct pt_load_segment *pls; + +- if (!NETDUMP_VALID()) ++ if (!VMCORE_VALID()) + return FALSE; + + fpsave = nd->ofp; + nd->ofp = fp; + +- netdump_print("netdump_data: \n"); ++ netdump_print("vmcore_data: \n"); + netdump_print(" flags: %lx (", nd->flags); + others = 0; + if (nd->flags & NETDUMP_LOCAL) + netdump_print("%sNETDUMP_LOCAL", others++ ? "|" : ""); ++ if (nd->flags & KDUMP_LOCAL) ++ netdump_print("%sKDUMP_LOCAL", others++ ? "|" : ""); + if (nd->flags & NETDUMP_REMOTE) + netdump_print("%sNETDUMP_REMOTE", others++ ? "|" : ""); + if (nd->flags & NETDUMP_ELF32) + netdump_print("%sNETDUMP_ELF32", others++ ? "|" : ""); + if (nd->flags & NETDUMP_ELF64) + netdump_print("%sNETDUMP_ELF64", others++ ? "|" : ""); ++ if (nd->flags & KDUMP_ELF32) ++ netdump_print("%sKDUMP_ELF32", others++ ? "|" : ""); ++ if (nd->flags & KDUMP_ELF64) ++ netdump_print("%sKDUMP_ELF64", others++ ? "|" : ""); + if (nd->flags & PARTIAL_DUMP) + netdump_print("%sPARTIAL_DUMP", others++ ? "|" : ""); + netdump_print(")\n"); ++ if ((pc->flags & RUNTIME) && symbol_exists("dump_level")) { ++ int dump_level; ++ if (readmem(symbol_value("dump_level"), KVADDR, &dump_level, ++ sizeof(dump_level), "dump_level", QUIET|RETURN_ON_ERROR)) { ++ netdump_print(" dump_level: %d (0x%x) %s", ++ dump_level, dump_level, ++ dump_level > 0 ? "(" : ""); ++ ++#define DUMP_EXCLUDE_CACHE 0x00000001 /* Exclude LRU & SwapCache pages*/ ++#define DUMP_EXCLUDE_CLEAN 0x00000002 /* Exclude all-zero pages */ ++#define DUMP_EXCLUDE_FREE 0x00000004 /* Exclude free pages */ ++#define DUMP_EXCLUDE_ANON 0x00000008 /* Exclude Anon pages */ ++#define DUMP_SAVE_PRIVATE 0x00000010 /* Save private pages */ ++ ++ others = 0; ++ if (dump_level & DUMP_EXCLUDE_CACHE) ++ netdump_print("%sDUMP_EXCLUDE_CACHE", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_EXCLUDE_CLEAN) ++ netdump_print("%sDUMP_EXCLUDE_CLEAN", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_EXCLUDE_FREE) ++ netdump_print("%sDUMP_EXCLUDE_FREE", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_EXCLUDE_ANON) ++ netdump_print("%sDUMP_EXCLUDE_ANON", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_SAVE_PRIVATE) ++ netdump_print("%sDUMP_SAVE_PRIVATE", ++ others++ ? "|" : ""); ++ netdump_print("%s\n", dump_level > 0 ? ")" : ""); ++ } else ++ netdump_print(" dump_level: (unknown)\n"); ++ } else if (!(pc->flags & RUNTIME) && symbol_exists("dump_level")) ++ netdump_print(" dump_level: (undetermined)\n"); ++ + netdump_print(" ndfd: %d\n", nd->ndfd); + netdump_print(" ofp: %lx\n", nd->ofp); + netdump_print(" header_size: %d\n", nd->header_size); +@@ -565,8 +834,10 @@ + pls->phys_start); + netdump_print(" phys_end: %llx\n", + pls->phys_end); ++ netdump_print(" zero_fill: %llx\n", ++ pls->zero_fill); + } +- netdump_print(" netdump_header: %lx\n", nd->netdump_header); ++ netdump_print(" elf_header: %lx\n", nd->elf_header); + netdump_print(" elf32: %lx\n", nd->elf32); + netdump_print(" notes32: %lx\n", nd->notes32); + netdump_print(" load32: %lx\n", nd->load32); +@@ -577,11 +848,74 @@ + netdump_print(" nt_prpsinfo: %lx\n", nd->nt_prpsinfo); + netdump_print(" nt_taskstruct: %lx\n", nd->nt_taskstruct); + netdump_print(" task_struct: %lx\n", nd->task_struct); +- netdump_print(" switch_stack: %lx\n\n", nd->switch_stack); ++ netdump_print(" page_size: %d\n", nd->page_size); ++ netdump_print(" switch_stack: %lx\n", nd->switch_stack); ++ netdump_print(" xen_kdump_data: %s\n", ++ XEN_CORE_DUMPFILE() ? " " : "(unused)"); ++ if (XEN_CORE_DUMPFILE()) { ++ netdump_print(" flags: %lx (", nd->xen_kdump_data->flags); ++ others = 0; ++ if (nd->xen_kdump_data->flags & KDUMP_P2M_INIT) ++ netdump_print("%sKDUMP_P2M_INIT", others++ ? "|" : ""); ++ if (nd->xen_kdump_data->flags & KDUMP_CR3) ++ netdump_print("%sKDUMP_CR3", others++ ? "|" : ""); ++ if (nd->xen_kdump_data->flags & KDUMP_MFN_LIST) ++ netdump_print("%sKDUMP_MFN_LIST", others++ ? "|" : ""); ++ netdump_print(")\n"); ++ netdump_print(" p2m_mfn: %lx\n", ++ nd->xen_kdump_data->p2m_mfn); ++ netdump_print(" cr3: %lx\n", ++ nd->xen_kdump_data->cr3); ++ netdump_print(" last_mfn_read: %lx\n", ++ nd->xen_kdump_data->last_mfn_read); ++ netdump_print(" last_pmd_read: %lx\n", ++ nd->xen_kdump_data->last_pmd_read); ++ netdump_print(" page: %lx\n", ++ nd->xen_kdump_data->page); ++ netdump_print(" accesses: %ld\n", ++ nd->xen_kdump_data->accesses); ++ netdump_print(" cache_hits: %ld ", ++ nd->xen_kdump_data->cache_hits); ++ if (nd->xen_kdump_data->accesses) ++ netdump_print("(%ld%%)", ++ nd->xen_kdump_data->cache_hits * 100 / nd->xen_kdump_data->accesses); ++ netdump_print("\n p2m_frames: %d\n", ++ nd->xen_kdump_data->p2m_frames); ++ netdump_print(" xen_phys_start: %lx\n", ++ nd->xen_kdump_data->xen_phys_start); ++ netdump_print(" xen_major_version: %d\n", ++ nd->xen_kdump_data->xen_major_version); ++ netdump_print(" xen_minor_version: %d\n", ++ nd->xen_kdump_data->xen_minor_version); ++ netdump_print(" p2m_mfn_frame_list: %lx\n", ++ nd->xen_kdump_data->p2m_mfn_frame_list); ++ for (i = 0; i < nd->xen_kdump_data->p2m_frames; i++) ++ netdump_print("%lx ", ++ nd->xen_kdump_data->p2m_mfn_frame_list[i]); ++ if (i) netdump_print("\n"); ++ } ++ netdump_print(" num_prstatus_notes: %d\n", nd->num_prstatus_notes); ++ netdump_print(" vmcoreinfo: %lx\n", (ulong)nd->vmcoreinfo); ++ netdump_print(" size_vmcoreinfo: %d\n", nd->size_vmcoreinfo); ++ netdump_print(" nt_prstatus_percpu: "); ++ wrap = sizeof(void *) == SIZEOF_32BIT ? 8 : 4; ++ flen = sizeof(void *) == SIZEOF_32BIT ? 8 : 16; ++ if (nd->num_prstatus_notes == 1) ++ netdump_print("%.*lx\n", flen, nd->nt_prstatus_percpu[0]); ++ else { ++ for (i = 0; i < nd->num_prstatus_notes; i++) { ++ if ((i % wrap) == 0) ++ netdump_print("\n "); ++ netdump_print("%.*lx ", flen, ++ nd->nt_prstatus_percpu[i]); ++ } ++ } ++ netdump_print("\n\n"); + +- switch (nd->flags & (NETDUMP_ELF32|NETDUMP_ELF64)) ++ switch (DUMPFILE_FORMAT(nd->flags)) + { + case NETDUMP_ELF32: ++ case KDUMP_ELF32: + dump_Elf32_Ehdr(nd->elf32); + dump_Elf32_Phdr(nd->notes32, ELFREAD); + for (i = 0; i < nd->num_pt_load_segments; i++) +@@ -594,6 +928,7 @@ + break; + + case NETDUMP_ELF64: ++ case KDUMP_ELF64: + dump_Elf64_Ehdr(nd->elf64); + dump_Elf64_Phdr(nd->notes64, ELFREAD); + for (i = 0; i < nd->num_pt_load_segments; i++) +@@ -865,6 +1200,9 @@ + netdump_print(" e_machine: %d ", elf->e_machine); + switch (elf->e_machine) + { ++ case EM_386: ++ netdump_print("(EM_386)\n"); ++ break; + case EM_IA_64: + netdump_print("(EM_IA_64)\n"); + break; +@@ -961,8 +1299,11 @@ + pls->phys_start = prog->p_paddr; + netdump_print(" p_filesz: %lu (%lx)\n", prog->p_filesz, + prog->p_filesz); +- if (store_pt_load_data) ++ if (store_pt_load_data) { + pls->phys_end = pls->phys_start + prog->p_filesz; ++ pls->zero_fill = (prog->p_filesz == prog->p_memsz) ? ++ 0 : pls->phys_start + prog->p_memsz; ++ } + netdump_print(" p_memsz: %lu (%lx)\n", prog->p_memsz, + prog->p_memsz); + netdump_print(" p_flags: %lx (", prog->p_flags); +@@ -1030,19 +1371,22 @@ + netdump_print("(?)\n"); + } + +- netdump_print(" p_offset: %ld (%lx)\n", prog->p_offset, ++ netdump_print(" p_offset: %lld (%llx)\n", prog->p_offset, + prog->p_offset); + if (store_pt_load_data) + pls->file_offset = prog->p_offset; +- netdump_print(" p_vaddr: %lx\n", prog->p_vaddr); +- netdump_print(" p_paddr: %lx\n", prog->p_paddr); ++ netdump_print(" p_vaddr: %llx\n", prog->p_vaddr); ++ netdump_print(" p_paddr: %llx\n", prog->p_paddr); + if (store_pt_load_data) + pls->phys_start = prog->p_paddr; +- netdump_print(" p_filesz: %lu (%lx)\n", prog->p_filesz, ++ netdump_print(" p_filesz: %llu (%llx)\n", prog->p_filesz, + prog->p_filesz); +- if (store_pt_load_data) ++ if (store_pt_load_data) { + pls->phys_end = pls->phys_start + prog->p_filesz; +- netdump_print(" p_memsz: %lu (%lx)\n", prog->p_memsz, ++ pls->zero_fill = (prog->p_filesz == prog->p_memsz) ? ++ 0 : pls->phys_start + prog->p_memsz; ++ } ++ netdump_print(" p_memsz: %llu (%llx)\n", prog->p_memsz, + prog->p_memsz); + netdump_print(" p_flags: %lx (", prog->p_flags); + others = 0; +@@ -1053,7 +1397,97 @@ + if (prog->p_flags & PF_R) + netdump_print("%sPF_R", others++ ? "|" : ""); + netdump_print(")\n"); +- netdump_print(" p_align: %ld\n", prog->p_align); ++ netdump_print(" p_align: %lld\n", prog->p_align); ++} ++ ++/* ++ * VMCOREINFO ++ * ++ * This is a ELF note intented for makedumpfile that is exported by the ++ * kernel that crashes and presented as ELF note to the /proc/vmcore ++ * of the panic kernel. ++ */ ++ ++#define VMCOREINFO_NOTE_NAME "VMCOREINFO" ++#define VMCOREINFO_NOTE_NAME_BYTES (sizeof(VMCOREINFO_NOTE_NAME)) ++ ++/* ++ * Reads a string value from VMCOREINFO. ++ * ++ * Returns a string (that has to be freed by the caller) that contains the ++ * value for key or NULL if the key has not been found. ++ */ ++static char * ++vmcoreinfo_read_string(const char *key) ++{ ++ int i, j, end; ++ size_t value_length; ++ size_t key_length = strlen(key); ++ char *vmcoreinfo = (char *)nd->vmcoreinfo; ++ char *value = NULL; ++ ++ if (!nd->vmcoreinfo) ++ return NULL; ++ ++ /* the '+ 1' is the equal sign */ ++ for (i = 0; i < (nd->size_vmcoreinfo - key_length + 1); i++) { ++ /* ++ * We must also check if we're at the beginning of VMCOREINFO ++ * or the separating newline is there, and of course if we ++ * have a equal sign after the key. ++ */ ++ if ((strncmp(vmcoreinfo+i, key, key_length) == 0) && ++ (i == 0 || vmcoreinfo[i-1] == '\n') && ++ (vmcoreinfo[i+key_length] == '=')) { ++ ++ end = -1; ++ ++ /* Found -- search for the next newline. */ ++ for (j = i + key_length + 1; ++ j < nd->size_vmcoreinfo; j++) { ++ if (vmcoreinfo[j] == '\n') { ++ end = j; ++ break; ++ } ++ } ++ ++ /* ++ * If we didn't find an end, we assume it's the end ++ * of VMCOREINFO data. ++ */ ++ if (end == -1) { ++ /* Point after the end. */ ++ end = nd->size_vmcoreinfo + 1; ++ } ++ ++ value_length = end - (1+ i + key_length); ++ value = malloc(value_length); ++ if (value) ++ strncpy(value, vmcoreinfo + i + key_length + 1, ++ value_length); ++ break; ++ } ++ } ++ ++ return value; ++} ++ ++/* ++ * Reads an integer value from VMCOREINFO. ++ */ ++static long ++vmcoreinfo_read_integer(const char *key, long default_value) ++{ ++ char *string; ++ long retval = default_value; ++ ++ string = vmcoreinfo_read_string(key); ++ if (string) { ++ retval = atol(string); ++ free(string); ++ } ++ ++ return retval; + } + + /* +@@ -1061,20 +1495,22 @@ + */ + + static size_t +-dump_Elf32_Nhdr(Elf32_Off offset, int store_addresses) ++dump_Elf32_Nhdr(Elf32_Off offset, int store) + { +- int i, lf; ++ int i, lf, words; + Elf32_Nhdr *note; + size_t len; + char buf[BUFSIZE]; + char *ptr; + ulong *uptr; ++ int xen_core, vmcoreinfo; + + note = (Elf32_Nhdr *)((char *)nd->elf32 + offset); + + netdump_print("Elf32_Nhdr:\n"); + netdump_print(" n_namesz: %ld ", note->n_namesz); + BZERO(buf, BUFSIZE); ++ xen_core = vmcoreinfo = FALSE; + ptr = (char *)note + sizeof(Elf32_Nhdr); + BCOPY(ptr, buf, note->n_namesz); + netdump_print("(\"%s\")\n", buf); +@@ -1085,17 +1521,26 @@ + { + case NT_PRSTATUS: + netdump_print("(NT_PRSTATUS)\n"); +- if (store_addresses) +- nd->nt_prstatus = (void *)note; ++ if (store) { ++ if (!nd->nt_prstatus) ++ nd->nt_prstatus = (void *)note; ++ for (i = 0; i < NR_CPUS; i++) { ++ if (!nd->nt_prstatus_percpu[i]) { ++ nd->nt_prstatus_percpu[i] = (void *)note; ++ nd->num_prstatus_notes++; ++ break; ++ } ++ } ++ } + break; + case NT_PRPSINFO: + netdump_print("(NT_PRPSINFO)\n"); +- if (store_addresses) ++ if (store) + nd->nt_prpsinfo = (void *)note; + break; + case NT_TASKSTRUCT: + netdump_print("(NT_TASKSTRUCT)\n"); +- if (store_addresses) { ++ if (store) { + nd->nt_taskstruct = (void *)note; + nd->task_struct = *((ulong *)(ptr + note->n_namesz)); + nd->switch_stack = *((ulong *) +@@ -1105,25 +1550,132 @@ + case NT_DISKDUMP: + netdump_print("(NT_DISKDUMP)\n"); + uptr = (ulong *)(ptr + note->n_namesz); +- if (*uptr) ++ if (*uptr && store) + nd->flags |= PARTIAL_DUMP; + break; ++#ifdef NOTDEF ++ /* ++ * Note: Based upon the original, abandoned, proposal for ++ * its contents -- keep around for potential future use. ++ */ ++ case NT_KDUMPINFO: ++ netdump_print("(NT_KDUMPINFO)\n"); ++ if (store) { ++ uptr = (note->n_namesz == 5) ? ++ (ulong *)(ptr + ((note->n_namesz + 3) & ~3)) : ++ (ulong *)(ptr + note->n_namesz); ++ nd->page_size = (uint)(1 << *uptr); ++ uptr++; ++ nd->task_struct = *uptr; ++ } ++ break; ++#endif + default: +- netdump_print("(?)\n"); ++ xen_core = STRNEQ(buf, "XEN CORE") || STRNEQ(buf, "Xen"); ++ vmcoreinfo = STRNEQ(buf, "VMCOREINFO"); ++ if (xen_core) { ++ netdump_print("(unknown Xen n_type)\n"); ++ if (store) ++ error(WARNING, "unknown Xen n_type: %lx\n\n", ++ note->n_type); ++ } else if (vmcoreinfo) ++ netdump_print("(unused)\n"); ++ else ++ netdump_print("(?)\n"); ++ break; ++ ++ case NT_XEN_KDUMP_CR3: ++ netdump_print("(NT_XEN_KDUMP_CR3) [obsolete]\n"); ++ if (store) ++ error(WARNING, ++ "obsolete Xen n_type: %lx (NT_XEN_KDUMP_CR3)\n\n", ++ note->n_type); ++ /* FALL THROUGH */ ++ ++ case XEN_ELFNOTE_CRASH_INFO: ++ /* ++ * x86 and x86_64: p2m mfn appended to crash_xen_info_t structure ++ */ ++ if (note->n_type == XEN_ELFNOTE_CRASH_INFO) ++ netdump_print("(XEN_ELFNOTE_CRASH_INFO)\n"); ++ xen_core = TRUE; ++ if (store) { ++ pc->flags |= XEN_CORE; ++ nd->xen_kdump_data = &xen_kdump_data; ++ nd->xen_kdump_data->last_mfn_read = UNINITIALIZED; ++ nd->xen_kdump_data->last_pmd_read = UNINITIALIZED; ++ ++ if ((note->n_type == NT_XEN_KDUMP_CR3) && ++ ((note->n_descsz/sizeof(ulong)) == 1)) { ++ nd->xen_kdump_data->flags |= KDUMP_CR3; ++ /* ++ * Use the first cr3 found. ++ */ ++ if (!nd->xen_kdump_data->cr3) { ++ uptr = (ulong *)(ptr + note->n_namesz); ++ uptr = (ulong *)roundup((ulong)uptr, 4); ++ nd->xen_kdump_data->cr3 = *uptr; ++ } ++ } else { ++ nd->xen_kdump_data->flags |= KDUMP_MFN_LIST; ++ uptr = (ulong *)(ptr + note->n_namesz); ++ uptr = (ulong *)roundup((ulong)uptr, 4); ++ words = note->n_descsz/sizeof(ulong); ++ /* ++ * If already set, overridden with --pfm_mfn ++ */ ++ if (!nd->xen_kdump_data->p2m_mfn) ++ nd->xen_kdump_data->p2m_mfn = *(uptr+(words-1)); ++ if (words > 9 && !nd->xen_kdump_data->xen_phys_start) ++ nd->xen_kdump_data->xen_phys_start = *(uptr+(words-2)); ++ nd->xen_kdump_data->xen_major_version = *uptr; ++ nd->xen_kdump_data->xen_minor_version = *(uptr+1); ++ } ++ } ++ break; ++ ++ case XEN_ELFNOTE_CRASH_REGS: ++ /* ++ * x86 and x86_64: cr0, cr2, cr3, cr4 ++ */ ++ xen_core = TRUE; ++ netdump_print("(XEN_ELFNOTE_CRASH_REGS)\n"); ++ break; + } + + uptr = (ulong *)(ptr + note->n_namesz); +- for (i = lf = 0; i < note->n_descsz/sizeof(ulong); i++) { +- if (((i%4)==0)) { +- netdump_print("%s ", +- i ? "\n" : ""); +- lf++; +- } else +- lf = 0; +- netdump_print("%08lx ", *uptr++); ++ ++ /* ++ * kdumps are off-by-1, because their n_namesz is 5 for "CORE". ++ */ ++ if ((nd->flags & KDUMP_ELF32) && (note->n_namesz == 5)) ++ uptr = (ulong *)(ptr + ((note->n_namesz + 3) & ~3)); ++ ++ if (xen_core) ++ uptr = (ulong *)roundup((ulong)uptr, 4); ++ ++ if (vmcoreinfo) { ++ netdump_print(" "); ++ ptr += note->n_namesz + 1; ++ for (i = 0; i < note->n_descsz; i++, ptr++) { ++ netdump_print("%c", *ptr); ++ if (*ptr == '\n') ++ netdump_print(" "); ++ } ++ lf = 0; ++ } else { ++ for (i = lf = 0; i < note->n_descsz/sizeof(ulong); i++) { ++ if (((i%4)==0)) { ++ netdump_print("%s ", ++ i ? "\n" : ""); ++ lf++; ++ } else ++ lf = 0; ++ netdump_print("%08lx ", *uptr++); ++ } + } + if (!lf || (note->n_type == NT_TASKSTRUCT) || +- (note->n_type == NT_DISKDUMP)) ++ (note->n_type == NT_DISKDUMP) || xen_core) + netdump_print("\n"); + + len = sizeof(Elf32_Nhdr); +@@ -1135,15 +1687,17 @@ + + + static size_t +-dump_Elf64_Nhdr(Elf64_Off offset, int store_addresses) ++dump_Elf64_Nhdr(Elf64_Off offset, int store) + { +- int i, lf; ++ int i, lf, words; + Elf64_Nhdr *note; + size_t len; + char buf[BUFSIZE]; + char *ptr; + ulonglong *uptr; + int *iptr; ++ ulong *up; ++ int xen_core, vmcoreinfo; + + note = (Elf64_Nhdr *)((char *)nd->elf64 + offset); + +@@ -1151,6 +1705,7 @@ + netdump_print(" n_namesz: %ld ", note->n_namesz); + BZERO(buf, BUFSIZE); + ptr = (char *)note + sizeof(Elf64_Nhdr); ++ xen_core = vmcoreinfo = FALSE; + BCOPY(ptr, buf, note->n_namesz); + netdump_print("(\"%s\")\n", buf); + +@@ -1160,17 +1715,26 @@ + { + case NT_PRSTATUS: + netdump_print("(NT_PRSTATUS)\n"); +- if (store_addresses) +- nd->nt_prstatus = (void *)note; ++ if (store) { ++ if (!nd->nt_prstatus) ++ nd->nt_prstatus = (void *)note; ++ for (i = 0; i < NR_CPUS; i++) { ++ if (!nd->nt_prstatus_percpu[i]) { ++ nd->nt_prstatus_percpu[i] = (void *)note; ++ nd->num_prstatus_notes++; ++ break; ++ } ++ } ++ } + break; + case NT_PRPSINFO: + netdump_print("(NT_PRPSINFO)\n"); +- if (store_addresses) ++ if (store) + nd->nt_prpsinfo = (void *)note; + break; + case NT_TASKSTRUCT: + netdump_print("(NT_TASKSTRUCT)\n"); +- if (store_addresses) { ++ if (store) { + nd->nt_taskstruct = (void *)note; + nd->task_struct = *((ulong *)(ptr + note->n_namesz)); + nd->switch_stack = *((ulong *) +@@ -1180,24 +1744,162 @@ + case NT_DISKDUMP: + netdump_print("(NT_DISKDUMP)\n"); + iptr = (int *)(ptr + note->n_namesz); +- if (*iptr) ++ if (*iptr && store) + nd->flags |= PARTIAL_DUMP; + if (note->n_descsz < sizeof(ulonglong)) + netdump_print(" %08x", *iptr); + break; ++#ifdef NOTDEF ++ /* ++ * Note: Based upon the original, abandoned, proposal for ++ * its contents -- keep around for potential future use. ++ */ ++ case NT_KDUMPINFO: ++ netdump_print("(NT_KDUMPINFO)\n"); ++ if (store) { ++ uint32_t *u32ptr; ++ ++ if (nd->elf64->e_machine == EM_386) { ++ u32ptr = (note->n_namesz == 5) ? ++ (uint *)(ptr + ((note->n_namesz + 3) & ~3)) : ++ (uint *)(ptr + note->n_namesz); ++ nd->page_size = 1 << *u32ptr; ++ u32ptr++; ++ nd->task_struct = *u32ptr; ++ } else { ++ uptr = (note->n_namesz == 5) ? ++ (ulonglong *)(ptr + ((note->n_namesz + 3) & ~3)) : ++ (ulonglong *)(ptr + note->n_namesz); ++ nd->page_size = (uint)(1 << *uptr); ++ uptr++; ++ nd->task_struct = *uptr; ++ } ++ } ++ break; ++#endif + default: +- netdump_print("(?)\n"); ++ xen_core = STRNEQ(buf, "XEN CORE") || STRNEQ(buf, "Xen"); ++ vmcoreinfo = STRNEQ(buf, "VMCOREINFO"); ++ if (xen_core) { ++ netdump_print("(unknown Xen n_type)\n"); ++ if (store) ++ error(WARNING, ++ "unknown Xen n_type: %lx\n\n", note->n_type); ++ } else if (vmcoreinfo) { ++ netdump_print("(unused)\n"); ++ ++ if (READ_PAGESIZE_FROM_VMCOREINFO() && store) { ++ nd->vmcoreinfo = (char *)nd->elf64 + offset + ++ (sizeof(Elf64_Nhdr) + ++ ((note->n_namesz + 3) & ~3)); ++ nd->size_vmcoreinfo = note->n_descsz; ++ nd->page_size = (uint) ++ vmcoreinfo_read_integer("PAGESIZE", 0); ++ } ++ } else ++ netdump_print("(?)\n"); ++ break; ++ ++ case NT_XEN_KDUMP_CR3: ++ netdump_print("(NT_XEN_KDUMP_CR3) [obsolete]\n"); ++ if (store) ++ error(WARNING, ++ "obsolete Xen n_type: %lx (NT_XEN_KDUMP_CR3)\n\n", ++ note->n_type); ++ /* FALL THROUGH */ ++ ++ case XEN_ELFNOTE_CRASH_INFO: ++ /* ++ * x86 and x86_64: p2m mfn appended to crash_xen_info_t structure ++ */ ++ if (note->n_type == XEN_ELFNOTE_CRASH_INFO) ++ netdump_print("(XEN_ELFNOTE_CRASH_INFO)\n"); ++ xen_core = TRUE; ++ if (store) { ++ pc->flags |= XEN_CORE; ++ nd->xen_kdump_data = &xen_kdump_data; ++ nd->xen_kdump_data->last_mfn_read = UNINITIALIZED; ++ nd->xen_kdump_data->last_pmd_read = UNINITIALIZED; ++ ++ if ((note->n_type == NT_XEN_KDUMP_CR3) && ++ ((note->n_descsz/sizeof(ulong)) == 1)) { ++ nd->xen_kdump_data->flags |= KDUMP_CR3; ++ /* ++ * Use the first cr3 found. ++ */ ++ if (!nd->xen_kdump_data->cr3) { ++ up = (ulong *)(ptr + note->n_namesz); ++ up = (ulong *)roundup((ulong)up, 4); ++ nd->xen_kdump_data->cr3 = *up; ++ } ++ } else { ++ nd->xen_kdump_data->flags |= KDUMP_MFN_LIST; ++ up = (ulong *)(ptr + note->n_namesz); ++ up = (ulong *)roundup((ulong)up, 4); ++ words = note->n_descsz/sizeof(ulong); ++ /* ++ * If already set, overridden with --p2m_mfn ++ */ ++ if (!nd->xen_kdump_data->p2m_mfn) ++ nd->xen_kdump_data->p2m_mfn = *(up+(words-1)); ++ if (words > 9 && !nd->xen_kdump_data->xen_phys_start) ++ nd->xen_kdump_data->xen_phys_start = *(up+(words-2)); ++ nd->xen_kdump_data->xen_major_version = *up; ++ nd->xen_kdump_data->xen_minor_version = *(up+1); ++ } ++ } ++ break; ++ ++ case XEN_ELFNOTE_CRASH_REGS: ++ /* ++ * x86 and x86_64: cr0, cr2, cr3, cr4 ++ */ ++ xen_core = TRUE; ++ netdump_print("(XEN_ELFNOTE_CRASH_REGS)\n"); ++ break; + } + + uptr = (ulonglong *)(ptr + note->n_namesz); +- for (i = lf = 0; i < note->n_descsz/sizeof(ulonglong); i++) { +- if (((i%2)==0)) { +- netdump_print("%s ", +- i ? "\n" : ""); +- lf++; +- } else +- lf = 0; +- netdump_print("%016llx ", *uptr++); ++ ++ /* ++ * kdumps are off-by-1, because their n_namesz is 5 for "CORE". ++ */ ++ if ((nd->flags & KDUMP_ELF64) && (note->n_namesz == 5)) ++ uptr = (ulonglong *)(ptr + ((note->n_namesz + 3) & ~3)); ++ ++ if (xen_core) ++ uptr = (ulonglong *)roundup((ulong)uptr, 4); ++ ++ if (BITS32() && (xen_core || (note->n_type == NT_PRSTATUS))) { ++ iptr = (int *)uptr; ++ for (i = lf = 0; i < note->n_descsz/sizeof(ulong); i++) { ++ if (((i%4)==0)) { ++ netdump_print("%s ", ++ i ? "\n" : ""); ++ lf++; ++ } else ++ lf = 0; ++ netdump_print("%08lx ", *iptr++); ++ } ++ } else if (vmcoreinfo) { ++ netdump_print(" "); ++ ptr += note->n_namesz + 1; ++ for (i = 0; i < note->n_descsz; i++, ptr++) { ++ netdump_print("%c", *ptr); ++ if (*ptr == '\n') ++ netdump_print(" "); ++ } ++ lf = 0; ++ } else { ++ for (i = lf = 0; i < note->n_descsz/sizeof(ulonglong); i++) { ++ if (((i%2)==0)) { ++ netdump_print("%s ", ++ i ? "\n" : ""); ++ lf++; ++ } else ++ lf = 0; ++ netdump_print("%016llx ", *uptr++); ++ } + } + if (!lf) + netdump_print("\n"); +@@ -1251,39 +1953,70 @@ + + default: + error(FATAL, +- "netdump support for ELF machine type %d not available\n", ++ "support for ELF machine type %d not available\n", + e_machine); + } + } + +-static void ++struct x86_64_user_regs_struct { ++ unsigned long r15,r14,r13,r12,rbp,rbx,r11,r10; ++ unsigned long r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax; ++ unsigned long rip,cs,eflags; ++ unsigned long rsp,ss; ++ unsigned long fs_base, gs_base; ++ unsigned long ds,es,fs,gs; ++}; ++ ++void + get_netdump_regs_x86_64(struct bt_info *bt, ulong *ripp, ulong *rspp) + { + Elf64_Nhdr *note; + size_t len; + char *user_regs; +- ulong rsp, rip; ++ ulong regs_size, rsp_offset, rip_offset; + + if (is_task_active(bt->task)) + bt->flags |= BT_DUMPFILE_SEARCH; + +- if (VALID_STRUCT(user_regs_struct) && (bt->task == tt->panic_task)) { +- note = (Elf64_Nhdr *)nd->nt_prstatus; ++ if (((NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) && ++ VALID_STRUCT(user_regs_struct) && (bt->task == tt->panic_task)) || ++ (KDUMP_DUMPFILE() && (kt->flags & DWARF_UNWIND) && ++ (bt->flags & BT_DUMPFILE_SEARCH))) { ++ if (nd->num_prstatus_notes > 1) ++ note = (Elf64_Nhdr *) ++ nd->nt_prstatus_percpu[bt->tc->processor]; ++ else ++ note = (Elf64_Nhdr *)nd->nt_prstatus; + + len = sizeof(Elf64_Nhdr); + len = roundup(len + note->n_namesz, 4); + len = roundup(len + note->n_descsz, 4); + +- user_regs = ((char *)note + len) +- - SIZE(user_regs_struct) - sizeof(long); ++ regs_size = VALID_STRUCT(user_regs_struct) ? ++ SIZE(user_regs_struct) : ++ sizeof(struct x86_64_user_regs_struct); ++ rsp_offset = VALID_MEMBER(user_regs_struct_rsp) ? ++ OFFSET(user_regs_struct_rsp) : ++ offsetof(struct x86_64_user_regs_struct, rsp); ++ rip_offset = VALID_MEMBER(user_regs_struct_rip) ? ++ OFFSET(user_regs_struct_rip) : ++ offsetof(struct x86_64_user_regs_struct, rip); + +- if (CRASHDEBUG(1)) { +- rsp = ULONG(user_regs + OFFSET(user_regs_struct_rsp)); +- rip = ULONG(user_regs + OFFSET(user_regs_struct_rip)); ++ user_regs = ((char *)note + len) - regs_size - sizeof(long); ++ ++ if (CRASHDEBUG(1)) + netdump_print("ELF prstatus rsp: %lx rip: %lx\n", +- rsp, rip); +- } ++ ULONG(user_regs + rsp_offset), ++ ULONG(user_regs + rip_offset)); ++ ++ if (KDUMP_DUMPFILE()) { ++ *rspp = ULONG(user_regs + rsp_offset); ++ *ripp = ULONG(user_regs + rip_offset); + ++ if (*ripp && *rspp) ++ return; ++ } ++ + bt->machdep = (void *)user_regs; + } + +@@ -1295,13 +2028,14 @@ + * the raw stack for some reasonable hooks. + */ + +-static void ++void + get_netdump_regs_x86(struct bt_info *bt, ulong *eip, ulong *esp) + { +- int i, search, panic; ++ int i, search, panic, panic_task; + char *sym; + ulong *up; + ulong ipintr_eip, ipintr_esp, ipintr_func; ++ ulong halt_eip, halt_esp; + int check_hardirq, check_softirq; + + if (!is_task_active(bt->task)) { +@@ -1309,17 +2043,31 @@ + return; + } + ++ panic_task = tt->panic_task == bt->task ? TRUE : FALSE; ++ + ipintr_eip = ipintr_esp = ipintr_func = panic = 0; ++ halt_eip = halt_esp = 0; + check_hardirq = check_softirq = tt->flags & IRQSTACKS ? TRUE : FALSE; + search = ((bt->flags & BT_TEXT_SYMBOLS) && (tt->flags & TASK_INIT_DONE)) + || (machdep->flags & OMIT_FRAME_PTR); +- + retry: + for (i = 0, up = (ulong *)bt->stackbuf; i < LONGS_PER_STACK; i++, up++){ + sym = closest_symbol(*up); +- if (STREQ(sym, "netconsole_netdump") || ++ ++ if (XEN_CORE_DUMPFILE()) { ++ if (STREQ(sym, "xen_machine_kexec")) { ++ *eip = *up; ++ *esp = bt->stackbase + ((char *)(up+1) - bt->stackbuf); ++ return; ++ } ++ if (STREQ(sym, "crash_kexec")) { ++ halt_eip = *up; ++ halt_esp = bt->stackbase + ((char *)(up+1) - bt->stackbuf); ++ } ++ } else if (STREQ(sym, "netconsole_netdump") || + STREQ(sym, "netpoll_start_netdump") || + STREQ(sym, "start_disk_dump") || ++ STREQ(sym, "crash_kexec") || + STREQ(sym, "disk_dump")) { + *eip = *up; + *esp = search ? +@@ -1354,7 +2102,7 @@ + next_sysrq: + *eip = *up; + *esp = bt->stackbase + ((char *)(up+4) - bt->stackbuf); +- machdep->flags |= SYSRQ; ++ pc->flags |= SYSRQ; + for (i++, up++; i < LONGS_PER_STACK; i++, up++) { + sym = closest_symbol(*up); + if (STREQ(sym, "sysrq_handle_crash")) +@@ -1371,7 +2119,15 @@ + *esp = search ? + bt->stackbase + ((char *)(up+1) - bt->stackbuf) : + *(up-1); +- machdep->flags |= SYSRQ; ++ pc->flags |= SYSRQ; ++ return; ++ } ++ ++ if (STREQ(sym, "crash_nmi_callback")) { ++ *eip = *up; ++ *esp = search ? ++ bt->stackbase + ((char *)(up+1) - bt->stackbuf) : ++ *(up-1); + return; + } + +@@ -1385,6 +2141,18 @@ + bt->stackbase + ((char *)(up-1) - bt->stackbuf); + ipintr_func = *(up - 2); + } ++ ++ if (XEN_CORE_DUMPFILE() && !panic_task && (bt->tc->pid == 0) && ++ STREQ(sym, "safe_halt")) { ++ halt_eip = *up; ++ halt_esp = bt->stackbase + ((char *)(up+1) - bt->stackbuf); ++ } ++ ++ if (XEN_CORE_DUMPFILE() && !panic_task && (bt->tc->pid == 0) && ++ !halt_eip && STREQ(sym, "xen_idle")) { ++ halt_eip = *up; ++ halt_esp = bt->stackbase + ((char *)(up+1) - bt->stackbuf); ++ } + } + + if (ipintr_eip) { +@@ -1393,6 +2161,12 @@ + return; + } + ++ if (halt_eip && halt_esp) { ++ *eip = halt_eip; ++ *esp = halt_esp; ++ return; ++ } ++ + if (panic) + return; + +@@ -1418,7 +2192,9 @@ + goto retry; + } + +- console("get_netdump_regs_x86: cannot find anything useful\n"); ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "get_netdump_regs_x86: cannot find anything useful (task: %lx)\n", bt->task); + + machdep->get_stack_frame(bt, eip, esp); + } +@@ -1429,8 +2205,24 @@ + Elf64_Nhdr *note; + size_t len; + +- if (bt->task == tt->panic_task) { +- note = (Elf64_Nhdr *)nd->nt_prstatus; ++ if ((bt->task == tt->panic_task) || ++ (is_task_active(bt->task) && nd->num_prstatus_notes > 1)) { ++ /* ++ * Registers are saved during the dump process for the ++ * panic task. Whereas in kdump, regs are captured for all ++ * CPUs if they responded to an IPI. ++ */ ++ if (nd->num_prstatus_notes > 1) { ++ if (bt->tc->processor >= nd->num_prstatus_notes) ++ error(FATAL, ++ "cannot determine NT_PRSTATUS ELF note " ++ "for %s task: %lx\n", ++ (bt->task == tt->panic_task) ? ++ "panic" : "active", bt->task); ++ note = (Elf64_Nhdr *) ++ nd->nt_prstatus_percpu[bt->tc->processor]; ++ } else ++ note = (Elf64_Nhdr *)nd->nt_prstatus; + + len = sizeof(Elf64_Nhdr); + len = roundup(len + note->n_namesz, 4); +@@ -1446,3 +2238,236 @@ + { + return (nd->flags & PARTIAL_DUMP ? TRUE : FALSE); + } ++ ++ ++/* ++ * kexec/kdump generated vmcore files are similar enough in ++ * nature to netdump/diskdump such that most vmcore access ++ * functionality may be borrowed from the equivalent netdump ++ * function. If not, re-work them here. ++ */ ++int ++is_kdump(char *file, ulong source_query) ++{ ++ return is_netdump(file, source_query); ++} ++ ++int ++kdump_init(char *unused, FILE *fptr) ++{ ++ return netdump_init(unused, fptr); ++} ++ ++ulong ++get_kdump_panic_task(void) ++{ ++ return get_netdump_panic_task(); ++} ++ ++int ++read_kdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) ++{ ++ if (XEN_CORE_DUMPFILE() && !XEN_HYPER_MODE()) { ++ if (!(nd->xen_kdump_data->flags & KDUMP_P2M_INIT)) { ++ if (!machdep->xen_kdump_p2m_create) ++ error(FATAL, ++ "xen kdump dumpfiles not supported on this architecture\n"); ++ ++ if ((nd->xen_kdump_data->page = ++ (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, ++ "cannot malloc xen kdump data page\n"); ++ ++ if (!machdep->xen_kdump_p2m_create(nd->xen_kdump_data)) ++ error(FATAL, ++ "cannot create xen kdump pfn-to-mfn mapping\n"); ++ ++ nd->xen_kdump_data->flags |= KDUMP_P2M_INIT; ++ } ++ ++ if ((paddr = xen_kdump_p2m(paddr)) == P2M_FAILURE) ++ return READ_ERROR; ++ } ++ ++ return read_netdump(fd, bufptr, cnt, addr, paddr); ++} ++ ++int ++write_kdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) ++{ ++ return write_netdump(fd, bufptr, cnt, addr, paddr); ++} ++ ++void ++get_kdump_regs(struct bt_info *bt, ulong *eip, ulong *esp) ++{ ++ get_netdump_regs(bt, eip, esp); ++} ++ ++uint ++kdump_page_size(void) ++{ ++ uint pagesz; ++ ++ if (!VMCORE_VALID()) ++ return 0; ++ ++ if (!(pagesz = nd->page_size)) ++ pagesz = (uint)getpagesize(); ++ ++ return pagesz; ++} ++ ++int ++kdump_free_memory(void) ++{ ++ return netdump_free_memory(); ++} ++ ++int ++kdump_memory_used(void) ++{ ++ return netdump_memory_used(); ++} ++ ++int ++kdump_memory_dump(FILE *fp) ++{ ++ return netdump_memory_dump(fp); ++} ++ ++/* ++ * Translate a xen domain's pseudo-physical address into the ++ * xen machine address. Since there's no compression involved, ++ * just the last phys_to_machine_mapping[] page read is cached, ++ * which essentially caches 1024 p2m translations. ++ */ ++static physaddr_t ++xen_kdump_p2m(physaddr_t pseudo) ++{ ++ ulong pfn, mfn_frame; ++ ulong *mfnptr; ++ ulong mfn_idx, frame_idx; ++ physaddr_t paddr; ++ struct xen_kdump_data *xkd = nd->xen_kdump_data; ++ ++ if (pc->curcmd_flags & XEN_MACHINE_ADDR) ++ return pseudo; ++ ++#ifdef IA64 ++ return ia64_xen_kdump_p2m(xkd, pseudo); ++#endif ++ ++ xkd->accesses++; ++ ++ pfn = (ulong)BTOP(pseudo); ++ mfn_idx = pfn / (PAGESIZE()/sizeof(ulong)); ++ frame_idx = pfn % (PAGESIZE()/sizeof(ulong)); ++ if (mfn_idx >= xkd->p2m_frames) ++ return P2M_FAILURE; ++ mfn_frame = xkd->p2m_mfn_frame_list[mfn_idx]; ++ ++ if (mfn_frame == xkd->last_mfn_read) ++ xkd->cache_hits++; ++ else if (read_netdump(0, xkd->page, PAGESIZE(), 0, ++ (physaddr_t)PTOB(mfn_frame)) != PAGESIZE()) ++ return P2M_FAILURE; ++ ++ xkd->last_mfn_read = mfn_frame; ++ ++ mfnptr = ((ulong *)(xkd->page)) + frame_idx; ++ paddr = (physaddr_t)PTOB((ulonglong)(*mfnptr)); ++ paddr |= PAGEOFFSET(pseudo); ++ ++ if (CRASHDEBUG(7)) ++ fprintf(fp, ++ "xen_dump_p2m(%llx): mfn_idx: %ld frame_idx: %ld" ++ " mfn_frame: %lx mfn: %lx => %llx\n", ++ (ulonglong)pseudo, mfn_idx, frame_idx, ++ mfn_frame, *mfnptr, (ulonglong)paddr); ++ ++ return paddr; ++} ++ ++struct vmcore_data * ++get_kdump_vmcore_data(void) ++{ ++ if (!VMCORE_VALID() || !KDUMP_DUMPFILE()) ++ return NULL; ++ ++ return &vmcore_data; ++} ++ ++/* ++ * Override the dom0 p2m mfn in the XEN_ELFNOTE_CRASH_INFO note ++ * in order to initiate a crash session of a guest kernel. ++ */ ++void ++xen_kdump_p2m_mfn(char *arg) ++{ ++ ulong value; ++ int errflag; ++ ++ errflag = 0; ++ value = htol(arg, RETURN_ON_ERROR|QUIET, &errflag); ++ if (!errflag) { ++ xen_kdump_data.p2m_mfn = value; ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "xen_kdump_data.p2m_mfn override: %lx\n", ++ value); ++ } else ++ error(WARNING, "invalid p2m_mfn argument: %s\n", arg); ++} ++ ++/* ++ * Fujitsu dom0/HV sadump-generated dumpfile, which requires ++ * the --p2m_mfn command line argument. ++ */ ++int ++is_sadump_xen(void) ++{ ++ if (xen_kdump_data.p2m_mfn) { ++ if (!XEN_CORE_DUMPFILE()) { ++ pc->flags |= XEN_CORE; ++ nd->xen_kdump_data = &xen_kdump_data; ++ nd->xen_kdump_data->last_mfn_read = UNINITIALIZED; ++ nd->xen_kdump_data->last_pmd_read = UNINITIALIZED; ++ nd->xen_kdump_data->flags |= KDUMP_MFN_LIST; ++ } ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++void ++set_xen_phys_start(char *arg) ++{ ++ ulong value; ++ int errflag = 0; ++ ++ value = htol(arg, RETURN_ON_ERROR|QUIET, &errflag); ++ if (!errflag) ++ xen_kdump_data.xen_phys_start = value; ++ else ++ error(WARNING, "invalid xen_phys_start argument: %s\n", arg); ++} ++ ++ulong ++xen_phys_start(void) ++{ ++ return nd->xen_kdump_data->xen_phys_start; ++} ++ ++int ++xen_major_version(void) ++{ ++ return nd->xen_kdump_data->xen_major_version; ++} ++ ++int ++xen_minor_version(void) ++{ ++ return nd->xen_kdump_data->xen_minor_version; ++} +--- crash/lkcd_dump_v5.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_dump_v5.h 2006-10-11 09:14:35.000000000 -0400 +@@ -1,8 +1,8 @@ + /* lkcd_dump_v5.h - core analysis suite + * + * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 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 +@@ -35,7 +35,7 @@ + #ifndef _DUMP_H + #define _DUMP_H + +-#include ++//#include + + /* define TRUE and FALSE for use in our dump modules */ + #ifndef FALSE +--- crash/main.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/main.c 2009-01-23 14:52:16.000000000 -0500 +@@ -1,8 +1,8 @@ + /* main.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 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 +@@ -16,6 +16,7 @@ + */ + + #include "defs.h" ++#include "xen_hyper_defs.h" + #include + #include + +@@ -23,23 +24,41 @@ + static int is_external_command(void); + static int is_builtin_command(void); + static int is_input_file(void); ++static void check_xen_hyper(void); ++static void show_untrusted_files(void); + + static struct option long_options[] = { +- {"memory_module", 1, 0, 0}, +- {"memory_device", 1, 0, 0}, ++ {"memory_module", required_argument, 0, 0}, ++ {"memory_device", required_argument, 0, 0}, + {"no_kallsyms", 0, 0, 0}, + {"no_modules", 0, 0, 0}, + {"no_namelist_gzip", 0, 0, 0}, +- {"help", 0, 0, 0}, ++ {"help", optional_argument, 0, 'h'}, + {"data_debug", 0, 0, 0}, + {"no_data_debug", 0, 0, 0}, + {"no_crashrc", 0, 0, 0}, + {"no_kmem_cache", 0, 0, 0}, ++ {"kmem_cache_delay", 0, 0, 0}, + {"readnow", 0, 0, 0}, + {"smp", 0, 0, 0}, +- {"machdep", 1, 0, 0}, ++ {"machdep", required_argument, 0, 0}, + {"version", 0, 0, 0}, + {"buildinfo", 0, 0, 0}, ++ {"shadow_page_tables", 0, 0, 0}, ++ {"cpus", required_argument, 0, 0}, ++ {"no_ikconfig", 0, 0, 0}, ++ {"hyper", 0, 0, 0}, ++ {"p2m_mfn", required_argument, 0, 0}, ++ {"xen_phys_start", required_argument, 0, 0}, ++ {"zero_excluded", 0, 0, 0}, ++ {"no_panic", 0, 0, 0}, ++ {"more", 0, 0, 0}, ++ {"less", 0, 0, 0}, ++ {"CRASHPAGER", 0, 0, 0}, ++ {"no_scroll", 0, 0, 0}, ++ {"reloc", required_argument, 0, 0}, ++ {"active", 0, 0, 0}, ++ {"minimal", 0, 0, 0}, + {0, 0, 0, 0} + }; + +@@ -55,7 +74,7 @@ + */ + opterr = 0; + optind = 0; +- while((c = getopt_long(argc, argv, "LgH:h:e:i:sSvc:d:tf", ++ while((c = getopt_long(argc, argv, "Lkgh::e:i:sSvc:d:tfp:m:", + long_options, &option_index)) != -1) { + switch (c) + { +@@ -64,52 +83,55 @@ + "memory_module")) + pc->memory_module = optarg; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "memory_device")) + pc->memory_device = optarg; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "no_kallsyms")) + kt->flags |= NO_KALLSYMS; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "no_modules")) + kt->flags |= NO_MODULE_ACCESS; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, ++ "no_ikconfig")) ++ kt->flags |= NO_IKCONFIG; ++ ++ else if (STREQ(long_options[option_index].name, + "no_namelist_gzip")) + pc->flags |= NAMELIST_NO_GZIP; + +- if (STREQ(long_options[option_index].name, "help")) { +- program_usage(LONG_FORM); +- clean_exit(0); +- } +- +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "data_debug")) + pc->flags |= DATADEBUG; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "no_data_debug")) + pc->flags &= ~DATADEBUG; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "no_kmem_cache")) + vt->flags |= KMEM_CACHE_UNAVAIL; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, ++ "kmem_cache_delay")) ++ vt->flags |= KMEM_CACHE_DELAY; ++ ++ else if (STREQ(long_options[option_index].name, + "readnow")) + pc->flags |= READNOW; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "smp")) + kt->flags |= SMP; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "machdep")) + machdep->cmdline_arg = optarg; + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "version")) { + pc->flags |= VERSION_QUERY; + display_version(); +@@ -117,12 +139,78 @@ + clean_exit(0); + } + +- if (STREQ(long_options[option_index].name, ++ else if (STREQ(long_options[option_index].name, + "buildinfo")) { + dump_build_data(); + clean_exit(0); + } + ++ else if (STREQ(long_options[option_index].name, ++ "shadow_page_tables")) ++ kt->xen_flags |= SHADOW_PAGE_TABLES; ++ ++ else if (STREQ(long_options[option_index].name, "cpus")) ++ kt->cpus_override = optarg; ++ ++ else if (STREQ(long_options[option_index].name, "hyper")) ++ pc->flags |= XEN_HYPER; ++ ++ else if (STREQ(long_options[option_index].name, "p2m_mfn")) ++ xen_kdump_p2m_mfn(optarg); ++ ++ else if (STREQ(long_options[option_index].name, "xen_phys_start")) ++ set_xen_phys_start(optarg); ++ ++ else if (STREQ(long_options[option_index].name, "zero_excluded")) ++ *diskdump_flags |= ZERO_EXCLUDED; ++ ++ else if (STREQ(long_options[option_index].name, "no_panic")) ++ tt->flags |= PANIC_TASK_NOT_FOUND; ++ ++ else if (STREQ(long_options[option_index].name, "more")) { ++ if ((pc->scroll_command != SCROLL_NONE) && ++ file_exists("/bin/more", NULL)) ++ pc->scroll_command = SCROLL_MORE; ++ } ++ ++ else if (STREQ(long_options[option_index].name, "less")) { ++ if ((pc->scroll_command != SCROLL_NONE) && ++ file_exists("/usr/bin/less", NULL)) ++ pc->scroll_command = SCROLL_LESS; ++ } ++ ++ else if (STREQ(long_options[option_index].name, "CRASHPAGER")) { ++ if ((pc->scroll_command != SCROLL_NONE) && ++ CRASHPAGER_valid()) ++ pc->scroll_command = SCROLL_CRASHPAGER; ++ } ++ ++ else if (STREQ(long_options[option_index].name, "no_scroll")) ++ pc->flags &= ~SCROLL; ++ ++ else if (STREQ(long_options[option_index].name, "no_crashrc")) ++ pc->flags |= NOCRASHRC; ++ ++ else if (STREQ(long_options[option_index].name, "active")) ++ tt->flags |= ACTIVE_ONLY; ++ ++ else if (STREQ(long_options[option_index].name, "reloc")) { ++ if (!calculate(optarg, &kt->relocate, NULL, 0)) { ++ error(INFO, "invalid --reloc argument: %s\n", ++ optarg); ++ program_usage(SHORT_FORM); ++ } ++ kt->flags |= RELOC_SET; ++ } ++ ++ else if (STREQ(long_options[option_index].name, "minimal")) ++ pc->flags |= MINIMAL_MODE; ++ ++ else { ++ error(INFO, "internal error: option %s unhandled\n", ++ long_options[option_index].name); ++ program_usage(SHORT_FORM); ++ } + break; + + case 'f': +@@ -133,14 +221,25 @@ + pc->flags |= KERNEL_DEBUG_QUERY; + break; + +- case 'H': +- cmd_usage(optarg, COMPLETE_HELP); +- clean_exit(0); +- + case 'h': +- cmd_usage(optarg, COMPLETE_HELP|PIPE_TO_LESS); ++ /* note: long_getopt's handling of optional arguments is weak. ++ * To it, an optional argument must be part of the same argument ++ * as the flag itself (eg. --help=commands or -hcommands). ++ * We want to accept "--help commands" or "-h commands". ++ * So we must do that part ourselves. ++ */ ++ if (optarg != NULL) ++ cmd_usage(optarg, COMPLETE_HELP|PIPE_TO_SCROLL|MUST_HELP); ++ else if (argv[optind] != NULL && argv[optind][0] != '-') ++ cmd_usage(argv[optind++], COMPLETE_HELP|PIPE_TO_SCROLL|MUST_HELP); ++ else ++ program_usage(LONG_FORM); + clean_exit(0); + ++ case 'k': ++ pc->flags |= KERNTYPES; ++ break; ++ + case 'e': + if (STREQ(optarg, "vi")) + pc->editing_mode = "vi"; +@@ -168,7 +267,7 @@ + case 's': + pc->flags |= SILENT; + pc->flags &= ~SCROLL; +- pc->scroll_command = SCROLL_NONE; ++// pc->scroll_command = SCROLL_NONE; (why?) + break; + + case 'L': +@@ -193,14 +292,18 @@ + set_vas_debug(pc->debug); + break; + ++ case 'p': ++ force_page_size(optarg); ++ break; ++ ++ case 'm': ++ machdep->cmdline_arg = optarg; ++ break; ++ + default: +- if (STREQ(argv[optind-1], "-h")) +- program_usage(LONG_FORM); +- else { +- error(INFO, "invalid option: %s\n", +- argv[optind-1]); +- program_usage(SHORT_FORM); +- } ++ error(INFO, "invalid option: %s\n", ++ argv[optind-1]); ++ program_usage(SHORT_FORM); + } + } + opterr = 1; +@@ -229,7 +332,7 @@ + } else if (!is_readable(argv[optind])) + program_usage(SHORT_FORM); + +- if (is_elf_file(argv[optind])) { ++ if (is_kernel(argv[optind])) { + if (pc->namelist || pc->server_namelist) { + if (!select_namelist(argv[optind])) { + error(INFO, +@@ -261,8 +364,36 @@ + } + pc->flags |= NETDUMP; + pc->dumpfile = argv[optind]; +- pc->readmem = read_netdump; +- pc->writemem = write_netdump; ++ ++ if (is_sadump_xen()) { ++ pc->readmem = read_kdump; ++ pc->writemem = write_kdump; ++ } else { ++ pc->readmem = read_netdump; ++ pc->writemem = write_netdump; ++ } ++ ++ } else if (is_kdump(argv[optind], KDUMP_LOCAL)) { ++ if (pc->flags & MEMORY_SOURCES) { ++ error(INFO, ++ "too many dumpfile arguments\n"); ++ program_usage(SHORT_FORM); ++ } ++ pc->flags |= KDUMP; ++ pc->dumpfile = argv[optind]; ++ pc->readmem = read_kdump; ++ pc->writemem = write_kdump; ++ ++ } else if (is_xendump(argv[optind])) { ++ if (pc->flags & MEMORY_SOURCES) { ++ error(INFO, ++ "too many dumpfile arguments\n"); ++ program_usage(SHORT_FORM); ++ } ++ pc->flags |= XENDUMP; ++ pc->dumpfile = argv[optind]; ++ pc->readmem = read_xendump; ++ pc->writemem = write_xendump; + + } else if (is_diskdump(argv[optind])) { + if (pc->flags & MEMORY_SOURCES) { +@@ -322,6 +453,8 @@ + optind++; + } + ++ check_xen_hyper(); ++ + if (setjmp(pc->main_loop_env)) + clean_exit(1); + +@@ -332,11 +465,11 @@ + buf_init(); + cmdline_init(); + mem_init(); ++ hq_init(); + machdep_init(PRE_SYMTAB); + symtab_init(); ++ paravirt_init(); + machdep_init(PRE_GDB); +- kernel_init(PRE_GDB); +- verify_version(); + datatype_init(); + + /* +@@ -361,17 +494,29 @@ + { + if (!(pc->flags & GDB_INIT)) { + gdb_session_init(); +- kernel_init(POST_GDB); +- machdep_init(POST_GDB); +- vm_init(); +- hq_init(); +- module_init(); +- help_init(); +- task_init(); +- vfs_init(); +- net_init(); +- dev_init(); +- machdep_init(POST_INIT); ++ show_untrusted_files(); ++ if (XEN_HYPER_MODE()) { ++#ifdef XEN_HYPERVISOR_ARCH ++ machdep_init(POST_GDB); ++ xen_hyper_init(); ++ machdep_init(POST_INIT); ++#else ++ error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED); ++#endif ++ } else if (!(pc->flags & MINIMAL_MODE)) { ++ read_in_kernel_config(IKCFG_INIT); ++ kernel_init(); ++ machdep_init(POST_GDB); ++ vm_init(); ++ machdep_init(POST_VM); ++ module_init(); ++ help_init(); ++ task_init(); ++ vfs_init(); ++ net_init(); ++ dev_init(); ++ machdep_init(POST_INIT); ++ } + } else + SIGACTION(SIGINT, restart, &pc->sigaction, NULL); + +@@ -379,11 +524,25 @@ + * Display system statistics and current context. + */ + if (!(pc->flags & SILENT) && !(pc->flags & RUNTIME)) { +- display_sys_stats(); +- show_context(CURRENT_CONTEXT()); +- fprintf(fp, "\n"); ++ if (XEN_HYPER_MODE()) { ++#ifdef XEN_HYPERVISOR_ARCH ++ xen_hyper_display_sys_stats(); ++ xen_hyper_show_vcpu_context(XEN_HYPER_VCPU_LAST_CONTEXT()); ++ fprintf(fp, "\n"); ++#else ++ error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED); ++#endif ++ } else if (!(pc->flags & MINIMAL_MODE)) { ++ display_sys_stats(); ++ show_context(CURRENT_CONTEXT()); ++ fprintf(fp, "\n"); ++ } + } + ++ if (pc->flags & MINIMAL_MODE) ++ error(NOTE, ++ "minimal mode commands: log, dis, rd, sym, eval and exit\n\n"); ++ + pc->flags |= RUNTIME; + + /* +@@ -426,8 +585,17 @@ + + if ((ct = get_command_table_entry(args[0]))) { + if (ct->flags & REFRESH_TASK_TABLE) { +- tt->refresh_task_table(); +- sort_context_array(); ++ if (XEN_HYPER_MODE()) { ++#ifdef XEN_HYPERVISOR_ARCH ++ xen_hyper_refresh_domain_context_space(); ++ xen_hyper_refresh_vcpu_context_space(); ++#else ++ error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED); ++#endif ++ } else { ++ tt->refresh_task_table(); ++ sort_context_array(); ++ } + } + if (!STREQ(pc->curcmd, pc->program_name)) + pc->lastcmd = pc->curcmd; +@@ -451,14 +619,25 @@ + if (is_datatype_command()) + goto reattempt; + +- if (is_gdb_command(TRUE, FAULT_ON_ERROR)) ++ if (!(pc->flags & MINIMAL_MODE) && ++ is_gdb_command(TRUE, FAULT_ON_ERROR)) + goto reattempt; + + if (REMOTE() && remote_execute()) + return; + + pc->curcmd = pc->program_name; +- error(INFO, "command not found: %s\n", args[0]); ++ ++ if (pc->flags & MINIMAL_MODE) ++ error(INFO, ++ "%s: command not available in minimal mode\n" ++ "NOTE: minimal mode commands: log, dis, rd, sym, eval and exit\n", ++ args[0]); ++ else ++ error(INFO, "command not found: %s\n", args[0]); ++ ++ if (pc->curcmd_flags & REPEAT) ++ pc->curcmd_flags &= ~REPEAT; + } + + +@@ -470,8 +649,11 @@ + { + struct command_table_entry *cp; + struct extension_table *ext; ++ ++ if ((pc->flags & MINIMAL_MODE) && !minimal_functions(name)) ++ return NULL; + +- for (cp = &base_command_table[0]; cp->name; cp++) { ++ for (cp = pc->cmd_table; cp->name; cp++) { + if (STREQ(cp->name, name)) + return cp; + } +@@ -591,6 +773,8 @@ + int i; + char *p1; + char buf[BUFSIZE]; ++ char homerc[BUFSIZE]; ++ char localrc[BUFSIZE]; + FILE *afp; + char *program; + +@@ -625,7 +809,8 @@ + machdep->verify_paddr = generic_verify_paddr; + pc->redhat_debug_loc = DEFAULT_REDHAT_DEBUG_LOCATION; + pc->cmdgencur = 0; +- pc->cmdgenspec = ~pc->cmdgencur; ++ pc->cmd_table = linux_command_table; ++ kt->BUG_bytes = -1; + + /* + * Get gdb version before initializing it since this might be one +@@ -637,7 +822,10 @@ + * Set up the default scrolling behavior for terminal output. + */ + if (isatty(fileno(stdout))) { +- if (file_exists("/usr/bin/less", NULL)) { ++ if (CRASHPAGER_valid()) { ++ pc->flags |= SCROLL; ++ pc->scroll_command = SCROLL_CRASHPAGER; ++ } else if (file_exists("/usr/bin/less", NULL)) { + pc->flags |= SCROLL; + pc->scroll_command = SCROLL_LESS; + } else if (file_exists("/bin/more", NULL)) { +@@ -685,11 +873,13 @@ + pc->home = "(unknown)"; + } else + strcpy(pc->home, p1); +- sprintf(buf, "%s/.%src", pc->home, pc->program_name); +- if (!(pc->flags & NOCRASHRC) && file_exists(buf, NULL)) { +- if ((afp = fopen(buf, "r")) == NULL) ++ sprintf(homerc, "%s/.%src", pc->home, pc->program_name); ++ if (!(pc->flags & NOCRASHRC) && file_exists(homerc, NULL)) { ++ if ((afp = fopen(homerc, "r")) == NULL) + error(INFO, "cannot open %s: %s\n", +- buf, strerror(errno)); ++ homerc, strerror(errno)); ++ else if (untrusted_file(afp, homerc)) ++ fclose(afp); + else { + while (fgets(buf, BUFSIZE, afp)) + resolve_rc_cmd(buf, ALIAS_RCHOME); +@@ -698,11 +888,14 @@ + } + } + +- sprintf(buf, ".%src", pc->program_name); +- if (!(pc->flags & NOCRASHRC) && file_exists(buf, NULL)) { +- if ((afp = fopen(buf, "r")) == NULL) ++ sprintf(localrc, ".%src", pc->program_name); ++ if (!same_file(homerc, localrc) && ++ !(pc->flags & NOCRASHRC) && file_exists(localrc, NULL)) { ++ if ((afp = fopen(localrc, "r")) == NULL) + error(INFO, "cannot open %s: %s\n", +- buf, strerror(errno)); ++ localrc, strerror(errno)); ++ else if (untrusted_file(afp, localrc)) ++ fclose(afp); + else { + while (fgets(buf, BUFSIZE, afp)) + resolve_rc_cmd(buf, ALIAS_RCLOCAL); +@@ -712,6 +905,8 @@ + + if (STREQ(pc->editing_mode, "no_mode")) + pc->editing_mode = "vi"; ++ ++ machdep_init(SETUP_ENV); + } + + +@@ -840,13 +1035,22 @@ + if (pc->flags & REM_S390D) + sprintf(&buf[strlen(buf)], + "%sREM_S390D", others++ ? "|" : ""); +- if (pc->flags & NETDUMP) ++ if (pc->flags & NETDUMP) + sprintf(&buf[strlen(buf)], + "%sNETDUMP", others++ ? "|" : ""); ++ if (pc->flags & XENDUMP) ++ sprintf(&buf[strlen(buf)], ++ "%sXENDUMP", others++ ? "|" : ""); ++ if (pc->flags & KDUMP) ++ sprintf(&buf[strlen(buf)], ++ "%sKDUMP", others++ ? "|" : ""); ++ if (pc->flags & SYSRQ) ++ sprintf(&buf[strlen(buf)], ++ "%sSYSRQ", others++ ? "|" : ""); + if (pc->flags & REM_NETDUMP) + sprintf(&buf[strlen(buf)], + "%sREM_NETDUMP", others++ ? "|" : ""); +- if (pc->flags & DISKDUMP) ++ if (pc->flags & DISKDUMP) + sprintf(&buf[strlen(buf)], + "%sDISKDUMP", others++ ? "|" : ""); + if (pc->flags & SYSMAP) +@@ -855,21 +1059,42 @@ + if (pc->flags & SYSMAP_ARG) + sprintf(&buf[strlen(buf)], + "%sSYSMAP_ARG", others++ ? "|" : ""); +- if (pc->flags & DATADEBUG) ++ if (pc->flags & DATADEBUG) + sprintf(&buf[strlen(buf)], + "%sDATADEBUG", others++ ? "|" : ""); +- if (pc->flags & FINDKERNEL) ++ if (pc->flags & FINDKERNEL) + sprintf(&buf[strlen(buf)], + "%sFINDKERNEL", others++ ? "|" : ""); +- if (pc->flags & VERSION_QUERY) ++ if (pc->flags & VERSION_QUERY) + sprintf(&buf[strlen(buf)], + "%sVERSION_QUERY", others++ ? "|" : ""); +- if (pc->flags & READNOW) ++ if (pc->flags & READNOW) + sprintf(&buf[strlen(buf)], + "%sREADNOW", others++ ? "|" : ""); +- if (pc->flags & NOCRASHRC) ++ if (pc->flags & NOCRASHRC) + sprintf(&buf[strlen(buf)], + "%sNOCRASHRC", others++ ? "|" : ""); ++ if (pc->flags & INIT_IFILE) ++ sprintf(&buf[strlen(buf)], ++ "%sINIT_IFILE", others++ ? "|" : ""); ++ if (pc->flags & XEN_HYPER) ++ sprintf(&buf[strlen(buf)], ++ "%sXEN_HYPER", others++ ? "|" : ""); ++ if (pc->flags & XEN_CORE) ++ sprintf(&buf[strlen(buf)], ++ "%sXEN_CORE", others++ ? "|" : ""); ++ if (pc->flags & PLEASE_WAIT) ++ sprintf(&buf[strlen(buf)], ++ "%sPLEASE_WAIT", others++ ? "|" : ""); ++ if (pc->flags & IFILE_ERROR) ++ sprintf(&buf[strlen(buf)], ++ "%sIFILE_ERROR", others++ ? "|" : ""); ++ if (pc->flags & MINIMAL_MODE) ++ sprintf(&buf[strlen(buf)], ++ "%sMINIMAL_MODE", others++ ? "|" : ""); ++ if (pc->flags & CRASHBUILTIN) ++ sprintf(&buf[strlen(buf)], ++ "%sCRASHBUILTIN", others++ ? "|" : ""); + + if (pc->flags) + strcat(buf, ")"); +@@ -933,10 +1158,36 @@ + fprintf(fp, " ifile_pipe: %lx\n", (ulong)pc->ifile_pipe); + fprintf(fp, " ifile_ofile: %lx\n", (ulong)pc->ifile_ofile); + fprintf(fp, " input_file: %s\n", pc->input_file); +- fprintf(fp, " scroll_command: %s\n", +- pc->scroll_command == SCROLL_NONE ? "(none)" : +- pc->scroll_command == SCROLL_LESS ? +- "/usr/bin/less" : "/bin/more"); ++ fprintf(fp, "ifile_in_progress: %lx (", pc->ifile_in_progress); ++ others = 0; ++ if (pc->ifile_in_progress & RCHOME_IFILE) ++ fprintf(fp, "%sRCHOME_IFILE", others++ ? "|" : ""); ++ if (pc->ifile_in_progress & RCLOCAL_IFILE) ++ fprintf(fp, "%sRCLOCAL_IFILE", others++ ? "|" : ""); ++ if (pc->ifile_in_progress & CMDLINE_IFILE) ++ fprintf(fp, "%sCMDLINE_IFILE", others++ ? "|" : ""); ++ if (pc->ifile_in_progress & RUNTIME_IFILE) ++ fprintf(fp, "%sRUNTIME_IFILE", others++ ? "|" : ""); ++ fprintf(fp, ")\n"); ++ fprintf(fp, " ifile_offset: %lld\n", (ulonglong)pc->ifile_offset); ++ fprintf(fp, "runtime_ifile_cmd: %s\n", pc->runtime_ifile_cmd ? ++ pc->runtime_ifile_cmd : "(unused)"); ++ fprintf(fp, " scroll_command: "); ++ switch (pc->scroll_command) ++ { ++ case SCROLL_NONE: ++ fprintf(fp, "SCROLL_NONE\n"); ++ break; ++ case SCROLL_LESS: ++ fprintf(fp, "SCROLL_LESS\n"); ++ break; ++ case SCROLL_MORE: ++ fprintf(fp, "SCROLL_MORE\n"); ++ break; ++ case SCROLL_CRASHPAGER: ++ fprintf(fp, "SCROLL_CRASHPAGER (%s)\n", getenv("CRASHPAGER")); ++ break; ++ } + + buf[0] = NULLCHAR; + fprintf(fp, " redirect: %lx ", pc->redirect); +@@ -1008,6 +1259,8 @@ + fprintf(fp, " tmp_fp: %lx\n", (ulong)pc->tmp_fp); + fprintf(fp, " tmpfile2: %lx\n", (ulong)pc->tmpfile2); + ++ fprintf(fp, " cmd_table: %s\n", XEN_HYPER_MODE() ? ++ "xen_hyper_command_table" : "linux_command_table"); + fprintf(fp, " curcmd: %s\n", pc->curcmd); + fprintf(fp, " lastcmd: %s\n", pc->lastcmd); + fprintf(fp, " cur_gdb_cmd: %d %s\n", pc->cur_gdb_cmd, +@@ -1016,7 +1269,30 @@ + gdb_command_string(pc->last_gdb_cmd, buf, FALSE)); + fprintf(fp, " cur_req: %lx\n", (ulong)pc->cur_req); + fprintf(fp, " cmdgencur: %ld\n", pc->cmdgencur); +- fprintf(fp, " cmdgenspec: %ld\n", pc->cmdgenspec); ++ fprintf(fp, " curcmd_flags: %lx (", pc->curcmd_flags); ++ others = 0; ++ if (pc->curcmd_flags & XEN_MACHINE_ADDR) ++ fprintf(fp, "%sXEN_MACHINE_ADDR", others ? "|" : ""); ++ if (pc->curcmd_flags & REPEAT) ++ fprintf(fp, "%sREPEAT", others ? "|" : ""); ++ if (pc->curcmd_flags & IDLE_TASK_SHOWN) ++ fprintf(fp, "%sIDLE_TASK_SHOWN", others ? "|" : ""); ++ if (pc->curcmd_flags & TASK_SPECIFIED) ++ fprintf(fp, "%sTASK_SPECIFIED", others ? "|" : ""); ++ if (pc->curcmd_flags & MEMTYPE_UVADDR) ++ fprintf(fp, "%sMEMTYPE_UVADDR", others ? "|" : ""); ++ if (pc->curcmd_flags & MEMTYPE_FILEADDR) ++ fprintf(fp, "%sMEMTYPE_FILEADDR", others ? "|" : ""); ++ if (pc->curcmd_flags & HEADER_PRINTED) ++ fprintf(fp, "%sHEADER_PRINTED", others ? "|" : ""); ++ if (pc->curcmd_flags & BAD_INSTRUCTION) ++ fprintf(fp, "%sBAD_INSTRUCTION", others ? "|" : ""); ++ if (pc->curcmd_flags & UD2A_INSTRUCTION) ++ fprintf(fp, "%sUD2A_INSTRUCTION", others ? "|" : ""); ++ if (pc->curcmd_flags & IRQ_IN_USE) ++ fprintf(fp, "%sIRQ_IN_USE", others ? "|" : ""); ++ fprintf(fp, ")\n"); ++ fprintf(fp, " curcmd_private: %llx\n", pc->curcmd_private); + fprintf(fp, " sigint_cnt: %d\n", pc->sigint_cnt); + fprintf(fp, " sigaction: %lx\n", (ulong)&pc->sigaction); + fprintf(fp, " gdb_sigaction: %lx\n", (ulong)&pc->gdb_sigaction); +@@ -1051,8 +1327,16 @@ + fprintf(fp, " readmem: read_daemon()\n"); + else if (pc->readmem == read_netdump) + fprintf(fp, " readmem: read_netdump()\n"); ++ else if (pc->readmem == read_xendump) ++ fprintf(fp, " readmem: read_xendump()\n"); ++ else if (pc->readmem == read_kdump) ++ fprintf(fp, " readmem: read_kdump()\n"); + else if (pc->readmem == read_memory_device) + fprintf(fp, " readmem: read_memory_device()\n"); ++ else if (pc->readmem == read_xendump_hyper) ++ fprintf(fp, " readmem: read_xendump_hyper()\n"); ++ else if (pc->readmem == read_diskdump) ++ fprintf(fp, " readmem: read_diskdump()\n"); + else + fprintf(fp, " readmem: %lx\n", (ulong)pc->readmem); + if (pc->writemem == write_dev_mem) +@@ -1065,8 +1349,14 @@ + fprintf(fp, " writemem: write_daemon()\n"); + else if (pc->writemem == write_netdump) + fprintf(fp, " writemem: write_netdump()\n"); ++ else if (pc->writemem == write_xendump) ++ fprintf(fp, " writemem: write_xendump()\n"); ++ else if (pc->writemem == write_kdump) ++ fprintf(fp, " writemem: write_kdump()\n"); + else if (pc->writemem == write_memory_device) + fprintf(fp, " writemem: write_memory_device()\n"); ++ else if (pc->writemem == write_diskdump) ++ fprintf(fp, " writemem: write_diskdump()\n"); + else + fprintf(fp, " writemem: %lx\n", (ulong)pc->writemem); + +@@ -1100,3 +1390,71 @@ + + exit(status); + } ++ ++/* ++ * Check whether this session is for xen hypervisor analysis. ++ */ ++static void ++check_xen_hyper(void) ++{ ++ if (!pc->namelist) ++ return; ++ ++ if (!XEN_HYPER_MODE()) { ++ if (STRNEQ(basename(pc->namelist), "xen-syms")) ++ pc->flags |= XEN_HYPER; ++ else ++ return; ++ } ++ ++#ifdef XEN_HYPERVISOR_ARCH ++ pc->cmd_table = xen_hyper_command_table; ++ if (pc->flags & XENDUMP) ++ pc->readmem = read_xendump_hyper; ++#else ++ error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED); ++#endif ++} ++ ++/* ++ * Reject untrusted .crashrc, $HOME/.crashrc, ++ * .gdbinit, and $HOME/.gdbinit files. ++ */ ++static char *untrusted_file_list[4] = { 0 }; ++ ++int ++untrusted_file(FILE *filep, char *filename) ++{ ++ struct stat sbuf; ++ int i; ++ ++ if (filep && (fstat(fileno(filep), &sbuf) == 0) && ++ (sbuf.st_uid == getuid()) && !(sbuf.st_mode & S_IWOTH)) ++ return FALSE; ++ ++ for (i = 0; i < 4; i++) { ++ if (!untrusted_file_list[i]) { ++ untrusted_file_list[i] = strdup(filename); ++ break; ++ } ++ } ++ ++ return TRUE; ++} ++ ++static void ++show_untrusted_files(void) ++{ ++ int i, cnt; ++ ++ for (i = cnt = 0; i < 4; i++) { ++ if (untrusted_file_list[i]) { ++ error(WARNING, "not using untrusted file: \"%s\"\n", ++ untrusted_file_list[i]); ++ free(untrusted_file_list[i]); ++ cnt++; ++ } ++ } ++ if (cnt) ++ fprintf(fp, "\n"); ++} +--- crash/xen_hyper.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/xen_hyper.c 2008-12-03 12:03:24.000000000 -0500 +@@ -0,0 +1,2019 @@ ++/* ++ * xen_hyper.c ++ * ++ * Portions Copyright (C) 2006-2007 Fujitsu Limited ++ * Portions Copyright (C) 2006-2007 VA Linux Systems Japan K.K. ++ * ++ * Authors: Itsuro Oda ++ * Fumihiko Kakuma ++ * ++ * This file is part of Xencrash. ++ * ++ * Xencrash 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 (version 2 of the License). ++ * ++ * Xencrash 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with Xencrash; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "defs.h" ++ ++#ifdef XEN_HYPERVISOR_ARCH ++#include "xen_hyper_defs.h" ++ ++static void xen_hyper_schedule_init(void); ++ ++/* ++ * Do initialization for Xen Hyper system here. ++ */ ++void ++xen_hyper_init(void) ++{ ++ char *buf; ++#if defined(X86) || defined(X86_64) ++ long member_offset; ++#endif ++ ++ if (machine_type("X86_64") && ++ symbol_exists("xen_phys_start") && !xen_phys_start()) ++ error(WARNING, ++ "This hypervisor is relocatable; if initialization fails below, try\n" ++ " using the \"--xen_phys_start
\" command line option.\n\n"); ++ ++ if (symbol_exists("crashing_cpu")) { ++ get_symbol_data("crashing_cpu", sizeof(xht->crashing_cpu), ++ &xht->crashing_cpu); ++ } else { ++ xht->crashing_cpu = XEN_HYPER_PCPU_ID_INVALID; ++ } ++ machdep->get_smp_cpus(); ++ machdep->memory_size(); ++ ++#ifdef IA64 ++ if (symbol_exists("__per_cpu_offset")) { ++ xht->flags |= XEN_HYPER_SMP; ++ if((xht->__per_cpu_offset = malloc(sizeof(ulong) * XEN_HYPER_MAX_CPUS())) == NULL) { ++ error(FATAL, "cannot malloc __per_cpu_offset space.\n"); ++ } ++ if (!readmem(symbol_value("__per_cpu_offset"), KVADDR, ++ xht->__per_cpu_offset, sizeof(ulong) * XEN_HYPER_MAX_CPUS(), ++ "__per_cpu_offset", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read __per_cpu_offset.\n"); ++ } ++ } ++#endif ++ ++#if defined(X86) || defined(X86_64) ++ if (symbol_exists("__per_cpu_shift")) { ++ xht->percpu_shift = (int)symbol_value("__per_cpu_shift"); ++ } else if (xen_major_version() >= 3 && xen_minor_version() >= 3) { ++ xht->percpu_shift = 13; ++ } else { ++ xht->percpu_shift = 12; ++ } ++ member_offset = MEMBER_OFFSET("cpuinfo_x86", "x86_model_id"); ++ buf = GETBUF(XEN_HYPER_SIZE(cpuinfo_x86)); ++ if (xen_hyper_test_pcpu_id(XEN_HYPER_CRASHING_CPU())) { ++ xen_hyper_x86_fill_cpu_data(XEN_HYPER_CRASHING_CPU(), buf); ++ } else { ++ xen_hyper_x86_fill_cpu_data(xht->cpu_idxs[0], buf); ++ } ++ strncpy(xht->utsname.machine, (char *)(buf + member_offset), ++ sizeof(xht->utsname.machine)-1); ++ FREEBUF(buf); ++#elif defined(IA64) ++ buf = GETBUF(XEN_HYPER_SIZE(cpuinfo_ia64)); ++ if (xen_hyper_test_pcpu_id(XEN_HYPER_CRASHING_CPU())) { ++ xen_hyper_ia64_fill_cpu_data(XEN_HYPER_CRASHING_CPU(), buf); ++ } else { ++ xen_hyper_ia64_fill_cpu_data(xht->cpu_idxs[0], buf); ++ } ++ strncpy(xht->utsname.machine, (char *)(buf + XEN_HYPER_OFFSET(cpuinfo_ia64_vendor)), ++ sizeof(xht->utsname.machine)-1); ++ FREEBUF(buf); ++#endif ++ ++#ifndef IA64 ++ XEN_HYPER_STRUCT_SIZE_INIT(note_buf_t, "note_buf_t"); ++ XEN_HYPER_STRUCT_SIZE_INIT(crash_note_t, "crash_note_t"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_t_core, "crash_note_t", "core"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_t_xen, "crash_note_t", "xen"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_t_xen_regs, "crash_note_t", "xen_regs"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_t_xen_info, "crash_note_t", "xen_info"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(crash_note_core_t, "crash_note_core_t"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_core_t_note, "crash_note_core_t", "note"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_core_t_desc, "crash_note_core_t", "desc"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(crash_note_xen_t, "crash_note_xen_t"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_xen_t_note, "crash_note_xen_t", "note"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_xen_t_desc, "crash_note_xen_t", "desc"); ++ XEN_HYPER_STRUCT_SIZE_INIT(crash_note_xen_core_t, "crash_note_xen_core_t"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_xen_core_t_note, "crash_note_xen_core_t", "note"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_xen_core_t_desc, "crash_note_xen_core_t", "desc"); ++ XEN_HYPER_STRUCT_SIZE_INIT(crash_note_xen_info_t, "crash_note_xen_info_t"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_xen_info_t_note, "crash_note_xen_info_t", "note"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(crash_note_xen_info_t_desc, "crash_note_xen_info_t", "desc"); ++ XEN_HYPER_STRUCT_SIZE_INIT(crash_xen_core_t, "crash_xen_core_t"); ++ XEN_HYPER_STRUCT_SIZE_INIT(crash_xen_info_t, "crash_xen_info_t"); ++ XEN_HYPER_STRUCT_SIZE_INIT(xen_crash_xen_regs_t, "xen_crash_xen_regs_t"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(ELF_Prstatus,"ELF_Prstatus"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_info, "ELF_Prstatus", "pr_info"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_cursig, "ELF_Prstatus", "pr_cursig"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_sigpend, "ELF_Prstatus", "pr_sigpend"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_sighold, "ELF_Prstatus", "pr_sighold"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_pid, "ELF_Prstatus", "pr_pid"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_ppid, "ELF_Prstatus", "pr_ppid"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_pgrp, "ELF_Prstatus", "pr_pgrp"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_sid, "ELF_Prstatus", "pr_sid"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_utime, "ELF_Prstatus", "pr_utime"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_stime, "ELF_Prstatus", "pr_stime"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_cutime, "ELF_Prstatus", "pr_cutime"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_cstime, "ELF_Prstatus", "pr_cstime"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_reg, "ELF_Prstatus", "pr_reg"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Prstatus_pr_fpvalid, "ELF_Prstatus", "pr_fpvalid"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Timeval_tv_sec, "ELF_Timeval", "tv_sec"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(ELF_Timeval_tv_usec, "ELF_Timeval", "tv_usec"); ++ XEN_HYPER_STRUCT_SIZE_INIT(ELF_Signifo,"ELF_Signifo"); ++ XEN_HYPER_STRUCT_SIZE_INIT(ELF_Gregset,"ELF_Gregset"); ++ XEN_HYPER_STRUCT_SIZE_INIT(ELF_Timeval,"ELF_Timeval"); ++#endif ++ XEN_HYPER_STRUCT_SIZE_INIT(domain, "domain"); ++ XEN_HYPER_STRUCT_SIZE_INIT(vcpu, "vcpu"); ++#ifndef IA64 ++ XEN_HYPER_STRUCT_SIZE_INIT(cpu_info, "cpu_info"); ++#endif ++ XEN_HYPER_STRUCT_SIZE_INIT(cpu_user_regs, "cpu_user_regs"); ++ ++ xht->idle_vcpu_size = get_array_length("idle_vcpu", NULL, 0); ++ xht->idle_vcpu_array = (ulong *)malloc(xht->idle_vcpu_size * sizeof(ulong)); ++ if (xht->idle_vcpu_array == NULL) { ++ error(FATAL, "cannot malloc idle_vcpu_array space.\n"); ++ } ++ if (!readmem(symbol_value("idle_vcpu"), KVADDR, xht->idle_vcpu_array, ++ xht->idle_vcpu_size * sizeof(ulong), "idle_vcpu_array", ++ RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read idle_vcpu array.\n"); ++ } ++ ++ /* ++ * Do some initialization. ++ */ ++#ifndef IA64 ++ xen_hyper_dumpinfo_init(); ++#endif ++ xhmachdep->pcpu_init(); ++ xen_hyper_domain_init(); ++ xen_hyper_vcpu_init(); ++ xen_hyper_misc_init(); ++ /* ++ * xen_hyper_post_init() have to be called after all initialize ++ * functions finished. ++ */ ++ xen_hyper_post_init(); ++} ++ ++/* ++ * Do initialization for Domain of Xen Hyper system here. ++ */ ++void ++xen_hyper_domain_init(void) ++{ ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_domain_id, "domain", "domain_id"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_tot_pages, "domain", "tot_pages"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_max_pages, "domain", "max_pages"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_xenheap_pages, "domain", "xenheap_pages"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_shared_info, "domain", "shared_info"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_sched_priv, "domain", "sched_priv"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_next_in_list, "domain", "next_in_list"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_domain_flags, "domain", "domain_flags"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_evtchn, "domain", "evtchn"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_is_hvm, "domain", "is_hvm"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_is_privileged, "domain", "is_privileged"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_debugger_attached, "domain", "debugger_attached"); ++ ++ /* ++ * Will be removed in Xen 4.4 (hg ae9b223a675d), ++ * need to check that with XEN_HYPER_VALID_MEMBER() before using ++ */ ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_is_polling, "domain", "is_polling"); ++ ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_is_dying, "domain", "is_dying"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_is_paused_by_controller, "domain", "is_paused_by_controller"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_is_shutting_down, "domain", "is_shutting_down"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_is_shut_down, "domain", "is_shut_down"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_vcpu, "domain", "vcpu"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(domain_arch, "domain", "arch"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(arch_shared_info, "arch_shared_info"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(arch_shared_info_max_pfn, "arch_shared_info", "max_pfn"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(arch_shared_info_pfn_to_mfn_frame_list_list, "arch_shared_info", "pfn_to_mfn_frame_list_list"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(arch_shared_info_nmi_reason, "arch_shared_info", "nmi_reason"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(shared_info, "shared_info"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(shared_info_vcpu_info, "shared_info", "vcpu_info"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(shared_info_evtchn_pending, "shared_info", "evtchn_pending"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(shared_info_evtchn_mask, "shared_info", "evtchn_mask"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(shared_info_arch, "shared_info", "arch"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(arch_domain, "arch_domain"); ++#ifdef IA64 ++ XEN_HYPER_MEMBER_OFFSET_INIT(arch_domain_mm, "arch_domain", "mm"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(mm_struct, "mm_struct"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(mm_struct_pgd, "mm_struct", "pgd"); ++#endif ++ ++ if((xhdt->domain_struct = malloc(XEN_HYPER_SIZE(domain))) == NULL) { ++ error(FATAL, "cannot malloc domain struct space.\n"); ++ } ++ if((xhdt->domain_struct_verify = malloc(XEN_HYPER_SIZE(domain))) == NULL) { ++ error(FATAL, "cannot malloc domain struct space to verification.\n"); ++ } ++ xen_hyper_refresh_domain_context_space(); ++ xhdt->flags |= XEN_HYPER_DOMAIN_F_INIT; ++} ++ ++/* ++ * Do initialization for vcpu of Xen Hyper system here. ++ */ ++void ++xen_hyper_vcpu_init(void) ++{ ++ XEN_HYPER_STRUCT_SIZE_INIT(timer, "timer"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(timer_expires, "timer", "expires"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(timer_cpu, "timer", "cpu"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(timer_function, "timer", "function"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(timer_data, "timer", "data"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(timer_heap_offset, "timer", "heap_offset"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(timer_killed, "timer", "killed"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(vcpu_runstate_info, "vcpu_runstate_info"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_runstate_info_state, "vcpu_runstate_info", "state"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_runstate_info_state_entry_time, "vcpu_runstate_info", "state_entry_time"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_runstate_info_time, "vcpu_runstate_info", "time"); ++ ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_vcpu_id, "vcpu", "vcpu_id"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_processor, "vcpu", "processor"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_vcpu_info, "vcpu", "vcpu_info"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_domain, "vcpu", "domain"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_next_in_list, "vcpu", "next_in_list"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_timer, "vcpu", "timer"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_sleep_tick, "vcpu", "sleep_tick"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_poll_timer, "vcpu", "poll_timer"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_sched_priv, "vcpu", "sched_priv"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_runstate, "vcpu", "runstate"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_runstate_guest, "vcpu", "runstate_guest"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_vcpu_flags, "vcpu", "vcpu_flags"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_pause_count, "vcpu", "pause_count"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_virq_to_evtchn, "vcpu", "virq_to_evtchn"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_cpu_affinity, "vcpu", "cpu_affinity"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_nmi_addr, "vcpu", "nmi_addr"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_vcpu_dirty_cpumask, "vcpu", "vcpu_dirty_cpumask"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(vcpu_arch, "vcpu", "arch"); ++ ++#ifdef IA64 ++ XEN_HYPER_ASSIGN_OFFSET(vcpu_thread_ksp) = ++ MEMBER_OFFSET("vcpu", "arch") + MEMBER_OFFSET("arch_vcpu", "_thread") + ++ MEMBER_OFFSET("thread_struct", "ksp"); ++#endif ++ ++ if((xhvct->vcpu_struct = malloc(XEN_HYPER_SIZE(vcpu))) == NULL) { ++ error(FATAL, "cannot malloc vcpu struct space.\n"); ++ } ++ if((xhvct->vcpu_struct_verify = malloc(XEN_HYPER_SIZE(vcpu))) == NULL) { ++ error(FATAL, "cannot malloc vcpu struct space to verification.\n"); ++ } ++ ++ xen_hyper_refresh_vcpu_context_space(); ++ xhvct->flags |= XEN_HYPER_VCPU_F_INIT; ++ xhvct->idle_vcpu = symbol_value("idle_vcpu"); ++} ++ ++/* ++ * Do initialization for pcpu of Xen Hyper system here. ++ */ ++#if defined(X86) || defined(X86_64) ++void ++xen_hyper_x86_pcpu_init(void) ++{ ++ ulong cpu_info; ++ ulong init_tss_base, init_tss; ++ ulong sp; ++ struct xen_hyper_pcpu_context *pcc; ++ char *buf, *bp; ++ int i, cpuid; ++ ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_info_guest_cpu_user_regs, "cpu_info", "guest_cpu_user_regs"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_info_processor_id, "cpu_info", "processor_id"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpu_info_current_vcpu, "cpu_info", "current_vcpu"); ++ ++ if((xhpct->pcpu_struct = malloc(XEN_HYPER_SIZE(cpu_info))) == NULL) { ++ error(FATAL, "cannot malloc pcpu struct space.\n"); ++ } ++ ++ /* get physical cpu context */ ++ xen_hyper_alloc_pcpu_context_space(XEN_HYPER_MAX_CPUS()); ++ init_tss_base = symbol_value("init_tss"); ++ buf = GETBUF(XEN_HYPER_SIZE(tss_struct)); ++ for_cpu_indexes(i, cpuid) ++ { ++ init_tss = init_tss_base + XEN_HYPER_SIZE(tss_struct) * cpuid; ++ if (!readmem(init_tss, KVADDR, buf, ++ XEN_HYPER_SIZE(tss_struct), "init_tss", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read init_tss.\n"); ++ } ++ if (machine_type("X86")) { ++ sp = ULONG(buf + XEN_HYPER_OFFSET(tss_struct_esp0)); ++ } else if (machine_type("X86_64")) { ++ sp = ULONG(buf + XEN_HYPER_OFFSET(tss_struct_rsp0)); ++ } ++ cpu_info = XEN_HYPER_GET_CPU_INFO(sp); ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "sp=%lx, cpu_info=%lx\n", sp, cpu_info); ++ } ++ if(!(bp = xen_hyper_read_pcpu(cpu_info))) { ++ error(FATAL, "cannot read cpu_info.\n"); ++ } ++ pcc = &xhpct->context_array[cpuid]; ++ xen_hyper_store_pcpu_context(pcc, cpu_info, bp); ++ xen_hyper_store_pcpu_context_tss(pcc, init_tss, buf); ++ } ++ FREEBUF(buf); ++} ++ ++#elif defined(IA64) ++void ++xen_hyper_ia64_pcpu_init(void) ++{ ++ struct xen_hyper_pcpu_context *pcc; ++ int i, cpuid; ++ ++ /* get physical cpu context */ ++ xen_hyper_alloc_pcpu_context_space(XEN_HYPER_MAX_CPUS()); ++ for_cpu_indexes(i, cpuid) ++ { ++ pcc = &xhpct->context_array[cpuid]; ++ pcc->processor_id = cpuid; ++ } ++} ++#endif ++ ++/* ++ * Do initialization for some miscellaneous thing ++ * of Xen Hyper system here. ++ */ ++void ++xen_hyper_misc_init(void) ++{ ++ XEN_HYPER_STRUCT_SIZE_INIT(schedule_data, "schedule_data"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(schedule_data_schedule_lock, "schedule_data", "schedule_lock"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(schedule_data_curr, "schedule_data", "curr"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(schedule_data_idle, "schedule_data", "idle"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(schedule_data_sched_priv, "schedule_data", "sched_priv"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(schedule_data_s_timer, "schedule_data", "s_timer"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(schedule_data_tick, "schedule_data", "tick"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(scheduler, "scheduler"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_name, "scheduler", "name"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_opt_name, "scheduler", "opt_name"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_sched_id, "scheduler", "sched_id"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_init, "scheduler", "init"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_tick, "scheduler", "tick"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_init_vcpu, "scheduler", "init_vcpu"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_destroy_domain, "scheduler", "destroy_domain"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_sleep, "scheduler", "sleep"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_wake, "scheduler", "wake"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_set_affinity, "scheduler", "set_affinity"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_do_schedule, "scheduler", "do_schedule"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_adjust, "scheduler", "adjust"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_dump_settings, "scheduler", "dump_settings"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(scheduler_dump_cpu_state, "scheduler", "dump_cpu_state"); ++ ++ xen_hyper_schedule_init(); ++} ++ ++/* ++ * Do initialization for scheduler of Xen Hyper system here. ++ */ ++#define XEN_HYPER_SCHEDULERS_ARRAY_CNT 10 ++#define XEN_HYPER_SCHEDULER_NAME 1024 ++ ++static void ++xen_hyper_schedule_init(void) ++{ ++ ulong addr, opt_sched, schedulers, opt_name; ++ long scheduler_opt_name; ++ long schedulers_buf[XEN_HYPER_SCHEDULERS_ARRAY_CNT]; ++ struct xen_hyper_sched_context *schc; ++ char *buf; ++ char opt_name_buf[XEN_HYPER_OPT_SCHED_SIZE]; ++ int i, cpuid, flag; ++ ++ /* get scheduler information */ ++ if((xhscht->scheduler_struct = ++ malloc(XEN_HYPER_SIZE(scheduler))) == NULL) { ++ error(FATAL, "cannot malloc scheduler struct space.\n"); ++ } ++ buf = GETBUF(XEN_HYPER_SCHEDULER_NAME); ++ opt_sched = symbol_value("opt_sched"); ++ if (!readmem(opt_sched, KVADDR, xhscht->opt_sched, ++ XEN_HYPER_OPT_SCHED_SIZE, "opt_sched,", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read opt_sched,.\n"); ++ } ++ schedulers = symbol_value("schedulers"); ++ scheduler_opt_name = XEN_HYPER_OFFSET(scheduler_opt_name); ++ addr = schedulers; ++ while (xhscht->name == NULL) { ++ if (!readmem(addr, KVADDR, schedulers_buf, ++ sizeof(long) * XEN_HYPER_SCHEDULERS_ARRAY_CNT, ++ "schedulers", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read schedulers.\n"); ++ } ++ for (i = 0; i < XEN_HYPER_SCHEDULERS_ARRAY_CNT; i++) { ++ if (schedulers_buf[i] == 0) { ++ error(FATAL, "schedule data not found.\n"); ++ } ++ if (!readmem(schedulers_buf[i], KVADDR, ++ xhscht->scheduler_struct, XEN_HYPER_SIZE(scheduler), ++ "scheduler", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read scheduler.\n"); ++ } ++ opt_name = ULONG(xhscht->scheduler_struct + ++ scheduler_opt_name); ++ if (!readmem(opt_name, KVADDR, opt_name_buf, ++ XEN_HYPER_OPT_SCHED_SIZE, "opt_name", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read opt_name.\n"); ++ } ++ if (strncmp(xhscht->opt_sched, opt_name_buf, ++ XEN_HYPER_OPT_SCHED_SIZE)) ++ continue; ++ xhscht->scheduler = schedulers_buf[i]; ++ xhscht->sched_id = INT(xhscht->scheduler_struct + ++ XEN_HYPER_OFFSET(scheduler_sched_id)); ++ addr = ULONG(xhscht->scheduler_struct + ++ XEN_HYPER_OFFSET(scheduler_name)); ++ if (!readmem(addr, KVADDR, buf, XEN_HYPER_SCHEDULER_NAME, ++ "scheduler_name", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read scheduler_name.\n"); ++ } ++ if (strlen(buf) >= XEN_HYPER_SCHEDULER_NAME) { ++ error(FATAL, "cannot read scheduler_name.\n"); ++ } ++ if((xhscht->name = malloc(strlen(buf) + 1)) == NULL) { ++ error(FATAL, "cannot malloc scheduler_name space.\n"); ++ } ++ BZERO(xhscht->name, strlen(buf) + 1); ++ strncpy(xhscht->name, buf, strlen(buf)); ++ break; ++ } ++ addr += sizeof(long) * XEN_HYPER_SCHEDULERS_ARRAY_CNT; ++ } ++ FREEBUF(buf); ++ ++ /* get schedule_data information */ ++ if((xhscht->sched_context_array = ++ malloc(sizeof(struct xen_hyper_sched_context) * XEN_HYPER_MAX_CPUS())) == NULL) { ++ error(FATAL, "cannot malloc xen_hyper_sched_context struct space.\n"); ++ } ++ BZERO(xhscht->sched_context_array, ++ sizeof(struct xen_hyper_sched_context) * XEN_HYPER_MAX_CPUS()); ++ buf = GETBUF(XEN_HYPER_SIZE(schedule_data)); ++ if (symbol_exists("per_cpu__schedule_data")) { ++ addr = symbol_value("per_cpu__schedule_data"); ++ flag = TRUE; ++ } else { ++ addr = symbol_value("schedule_data"); ++ flag = FALSE; ++ } ++ for_cpu_indexes(i, cpuid) ++ { ++ schc = &xhscht->sched_context_array[cpuid]; ++ if (flag) { ++ schc->schedule_data = ++ xen_hyper_per_cpu(addr, i); ++ } else { ++ schc->schedule_data = addr + ++ XEN_HYPER_SIZE(schedule_data) * i; ++ } ++ if (!readmem(schc->schedule_data, ++ KVADDR, buf, XEN_HYPER_SIZE(schedule_data), ++ "schedule_data", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read schedule_data.\n"); ++ } ++ schc->cpu_id = cpuid; ++ schc->curr = ULONG(buf + XEN_HYPER_OFFSET(schedule_data_curr)); ++ schc->idle = ULONG(buf + XEN_HYPER_OFFSET(schedule_data_idle)); ++ schc->sched_priv = ++ ULONG(buf + XEN_HYPER_OFFSET(schedule_data_sched_priv)); ++ if (XEN_HYPER_VALID_MEMBER(schedule_data_tick)) ++ schc->tick = ULONG(buf + XEN_HYPER_OFFSET(schedule_data_tick)); ++ } ++ FREEBUF(buf); ++} ++ ++/* ++ * This should be called after all initailize process finished. ++ */ ++void ++xen_hyper_post_init(void) ++{ ++ struct xen_hyper_pcpu_context *pcc; ++ int i, cpuid; ++ ++ /* set current vcpu to pcpu context */ ++ for_cpu_indexes(i, cpuid) ++ { ++ pcc = &xhpct->context_array[cpuid]; ++ if (!pcc->current_vcpu) { ++ pcc->current_vcpu = ++ xen_hyper_get_active_vcpu_from_pcpuid(cpuid); ++ } ++ } ++ ++ /* set pcpu last */ ++ if (!(xhpct->last = ++ xen_hyper_id_to_pcpu_context(XEN_HYPER_CRASHING_CPU()))) { ++ xhpct->last = &xhpct->context_array[xht->cpu_idxs[0]]; ++ } ++ ++ /* set vcpu last */ ++ if (xhpct->last) { ++ xhvct->last = ++ xen_hyper_vcpu_to_vcpu_context(xhpct->last->current_vcpu); ++ /* set crashing vcpu */ ++ xht->crashing_vcc = xhvct->last; ++ } ++ if (!xhvct->last) { ++ xhvct->last = xhvct->vcpu_context_arrays->context_array; ++ } ++ ++ /* set domain last */ ++ if (xhvct->last) { ++ xhdt->last = ++ xen_hyper_domain_to_domain_context(xhvct->last->domain); ++ } ++ if (!xhdt->last) { ++ xhdt->last = xhdt->context_array; ++ } ++} ++ ++/* ++ * Do initialization for dump information here. ++ */ ++void ++xen_hyper_dumpinfo_init(void) ++{ ++ Elf32_Nhdr *note; ++ char *buf, *bp, *np, *upp; ++ char *nccp, *xccp; ++ ulong addr; ++ long size; ++ int i, cpuid, samp_cpuid; ++ ++ /* ++ * NOTE kakuma: It is not clear that what kind of ++ * a elf note format each one of the xen uses. ++ * So, we decide it confirming whether a symbol exists. ++ */ ++ if (STRUCT_EXISTS("note_buf_t")) ++ xhdit->note_ver = XEN_HYPER_ELF_NOTE_V1; ++ else if (STRUCT_EXISTS("crash_note_xen_t")) ++ xhdit->note_ver = XEN_HYPER_ELF_NOTE_V2; ++ else if (STRUCT_EXISTS("crash_xen_core_t")) { ++ if (STRUCT_EXISTS("crash_note_xen_core_t")) ++ xhdit->note_ver = XEN_HYPER_ELF_NOTE_V3; ++ else ++ xhdit->note_ver = XEN_HYPER_ELF_NOTE_V4; ++ } else { ++ error(WARNING, "found unsupported elf note format while checking of xen dumpinfo.\n"); ++ return; ++ } ++ if (!xen_hyper_test_pcpu_id(XEN_HYPER_CRASHING_CPU())) { ++ error(WARNING, "crashing_cpu not found.\n"); ++ return; ++ } ++ ++ /* allocate a context area */ ++ size = sizeof(struct xen_hyper_dumpinfo_context) * XEN_HYPER_MAX_CPUS(); ++ if((xhdit->context_array = malloc(size)) == NULL) { ++ error(FATAL, "cannot malloc dumpinfo table context space.\n"); ++ } ++ BZERO(xhdit->context_array, size); ++ size = sizeof(struct xen_hyper_dumpinfo_context_xen_core) * XEN_HYPER_MAX_CPUS(); ++ if((xhdit->context_xen_core_array = malloc(size)) == NULL) { ++ error(FATAL, "cannot malloc dumpinfo table context_xen_core_array space.\n"); ++ } ++ BZERO(xhdit->context_xen_core_array, size); ++ addr = symbol_value("per_cpu__crash_notes"); ++ for (i = 0; i < XEN_HYPER_MAX_CPUS(); i++) { ++ ulong addr_notes; ++ ++ addr_notes = xen_hyper_per_cpu(addr, i); ++ if (xhdit->note_ver == XEN_HYPER_ELF_NOTE_V4) { ++ if (!readmem(addr_notes, KVADDR, &(xhdit->context_array[i].note), ++ sizeof(ulong), "per_cpu__crash_notes", RETURN_ON_ERROR)) { ++ error(WARNING, "cannot read per_cpu__crash_notes.\n"); ++ return; ++ } ++ } else { ++ xhdit->context_array[i].note = addr_notes; ++ } ++ } ++ ++ if (xhdit->note_ver == XEN_HYPER_ELF_NOTE_V1) { ++ xhdit->note_size = XEN_HYPER_SIZE(note_buf_t); ++ } else if (xhdit->note_ver == XEN_HYPER_ELF_NOTE_V4) { ++ xhdit->note_size = XEN_HYPER_ELF_NOTE_V4_NOTE_SIZE; ++ } else { ++ xhdit->note_size = XEN_HYPER_SIZE(crash_note_t); ++ } ++ ++ /* read a sample note */ ++ buf = GETBUF(xhdit->note_size); ++ if (xhdit->note_ver == XEN_HYPER_ELF_NOTE_V4) ++ samp_cpuid = xht->cpu_idxs[0]; ++ else ++ samp_cpuid = XEN_HYPER_CRASHING_CPU(); ++ xhdit->xen_info_cpu = samp_cpuid; ++ if (!xen_hyper_fill_elf_notes(xhdit->context_array[samp_cpuid].note, ++ buf, XEN_HYPER_ELF_NOTE_FILL_T_NOTE)) { ++ error(FATAL, "cannot read per_cpu__crash_notes.\n"); ++ } ++ bp = buf; ++ ++ /* Get elf format information for each version. */ ++ switch (xhdit->note_ver) { ++ case XEN_HYPER_ELF_NOTE_V1: ++ /* core data */ ++ note = (Elf32_Nhdr *)bp; ++ np = bp + sizeof(Elf32_Nhdr); ++ upp = np + note->n_namesz; ++ upp = (char *)roundup((ulong)upp, 4); ++ xhdit->core_offset = (Elf_Word)((ulong)upp - (ulong)note); ++ note = (Elf32_Nhdr *)(upp + note->n_descsz); ++ /* cr3 data */ ++ np = (char *)note + sizeof(Elf32_Nhdr); ++ upp = np + note->n_namesz; ++ upp = (char *)roundup((ulong)upp, 4); ++ upp = upp + note->n_descsz; ++ xhdit->core_size = upp - bp; ++ break; ++ case XEN_HYPER_ELF_NOTE_V2: ++ /* core data */ ++ xhdit->core_offset = XEN_HYPER_OFFSET(crash_note_core_t_desc); ++ xhdit->core_size = XEN_HYPER_SIZE(crash_note_core_t); ++ /* xen core */ ++ xhdit->xen_info_offset = XEN_HYPER_OFFSET(crash_note_xen_t_desc); ++ xhdit->xen_info_size = XEN_HYPER_SIZE(crash_note_xen_t); ++ break; ++ case XEN_HYPER_ELF_NOTE_V3: ++ /* core data */ ++ xhdit->core_offset = XEN_HYPER_OFFSET(crash_note_core_t_desc); ++ xhdit->core_size = XEN_HYPER_SIZE(crash_note_core_t); ++ /* xen core */ ++ xhdit->xen_core_offset = XEN_HYPER_OFFSET(crash_note_xen_core_t_desc); ++ xhdit->xen_core_size = XEN_HYPER_SIZE(crash_note_xen_core_t); ++ /* xen info */ ++ xhdit->xen_info_offset = XEN_HYPER_OFFSET(crash_note_xen_info_t_desc); ++ xhdit->xen_info_size = XEN_HYPER_SIZE(crash_note_xen_info_t); ++ break; ++ case XEN_HYPER_ELF_NOTE_V4: ++ /* core data */ ++ note = (Elf32_Nhdr *)bp; ++ np = bp + sizeof(Elf32_Nhdr); ++ upp = np + note->n_namesz; ++ upp = (char *)roundup((ulong)upp, 4); ++ xhdit->core_offset = (Elf_Word)((ulong)upp - (ulong)note); ++ upp = upp + note->n_descsz; ++ xhdit->core_size = (Elf_Word)((ulong)upp - (ulong)note); ++ if (XEN_HYPER_ELF_NOTE_V4_NOTE_SIZE < xhdit->core_size + 32) { ++ error(WARNING, "note size is assumed on crash is incorrect.(core data)\n"); ++ return; ++ } ++ /* xen core */ ++ note = (Elf32_Nhdr *)upp; ++ np = (char *)note + sizeof(Elf32_Nhdr); ++ upp = np + note->n_namesz; ++ upp = (char *)roundup((ulong)upp, 4); ++ xhdit->xen_core_offset = (Elf_Word)((ulong)upp - (ulong)note); ++ upp = upp + note->n_descsz; ++ xhdit->xen_core_size = (Elf_Word)((ulong)upp - (ulong)note); ++ if (XEN_HYPER_ELF_NOTE_V4_NOTE_SIZE < ++ xhdit->core_size + xhdit->xen_core_size + 32) { ++ error(WARNING, "note size is assumed on crash is incorrect.(xen core)\n"); ++ return; ++ } ++ /* xen info */ ++ note = (Elf32_Nhdr *)upp; ++ np = (char *)note + sizeof(Elf32_Nhdr); ++ upp = np + note->n_namesz; ++ upp = (char *)roundup((ulong)upp, 4); ++ xhdit->xen_info_offset = (Elf_Word)((ulong)upp - (ulong)note); ++ upp = upp + note->n_descsz; ++ xhdit->xen_info_size = (Elf_Word)((ulong)upp - (ulong)note); ++ if (XEN_HYPER_ELF_NOTE_V4_NOTE_SIZE < ++ xhdit->core_size + xhdit->xen_core_size + xhdit->xen_info_size) { ++ error(WARNING, "note size is assumed on crash is incorrect.(xen info)\n"); ++ return; ++ } ++ xhdit->note_size = xhdit->core_size + xhdit->xen_core_size + xhdit->xen_info_size; ++ break; ++ default: ++ error(FATAL, "logic error in cheking elf note format occurs.\n"); ++ } ++ ++ /* fill xen info context. */ ++ if (xhdit->note_ver >= XEN_HYPER_ELF_NOTE_V3) { ++ if((xhdit->crash_note_xen_info_ptr = ++ malloc(xhdit->xen_info_size)) == NULL) { ++ error(FATAL, "cannot malloc dumpinfo table " ++ "crash_note_xen_info_ptr space.\n"); ++ } ++ memcpy(xhdit->crash_note_xen_info_ptr, ++ bp + xhdit->core_size + xhdit->xen_core_size, ++ xhdit->xen_info_size); ++ xhdit->context_xen_info.note = ++ xhdit->context_array[samp_cpuid].note + ++ xhdit->core_size + xhdit->xen_core_size; ++ xhdit->context_xen_info.pcpu_id = samp_cpuid; ++ xhdit->context_xen_info.crash_xen_info_ptr = ++ xhdit->crash_note_xen_info_ptr + xhdit->xen_info_offset; ++ } ++ ++ /* allocate note core */ ++ size = xhdit->core_size * XEN_HYPER_NR_PCPUS(); ++ if(!(xhdit->crash_note_core_array = malloc(size))) { ++ error(FATAL, "cannot malloc crash_note_core_array space.\n"); ++ } ++ nccp = xhdit->crash_note_core_array; ++ BZERO(nccp, size); ++ ++ /* allocate xen core */ ++ if (xhdit->note_ver >= XEN_HYPER_ELF_NOTE_V2) { ++ size = xhdit->xen_core_size * XEN_HYPER_NR_PCPUS(); ++ if(!(xhdit->crash_note_xen_core_array = malloc(size))) { ++ error(FATAL, "cannot malloc dumpinfo table " ++ "crash_note_xen_core_array space.\n"); ++ } ++ xccp = xhdit->crash_note_xen_core_array; ++ BZERO(xccp, size); ++ } ++ ++ /* fill a context. */ ++ for_cpu_indexes(i, cpuid) ++ { ++ /* fill core context. */ ++ addr = xhdit->context_array[cpuid].note; ++ if (!xen_hyper_fill_elf_notes(addr, nccp, ++ XEN_HYPER_ELF_NOTE_FILL_T_CORE)) { ++ error(FATAL, "cannot read elf note core.\n"); ++ } ++ xhdit->context_array[cpuid].pcpu_id = cpuid; ++ xhdit->context_array[cpuid].ELF_Prstatus_ptr = ++ nccp + xhdit->core_offset; ++ xhdit->context_array[cpuid].pr_reg_ptr = ++ nccp + xhdit->core_offset + ++ XEN_HYPER_OFFSET(ELF_Prstatus_pr_reg); ++ ++ /* Is there xen core data? */ ++ if (xhdit->note_ver < XEN_HYPER_ELF_NOTE_V2) { ++ nccp += xhdit->core_size; ++ continue; ++ } ++ if (xhdit->note_ver == XEN_HYPER_ELF_NOTE_V2 && ++ cpuid != samp_cpuid) { ++ xccp += xhdit->xen_core_size; ++ nccp += xhdit->core_size; ++ continue; ++ } ++ ++ /* fill xen core context, in case of more elf note V2. */ ++ xhdit->context_xen_core_array[cpuid].note = ++ xhdit->context_array[cpuid].note + ++ xhdit->core_size; ++ xhdit->context_xen_core_array[cpuid].pcpu_id = cpuid; ++ xhdit->context_xen_core_array[cpuid].crash_xen_core_ptr = ++ xccp + xhdit->xen_core_offset; ++ if (!xen_hyper_fill_elf_notes(xhdit->context_xen_core_array[cpuid].note, ++ xccp, XEN_HYPER_ELF_NOTE_FILL_T_XEN_CORE)) { ++ error(FATAL, "cannot read elf note xen core.\n"); ++ } ++ xccp += xhdit->xen_core_size; ++ nccp += xhdit->core_size; ++ } ++ ++ FREEBUF(buf); ++} ++ ++/* ++ * Get dump information context from physical cpu id. ++ */ ++struct xen_hyper_dumpinfo_context * ++xen_hyper_id_to_dumpinfo_context(uint id) ++{ ++ if (!xen_hyper_test_pcpu_id(id)) ++ return NULL; ++ return &xhdit->context_array[id]; ++} ++ ++/* ++ * Get dump information context from ELF Note address. ++ */ ++struct xen_hyper_dumpinfo_context * ++xen_hyper_note_to_dumpinfo_context(ulong note) ++{ ++ int i; ++ ++ for (i = 0; i < XEN_HYPER_MAX_CPUS(); i++) { ++ if (note == xhdit->context_array[i].note) { ++ return &xhdit->context_array[i]; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Fill ELF Notes header here. ++ * This assume that variable note has a top address of an area for ++ * specified type. ++ */ ++char * ++xen_hyper_fill_elf_notes(ulong note, char *note_buf, int type) ++{ ++ long size; ++ ulong rp = note; ++ ++ if (type == XEN_HYPER_ELF_NOTE_FILL_T_NOTE) ++ size = xhdit->note_size; ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_CORE) ++ size = xhdit->core_size; ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_XEN_CORE) ++ size = xhdit->xen_core_size; ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_XEN_CORE_M) ++ size = xhdit->core_size + xhdit->xen_core_size; ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_PRS) ++ size = XEN_HYPER_SIZE(ELF_Prstatus); ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_XEN_REGS) ++ size = XEN_HYPER_SIZE(xen_crash_xen_regs_t); ++ else ++ return NULL; ++ ++ if (!readmem(rp, KVADDR, note_buf, size, ++ "note_buf_t or crash_note_t", RETURN_ON_ERROR)) { ++ if (type == XEN_HYPER_ELF_NOTE_FILL_T_NOTE) ++ error(WARNING, "cannot fill note_buf_t or crash_note_t.\n"); ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_CORE) ++ error(WARNING, "cannot fill note core.\n"); ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_XEN_CORE) ++ error(WARNING, "cannot fill note xen core.\n"); ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_XEN_CORE_M) ++ error(WARNING, "cannot fill note core & xen core.\n"); ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_PRS) ++ error(WARNING, "cannot fill ELF_Prstatus.\n"); ++ else if (type == XEN_HYPER_ELF_NOTE_FILL_T_XEN_REGS) ++ error(WARNING, "cannot fill xen_crash_xen_regs_t.\n"); ++ return NULL; ++ } ++ return note_buf; ++} ++ ++ ++ ++/* ++ * Get domain status. ++ */ ++ulong ++xen_hyper_domain_state(struct xen_hyper_domain_context *dc) ++{ ++ if (ACTIVE()) { ++ if (xen_hyper_read_domain_verify(dc->domain) == NULL) { ++ return XEN_HYPER_DOMF_ERROR; ++ } ++ } ++ return dc->domain_flags; ++} ++ ++/* ++ * Allocate domain context space. ++ */ ++void ++xen_hyper_refresh_domain_context_space(void) ++{ ++ char *domain_struct; ++ ulong domain, next, dom_xen, dom_io, idle_vcpu; ++ struct xen_hyper_domain_context *dc; ++ struct xen_hyper_domain_context *dom0; ++ int i; ++ ++ if ((xhdt->flags & XEN_HYPER_DOMAIN_F_INIT) && !ACTIVE()) { ++ return; ++ } ++ ++ XEN_HYPER_RUNNING_DOMAINS() = XEN_HYPER_NR_DOMAINS() = ++ xen_hyper_get_domains(); ++ xen_hyper_alloc_domain_context_space(XEN_HYPER_NR_DOMAINS()); ++ ++ dc = xhdt->context_array; ++ ++ /* restore an dom_io context. */ ++ get_symbol_data("dom_io", sizeof(dom_io), &dom_io); ++ if ((domain_struct = xen_hyper_read_domain(dom_io)) == NULL) { ++ error(FATAL, "cannot read dom_io.\n"); ++ } ++ xen_hyper_store_domain_context(dc, dom_io, domain_struct); ++ xhdt->dom_io = dc; ++ dc++; ++ ++ /* restore an dom_xen context. */ ++ get_symbol_data("dom_xen", sizeof(dom_xen), &dom_xen); ++ if ((domain_struct = xen_hyper_read_domain(dom_xen)) == NULL) { ++ error(FATAL, "cannot read dom_xen.\n"); ++ } ++ xen_hyper_store_domain_context(dc, dom_xen, domain_struct); ++ xhdt->dom_xen = dc; ++ dc++; ++ ++ /* restore an idle domain context. */ ++ for (i = 0; i < xht->idle_vcpu_size; i += XEN_HYPER_MAX_VIRT_CPUS) { ++ idle_vcpu = xht->idle_vcpu_array[i]; ++ if (idle_vcpu == 0) ++ break; ++ if (!readmem(idle_vcpu + MEMBER_OFFSET("vcpu", "domain"), ++ KVADDR, &domain, sizeof(domain), "domain", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read domain member in vcpu.\n"); ++ } ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "idle_vcpu=%lx, domain=%lx\n", idle_vcpu, domain); ++ } ++ if ((domain_struct = xen_hyper_read_domain(domain)) == NULL) { ++ error(FATAL, "cannot read idle domain.\n"); ++ } ++ xen_hyper_store_domain_context(dc, domain, domain_struct); ++ if (i == 0) ++ xhdt->idle_domain = dc; ++ dc++; ++ } ++ ++ /* restore domain contexts from dom0 symbol. */ ++ xen_hyper_get_domain_next(XEN_HYPER_DOMAIN_READ_DOM0, &next); ++ domain = next; ++ dom0 = dc; ++ while((domain_struct = ++ xen_hyper_get_domain_next(XEN_HYPER_DOMAIN_READ_NEXT, &next)) != NULL) { ++ xen_hyper_store_domain_context(dc, domain, domain_struct); ++ domain = next; ++ dc++; ++ } ++ xhdt->dom0 = dom0; ++} ++ ++/* ++ * Get number of domain. ++ */ ++int ++xen_hyper_get_domains(void) ++{ ++ ulong domain, next_in_list; ++ long domain_next_in_list; ++ int i, j; ++ ++ get_symbol_data("dom0", sizeof(void *), &domain); ++ domain_next_in_list = MEMBER_OFFSET("domain", "next_in_list"); ++ i = 0; ++ while (domain != 0) { ++ i++; ++ next_in_list = domain + domain_next_in_list; ++ if (!readmem(next_in_list, KVADDR, &domain, sizeof(void *), ++ "domain.next_in_list", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read domain.next_in_list.\n"); ++ } ++ } ++ i += 2; /* for dom_io, dom_xen */ ++ /* for idle domains */ ++ for (j = 0; j < xht->idle_vcpu_size; j += XEN_HYPER_MAX_VIRT_CPUS) { ++ if (xht->idle_vcpu_array[j]) ++ i++; ++ } ++ return i; ++} ++ ++/* ++ * Get next domain struct. ++ * mod - XEN_HYPER_DOMAIN_READ_DOM0:start from dom0 symbol ++ * - XEN_HYPER_DOMAIN_READ_INIT:start from xhdt->context_array ++ * - XEN_HYPER_DOMAIN_READ_NEXT:next ++ */ ++char * ++xen_hyper_get_domain_next(int mod, ulong *next) ++{ ++ static int idx = 0; ++ ++ char *domain_struct; ++ struct xen_hyper_domain_context *dc; ++ ++ switch (mod) { ++ case XEN_HYPER_DOMAIN_READ_DOM0: ++ /* Case of search from dom0 symbol. */ ++ idx = 0; ++ if (xhdt->dom0) { ++ *next = xhdt->dom0->domain; ++ } else { ++ get_symbol_data("dom0", sizeof(void *), next); ++ } ++ return xhdt->domain_struct; ++ break; ++ case XEN_HYPER_DOMAIN_READ_INIT: ++ /* Case of search from context_array. */ ++ if (xhdt->context_array && xhdt->context_array->domain) { ++ idx = 1; /* this has a next index. */ ++ *next = xhdt->context_array->domain; ++ } else { ++ idx = 0; ++ *next = 0; ++ return NULL; ++ } ++ return xhdt->domain_struct; ++ break; ++ case XEN_HYPER_DOMAIN_READ_NEXT: ++ break; ++ default : ++ error(FATAL, "xen_hyper_get_domain_next mod error: %d\n", mod); ++ return NULL; ++ } ++ ++ /* Finished search */ ++ if (!*next) { ++ return NULL; ++ } ++ ++ domain_struct = NULL; ++ /* Is domain context array valid? */ ++ if (idx) { ++ if ((domain_struct = ++ xen_hyper_read_domain(*next)) == NULL) { ++ error(FATAL, "cannot get next domain from domain context array.\n"); ++ } ++ if (idx > XEN_HYPER_NR_DOMAINS()) { ++ *next = 0; ++ } else { ++ dc = xhdt->context_array; ++ dc += idx; ++ *next = dc->domain; ++ idx++; ++ } ++ return domain_struct; ++ } ++ ++ /* Search from dom0 symbol. */ ++ if ((domain_struct = ++ xen_hyper_read_domain(*next)) == NULL) { ++ error(FATAL, "cannot get next domain from dom0 symbol.\n"); ++ } ++ *next = ULONG(domain_struct + XEN_HYPER_OFFSET(domain_next_in_list)); ++ return domain_struct; ++} ++ ++/* ++ * from domain address to id. ++ */ ++domid_t ++xen_hyper_domain_to_id(ulong domain) ++{ ++ struct xen_hyper_domain_context *dc; ++ ++ /* Is domain context array valid? */ ++ if (xhdt->context_array && xhdt->context_array->domain) { ++ if ((dc = xen_hyper_domain_to_domain_context(domain)) == NULL) { ++ return XEN_HYPER_DOMAIN_ID_INVALID; ++ } else { ++ return dc->domain_id; ++ } ++ } else { ++ return XEN_HYPER_DOMAIN_ID_INVALID; ++ } ++} ++ ++/* ++ * Get domain struct from id. ++ */ ++char * ++xen_hyper_id_to_domain_struct(domid_t id) ++{ ++ char *domain_struct; ++ struct xen_hyper_domain_context *dc; ++ ++ domain_struct = NULL; ++ ++ /* Is domain context array valid? */ ++ if (xhdt->context_array && xhdt->context_array->domain) { ++ if ((dc = xen_hyper_id_to_domain_context(id)) == NULL) { ++ return NULL; ++ } else { ++ if ((domain_struct = ++ xen_hyper_read_domain(dc->domain)) == NULL) { ++ error(FATAL, "cannot get domain from domain context array with id.\n"); ++ } ++ return domain_struct; ++ } ++ } else { ++ return NULL; ++ } ++} ++ ++/* ++ * Get domain context from domain address. ++ */ ++struct xen_hyper_domain_context * ++xen_hyper_domain_to_domain_context(ulong domain) ++{ ++ struct xen_hyper_domain_context *dc; ++ int i; ++ ++ if (xhdt->context_array == NULL || ++ xhdt->context_array->domain == 0) { ++ return NULL; ++ } ++ if (!domain) { ++ return NULL; ++ } ++ for (i = 0, dc = xhdt->context_array; i < XEN_HYPER_NR_DOMAINS(); ++ i++, dc++) { ++ if (domain == dc->domain) { ++ return dc; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Get domain context from domain id. ++ */ ++struct xen_hyper_domain_context * ++xen_hyper_id_to_domain_context(domid_t id) ++{ ++ struct xen_hyper_domain_context *dc; ++ int i; ++ ++ if (xhdt->context_array == NULL || ++ xhdt->context_array->domain == 0) { ++ return NULL; ++ } ++ if (id == XEN_HYPER_DOMAIN_ID_INVALID) { ++ return NULL; ++ } ++ for (i = 0, dc = xhdt->context_array; i < XEN_HYPER_NR_DOMAINS(); ++ i++, dc++) { ++ if (id == dc->domain_id) { ++ return dc; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Store domain struct contents. ++ */ ++struct xen_hyper_domain_context * ++xen_hyper_store_domain_context(struct xen_hyper_domain_context *dc, ++ ulong domain, char *dp) ++{ ++ int i; ++ ++ dc->domain = domain; ++ BCOPY((char *)(dp + XEN_HYPER_OFFSET(domain_domain_id)), ++ &dc->domain_id, sizeof(domid_t)); ++ dc->tot_pages = UINT(dp + XEN_HYPER_OFFSET(domain_tot_pages)); ++ dc->max_pages = UINT(dp + XEN_HYPER_OFFSET(domain_max_pages)); ++ dc->xenheap_pages = UINT(dp + XEN_HYPER_OFFSET(domain_xenheap_pages)); ++ dc->shared_info = ULONG(dp + XEN_HYPER_OFFSET(domain_shared_info)); ++ dc->sched_priv = ULONG(dp + XEN_HYPER_OFFSET(domain_sched_priv)); ++ dc->next_in_list = ULONG(dp + XEN_HYPER_OFFSET(domain_next_in_list)); ++ if (XEN_HYPER_VALID_MEMBER(domain_domain_flags)) ++ dc->domain_flags = ULONG(dp + XEN_HYPER_OFFSET(domain_domain_flags)); ++ else if (XEN_HYPER_VALID_MEMBER(domain_is_shut_down)) { ++ dc->domain_flags = 0; ++ if (*(dp + XEN_HYPER_OFFSET(domain_is_hvm))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_HVM; ++ } else if (*(dp + XEN_HYPER_OFFSET(domain_is_privileged))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_privileged; ++ } else if (*(dp + XEN_HYPER_OFFSET(domain_debugger_attached))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_debugging; ++ } else if (XEN_HYPER_VALID_MEMBER(domain_is_polling) && ++ *(dp + XEN_HYPER_OFFSET(domain_is_polling))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_polling; ++ } else if (*(dp + XEN_HYPER_OFFSET(domain_is_paused_by_controller))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_ctrl_pause; ++ } else if (*(dp + XEN_HYPER_OFFSET(domain_is_dying))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_dying; ++ } else if (*(dp + XEN_HYPER_OFFSET(domain_is_shutting_down))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_shuttingdown; ++ } else if (*(dp + XEN_HYPER_OFFSET(domain_is_shut_down))) { ++ dc->domain_flags |= XEN_HYPER_DOMS_shutdown; ++ } ++ } else { ++ dc->domain_flags = XEN_HYPER_DOMF_ERROR; ++ } ++ dc->evtchn = ULONG(dp + XEN_HYPER_OFFSET(domain_evtchn)); ++ for (i = 0; i < XEN_HYPER_MAX_VIRT_CPUS; i++) { ++ dc->vcpu[i] = ULONG(dp + XEN_HYPER_OFFSET(domain_vcpu) + i*sizeof(void *)); ++ if (dc->vcpu[i]) XEN_HYPER_NR_VCPUS_IN_DOM(dc)++; ++ } ++ ++ return dc; ++} ++ ++/* ++ * Read domain struct from domain context. ++ */ ++char * ++xen_hyper_read_domain_from_context(struct xen_hyper_domain_context *dc) ++{ ++ return xen_hyper_fill_domain_struct(dc->domain, xhdt->domain_struct); ++} ++ ++/* ++ * Read domain struct. ++ */ ++char * ++xen_hyper_read_domain(ulong domain) ++{ ++ return xen_hyper_fill_domain_struct(domain, xhdt->domain_struct); ++} ++ ++/* ++ * Read domain struct to verification. ++ */ ++char * ++xen_hyper_read_domain_verify(ulong domain) ++{ ++ return xen_hyper_fill_domain_struct(domain, xhdt->domain_struct_verify); ++} ++ ++/* ++ * Fill domain struct. ++ */ ++char * ++xen_hyper_fill_domain_struct(ulong domain, char *domain_struct) ++{ ++ if (!readmem(domain, KVADDR, domain_struct, ++ XEN_HYPER_SIZE(domain), "fill_domain_struct", ++ ACTIVE() ? (RETURN_ON_ERROR|QUIET) : RETURN_ON_ERROR)) { ++ error(WARNING, "cannot fill domain struct.\n"); ++ return NULL; ++ } ++ return domain_struct; ++} ++ ++/* ++ * Allocate domain context space. ++ */ ++void ++xen_hyper_alloc_domain_context_space(int domains) ++{ ++ if (xhdt->context_array == NULL) { ++ if (!(xhdt->context_array = ++ malloc(domains * sizeof(struct xen_hyper_domain_context)))) { ++ error(FATAL, "cannot malloc context array (%d domains).", ++ domains); ++ } ++ xhdt->context_array_cnt = domains; ++ } else if (domains > xhdt->context_array_cnt) { ++ if (!(xhdt->context_array = ++ realloc(xhdt->context_array, ++ domains * sizeof(struct xen_hyper_domain_context)))) { ++ error(FATAL, "cannot realloc context array (%d domains).", ++ domains); ++ } ++ xhdt->context_array_cnt = domains; ++ } ++ BZERO(xhdt->context_array, ++ domains * sizeof(struct xen_hyper_domain_context)); ++} ++ ++ ++ ++/* ++ * Get vcpu status. ++ */ ++int ++xen_hyper_vcpu_state(struct xen_hyper_vcpu_context *vcc) ++{ ++ if (ACTIVE()) { ++ if (xen_hyper_read_vcpu_verify(vcc->vcpu) == NULL) { ++ return XEN_HYPER_RUNSTATE_ERROR; ++ } ++ } ++ return vcc->state; ++} ++ ++/* ++ * Allocate vcpu context space. ++ */ ++void ++xen_hyper_refresh_vcpu_context_space(void) ++{ ++ struct xen_hyper_domain_context *dc; ++ struct xen_hyper_vcpu_context_array *vcca; ++ struct xen_hyper_vcpu_context *vcc; ++ int i, j; ++ ++ if ((xhvct->flags & XEN_HYPER_VCPU_F_INIT) && !ACTIVE()) { ++ return; ++ } ++ ++ xen_hyper_alloc_vcpu_context_arrays_space(XEN_HYPER_NR_DOMAINS()); ++ for (i = 0, xht->vcpus = 0, dc = xhdt->context_array, ++ vcca = xhvct->vcpu_context_arrays; ++ i < XEN_HYPER_NR_DOMAINS(); i++, dc++, vcca++) { ++ dc->vcpu_context_array = vcca; ++ xen_hyper_alloc_vcpu_context_space(vcca, ++ XEN_HYPER_NR_VCPUS_IN_DOM(dc)); ++ for (j = 0, vcc = vcca->context_array; ++ j < XEN_HYPER_NR_VCPUS_IN_DOM(dc); j++, vcc++) { ++ xen_hyper_read_vcpu(dc->vcpu[j]); ++ xen_hyper_store_vcpu_context(vcc, dc->vcpu[j], ++ xhvct->vcpu_struct); ++ } ++ if (dc == xhdt->idle_domain) { ++ xhvct->idle_vcpu_context_array = vcca; ++ } ++ xht->vcpus += vcca->context_array_cnt; ++ } ++} ++ ++/* ++ * Get vcpu context from vcpu address. ++ */ ++struct xen_hyper_vcpu_context * ++xen_hyper_vcpu_to_vcpu_context(ulong vcpu) ++{ ++ struct xen_hyper_vcpu_context_array *vcca; ++ struct xen_hyper_vcpu_context *vcc; ++ int i, j; ++ ++ if (!vcpu) { ++ return NULL; ++ } ++ for (i = 0, vcca = xhvct->vcpu_context_arrays; ++ i < xhvct->vcpu_context_arrays_cnt; i++, vcca++) { ++ for (j = 0, vcc = vcca->context_array; ++ j < vcca->context_array_cnt; j++, vcc++) { ++ if (vcpu == vcc->vcpu) { ++ return vcc; ++ } ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Get vcpu context. ++ */ ++struct xen_hyper_vcpu_context * ++xen_hyper_id_to_vcpu_context(ulong domain, domid_t did, int vcid) ++{ ++ struct xen_hyper_vcpu_context_array *vcca; ++ struct xen_hyper_vcpu_context *vcc; ++ int i; ++ ++ if (vcid == XEN_HYPER_VCPU_ID_INVALID) { ++ return NULL; ++ } ++ if ((vcca = xen_hyper_domain_to_vcpu_context_array(domain))) { ++ ; ++ } else if (!(vcca = xen_hyper_domid_to_vcpu_context_array(did))) { ++ return NULL; ++ } ++ for (i = 0, vcc = vcca->context_array; ++ i < vcca->context_array_cnt; i++, vcc++) { ++ if (vcid == vcc->vcpu_id) { ++ return vcc; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Get pointer of a vcpu context array from domain address. ++ */ ++struct xen_hyper_vcpu_context_array * ++xen_hyper_domain_to_vcpu_context_array(ulong domain) ++{ ++ struct xen_hyper_domain_context *dc; ++ ++ if(!(dc = xen_hyper_domain_to_domain_context(domain))) { ++ return NULL; ++ } ++ return dc->vcpu_context_array; ++} ++ ++/* ++ * Get pointer of a vcpu context array from domain id. ++ */ ++struct xen_hyper_vcpu_context_array * ++xen_hyper_domid_to_vcpu_context_array(domid_t id) ++{ ++ struct xen_hyper_domain_context *dc; ++ ++ if (!(dc = xen_hyper_id_to_domain_context(id))) { ++ return NULL; ++ } ++ return dc->vcpu_context_array; ++} ++ ++/* ++ * Store vcpu struct contents. ++ */ ++struct xen_hyper_vcpu_context * ++xen_hyper_store_vcpu_context(struct xen_hyper_vcpu_context *vcc, ++ ulong vcpu, char *vcp) ++{ ++ vcc->vcpu = vcpu; ++ vcc->vcpu_id = INT(vcp + XEN_HYPER_OFFSET(vcpu_vcpu_id)); ++ vcc->processor = INT(vcp + XEN_HYPER_OFFSET(vcpu_processor)); ++ vcc->vcpu_info = ULONG(vcp + XEN_HYPER_OFFSET(vcpu_vcpu_info)); ++ vcc->domain = ULONG(vcp + XEN_HYPER_OFFSET(vcpu_domain)); ++ vcc->next_in_list = ULONG(vcp + XEN_HYPER_OFFSET(vcpu_next_in_list)); ++ if (XEN_HYPER_VALID_MEMBER(vcpu_sleep_tick)) ++ vcc->sleep_tick = ULONG(vcp + XEN_HYPER_OFFSET(vcpu_sleep_tick)); ++ vcc->sched_priv = ULONG(vcp + XEN_HYPER_OFFSET(vcpu_sched_priv)); ++ vcc->state = INT(vcp + XEN_HYPER_OFFSET(vcpu_runstate) + ++ XEN_HYPER_OFFSET(vcpu_runstate_info_state)); ++ vcc->state_entry_time = ULONGLONG(vcp + ++ XEN_HYPER_OFFSET(vcpu_runstate) + ++ XEN_HYPER_OFFSET(vcpu_runstate_info_state_entry_time)); ++ vcc->runstate_guest = ULONG(vcp + XEN_HYPER_OFFSET(vcpu_runstate_guest)); ++ if (XEN_HYPER_VALID_MEMBER(vcpu_vcpu_flags)) ++ vcc->vcpu_flags = ULONG(vcp + XEN_HYPER_OFFSET(vcpu_vcpu_flags)); ++ else ++ vcc->vcpu_flags = XEN_HYPER_VCPUF_ERROR; ++ return vcc; ++} ++ ++/* ++ * Read vcpu struct from vcpu context. ++ */ ++char * ++xen_hyper_read_vcpu_from_context(struct xen_hyper_vcpu_context *vcc) ++{ ++ return xen_hyper_fill_vcpu_struct(vcc->vcpu, xhvct->vcpu_struct); ++} ++ ++/* ++ * Read vcpu struct. ++ */ ++char * ++xen_hyper_read_vcpu(ulong vcpu) ++{ ++ return xen_hyper_fill_vcpu_struct(vcpu, xhvct->vcpu_struct); ++} ++ ++/* ++ * Read vcpu struct to verification. ++ */ ++char * ++xen_hyper_read_vcpu_verify(ulong vcpu) ++{ ++ return xen_hyper_fill_vcpu_struct(vcpu, xhvct->vcpu_struct_verify); ++} ++ ++/* ++ * Fill vcpu struct. ++ */ ++char * ++xen_hyper_fill_vcpu_struct(ulong vcpu, char *vcpu_struct) ++{ ++ if (!readmem(vcpu, KVADDR, vcpu_struct, ++ XEN_HYPER_SIZE(vcpu), "fill_vcpu_struct", ++ ACTIVE() ? (RETURN_ON_ERROR|QUIET) : RETURN_ON_ERROR)) { ++ error(WARNING, "cannot fill vcpu struct.\n"); ++ return NULL; ++ } ++ return vcpu_struct; ++} ++ ++/* ++ * Allocate vcpu context arrays space. ++ */ ++void ++xen_hyper_alloc_vcpu_context_arrays_space(int domains) ++{ ++ struct xen_hyper_vcpu_context_array *vcca; ++ ++ if (xhvct->vcpu_context_arrays == NULL) { ++ if (!(xhvct->vcpu_context_arrays = ++ malloc(domains * sizeof(struct xen_hyper_vcpu_context_array)))) { ++ error(FATAL, "cannot malloc context arrays (%d domains).", ++ domains); ++ } ++ BZERO(xhvct->vcpu_context_arrays, domains * sizeof(struct xen_hyper_vcpu_context_array)); ++ xhvct->vcpu_context_arrays_cnt = domains; ++ } else if (domains > xhvct->vcpu_context_arrays_cnt) { ++ if (!(xhvct->vcpu_context_arrays = ++ realloc(xhvct->vcpu_context_arrays, ++ domains * sizeof(struct xen_hyper_vcpu_context_array)))) { ++ error(FATAL, "cannot realloc context arrays (%d domains).", ++ domains); ++ } ++ vcca = xhvct->vcpu_context_arrays + domains; ++ BZERO(vcca, (domains - xhvct->vcpu_context_arrays_cnt) * ++ sizeof(struct xen_hyper_vcpu_context_array)); ++ xhvct->vcpu_context_arrays_cnt = domains; ++ } ++} ++ ++/* ++ * Allocate vcpu context space. ++ */ ++void ++xen_hyper_alloc_vcpu_context_space(struct xen_hyper_vcpu_context_array *vcca, int vcpus) ++{ ++ if (!vcpus) { ++ if (vcca->context_array != NULL) { ++ free(vcca->context_array); ++ vcca->context_array = NULL; ++ } ++ vcca->context_array_cnt = vcpus; ++ } else if (vcca->context_array == NULL) { ++ if (!(vcca->context_array = ++ malloc(vcpus * sizeof(struct xen_hyper_vcpu_context)))) { ++ error(FATAL, "cannot malloc context array (%d vcpus).", ++ vcpus); ++ } ++ vcca->context_array_cnt = vcpus; ++ } else if (vcpus > vcca->context_array_cnt) { ++ if (!(vcca->context_array = ++ realloc(vcca->context_array, ++ vcpus * sizeof(struct xen_hyper_vcpu_context_array)))) { ++ error(FATAL, "cannot realloc context array (%d vcpus).", ++ vcpus); ++ } ++ vcca->context_array_cnt = vcpus; ++ } ++ vcca->context_array_valid = vcpus; ++ BZERO(vcca->context_array, vcpus * sizeof(struct xen_hyper_vcpu_context)); ++} ++ ++ ++ ++/* ++ * Get pcpu context from pcpu id. ++ */ ++struct xen_hyper_pcpu_context * ++xen_hyper_id_to_pcpu_context(uint id) ++{ ++ if (xhpct->context_array == NULL) { ++ return NULL; ++ } ++ if (!xen_hyper_test_pcpu_id(id)) { ++ return NULL; ++ } ++ return &xhpct->context_array[id]; ++} ++ ++/* ++ * Get pcpu context from pcpu address. ++ */ ++struct xen_hyper_pcpu_context * ++xen_hyper_pcpu_to_pcpu_context(ulong pcpu) ++{ ++ struct xen_hyper_pcpu_context *pcc; ++ int i; ++ uint cpuid; ++ ++ if (xhpct->context_array == NULL) { ++ return NULL; ++ } ++ if (!pcpu) { ++ return NULL; ++ } ++ for_cpu_indexes(i, cpuid) ++ { ++ pcc = &xhpct->context_array[cpuid]; ++ if (pcpu == pcc->pcpu) { ++ return pcc; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Store pcpu struct contents. ++ */ ++struct xen_hyper_pcpu_context * ++xen_hyper_store_pcpu_context(struct xen_hyper_pcpu_context *pcc, ++ ulong pcpu, char *pcp) ++{ ++ pcc->pcpu = pcpu; ++ pcc->processor_id = ++ UINT(pcp + XEN_HYPER_OFFSET(cpu_info_processor_id)); ++ pcc->guest_cpu_user_regs = (ulong)(pcpu + ++ XEN_HYPER_OFFSET(cpu_info_guest_cpu_user_regs)); ++ pcc->current_vcpu = ++ ULONG(pcp + XEN_HYPER_OFFSET(cpu_info_current_vcpu)); ++ return pcc; ++} ++ ++/* ++ * Store init_tss contents. ++ */ ++struct xen_hyper_pcpu_context * ++xen_hyper_store_pcpu_context_tss(struct xen_hyper_pcpu_context *pcc, ++ ulong init_tss, char *tss) ++{ ++ int i; ++ uint64_t *ist_p; ++ ++ pcc->init_tss = init_tss; ++ if (machine_type("X86")) { ++ pcc->sp.esp0 = ULONG(tss + XEN_HYPER_OFFSET(tss_struct_esp0)); ++ } else if (machine_type("X86_64")) { ++ pcc->sp.rsp0 = ULONG(tss + XEN_HYPER_OFFSET(tss_struct_rsp0)); ++ ist_p = (uint64_t *)(tss + XEN_HYPER_OFFSET(tss_struct_ist)); ++ for (i = 0; i < XEN_HYPER_TSS_IST_MAX; i++, ist_p++) { ++ pcc->ist[i] = ULONG(ist_p); ++ } ++ } ++ return pcc; ++} ++ ++/* ++ * Read pcpu struct. ++ */ ++char * ++xen_hyper_read_pcpu(ulong pcpu) ++{ ++ return xen_hyper_fill_pcpu_struct(pcpu, xhpct->pcpu_struct); ++} ++ ++/* ++ * Fill pcpu struct. ++ */ ++char * ++xen_hyper_fill_pcpu_struct(ulong pcpu, char *pcpu_struct) ++{ ++ if (!readmem(pcpu, KVADDR, pcpu_struct, ++ XEN_HYPER_SIZE(cpu_info), "fill_pcpu_struct", ++ ACTIVE() ? (RETURN_ON_ERROR|QUIET) : RETURN_ON_ERROR)) { ++ error(WARNING, "cannot fill pcpu_struct.\n"); ++ return NULL; ++ } ++ return pcpu_struct; ++} ++ ++/* ++ * Allocate pcpu context space. ++ */ ++void ++xen_hyper_alloc_pcpu_context_space(int pcpus) ++{ ++ if (xhpct->context_array == NULL) { ++ if (!(xhpct->context_array = ++ malloc(pcpus * sizeof(struct xen_hyper_pcpu_context)))) { ++ error(FATAL, "cannot malloc context array (%d pcpus).", ++ pcpus); ++ } ++ } ++ BZERO(xhpct->context_array, pcpus * sizeof(struct xen_hyper_pcpu_context)); ++} ++ ++ ++ ++/* ++ * Fill cpu_data. ++ */ ++char * ++xen_hyper_x86_fill_cpu_data(int idx, char *cpuinfo_x86) ++{ ++ ulong cpu_data; ++ ++ if (!xen_hyper_test_pcpu_id(idx) || !xht->cpu_data_address) ++ return NULL; ++ cpu_data = xht->cpu_data_address + XEN_HYPER_SIZE(cpuinfo_x86) * idx; ++ if (!readmem(cpu_data, KVADDR, cpuinfo_x86, XEN_HYPER_SIZE(cpuinfo_x86), ++ "cpu_data", RETURN_ON_ERROR)) { ++ error(WARNING, "cannot read cpu_data.\n"); ++ return NULL; ++ } ++ return cpuinfo_x86; ++} ++ ++char * ++xen_hyper_ia64_fill_cpu_data(int idx, char *cpuinfo_ia64) ++{ ++ ulong cpu_data; ++ ++ if (!xen_hyper_test_pcpu_id(idx) || !xht->cpu_data_address) ++ return NULL; ++ cpu_data = xen_hyper_per_cpu(xht->cpu_data_address, idx); ++ if (!readmem(cpu_data, KVADDR, cpuinfo_ia64, XEN_HYPER_SIZE(cpuinfo_ia64), ++ "cpu_data", RETURN_ON_ERROR)) { ++ error(WARNING, "cannot read cpu_data.\n"); ++ return NULL; ++ } ++ return cpuinfo_ia64; ++} ++ ++/* ++ * Return whether vcpu is crashing. ++ */ ++int ++xen_hyper_is_vcpu_crash(struct xen_hyper_vcpu_context *vcc) ++{ ++ if (vcc == xht->crashing_vcc) ++ return TRUE; ++ return FALSE; ++} ++ ++/* ++ * Test whether cpu for pcpu id exists. ++ */ ++int ++xen_hyper_test_pcpu_id(uint pcpu_id) ++{ ++ ulong *cpumask = xht->cpumask; ++ uint i, j; ++ ++ if (pcpu_id == XEN_HYPER_PCPU_ID_INVALID || ++ pcpu_id > XEN_HYPER_MAX_CPUS()) { ++ return FALSE; ++ } ++ ++ i = pcpu_id / (sizeof(ulong) * 8); ++ j = pcpu_id % (sizeof(ulong) * 8); ++ cpumask += i; ++ if (*cpumask & (1UL << j)) { ++ return TRUE; ++ } else { ++ return FALSE; ++ } ++} ++ ++ ++ ++/* ++ * Calculate and return the uptime. ++ */ ++ulonglong ++xen_hyper_get_uptime_hyper(void) ++{ ++ ulong jiffies, tmp1, tmp2; ++ ulonglong jiffies_64, wrapped; ++ ++ if (symbol_exists("jiffies_64")) { ++ get_symbol_data("jiffies_64", sizeof(ulonglong), &jiffies_64); ++ 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); ++ } ++ } else if (symbol_exists("jiffies")) { ++ get_symbol_data("jiffies", sizeof(long), &jiffies); ++ jiffies_64 = (ulonglong)jiffies; ++ } else { ++ jiffies_64 = 0; /* hypervisor does not have uptime */ ++ } ++ ++ return jiffies_64; ++} ++ ++/* ++ * Get cpu informatin around. ++ */ ++void ++xen_hyper_get_cpu_info(void) ++{ ++ ulong addr; ++ ulong *cpumask; ++ uint *cpu_idx; ++ int i, j, cpus; ++ ++ get_symbol_data("max_cpus", sizeof(xht->max_cpus), &xht->max_cpus); ++ XEN_HYPER_STRUCT_SIZE_INIT(cpumask_t, "cpumask_t"); ++ if (XEN_HYPER_SIZE(cpumask_t) * 8 > xht->max_cpus) { ++ xht->max_cpus = XEN_HYPER_SIZE(cpumask_t) * 8; ++ } ++ if (xht->cpumask) { ++ free(xht->cpumask); ++ } ++ if((xht->cpumask = malloc(XEN_HYPER_SIZE(cpumask_t))) == NULL) { ++ error(FATAL, "cannot malloc cpumask space.\n"); ++ } ++ /* kakuma: It may be better to use cpu_present_map. */ ++ addr = symbol_value("cpu_online_map"); ++ if (!readmem(addr, KVADDR, xht->cpumask, ++ XEN_HYPER_SIZE(cpumask_t), "cpu_online_map", RETURN_ON_ERROR)) { ++ error(FATAL, "cannot read cpu_online_map.\n"); ++ } ++ if (xht->cpu_idxs) { ++ free(xht->cpu_idxs); ++ } ++ if((xht->cpu_idxs = malloc(sizeof(uint) * XEN_HYPER_MAX_CPUS())) == NULL) { ++ error(FATAL, "cannot malloc cpu_idxs space.\n"); ++ } ++ memset(xht->cpu_idxs, 0xff, sizeof(uint) * XEN_HYPER_MAX_CPUS()); ++ ++ for (i = cpus = 0, cpumask = xht->cpumask, cpu_idx = xht->cpu_idxs; ++ i < (XEN_HYPER_SIZE(cpumask_t)/sizeof(ulong)); i++, cpumask++) { ++ for (j = 0; j < sizeof(ulong) * 8; j++) { ++ if (*cpumask & (1UL << j)) { ++ *cpu_idx++ = i * sizeof(ulong) * 8 + j; ++ cpus++; ++ } ++ } ++ } ++ xht->pcpus = cpus; ++} ++ ++/* ++ * Calculate the number of physical cpu for x86. ++ */ ++int ++xen_hyper_x86_get_smp_cpus(void) ++{ ++ if (xht->pcpus) { ++ return xht->pcpus; ++ } ++ xen_hyper_get_cpu_info(); ++ return xht->pcpus; ++} ++ ++/* ++ * Calculate used memory size for x86. ++ */ ++uint64_t ++xen_hyper_x86_memory_size(void) ++{ ++ ulong vaddr; ++ ++ if (machdep->memsize) { ++ return machdep->memsize; ++ } ++ vaddr = symbol_value("total_pages"); ++ if (!readmem(vaddr, KVADDR, &xht->total_pages, sizeof(xht->total_pages), ++ "total_pages", RETURN_ON_ERROR)) { ++ error(WARNING, "cannot read total_pages.\n"); ++ } ++ xht->sys_pages = xht->total_pages; ++ machdep->memsize = (uint64_t)(xht->sys_pages) * (uint64_t)(machdep->pagesize); ++ return machdep->memsize; ++} ++ ++ ++/* ++ * Calculate the number of physical cpu for ia64. ++ */ ++int ++xen_hyper_ia64_get_smp_cpus(void) ++{ ++ return xen_hyper_x86_get_smp_cpus(); ++} ++ ++/* ++ * Calculate used memory size for ia64. ++ */ ++uint64_t ++xen_hyper_ia64_memory_size(void) ++{ ++ return xen_hyper_x86_memory_size(); ++} ++ ++/* ++ * Calculate and return the speed of the processor. ++ */ ++ulong ++xen_hyper_ia64_processor_speed(void) ++{ ++ ulong mhz, proc_freq; ++ ++ if (machdep->mhz) ++ return(machdep->mhz); ++ ++ mhz = 0; ++ ++ if (!xht->cpu_data_address || ++ !XEN_HYPER_VALID_STRUCT(cpuinfo_ia64) || ++ XEN_HYPER_INVALID_MEMBER(cpuinfo_ia64_proc_freq)) ++ return (machdep->mhz = mhz); ++ ++ readmem(xen_hyper_per_cpu(xht->cpu_data_address, xht->cpu_idxs[0]) + ++ XEN_HYPER_OFFSET(cpuinfo_ia64_proc_freq), ++ KVADDR, &proc_freq, sizeof(ulong), ++ "cpuinfo_ia64 proc_freq", FAULT_ON_ERROR); ++ ++ mhz = proc_freq/1000000; ++ ++ return (machdep->mhz = mhz); ++} ++ ++ ++ ++/* ++ * Print an aligned string with specified length. ++ */ ++void ++xen_hyper_fpr_indent(FILE *fp, int len, char *str1, char *str2, int flag) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ int sl, r; ++ char *s1, *s2; ++ ++ sl = strlen(str1); ++ if (sl > len) { ++ r = 0; ++ } else { ++ r = len - sl; ++ } ++ ++ memset(buf, ' ', sizeof(buf)); ++ buf[r] = '\0'; ++ if (flag & XEN_HYPER_PRI_L) { ++ s1 = str1; ++ s2 = buf; ++ } else { ++ s1 = buf; ++ s2 = str1; ++ } ++ if (str2) { ++ fprintf(fp, "%s%s%s", s1, s2, str2); ++ } else { ++ fprintf(fp, "%s%s", s1, s2); ++ } ++ if (flag & XEN_HYPER_PRI_LF) { ++ fprintf(fp, "\n"); ++ } ++} ++ ++ulong ++xen_hyper_get_active_vcpu_from_pcpuid(ulong pcpuid) ++{ ++ struct xen_hyper_pcpu_context *pcc; ++ struct xen_hyper_vcpu_context_array *vcca; ++ struct xen_hyper_vcpu_context *vcc; ++ int i, j; ++ ++ if (!xen_hyper_test_pcpu_id(pcpuid)) ++ return 0; ++ ++ pcc = &xhpct->context_array[pcpuid]; ++ if (pcc->current_vcpu) ++ return pcc->current_vcpu; ++ ++ for (i = 0, vcca = xhvct->vcpu_context_arrays; ++ i < xhvct->vcpu_context_arrays_cnt; i++, vcca++) { ++ for (j = 0, vcc = vcca->context_array; ++ j < vcca->context_array_cnt; j++, vcc++) { ++ if (vcc->processor == pcpuid && ++ vcc->state == XEN_HYPER_RUNSTATE_running) { ++ return vcc->vcpu; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++ulong ++xen_hyper_pcpu_to_active_vcpu(ulong pcpu) ++{ ++ ulong vcpu; ++ ++ /* if pcpu is vcpu address, return it. */ ++ if (pcpu & (~(PAGESIZE() - 1))) { ++ return pcpu; ++ } ++ ++ if(!(vcpu = XEN_HYPER_CURR_VCPU(pcpu))) ++ error(FATAL, "invalid pcpu id\n"); ++ return vcpu; ++} ++ ++void ++xen_hyper_print_bt_header(FILE *out, ulong vcpu, int newline) ++{ ++ struct xen_hyper_vcpu_context *vcc; ++ ++ if (newline) ++ fprintf(out, "\n"); ++ ++ vcc = xen_hyper_vcpu_to_vcpu_context(vcpu); ++ if (!vcc) ++ error(FATAL, "invalid vcpu\n"); ++ fprintf(out, "PCPU: %2d VCPU: %lx\n", vcc->processor, vcpu); ++} ++#endif +--- crash/configure.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/configure.c 2009-01-26 14:55:25.000000000 -0500 +@@ -1,8 +1,8 @@ + /* configure.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -56,7 +56,7 @@ + + void build_configure(void); + void release_configure(char *); +-void make_rh_rpm_package(char *); ++void make_rh_rpm_package(char *, int); + void unconfigure(void); + void set_warnings(int); + void show_configuration(void); +@@ -222,7 +222,7 @@ + + setup_gdb_defaults(); + +- while ((c = getopt(argc, argv, "gsqnWwubdr:p:")) > 0) { ++ while ((c = getopt(argc, argv, "gsqnWwubdr:p:P:")) > 0) { + switch (c) { + case 'q': + target_data.flags |= QUIET; +@@ -239,7 +239,10 @@ + release_configure(optarg); + break; + case 'p': +- make_rh_rpm_package(optarg); ++ make_rh_rpm_package(optarg, 0); ++ break; ++ case 'P': ++ make_rh_rpm_package(optarg, 1); + break; + case 'W': + case 'w': +@@ -566,10 +569,11 @@ + * Create an .rh_rpm_package file if the passed-in variable is set. + */ + void +-make_rh_rpm_package(char *package) ++make_rh_rpm_package(char *package, int release) + { +- char *p; ++ char *p, *cur; + FILE *fp; ++ char buf[256]; + + if ((strcmp(package, "remove") == 0)) { + if (file_exists(".rh_rpm_package")) { +@@ -589,6 +593,33 @@ + if (!strlen(++p)) + return; + ++ if (release) { ++ if (!(fp = popen("./crash -v", "r"))) { ++ fprintf(stderr, "cannot execute \"crash -v\"\n"); ++ exit(1); ++ } ++ cur = NULL; ++ while (fgets(buf, 256, fp)) { ++ if (strncmp(buf, "crash ", 6) == 0) { ++ cur = &buf[6]; ++ break; ++ } ++ } ++ fclose(fp); ++ ++ if (!cur) { ++ fprintf(stderr, "cannot get version from \"crash -v\"\n"); ++ exit(1); ++ } ++ strip_linefeeds(cur); ++ ++ if (strcmp(cur, p) != 0) { ++ fprintf(stderr, "./crash version: %s\n", cur); ++ fprintf(stderr, "release version: %s\n", p); ++ exit(1); ++ } ++ } ++ + if ((fp = fopen(".rh_rpm_package", "w")) == NULL) { + perror("fopen"); + fprintf(stderr, "cannot open .rh_rpm_package\n"); +@@ -910,7 +941,7 @@ + p = &LASTCHAR(line); + + while (*p == '\n') +- *p = (char)NULL; ++ *p = '\0'; + + return(line); + } +@@ -1121,14 +1152,14 @@ + printf("#\n"); + printf("# crash core analysis suite\n"); + printf("#\n"); +- printf("Summary: crash utility for live systems; netdump, diskdump, LKCD or mcore dumpfiles\n"); ++ printf("Summary: crash utility for live systems; netdump, diskdump, kdump, LKCD or mcore dumpfiles\n"); + printf("Name: %s\n", lower_case(target_data.program, buf)); + printf("Version: %s\n", Version); + printf("Release: %s\n", Release); +- printf("License: GPL\n"); ++ printf("License: GPLv2\n"); + printf("Group: Development/Debuggers\n"); + printf("Source: %%{name}-%%{version}-%%{release}.tar.gz\n"); +- printf("URL: ftp://people.redhat.com/anderson/%%{name}-%%{version}-%%{release}.tar.gz\n"); ++ printf("URL: http://people.redhat.com/anderson\n"); + printf("Distribution: Linux 2.2 or greater\n"); + printf("Vendor: Red Hat, Inc.\n"); + printf("Packager: Dave Anderson \n"); +@@ -1136,12 +1167,24 @@ + printf("ExclusiveArch: i386 alpha ia64 ppc ppc64 ppc64pseries ppc64iseries x86_64 s390 s390x\n"); + printf("Buildroot: %%{_tmppath}/%%{name}-root\n"); + printf("BuildRequires: ncurses-devel zlib-devel\n"); ++ printf("Requires: binutils\n"); + printf("# Patch0: crash-3.3-20.installfix.patch (patch example)\n"); + printf("\n"); + printf("%%description\n"); + printf("The core analysis suite is a self-contained tool that can be used to\n"); + printf("investigate either live systems, kernel core dumps created from the\n"); +- printf("netdump and diskdump packages from Red Hat Linux, the mcore kernel patch\n"); ++ printf("netdump, diskdump and kdump facilities from Red Hat Linux, the mcore kernel patch\n"); ++ printf("offered by Mission Critical Linux, or the LKCD kernel patch.\n"); ++ printf("\n"); ++ printf("%%package devel\n"); ++ printf("Requires: %%{name} = %%{version}, zlib-devel\n"); ++ printf("Summary: crash utility for live systems; netdump, diskdump, kdump, LKCD or mcore dumpfiles\n"); ++ printf("Group: Development/Debuggers\n"); ++ printf("\n"); ++ printf("%%description devel\n"); ++ printf("The core analysis suite is a self-contained tool that can be used to\n"); ++ printf("investigate either live systems, kernel core dumps created from the\n"); ++ printf("netdump, diskdump and kdump packages from Red Hat Linux, the mcore kernel patch\n"); + printf("offered by Mission Critical Linux, or the LKCD kernel patch.\n"); + printf("\n"); + printf("%%prep\n"); +@@ -1158,6 +1201,8 @@ + printf("make DESTDIR=%%{buildroot} install\n"); + printf("mkdir -p %%{buildroot}%%{_mandir}/man8\n"); + printf("cp crash.8 %%{buildroot}%%{_mandir}/man8/crash.8\n"); ++ printf("mkdir -p %%{buildroot}%%{_includedir}/crash\n"); ++ printf("cp defs.h %%{buildroot}%%{_includedir}/crash\n"); + printf("\n"); + printf("%%clean\n"); + printf("rm -rf %%{buildroot}\n"); +@@ -1167,6 +1212,10 @@ + printf("%%{_mandir}/man8/crash.8*\n"); + /* printf("/usr/bin/crashd\n"); */ + printf("%%doc README\n"); ++ printf("\n"); ++ printf("%%files devel\n"); ++ printf("%%defattr(-,root,root)\n"); ++ printf("%%{_includedir}/*\n"); + } + + /* +--- crash/help.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/help.c 2009-01-26 14:57:23.000000000 -0500 +@@ -1,8 +1,8 @@ + /* help.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -19,7 +19,6 @@ + + static void reshuffle_cmdlist(void); + static int sort_command_name(const void *, const void *); +-static void display_help_screen(char *); + static void display_commands(void); + static void display_copying_info(void); + static void display_warranty_info(void); +@@ -106,34 +105,33 @@ + void + program_usage(int form) + { +- int i; +- char **p; +- FILE *less; ++ if (form == SHORT_FORM) { ++ fprintf(fp, program_usage_info[0], pc->program_name); ++ fprintf(fp, "\nEnter \"%s -h\" for details.\n", ++ pc->program_name); ++ clean_exit(1); ++ } else { ++ FILE *scroll; ++ char *scroll_command; ++ char **p; ++ ++ if ((scroll_command = setup_scroll_command()) && ++ (scroll = popen(scroll_command, "w"))) ++ fp = scroll; ++ else ++ scroll = NULL; + +- if (form == LONG_FORM) +- less = popen("/usr/bin/less", "w"); +- else +- less = NULL; +- +- p = program_usage_info; +- +- if (form == LONG_FORM) { +- if (less) +- fp = less; +- for (i = 0; program_usage_info[i]; i++, p++) { +- fprintf(fp, *p, pc->program_name); ++ for (p = program_usage_info; *p; p++) { ++ fprintf(fp, *p, pc->program_name); + fprintf(fp, "\n"); + } +- } else { +- fprintf(fp, *p, pc->program_name); +- fprintf(fp, "\nEnter \"%s -h\" for details.\n", +- pc->program_name); +- } +- fflush(fp); +- if (less) +- pclose(less); ++ fflush(fp); + +- clean_exit(1); ++ if (scroll) ++ pclose(scroll); ++ ++ clean_exit(0); ++ } + } + + +@@ -147,14 +145,16 @@ + struct command_table_entry *cp; + struct extension_table *ext; + +- for (pc->ncmds = 0, cp = &base_command_table[0]; cp->name; cp++) { ++ for (pc->ncmds = 0, cp = pc->cmd_table; cp->name; cp++) { + if (!(cp->flags & HIDDEN_COMMAND)) + pc->ncmds++; + } + + for (ext = extension_table; ext; ext = ext->next) { +- for (cp = ext->command_table; cp->name; cp++) +- pc->ncmds++; ++ for (cp = ext->command_table; cp->name; cp++) { ++ if (!(cp->flags & (CLEANUP|HIDDEN_COMMAND))) ++ pc->ncmds++; ++ } + } + + if (!pc->cmdlist) { +@@ -188,14 +188,16 @@ + for (i = 0; i < pc->cmdlistsz; i++) + pc->cmdlist[i] = NULL; + +- for (cnt = 0, cp = &base_command_table[0]; cp->name; cp++) { ++ for (cnt = 0, cp = pc->cmd_table; cp->name; cp++) { + if (!(cp->flags & HIDDEN_COMMAND)) + pc->cmdlist[cnt++] = cp->name; + } + + for (ext = extension_table; ext; ext = ext->next) { +- for (cp = ext->command_table; cp->name; cp++) +- pc->cmdlist[cnt++] = cp->name; ++ for (cp = ext->command_table; cp->name; cp++) { ++ if (!(cp->flags & (CLEANUP|HIDDEN_COMMAND))) ++ pc->cmdlist[cnt++] = cp->name; ++ } + } + + if (cnt > pc->cmdlistsz) +@@ -239,7 +241,7 @@ + oflag = 0; + + while ((c = getopt(argcnt, args, +- "efNDdmM:ngcaBbHhksvVoptTzLxO")) != EOF) { ++ "efNDdmM:ngcaBbHhkKsvVoptTzLxO")) != EOF) { + switch(c) + { + case 'e': +@@ -303,7 +305,11 @@ + return; + + case 'k': +- dump_kernel_table(); ++ dump_kernel_table(!VERBOSE); ++ return; ++ ++ case 'K': ++ dump_kernel_table(VERBOSE); + return; + + case 's': +@@ -349,6 +355,7 @@ + fprintf(fp, " -D - dumpfile memory usage\n"); + fprintf(fp, " -f - filesys table\n"); + fprintf(fp, " -k - kernel_table\n"); ++ fprintf(fp, " -K - kernel_table (verbose)\n"); + fprintf(fp, " -M machine specific\n"); + fprintf(fp, " -m - machdep_table\n"); + fprintf(fp, " -s - symbol table data\n"); +@@ -389,7 +396,7 @@ + if (oflag) + dump_offset_table(args[optind], FALSE); + else +- cmd_usage(args[optind], COMPLETE_HELP); ++ cmd_usage(args[optind], COMPLETE_HELP|MUST_HELP); + optind++; + } while (args[optind]); + } +@@ -398,7 +405,7 @@ + * Format and display the help menu. + */ + +-static void ++void + display_help_screen(char *indent) + { + int i, j, rows; +@@ -508,16 +515,16 @@ + " active perform the command(s) on the active thread on each CPU.\n", + " If none of the task-identifying arguments above are entered, the command", + " will be performed on all tasks.\n", +-" command select one or more of the following commands on the tasks", ++" command select one or more of the following commands to be run on the tasks", + " selected, or on all tasks:\n", +-" bt same as the \"bt\" command (optional flags: -r -t -l -e -R -f)", +-" vm same as the \"vm\" command (optional flags: -p -v -m -R)", +-" task same as the \"task\" command (optional flag: -R)", +-" files same as the \"files\" command (optional flag: -R)", +-" net same as the \"net\" command (optional flags: -s -S -R)", +-" set same as the \"set\" command", +-" sig same as the \"sig\" command", +-" vtop same as the \"vtop\" command (optional flags: -c -u -k)\n", ++" bt run the \"bt\" command (optional flags: -r -t -l -e -R -f -o)", ++" vm run the \"vm\" command (optional flags: -p -v -m -R)", ++" task run the \"task\" command (optional flag: -R)", ++" files run the \"files\" command (optional flag: -R)", ++" net run the \"net\" command (optional flags: -s -S -R)", ++" set run the \"set\" command", ++" sig run the \"sig\" command (optional flag: -g)", ++" vtop run the \"vtop\" command (optional flags: -c -u -k)\n", + " flag Pass this optional flag to the command selected.", + " argument Pass this argument to the command selected.", + " ", +@@ -651,6 +658,10 @@ + " argument is entered, the current value of the %s variable is shown. These", + " are the %s variables, acceptable arguments, and purpose:\n", + " scroll on | off controls output scrolling.", ++" scroll less /usr/bin/less as the output scrolling program.", ++" scroll more /bin/more as the output scrolling program.", ++" scroll CRASHPAGER use CRASHPAGER environment variable as the", ++" output scrolling program.", + " radix 10 | 16 sets output radix to 10 or 16.", + " refresh on | off controls internal task list refresh.", + " print_max number set maximum number of array elements to print.", +@@ -665,6 +676,10 @@ + " edit vi | emacs set line editing mode (from .%src file only).", + " namelist filename name of kernel (from .%src file only).", + " dumpfile filename name of core dumpfile (from .%src file only).", ++" zero_excluded on | off controls whether excluded pages from a dumpfile", ++" should return zero-filled memory.", ++" null-stop on | off if on, gdb's printing of character arrays will", ++" stop at the first NULL encountered.", + " ", + " Internal variables may be set in four manners:\n", + " 1. entering the set command in $HOME/.%src.", +@@ -694,11 +709,11 @@ + " STATE: TASK_RUNNING (PANIC)\n", + " Turn off output scrolling:\n", + " %s> set scroll off", +-" scroll: off", ++" scroll: off (/usr/bin/less)", + " ", + " Show the current state of %s internal variables:\n", + " %s> set -v", +-" scroll: on", ++" scroll: on (/usr/bin/less)", + " radix: 10 (decimal)", + " refresh: on", + " print_max: 256", +@@ -710,6 +725,8 @@ + " edit: vi", + " namelist: vmlinux", + " dumpfile: vmcore", ++" zero_excluded: off", ++" null-stop: on", + " ", + " Show the current context:\n", + " %s> set", +@@ -724,10 +741,14 @@ + char *help_p[] = { + "p", + "print the value of an expression", +-"expression", ++"[-x|-d][-u] expression", + " This command passes its arguments on to gdb \"print\" command for evaluation.", + "", +-" expression The expression to be evaluated.\n", ++" expression The expression to be evaluated.", ++" -x override default output format with hexadecimal format.", ++" -d override default output format with decimal format.", ++" -u the expression evaluates to a user address reference.", ++"", + " The default output format is decimal, but that can be changed at any time", + " with the two built-in aliases \"hex\" and \"dec\". Alternatively, there", + " are two other built-in aliases, \"px\" and \"pd\", which force the command", +@@ -787,7 +808,7 @@ + char *help_ps[] = { + "ps", + "display process status information", +-"[-k|-u][-s][-p|-c|-t|-l] [pid | taskp | command] ...", ++"[-k|-u][-s][-p|-c|-t|-l|-a|-g|-r] [pid | taskp | command] ...", + " This command displays process status for selected, or all, processes" , + " in the system. If no arguments are entered, the process data is", + " is displayed for all processes. Selected process identifiers can be", +@@ -822,8 +843,9 @@ + " On SMP machines, the active task on each CPU will be highlighted by an", + " angle bracket (\">\") preceding its information.", + " ", +-" Alternatively, information regarding parent-child relationships, or", +-" per-task time usage data may be displayed:", ++" Alternatively, information regarding parent-child relationships,", ++" per-task time usage data, argument/environment data, thread groups,", ++" or resource limits may be displayed:", + " ", + " -p display the parental hierarchy of selected, or all, tasks.", + " -c display the children of selected, or all, tasks.", +@@ -832,6 +854,10 @@ + " -l display the task last_run or timestamp value, whichever applies,", + " of selected, or all, tasks; the list is sorted with the most", + " recently-run task (largest last_run/timestamp) shown first.", ++" -a display the command line arguments and environment strings of", ++" selected, or all, user-mode tasks.", ++" -g display tasks by thread group, of selected, or all, tasks.", ++" -r display resource limits (rlimits) of selected, or all, tasks.", + "\nEXAMPLES", + " Show the process status of all current tasks:\n", + " %s> ps", +@@ -1031,13 +1057,73 @@ + " 381 1 0 c34ddf28 IN 0.2 1316 224 automount", + " 391 1 1 c2777f28 IN 0.2 1316 224 automount", + " ...", ++" ", ++" Display the argument and environment data for the automount task:\n", ++" %s> ps -a automount", ++" PID: 3948 TASK: f722ee30 CPU: 0 COMMAND: \"automount\"", ++" ARG: /usr/sbin/automount --timeout=60 /net program /etc/auto.net", ++" ENV: SELINUX_INIT=YES", ++" CONSOLE=/dev/console", ++" TERM=linux", ++" INIT_VERSION=sysvinit-2.85", ++" PATH=/sbin:/usr/sbin:/bin:/usr/bin", ++" LC_MESSAGES=en_US", ++" RUNLEVEL=3", ++" runlevel=3", ++" PWD=/", ++" LANG=ja_JP.UTF-8", ++" PREVLEVEL=N", ++" previous=N", ++" HOME=/", ++" SHLVL=2", ++" _=/usr/sbin/automount", ++" ", ++" Display the tasks in the thread group containing task c20ab0b0:\n", ++" %s> ps -g c20ab0b0", ++" PID: 6425 TASK: f72f50b0 CPU: 0 COMMAND: \"firefox-bin\"", ++" PID: 6516 TASK: f71bf1b0 CPU: 0 COMMAND: \"firefox-bin\"", ++" PID: 6518 TASK: d394b930 CPU: 0 COMMAND: \"firefox-bin\"", ++" PID: 6520 TASK: c20aa030 CPU: 0 COMMAND: \"firefox-bin\"", ++" PID: 6523 TASK: c20ab0b0 CPU: 0 COMMAND: \"firefox-bin\"", ++" PID: 6614 TASK: f1f181b0 CPU: 0 COMMAND: \"firefox-bin\"", ++" ", ++" Display the tasks in the thread group for each instance of the", ++" program named \"multi-thread\":\n", ++" %s> ps -g multi-thread", ++" PID: 2522 TASK: 1003f0dc7f0 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2523 TASK: 10037b13030 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2524 TASK: 1003e064030 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2525 TASK: 1003e13a7f0 CPU: 1 COMMAND: \"multi-thread\"", ++" ", ++" PID: 2526 TASK: 1002f82b7f0 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2527 TASK: 1003e1737f0 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2528 TASK: 10035b4b7f0 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2529 TASK: 1003f0c37f0 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2530 TASK: 10035597030 CPU: 1 COMMAND: \"multi-thread\"", ++" PID: 2531 TASK: 100184be7f0 CPU: 1 COMMAND: \"multi-thread\"", ++" ", ++" Display the resource limits of \"bash\" task 13896:\n", ++" %s> ps -r 13896", ++" PID: 13896 TASK: cf402000 CPU: 0 COMMAND: \"bash\"", ++" RLIMIT CURRENT MAXIMUM", ++" CPU (unlimited) (unlimited)", ++" FSIZE (unlimited) (unlimited)", ++" DATA (unlimited) (unlimited)", ++" STACK 10485760 (unlimited)", ++" CORE (unlimited) (unlimited)", ++" RSS (unlimited) (unlimited)", ++" NPROC 4091 4091", ++" NOFILE 1024 1024", ++" MEMLOCK 4096 4096", ++" AS (unlimited) (unlimited)", ++" LOCKS (unlimited) (unlimited)", + NULL + }; + + char *help_rd[] = { + "rd", + "read memory", +-"[-dDsup][-8|-16|-32|-64][-o offs][-e addr] [address|symbol] [count]", ++"[-dDsSupxmf][-8|-16|-32|-64][-o offs][-e addr] [address|symbol] [count]", + " This command displays the contents of memory, with the output formatted", + " in several different manners. The starting address may be entered either", + " symbolically or by address. The default output size is the size of a long", +@@ -1046,9 +1132,15 @@ + " -p address argument is a physical address.", + " -u address argument is a user virtual address; only required on", + " processors with common user and kernel virtual address spaces.", ++" -m address argument is a xen host machine address.", ++" -f address argument is a dumpfile offset.", + " -d display output in signed decimal format (default is hexadecimal).", + " -D display output in unsigned decimal format (default is hexadecimal).", + " -s displays output symbolically when appropriate.", ++" -S displays output symbolically when appropriate; if the address", ++" references a slab cache object, the name of the slab cache will", ++" be displayed in brackets.", ++" -x do not display ASCII translation at end of each line.", + #ifdef NOTDEF + " -o Shows offset value from the starting address.", + #endif +@@ -1064,40 +1156,44 @@ + " 3. -u specifies a user virtual address, but is only necessary on", + " processors with common user and kernel virtual address spaces.", + " symbol symbol of starting address to read.", +-" count number of memory locations to display (default is 1).", ++" count number of memory locations to display (default is 1); if entered,", ++" must be the last argument on the command line.", + "\nEXAMPLES", + " Display the kernel_version string:\n", + " %s> rd kernel_version 4 ", + " c0226a6c: 2e322e32 35312d35 00000000 00000001 2.2.5-15........\n", +-" Display the same block of memory, with and without symbols:\n", +-" %s> rd c1157f00 52 ", +-" c1157f00: c0131f7a 00000400 00000015 c013206e z...........n ..", +-" c1157f10: 00000100 c3d4c140 00000100 00000246 ....@.......F...", +-" c1157f20: 019b2065 c2a5bb90 080ac618 c02a83d0 e ............*.", +-" c1157f30: 40000025 01a45067 c1156000 00000000 %..@gP...`......", +-" c1157f40: c011b4f7 c1156000 c2a5bb90 080ac618 .....`..........", +-" c1157f50: 00000001 00000000 c1a45000 c19b2000 .........P... ..", +-" c1157f60: c1157f84 0000003b c022c000 c1156000 ....;.....\"..`..", +-" c1157f70: 00000000 fffffe00 bffff6fc 0000002e ................", +-" c1157f80: c022c000 ffffffff c01178ba c1156000 ..\"......x...`..", +-" c1157f90: 00000000 080ac618 bffff6ac 00000001 ................", +-" c1157fa0: c1156000 c1156000 c1157fb8 c1156000 .`...`.......`..", +-" c1157fb0: c1157fb8 c1156000 c1156000 c115608c .....`...`...`..", +-" c1157fc0: c01096c8 ffffffff bffff6fc 00000002 ................\n", +-" %s> rd -s c1157f00 52", +-" c1157f00: alloc_fd_array+0x1a 00000400 00000015 expand_fd_array+0x72 ", +-" c1157f10: 00000100 c3d4c140 00000100 00000246 ", +-" c1157f20: 019b2065 c2a5bb90 080ac618 c02a83d0 ", +-" c1157f30: 40000025 01a45067 c1156000 00000000 ", +-" c1157f40: do_wp_page+0x17f c1156000 c2a5bb90 080ac618 ", +-" c1157f50: 00000001 00000000 c1a45000 c19b2000 ", +-" c1157f60: c1157f84 0000003b init_task_union c1156000 ", +-" c1157f70: 00000000 fffffe00 bffff6fc 0000002e ", +-" c1157f80: init_task_union ffffffff sys_wait4+0x2be c1156000 ", +-" c1157f90: 00000000 080ac618 bffff6ac 00000001 ", +-" c1157fa0: c1156000 c1156000 c1157fb8 c1156000 ", +-" c1157fb0: c1157fb8 c1156000 c1156000 c115608c ", +-" c1157fc0: system_call+0x34 ffffffff bffff6fc 00000002\n", ++" Display the same block of memory, first without symbols, again", ++" with symbols, and then with symbols and slab cache references:\n", ++" %s> rd dff12e80 36", ++" dff12e80: dff12e94 00000000 c05a363a dff12ed0 ........:6Z.....", ++" dff12e90: 00000001 dff12e98 0041fe3f ffffffff ........?.A.....", ++" dff12ea0: 00000001 d5147800 00000000 def8abc0 .....x..........", ++" dff12eb0: dff12ebc c05a4aa0 00000000 dff12ed0 .....JZ.........", ++" dff12ec0: 00000001 00000000 00000000 00000000 ................", ++" dff12ed0: 0808b353 00000000 dff12efc c0698220 S........... .i.", ++" dff12ee0: dff12efc df7c6480 00000001 c046f99b .....d|.......F.", ++" dff12ef0: 00000000 00000000 0808b352 dff12f68 ........R...h/..", ++" dff12f00: c155a128 00000000 00000001 ffffffff (.U.............", ++" %s> rd -s dff12e80 36", ++" dff12e80: dff12e94 00000000 sock_aio_write+83 dff12ed0 ", ++" dff12e90: 00000001 dff12e98 0041fe3f ffffffff ", ++" dff12ea0: 00000001 d5147800 00000000 def8abc0 ", ++" dff12eb0: dff12ebc sys_recvfrom+207 00000000 dff12ed0 ", ++" dff12ec0: 00000001 00000000 00000000 00000000 ", ++" dff12ed0: 0808b353 00000000 dff12efc socket_file_ops ", ++" dff12ee0: dff12efc df7c6480 00000001 do_sync_write+182 ", ++" dff12ef0: 00000000 00000000 0808b352 dff12f68 ", ++" dff12f00: c155a128 00000000 00000001 ffffffff ", ++" %s> rd -S dff12e80 36", ++" dff12e80: [size-4096] 00000000 sock_aio_write+83 [size-4096] ", ++" dff12e90: 00000001 [size-4096] 0041fe3f ffffffff ", ++" dff12ea0: 00000001 [sock_inode_cache] 00000000 [filp] ", ++" dff12eb0: [size-4096] sys_recvfrom+207 00000000 [size-4096] ", ++" dff12ec0: 00000001 00000000 00000000 00000000 ", ++" dff12ed0: 0808b353 00000000 [size-4096] socket_file_ops ", ++" dff12ee0: [size-4096] [filp] 00000001 do_sync_write+182 ", ++" dff12ef0: 00000000 00000000 0808b352 [size-4096] ", ++" dff12f00: [vm_area_struct] 00000000 00000001 ffffffff\n", + " Read jiffies in hexadecimal and decimal format:\n", + " %s> rd jiffies", + " c0213ae0: 0008cc3a :...\n", +@@ -1155,7 +1251,7 @@ + "bt", + "backtrace", + #if defined(GDB_6_0) || defined(GDB_6_1) +-"[-a|-r|-t|-l|-e|-E|-f] [-R ref] [ -I ip ] [-S sp] [pid | taskp]", ++"[-a|-r|-t|-T|-l|-e|-E|-f|-o|-O] [-R ref] [ -I ip ] [-S sp] [pid | taskp]", + #else + "[-a|-r|-t|-l|-e|-f|-g] [-R ref] [ -I ip ] [-S sp] [pid | taskp]", + #endif +@@ -1167,14 +1263,26 @@ + " pages of memory containing the task_union structure.", + " -t display all text symbols found from the last known stack location", + " to the top of the stack. (helpful if the back trace fails)", ++" -T display all text symbols found from just above the task_struct or", ++" thread_info to the top of the stack. (helpful if the back trace", ++" fails or the -t option starts too high in the process stack).", + " -l show file and line number of each stack trace text location.", + " -e search the stack for possible kernel and user mode exception frames.", +-" -E search the IRQ stacks (x86, x86_64 and PPC64), and the exception", ++" -E search the IRQ stacks (x86, x86_64 and ppc64), and the exception", + " stacks (x86_64) for possible exception frames; all other arguments", + " will be ignored since this is not a context-sensitive operation.", + " -f display all stack data contained in a frame; this option can be", +-" used to determine the arguments passed to each function (x86 only);", +-" on IA64, the argument register contents are dumped.", ++" used to determine the arguments passed to each function; on ia64,", ++" the argument register contents are dumped.", ++" -o x86: use old backtrace method, permissable only on kernels that were", ++" compiled without the -fomit-frame_pointer.", ++" x86_64: use old backtrace method, which dumps potentially stale", ++" kernel text return addresses found on the stack.", ++" -O x86: use old backtrace method by default, permissable only on kernels", ++" that were compiled without the -fomit-frame_pointer; subsequent usage", ++" of this option toggles the backtrace method.", ++" x86_64: use old backtrace method by default; subsequent usage of this", ++" option toggles the backtrace method.", + #if !defined(GDB_6_0) && !defined(GDB_6_1) + " -g use gdb stack trace code. (alpha only)", + #endif +@@ -1189,11 +1297,8 @@ + " Note that all examples below are for x86 only. The output format will differ", + " for other architectures. x86 backtraces from kernels that were compiled", + " with the --fomit-frame-pointer CFLAG occasionally will drop stack frames,", +-" or display a stale frame reference. x86_64 backtraces are only slightly", +-" more intelligent than those generated from kernel oops messages; text return", +-" addresses shown in the back trace may include stale references. When in", +-" doubt as to the accuracy of a backtrace, the -t option may help fill in", +-" the blanks.\n", ++" or display a stale frame reference. When in doubt as to the accuracy of a", ++" backtrace, the -t or -T options may help fill in the blanks.\n", + "EXAMPLES", + " Display the stack trace of the active task(s) when the kernel panicked:\n", + " %s> bt -a", +@@ -1437,14 +1542,22 @@ + " ", + " Below is an example shared object file consisting of just one command, ", + " called \"echo\", which simply echoes back all arguments passed to it.", +-" Note the comments contained within it for further details. To build it,", +-" cut and paste the following output into a file, and call it, for example,", +-" \"extlib.c\". Then compile like so:", ++" Note the comments contained within it for further details. Cut and paste", ++" the following output into a file, and call it, for example, \"echo.c\".", ++" Then compiled in either of two manners. Either manually like so:", ++" ", ++" gcc -nostartfiles -shared -rdynamic -o echo.so echo.c -fPIC -D $(TARGET_CFLAGS)", ++" ", ++" where must be one of the MACHINE_TYPE #define's in defs.h,", ++" and where $(TARGET_CFLAGS) is the same as it is declared in the top-level", ++" Makefile after a build is completed. Or alternatively, the \"echo.c\" file", ++" can be copied into the \"extensions\" subdirectory, and compiled automatically", ++" like so:", + " ", +-" gcc -nostartfiles -shared -rdynamic -o extlib.so extlib.c", ++" make extensions", + " ", +-" The extlib.so file may be dynamically linked into %s during runtime, or", +-" during initialization by putting \"extend extlib.so\" into a .%src file", ++" The echo.so file may be dynamically linked into %s during runtime, or", ++" during initialization by putting \"extend echo.so\" into a .%src file", + " located in the current directory, or in the user's $HOME directory.", + " ", + "---------------------------------- cut here ----------------------------------", +@@ -1556,7 +1669,7 @@ + " PROCESSOR SPEED: 1993 Mhz", + " HZ: 100", + " PAGE SIZE: 4096", +-" L1 CACHE SIZE: 32", ++// " L1 CACHE SIZE: 32", + " KERNEL VIRTUAL BASE: c0000000", + " KERNEL VMALLOC BASE: e0800000", + " KERNEL STACK SIZE: 8192", +@@ -1583,7 +1696,8 @@ + " This command displays the timer queue entries, both old- and new-style,", + " in chronological order. In the case of the old-style timers, the", + " timer_table array index is shown; in the case of the new-style timers, ", +-" the timer_list address is shown.", ++" the timer_list address is shown. On later kernels, the timer data is", ++" per-cpu.", + "\nEXAMPLES", + " %s> timer", + " JIFFIES", +@@ -1610,6 +1724,37 @@ + " 372010 c2323f7c c0112d6c ", + " 372138 c2191f10 c0112d6c ", + " 8653052 c1f13f10 c0112d6c ", ++" ", ++" Display the timer queue on a 2-cpu system:\n", ++" %s> timer", ++" TVEC_BASES[0]: c1299be0", ++" JIFFIES", ++" 18256298", ++" EXPIRES TIMER_LIST FUNCTION", ++" 18256406 cd5ddec0 c01232bb ", ++" 18256677 ceea93e0 c011e3cc ", ++" 18256850 ceea7f64 c01232bb ", ++" 18258751 cd1d4f64 c01232bb ", ++" 18258792 cf5782f0 c011e3cc ", ++" 18261266 c03c9f80 c022fad5 ", ++" 18262196 c02dc2e0 c0233329 ", ++" 18270518 ceb8bf1c c01232bb ", ++" 18271327 c03c9120 c0222074 ", ++" 18271327 c03ca580 c0233ace ", ++" 18272532 c02d1e18 c0129946 ", ++" 18276518 c03c9fc0 c022fd40 ", ++" 18332334 ceea9970 c011e3cc ", ++" 18332334 cfb6a840 c011e3cc ", ++" 18665378 cec25ec0 c01232bb ", ++" TVEC_BASES[1]: c12a1be0", ++" JIFFIES", ++" 18256298", ++" EXPIRES TIMER_LIST FUNCTION", ++" 18256493 c02c7d00 c013dad5 ", ++" 18256499 c12a2db8 c0129946 ", ++" 18277900 ceebaec0 c01232bb ", ++" 18283769 cf739f64 c01232bb ", ++" 18331902 cee8af64 c01232bb ", + NULL + }; + +@@ -1905,7 +2050,7 @@ + char *help_irq[] = { + "irq", + "IRQ data", +-"[-d | -b | [index ...]]", ++"[[[index ...] | -u] | -d | -b]", + " This command collaborates the data in an irq_desc_t, along with its", + " associated hw_interrupt_type and irqaction structure data, into a", + " consolidated per-IRQ display. Alternatively, the intel interrupt", +@@ -1913,6 +2058,7 @@ + " If no index value argument(s) nor any options are entered, the IRQ", + " data for all IRQs will be displayed.\n", + " index a valid IRQ index.", ++" -u dump data for in-use IRQs only.", + " -d dump the intel interrupt descriptor table.", + " -b dump bottom half data.", + "\nEXAMPLES", +@@ -2013,7 +2159,7 @@ + char *help_sys[] = { + "sys", + "system data", +-"[-c [name|number]] ", ++"[-c [name|number]] config", + " This command displays system-specific data. If no arguments are entered,\n" + " the same system data shown during %s invocation is shown.\n", + " -c [name|number] If no name or number argument is entered, dump all", +@@ -2023,6 +2169,8 @@ + " that number is displayed. If the current output radix", + " has been set to 16, the system call numbers will be ", + " displayed in hexadecimal.", ++" config If the kernel was configured with CONFIG_IKCONFIG, then", ++" dump the in-kernel configuration data.", + " -panic Panic a live system. Requires write permission to", + " /dev/mem. Results in the %s context causing an", + " \"Attempted to kill the idle task!\" panic. (The dump", +@@ -2043,6 +2191,27 @@ + " VERSION: #24 SMP Mon Oct 11 17:41:40 CDT 1999", + " MACHINE: i686 (500 MHz)", + " MEMORY: 1 GB", ++"\n Dump the system configuration data (if CONFIG_IKCONFIG):\n", ++" %s> sys config", ++" #", ++" # Automatically generated make config: don't edit", ++" # Linux kernel version: 2.6.16", ++" # Mon Apr 10 07:58:06 2006", ++" #", ++" CONFIG_X86_64=y", ++" CONFIG_64BIT=y", ++" CONFIG_X86=y", ++" CONFIG_SEMAPHORE_SLEEPERS=y", ++" CONFIG_MMU=y", ++" CONFIG_RWSEM_GENERIC_SPINLOCK=y", ++" CONFIG_GENERIC_CALIBRATE_DELAY=y", ++" CONFIG_X86_CMPXCHG=y", ++" CONFIG_EARLY_PRINTK=y", ++" CONFIG_GENERIC_ISA_DMA=y", ++" CONFIG_GENERIC_IOMAP=y", ++" CONFIG_ARCH_MAY_HAVE_PC_FDC=y", ++" CONFIG_DMI=y", ++" ...", + "\n Dump the system call table:\n", + " %s> sys -c", + " NUM SYSTEM CALL FILE AND LINE NUMBER", +@@ -2191,13 +2360,18 @@ + char *help_mount[] = { + "mount", + "mounted filesystem data", +-"[-f] [-i] [vfsmount | superblock | devname | dirname | inode]", ++"[-f] [-i] [-n pid|task] [vfsmount|superblock|devname|dirname|inode]", + " This command displays basic information about the currently-mounted", + " filesystems. The per-filesystem dirty inode list or list of open", + " files for the filesystem may also be displayed.\n", + " -f dump dentries and inodes for open files in each filesystem.", + " -i dump all dirty inodes associated with each filesystem.\n", +-" Filesystems may be selected in the following forms:\n", ++" For kernels supporting namespaces, the -n option may be used to", ++" display the mounted filesystems with respect to the namespace of a", ++" specified task:\n", ++" -n pid a process PID.", ++" -n task a hexadecimal task_struct pointer.\n", ++" Specific filesystems may be selected using the following forms:\n", + " vfsmount hexadecimal address of filesystem vfsmount structure.", + " superblock hexadecimal address of filesystem super_block structure.", + " devname device name of filesystem.", +@@ -2721,22 +2895,22 @@ + char *help_sig[] = { + "sig", + "task signal handling", +-"[[-l] | [-s sigset]] | [pid | taskp] ...", ++"[[-l] | [-s sigset]] | [-g] [pid | taskp] ...", + " This command displays signal-handling data of one or more tasks. Multiple", + " task or PID numbers may be entered; if no arguments are entered, the signal", + " handling data of the current context will be displayed. The default display", + " shows:", + " ", +-" 1. Whether the task has an unblocked signal pending.", +-" 2. The contents of the \"signal\" and \"blocked\" sigset_t structures", +-" from the task_struct, both of which are represented as a 64-bit ", +-" hexadecimal value.", +-" 3. A formatted dump of the \"sig\" signal_struct structure referenced by", ++" 1. A formatted dump of the \"sig\" signal_struct structure referenced by", + " the task_struct. For each defined signal, it shows the sigaction", + " structure address, the signal handler, the signal sigset_t mask ", + " (also expressed as a 64-bit hexadecimal value), and the flags.", +-" 4. For each queued signal, if any, its signal number and associated", +-" siginfo structure address.", ++" 2. Whether the task has an unblocked signal pending.", ++" 3. The contents of the \"blocked\" and \"signal\" sigset_t structures", ++" from the task_struct/signal_struct, both of which are represented ", ++" as a 64-bit hexadecimal value.", ++" 4. For each queued signal, private and/or shared, if any, its signal", ++" number and associated siginfo structure address.", + " ", + " The -l option lists the signal numbers and their name(s). The -s option", + " translates a 64-bit hexadecimal value representing the contents of a", +@@ -2744,56 +2918,105 @@ + " ", + " pid a process PID.", + " taskp a hexadecimal task_struct pointer.", ++" -g displays signal information for all threads in a task's ", ++" thread group.", + " -l displays the defined signal numbers and names.", + " -s sigset translates a 64-bit hexadecimal value representing a sigset_t", + " into a list of signal names associated with the bits set.", + "\nEXAMPLES", +-" Dump the signal-handling data of PID 614:\n", +-" %s> sig 614", +-" PID: 614 TASK: c6f26000 CPU: 1 COMMAND: \"httpd\"", +-" SIGPENDING: no", +-" SIGNAL: 0000000000000000", +-" BLOCKED: 0000000000000000", +-" SIGNAL_STRUCT: c1913800 COUNT: 1", ++" Dump the signal-handling data of PID 8970:\n", ++" %s> sig 8970", ++" PID: 8970 TASK: f67d8560 CPU: 1 COMMAND: \"procsig\"", ++" SIGNAL_STRUCT: f6018680 COUNT: 1", + " SIG SIGACTION HANDLER MASK FLAGS ", +-" [1] c1913804 8057c98 0000000000000201 0 ", +-" [2] c1913818 8057c8c 0000000000000000 0 ", +-" [3] c191382c SIG_DFL 0000000000000000 0 ", +-" [4] c1913840 8057bd8 0000000000000000 80000000 (SA_RESETHAND)", +-" [5] c1913854 SIG_DFL 0000000000000000 0 ", +-" [6] c1913868 8057bd8 0000000000000000 80000000 (SA_RESETHAND)", +-" [7] c191387c 8057bd8 0000000000000000 80000000 (SA_RESETHAND)", +-" [8] c1913890 SIG_DFL 0000000000000000 0 ", +-" [9] c19138a4 SIG_DFL 0000000000000000 0 ", +-" [10] c19138b8 8057c98 0000000000000201 0 ", +-" [11] c19138cc 8057bd8 0000000000000000 80000000 (SA_RESETHAND)", +-" [12] c19138e0 SIG_DFL 0000000000000000 0 ", +-" [13] c19138f4 SIG_IGN 0000000000000000 0 ", +-" [14] c1913908 SIG_DFL 0000000000000000 0 ", +-" [15] c191391c 8057c8c 0000000000000000 0 ", +-" [16] c1913930 SIG_DFL 0000000000000000 0 ", +-" [17] c1913944 SIG_DFL 0000000000000000 0 ", +-" [18] c1913958 SIG_DFL 0000000000000000 0 ", +-" [19] c191396c SIG_DFL 0000000000000000 0 ", +-" [20] c1913980 SIG_DFL 0000000000000000 0 ", +-" [21] c1913994 SIG_DFL 0000000000000000 0 ", +-" [22] c19139a8 SIG_DFL 0000000000000000 0 ", +-" [23] c19139bc SIG_DFL 0000000000000000 0 ", +-" [24] c19139d0 SIG_DFL 0000000000000000 0 ", +-" [25] c19139e4 SIG_DFL 0000000000000000 0 ", +-" [26] c19139f8 SIG_DFL 0000000000000000 0 ", +-" [27] c1913a0c SIG_DFL 0000000000000000 0 ", +-" [28] c1913a20 SIG_DFL 0000000000000000 0 ", +-" [29] c1913a34 SIG_DFL 0000000000000000 0 ", +-" [30] c1913a48 SIG_DFL 0000000000000000 0 ", +-" [31] c1913a5c SIG_DFL 0000000000000000 0 ", +-" SIGQUEUE: (empty)", ++" [1] f7877684 SIG_DFL 0000000000000000 0 ", ++" [2] f7877698 SIG_DFL 0000000000000000 0 ", ++" ...", ++" [8] f7877710 SIG_DFL 0000000000000000 0 ", ++" [9] f7877724 SIG_DFL 0000000000000000 0 ", ++" [10] f7877738 804867a 0000000000000000 80000000 (SA_RESETHAND)", ++" [11] f787774c SIG_DFL 0000000000000000 0 ", ++" [12] f7877760 804867f 0000000000000000 10000004 (SA_SIGINFO|SA_RESTART)", ++" [13] f7877774 SIG_DFL 0000000000000000 0 ", ++" ...", ++" [31] f78778dc SIG_DFL 0000000000000000 0 ", ++" [32] f78778f0 SIG_DFL 0000000000000000 0 ", ++" [33] f7877904 SIG_DFL 0000000000000000 0 ", ++" [34] f7877918 804867f 0000000000000000 10000004 (SA_SIGINFO|SA_RESTART)", ++" [35] f787792c SIG_DFL 0000000000000000 0 ", ++" [36] f7877940 SIG_DFL 0000000000000000 0 ", ++" ...", ++" [58] f7877af8 SIG_DFL 0000000000000000 0 ", ++" [59] f7877b0c SIG_DFL 0000000000000000 0 ", ++" [60] f7877b20 SIG_DFL 0000000000000000 0 ", ++" [61] f7877b34 SIG_DFL 0000000000000000 0 ", ++" [62] f7877b48 SIG_DFL 0000000000000000 0 ", ++" [63] f7877b5c SIG_DFL 0000000000000000 0 ", ++" [64] f7877b70 804867f 0000000000000000 10000004 (SA_SIGINFO|SA_RESTART)", ++" SIGPENDING: no", ++" BLOCKED: 8000000200000800", ++" PRIVATE_PENDING", ++" SIGNAL: 0000000200000800", ++" SIGQUEUE: SIG SIGINFO ", ++" 12 f51b9c84", ++" 34 f51b9594", ++" SHARED_PENDING", ++" SIGNAL: 8000000000000800", ++" SIGQUEUE: SIG SIGINFO ", ++" 12 f51b9188", ++" 64 f51b9d18", ++" 64 f51b9500", ++" ", ++" Dump the signal-handling data for all tasks in the thread group containing", ++" PID 2578:\n", ++" %s> sig -g 2578", ++" PID: 2387 TASK: f617d020 CPU: 0 COMMAND: \"slapd\"", ++" SIGNAL_STRUCT: f7dede00 COUNT: 6", ++" SIG SIGACTION HANDLER MASK FLAGS", ++" [1] c1f60c04 a258a7 0000000000000000 10000000 (SA_RESTART)", ++" [2] c1f60c18 a258a7 0000000000000000 10000000 (SA_RESTART)", ++" [3] c1f60c2c SIG_DFL 0000000000000000 0", ++" [4] c1f60c40 SIG_DFL 0000000000000000 0", ++" [5] c1f60c54 a258a7 0000000000000000 10000000 (SA_RESTART)", ++" [6] c1f60c68 SIG_DFL 0000000000000000 0", ++" [7] c1f60c7c SIG_DFL 0000000000000000 0", ++" [8] c1f60c90 SIG_DFL 0000000000000000 0", ++" [9] c1f60ca4 SIG_DFL 0000000000000000 0", ++" [10] c1f60cb8 a25911 0000000000000000 10000000 (SA_RESTART)", ++" ...", ++" [64] c1f610f0 SIG_DFL 0000000000000000 0", ++" SHARED_PENDING", ++" SIGNAL: 0000000000000000", ++" SIGQUEUE: (empty)", ++" ", ++" PID: 2387 TASK: f617d020 CPU: 0 COMMAND: \"slapd\"", ++" SIGPENDING: no", ++" BLOCKED: 0000000000000000", ++" PRIVATE_PENDING", ++" SIGNAL: 0000000000000000", ++" SIGQUEUE: (empty)", ++" ", ++" PID: 2392 TASK: f6175aa0 CPU: 0 COMMAND: \"slapd\"", ++" SIGPENDING: no", ++" BLOCKED: 0000000000000000", ++" PRIVATE_PENDING", ++" SIGNAL: 0000000000000000", ++" SIGQUEUE: (empty)", ++" ", ++" PID: 2523 TASK: f7cd4aa0 CPU: 1 COMMAND: \"slapd\"", ++" SIGPENDING: no", ++" BLOCKED: 0000000000000000", ++" PRIVATE_PENDING", ++" SIGNAL: 0000000000000000", ++" SIGQUEUE: (empty)", ++" ", ++" ...", + " ", + " Translate the sigset_t mask value, cut-and-pasted from the signal handling", + " data from signals 1 and 10 above:", + " ", +-" %s> sig -s 0000000000000201", +-" SIGHUP SIGUSR1", ++" %s> sig -s 800A000000000201", ++" SIGHUP SIGUSR1 SIGRTMAX-14 SIGRTMAX-12 SIGRTMAX", + " ", + " List the signal numbers and their names:", + " ", +@@ -2829,6 +3052,40 @@ + " [29] SIGIO/SIGPOLL", + " [30] SIGPWR", + " [31] SIGSYS", ++" [32] SIGRTMIN", ++" [33] SIGRTMIN+1", ++" [34] SIGRTMIN+2", ++" [35] SIGRTMIN+3", ++" [36] SIGRTMIN+4", ++" [37] SIGRTMIN+5", ++" [38] SIGRTMIN+6", ++" [39] SIGRTMIN+7", ++" [40] SIGRTMIN+8", ++" [41] SIGRTMIN+9", ++" [42] SIGRTMIN+10", ++" [43] SIGRTMIN+11", ++" [44] SIGRTMIN+12", ++" [45] SIGRTMIN+13", ++" [46] SIGRTMIN+14", ++" [47] SIGRTMIN+15", ++" [48] SIGRTMIN+16", ++" [49] SIGRTMAX-15", ++" [50] SIGRTMAX-14", ++" [51] SIGRTMAX-13", ++" [52] SIGRTMAX-12", ++" [53] SIGRTMAX-11", ++" [54] SIGRTMAX-10", ++" [55] SIGRTMAX-9", ++" [56] SIGRTMAX-8", ++" [57] SIGRTMAX-7", ++" [58] SIGRTMAX-6", ++" [59] SIGRTMAX-5", ++" [60] SIGRTMAX-4", ++" [61] SIGRTMAX-3", ++" [62] SIGRTMAX-2", ++" [63] SIGRTMAX-1", ++" [64] SIGRTMAX", ++ + + NULL + }; +@@ -2836,8 +3093,8 @@ + char *help_struct[] = { + "struct", + "structure contents", +-"struct_name[.member] [[-o][-l offset][-r] [address | symbol] [count]]\n" +-" [-c count]", ++"struct_name[.member[,member]][-o][-l offset][-rfu] [address | symbol]\n" ++" [count | -c count]", + " This command displays either a structure definition, or a formatted display", + " of the contents of a structure at a specified address. When no address is", + " specified, the structure definition is shown along with the structure size.", +@@ -2845,7 +3102,8 @@ + " the scope of the data displayed to that particular member; when no address", + " is specified, the member's offset and definition are shown.\n", + " struct_name name of a C-code structure used by the kernel.", +-" .member name of a structure member.", ++" .member name of a structure member; to display multiple members of a", ++" structure, use a comma-separated list of members.", + " -o show member offsets when displaying structure definitions.", + " -l offset if the address argument is a pointer to a list_head structure", + " that is embedded in the target data structure, the offset", +@@ -2854,6 +3112,9 @@ + " 1. in \"structure.member\" format.", + " 2. a number of bytes. ", + " -r raw dump of structure data.", ++" -f address argument is a dumpfile offset.", ++" -u address argument is a user virtual address in the current", ++" context.", + " address hexadecimal address of a structure; if the address points", + " to an embedded list_head structure contained within the", + " target data structure, then the \"-l\" option must be used.", +@@ -2944,6 +3205,21 @@ + " struct mm_struct {", + " [12] pgd_t *pgd;", + " }\n", ++" Display the flags and virtual members of 4 contigous page structures", ++" in the mem_map page structure array:\n", ++" %s> page.flags,virtual c101196c 4", ++" flags = 0x8000,", ++" virtual = 0xc04b0000", ++" ", ++" flags = 0x8000,", ++" virtual = 0xc04b1000", ++" ", ++" flags = 0x8000,", ++" virtual = 0xc04b2000", ++" ", ++" flags = 0x8000,", ++" virtual = 0xc04b3000", ++" ", + " Display the array of tcp_sl_timer structures declared by tcp_slt_array[]:\n", + " %s> struct tcp_sl_timer tcp_slt_array 4", + " struct tcp_sl_timer {", +@@ -3052,8 +3328,8 @@ + char *help_union[] = { + "union", + "union contents", +-"union_name[.member] [[-o][-l offset][-r] [address | symbol] [count]]\n" +-" [-c count]", ++"union_name[.member[,member]] [-o][-l offset][-rfu] [address | symbol]\n" ++" [count | -c count]", + " This command displays either a union definition, or a formatted display", + " of the contents of a union at a specified address. When no address is", + " specified, the union definition is shown along with the union size.", +@@ -3061,7 +3337,8 @@ + " the scope of the data displayed to that particular member; when no address", + " is specified, the member's offset (always 0) and definition are shown.\n", + " union_name name of a C-code union used by the kernel.", +-" .member name of a union member.", ++" .member name of a union member; to display multiple members of a", ++" union, use a comma-separated list of members.", + " -o show member offsets when displaying union definitions.", + " (always 0)", + " -l offset if the address argument is a pointer to a list_head structure", +@@ -3071,6 +3348,9 @@ + " 1. in \"structure.member\" format.", + " 2. a number of bytes. ", + " -r raw dump of union data.", ++" -f address argument is a dumpfile offset.", ++" -u address argument is a user virtual address in the current", ++" context.", + " address hexadecimal address of a union; if the address points", + " to an embedded list_head structure contained within the", + " target union structure, then the \"-l\" option must be used.", +@@ -3152,7 +3432,7 @@ + char *help_mod[] = { + "mod", + "module information and loading of symbols and debugging data", +-"[ -s module [objfile] | -d module | -S [directory] | -D | -r ] ", ++"[ -s module [objfile] | -d module | -S [directory] | -D | -r | -o ] ", + " With no arguments, this command displays basic information of the currently", + " installed modules, consisting of the module address, name, size, the", + " object file name (if known), and whether the module was compiled with", +@@ -3203,6 +3483,7 @@ + " -r Reinitialize module data. All currently-loaded symbolic", + " and debugging data will be deleted, and the installed", + " module list will be updated (live system only).", ++" -o Load module symbols with old mechanism.", + " ", + " After symbolic and debugging data have been loaded, backtraces and text", + " disassembly will be displayed appropriately. Depending upon the processor", +@@ -3322,9 +3603,10 @@ + char *help__list[] = { + "list", + "linked list", +-"[[-o] offset] [-e end] [-s struct[.member]] [-H] start", ++"[[-o] offset] [-e end] [-s struct[.member[,member]]] [-H] start", + " This command dumps the contents of a linked list. The entries in a linked", +-" are typically data structures that are tied together in one of two formats:", ++" list are typically data structures that are tied together in one of two", ++" formats:", + " ", + " 1. A starting address points to a data structure; that structure contains", + " a member that is a pointer to the next structure, and so on. The list", +@@ -3335,7 +3617,7 @@ + " c. a pointer to the first item pointed to by the start address.", + " d. a pointer to its containing structure.", + " ", +-" 2. Many Linux lists are linked via embedded list_head structures contained ", ++" 2. Most Linux lists are linked via embedded list_head structures contained ", + " within the data structures in the list. The linked list is headed by an", + " external LIST_HEAD, which is simply a list_head structure initialized to", + " point to itself, signifying that the list is empty:", +@@ -3370,15 +3652,17 @@ + " entered.", + " -s struct For each address in list, format and print as this type of", + " structure; use the \"struct.member\" format in order to display", +-" a particular member of the structure.", ++" a particular member of the structure. To display multiple", ++" members of a structure, use a comma-separated list of members.", + " ", + " The meaning of the \"start\" argument, which can be expressed either", + " symbolically or in hexadecimal format, depends upon whether the -H option", + " is pre-pended or not:", + " ", + " start The address of the first structure in the list.", +-" -H start The address of the LIST_HEAD structure, typically expressed", +-" symbolically.", ++" -H start The address of the list_head structure, typically expressed", ++" symbolically, but also can be an expression evaluating to the", ++" address of the starting list_head structure.", + "\nEXAMPLES", + " Note that each task_struct is linked to its parent's task_struct via the", + " p_pptr member:", +@@ -3416,31 +3700,66 @@ + " The list of currently-registered file system types are headed up by a", + " struct file_system_type pointer named \"file_systems\", and linked by", + " the \"next\" field in each file_system_type structure. The following", +-" sequence displays the address and name of each registered file system type:", ++" sequence displays the structure address followed by the name and ", ++" fs_flags members of each registered file system type:", + " ", + " %s> p file_systems", +-" file_systems = $2 = (struct file_system_type *) 0xc02ebea0", +-" %s> list file_system_type.next -s file_system_type.name 0xc02ebea0", +-" c02ebea0", +-" name = 0xc0280372 \"proc\", ", +-" c02fd4a0", +-" name = 0xc02bf348 \"sockfs\", ", +-" c02eb544", +-" name = 0xc027c25a \"tmpfs\", ", +-" c02eb52c", +-" name = 0xc027c256 \"shm\", ", +-" c02ebbe0", +-" name = 0xc027e054 \"pipefs\", ", +-" c02ec9c0", +-" name = 0xc0283c13 \"ext2\", ", +-" c02ecaa8", +-" name = 0xc0284567 \"iso9660\", ", +-" c02ecc08", +-" name = 0xc0284cf5 \"nfs\", ", +-" c02edc60", +-" name = 0xc028d832 \"autofs\", ", +-" c02edfa0", +-" name = 0xc028e1e0 \"devpts\"", ++" file_systems = $1 = (struct file_system_type *) 0xc03adc90", ++" %s> list file_system_type.next -s file_system_type.name,fs_flags 0xc03adc90", ++" c03adc90", ++" name = 0xc02c05c8 \"rootfs\",", ++" fs_flags = 0x30,", ++" c03abf94", ++" name = 0xc02c0319 \"bdev\",", ++" fs_flags = 0x10,", ++" c03acb40", ++" name = 0xc02c07c4 \"proc\",", ++" fs_flags = 0x8,", ++" c03e9834", ++" name = 0xc02cfc83 \"sockfs\",", ++" fs_flags = 0x10,", ++" c03ab8e4", ++" name = 0xc02bf512 \"tmpfs\",", ++" fs_flags = 0x20,", ++" c03ab8c8", ++" name = 0xc02c3d6b \"shm\",", ++" fs_flags = 0x20,", ++" c03ac394", ++" name = 0xc02c03cf \"pipefs\",", ++" fs_flags = 0x10,", ++" c03ada74", ++" name = 0xc02c0e6b \"ext2\",", ++" fs_flags = 0x1,", ++" c03adc74", ++" name = 0xc02c0e70 \"ramfs\",", ++" fs_flags = 0x20,", ++" c03ade74", ++" name = 0xc02c0e76 \"hugetlbfs\",", ++" fs_flags = 0x20,", ++" c03adf8c", ++" name = 0xc02c0f84 \"iso9660\",", ++" fs_flags = 0x1,", ++" c03aec14", ++" name = 0xc02c0ffd \"devpts\",", ++" fs_flags = 0x8,", ++" c03e93f4", ++" name = 0xc02cf1b9 \"pcihpfs\",", ++" fs_flags = 0x28,", ++" e0831a14", ++" name = 0xe082f89f \"ext3\",", ++" fs_flags = 0x1,", ++" e0846af4", ++" name = 0xe0841ac6 \"usbdevfs\",", ++" fs_flags = 0x8,", ++" e0846b10", ++" name = 0xe0841acf \"usbfs\",", ++" fs_flags = 0x8,", ++" e0992370", ++" name = 0xe099176c \"autofs\",", ++" fs_flags = 0x0,", ++" e2dcc030", ++" name = 0xe2dc8849 \"nfs\",", ++" fs_flags = 0x48000,", + " ", + " In some kernels, the system run queue is a linked list headed up by the", + " \"runqueue_head\", which is defined like so:", +@@ -3555,7 +3874,7 @@ + char *help_kmem[] = { + "kmem", + "kernel memory", +-"[-f|-F|-p|-c|-C|-i|-s|-S|-v|-n] [-[l|L][a|i]] [slab-name] [[-P] address]", ++"[-f|-F|-p|-c|-C|-i|-s|-S|-v|-V|-n|-z] [-[l|L][a|i]] [slab] [[-P] address]", + " This command displays information about the use of kernel memory.\n", + " -f displays the contents of the system free memory headers.", + " also verifies that the page count equals nr_free_pages.", +@@ -3567,23 +3886,33 @@ + " -i displays general memory usage information", + " -s displays basic kmalloc() slab data.", + " -S displays all kmalloc() slab data, including all slab objects,", +-" and whether each object is in use or is free.", ++" and whether each object is in use or is free. If CONFIG_SLUB,", ++" slab data for each per-cpu slab is displayed, along with the", ++" address of each kmem_cache_node, its count of full and partial", ++" slabs, and a list of all tracked slabs.", + " -v displays the vmlist entries.", ++" -V displays the kernel vm_stat table if it exists, the cumulative", ++" page_states counter values if they exist, and/or the cumulative", ++" vm_event_states counter values if they exist.", + " -n display memory node data (if supported).", ++" -z displays per-zone memory statistics.", + " -la walks through the active_list and verifies nr_active_pages.", + " -li walks through the inactive_list and verifies nr_inactive_pages.", + " -La same as -la, but also dumps each page in the active_list.", + " -Li same as -li, but also dumps each page in the inactive_list.", +-" slab-name when used with -s or -S, limits the command to only the slab cache", +-" of name \"slab-name\". If the slab-name argument is \"list\", then", ++" slab when used with -s or -S, limits the command to only the slab cache", ++" of name \"slab\". If the slab argument is \"list\", then", + " all slab cache names and addresses are listed.", + " -P declares that the following address argument is a physical address.", + " address when used without any flag, the address can be a kernel virtual,", + " or physical address; a search is made through the symbol table,", + " the kmalloc() slab subsystem, the free list, the page_hash_table,", +-" the vmalloc() vmlist subsystem, and the mem_map array. If found", +-" in any of those areas, the information will be dumped in the", +-" same manner as if the flags were used.", ++" the vmalloc() vmlist subsystem, the current set of task_structs", ++" and kernel stacks, and the mem_map array. If found in any of", ++" those areas, the information will be dumped in the same manner as", ++" if the location-specific flags were used; if contained within a", ++" curent task_struct or kernel stack, that task's context will be", ++" displayed.", + " address when used with -s or -S, searches the kmalloc() slab subsystem", + " for the slab containing of this virtual address, showing whether", + " it is in use or free.", +@@ -3781,6 +4110,24 @@ + " c2f8ab60 c8095000 - c8097000 8192", + " c2f519e0 c8097000 - c8099000 8192", + " ", ++" Dump the vm_table contents:\n", ++" %s> kmem -V", ++" NR_ANON_PAGES: 38989", ++" NR_FILE_MAPPED: 3106", ++" NR_FILE_PAGES: 169570", ++" NR_SLAB: 32439", ++" NR_PAGETABLE: 1181", ++" NR_FILE_DIRTY: 4633", ++" NR_WRITEBACK: 0", ++" NR_UNSTABLE_NFS: 0", ++" NR_BOUNCE: 0", ++" NUMA_HIT: 63545992", ++" NUMA_MISS: 0", ++" NUMA_FOREIGN: 0", ++" NUMA_INTERLEAVE_HIT: 24002", ++" NUMA_LOCAL: 63545992", ++" NUMA_OTHER: 0", ++" ", + " Determine (and verify) the page cache size:\n", + " %s> kmem -c", + " page_cache_size: 18431 (verified)", +@@ -3979,18 +4326,21 @@ + char *help_dis[] = { + "dis", + "disassemble", +-"[-r][-l][-u] [address | symbol | (expression)] [count]", ++"[-r][-l][-u][-b [num]] [address | symbol | (expression)] [count]", + " This command disassembles source code instructions starting (or ending) at", + " a text address that may be expressed by value, symbol or expression:\n", + " -r (reverse) displays all instructions from the start of the ", + " routine up to and including the designated address.", + " -l displays source code line number data in addition to the ", + " disassembly output.", +-" -u address is a user virtual address; otherwise the address is ", +-" assumed to be a kernel virtual address. If this option is", +-" used, then -r and -l are ignored.", ++" -u address is a user virtual address in the current context;", ++" otherwise the address is assumed to be a kernel virtual address.", ++" If this option is used, then -r and -l are ignored.", ++" -b [num] modify the pre-calculated number of encoded bytes to skip after", ++" a kernel BUG (\"ud2a\") instruction; with no argument, displays", ++" the current number of bytes being skipped. (x86 and x86_64 only)", + " address starting hexadecimal text address.", +-" symbol symbol of starting text address. On PPC64, the symbol", ++" symbol symbol of starting text address. On ppc64, the symbol", + " preceded by '.' is used.", + " (expression) expression evaluating to a starting text address.", + " count the number of instructions to be disassembled (default is 1).", +@@ -4419,10 +4769,11 @@ + " Display various network related data:\n", + " -a display the ARP cache.", + " -s display open network socket/sock addresses, their family and type,", +-" and their source and destination addresses and ports.", ++" and for INET and INET6 families, their source and destination", ++" addresses and ports.", + " -S displays open network socket/sock addresses followed by a dump", + " of both structures.", +-" -n addr translates an IP address expressed as a decimal or hexadecimal ", ++" -n addr translates an IPv4 address expressed as a decimal or hexadecimal", + " value into a standard numbers-and-dots notation.", + " -R ref socket or sock address, or file descriptor.", + " pid a process PID.", +@@ -4450,8 +4801,8 @@ + " Display the sockets for PID 2517, using both -s and -S output formats:\n", + " %s> net -s 2517", + " PID: 2517 TASK: c1598000 CPU: 1 COMMAND: \"rlogin\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 3 c57375dc c1ff1850 INET:STREAM 10.1.8.20:1023 10.1.16.62:513", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 3 c57375dc c1ff1850 INET:STREAM 10.1.8.20-1023 10.1.16.62-513", + " ", + " %s> net -S 2517", + " PID: 2517 TASK: c1598000 CPU: 1 COMMAND: \"rlogin\"", +@@ -4497,52 +4848,52 @@ + " From \"foreach\", find all tasks with references to socket c08ea3cc:\n", + " %s> foreach net -s -R c08ea3cc", + " PID: 2184 TASK: c7026000 CPU: 1 COMMAND: \"klines.kss\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2200 TASK: c670a000 CPU: 1 COMMAND: \"kpanel\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2201 TASK: c648a000 CPU: 1 COMMAND: \"kbgndwm\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 19294 TASK: c250a000 CPU: 0 COMMAND: \"prefdm\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2194 TASK: c62dc000 CPU: 1 COMMAND: \"kaudioserver\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2195 TASK: c6684000 CPU: 1 COMMAND: \"maudio\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2196 TASK: c6b58000 CPU: 1 COMMAND: \"kwmsound\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2197 TASK: c6696000 CPU: 0 COMMAND: \"kfm\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2199 TASK: c65ec000 CPU: 0 COMMAND: \"krootwm\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 694 TASK: c1942000 CPU: 0 COMMAND: \"prefdm\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 698 TASK: c6a2c000 CPU: 1 COMMAND: \"X\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + " PID: 2159 TASK: c4a5a000 CPU: 1 COMMAND: \"kwm\"", +-" FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT", +-" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0:1026 0.0.0.0:0", ++" FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT", ++" 5 c08ea3cc c50d3c80 INET:STREAM 0.0.0.0-1026 0.0.0.0-0", + " ", + NULL + }; +@@ -4584,21 +4935,22 @@ + void + cmd_usage(char *cmd, int helpflag) + { +- int i; +- int found; +- char **p; ++ char **p, *scroll_command; + struct command_table_entry *cp; + char buf[BUFSIZE]; +- struct alias_data *ad; +- FILE *less; ++ FILE *scroll; ++ int i; + +- if (helpflag & PIPE_TO_LESS) { +- if ((less = popen("/usr/bin/less", "w")) != NULL) +- fp = less; +- helpflag &= ~PIPE_TO_LESS; +- } else +- less = NULL; +- ++ if (helpflag & PIPE_TO_SCROLL) { ++ if ((scroll_command = setup_scroll_command()) && ++ (scroll = popen(scroll_command, "w"))) ++ fp = scroll; ++ else ++ scroll = NULL; ++ } else { ++ scroll_command = NULL; ++ scroll = NULL; ++ } + + if (STREQ(cmd, "copying")) { + display_copying_info(); +@@ -4641,46 +4993,50 @@ + goto done_usage; + } + +- found = FALSE; +-retry: +- if ((cp = get_command_table_entry(cmd))) { +- if ((p = cp->help_data)) +- found = TRUE; +- } ++ /* look up command, possibly through an alias */ ++ for (;;) { ++ struct alias_data *ad; ++ ++ cp = get_command_table_entry(cmd); ++ if (cp != NULL) ++ break; /* found command */ ++ ++ /* try for an alias */ ++ ad = is_alias(cmd); ++ if (ad == NULL) ++ break; /* neither command nor alias */ + +- /* +- * Check for alias names or gdb commands. +- */ +- if (!found) { +- if ((ad = is_alias(cmd))) { +- cmd = ad->args[0]; +- goto retry; +- } ++ cmd = ad->args[0]; ++ cp = get_command_table_entry(cmd); ++ } + +- if (helpflag == SYNOPSIS) { +- fprintf(fp, +- "No usage data for the \"%s\" command is available.\n", ++ if (cp == NULL || (p = cp->help_data) == NULL) { ++ if (helpflag & SYNOPSIS) { ++ fprintf(fp, ++ "No usage data for the \"%s\" command" ++ " is available.\n", + cmd); + RESTART(); + } + +- if (STREQ(pc->curcmd, "help")) { +- if (cp) +- fprintf(fp, +- "No help data for the \"%s\" command is available.\n", ++ if (helpflag & MUST_HELP) { ++ if (cp || !(pc->flags & GDB_INIT)) ++ fprintf(fp, ++ "No help data for the \"%s\" command" ++ " is available.\n", + cmd); + else if (!gdb_pass_through(concat_args(buf, 0, FALSE), + NULL, GNU_RETURN_ON_ERROR)) + fprintf(fp, +- "No help data for \"%s\" is available.\n", +- cmd); ++ "No help data for \"%s\" is available.\n", ++ cmd); + } + goto done_usage; + } + + p++; + +- if (helpflag == SYNOPSIS) { ++ if (helpflag & SYNOPSIS) { + p++; + fprintf(fp, "Usage: %s ", cmd); + fprintf(fp, *p, pc->program_name, pc->program_name); +@@ -4711,10 +5067,12 @@ + + done_usage: + +- if (less) { +- fflush(less); +- pclose(less); ++ if (scroll) { ++ fflush(scroll); ++ pclose(scroll); + } ++ if (scroll_command) ++ FREEBUF(scroll_command); + } + + +@@ -4812,7 +5170,9 @@ + "The default output radix for gdb output and certain %s commands is", + "hexadecimal. This can be changed to decimal by entering \"set radix 10\"", + "or the alias \"dec\". It can be reverted back to hexadecimal by entering", +-"\"set radix 16\" or the alias \"hex\".", ++"\"set radix 16\" or the alias \"hex\".\n", ++"To execute an external shell command, precede the command with an \"!\".", ++"To escape to a shell, enter \"!\" alone.", + " ", + NULL + }; +@@ -4854,10 +5214,13 @@ + static + char *version_info[] = { + +-"Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.", +-"Copyright (C) 2004, 2005 IBM Corporation", +-"Copyright (C) 1999-2005 Hewlett-Packard Co", +-"Copyright (C) 1999, 2002 Silicon Graphics, Inc.", ++"Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.", ++"Copyright (C) 2004, 2005, 2006 IBM Corporation", ++"Copyright (C) 1999-2006 Hewlett-Packard Co", ++"Copyright (C) 2005, 2006 Fujitsu Limited", ++"Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.", ++"Copyright (C) 2005 NEC Corporation", ++"Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.", + "Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.", + "This program is free software, covered by the GNU General Public License,", + "and you are welcome to change it and/or distribute copies of it under", +--- crash/lkcd_v8.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_v8.c 2007-11-09 17:29:14.000000000 -0500 +@@ -23,9 +23,185 @@ + #include "lkcd_dump_v8.h" /* REMIND */ + + static dump_header_t dump_header_v8 = { 0 }; +-// static dump_header_asm_t dump_header_asm_v8 = { 0 }; ++#ifndef HAVE_NO_DUMP_HEADER_ASM ++static dump_header_asm_t dump_header_asm_v8 = { 0 }; ++#endif + static dump_page_t dump_page = { 0 }; + static void mclx_cache_page_headers_v8(void); ++static off_t lkcd_offset_to_first_page = LKCD_OFFSET_TO_FIRST_PAGE; ++ ++#if defined(X86_64) ++ ++int ++get_lkcd_regs_for_cpu_arch(int cpu, ulong *eip, ulong *esp) ++{ ++ if (eip) ++ *eip = dump_header_asm_v8.dha_smp_regs[cpu].rip; ++ if (esp) ++ *esp = dump_header_asm_v8.dha_smp_regs[cpu].rsp; ++ ++ return 0; ++} ++ ++#elif defined(X86) ++ ++int ++get_lkcd_regs_for_cpu_arch(int cpu, ulong *eip, ulong *esp) ++{ ++ if (eip) ++ *eip = dump_header_asm_v8.dha_smp_regs[cpu].eip; ++ if (esp) ++ *esp = dump_header_asm_v8.dha_smp_regs[cpu].esp; ++ ++ return 0; ++} ++ ++#else ++ ++int ++get_lkcd_regs_for_cpu_arch(int cpu, ulong *eip, ulong *esp) ++{ ++ return -1; ++} ++ ++#endif ++ ++ ++ ++int ++get_lkcd_regs_for_cpu_v8(struct bt_info *bt, ulong *eip, ulong *esp) ++{ ++ int cpu = bt->tc->processor; ++ ++ if (!bt || !bt->tc) { ++ fprintf(stderr, "get_lkcd_regs_for_cpu_v8: invalid tc " ++ "(CPU=%d)\n", cpu); ++ return -EINVAL; ++ } ++ ++ if (cpu >= NR_CPUS) { ++ fprintf(stderr, "get_lkcd_regs_for_cpu_v8, cpu (%d) too high\n", cpu); ++ return -EINVAL; ++ } ++ ++ return get_lkcd_regs_for_cpu_arch(cpu, eip, esp); ++} ++ ++ ++#ifndef HAVE_NO_DUMP_HEADER_ASM ++int ++lkcd_dump_init_v8_arch(dump_header_t *dh) ++{ ++ off_t ret_of; ++ ssize_t ret_sz; ++ uint32_t hdr_size, offset, nr_cpus; ++ dump_header_asm_t arch_hdr; ++ char *hdr_buf = NULL; ++ ++ ret_of = lseek(lkcd->fd, dh->dh_header_size + ++ offsetof(dump_header_asm_t, dha_header_size), ++ SEEK_SET); ++ if (ret_of < 0) { ++ perror("lseek failed in " __FILE__ ":" STR(__LINE__)); ++ goto err; ++ } ++ ++ ret_sz = read(lkcd->fd, (char *)&hdr_size, sizeof(hdr_size)); ++ if (ret_sz != sizeof(hdr_size)) { ++ perror("Reading hdr_size failed in " __FILE__ ":" STR(__LINE__)); ++ goto err; ++ } ++ ++ ret_of = lseek(lkcd->fd, dh->dh_header_size, SEEK_SET); ++ if (ret_of < 0) { ++ perror("lseek failed in " __FILE__ ":" STR(__LINE__)); ++ goto err; ++ } ++ ++ hdr_buf = (char *)malloc(hdr_size); ++ if (!hdr_buf) { ++ perror("Could not allocate memory for dump header\n"); ++ goto err; ++ } ++ ++ ret_sz = read(lkcd->fd, (char *)hdr_buf, hdr_size); ++ if (ret_sz != hdr_size) { ++ perror("Could not read header " __FILE__ ":" STR(__LINE__)); ++ goto err; ++ } ++ ++ ++ /* ++ * Though we have KL_NR_CPUS is 128, the header size is different ++ * CONFIG_NR_CPUS might be different in the kernel. Hence, need ++ * to find out how many CPUs are configured. ++ */ ++ offset = offsetof(dump_header_asm_t, dha_smp_regs[0]); ++ nr_cpus = (hdr_size - offset) / sizeof(dump_CPU_info_t); ++ ++ /* check for CPU overflow */ ++ if (nr_cpus > NR_CPUS) { ++ fprintf(stderr, "CPU number too high %d (%s:%d)\n", ++ nr_cpus, __FILE__, __LINE__); ++ goto err; ++ } ++ ++ /* parts that don't depend on the number of CPUs */ ++ memcpy(&arch_hdr, (void *)hdr_buf, offset); ++ ++ /* registers */ ++ memcpy(&arch_hdr.dha_smp_regs, (void *)&hdr_buf[offset], ++ nr_cpus * sizeof(struct pt_regs)); ++ offset += nr_cpus * sizeof(struct pt_regs); ++ ++ /* current task */ ++ memcpy(&arch_hdr.dha_smp_current_task, (void *)&hdr_buf[offset], ++ nr_cpus * sizeof(&arch_hdr.dha_smp_current_task[0])); ++ offset += nr_cpus * sizeof(&arch_hdr.dha_smp_current_task[0]); ++ ++ /* stack */ ++ memcpy(&arch_hdr.dha_stack, (void *)&hdr_buf[offset], ++ nr_cpus * sizeof(&arch_hdr.dha_stack[0])); ++ offset += nr_cpus * sizeof(&arch_hdr.dha_stack[0]); ++ ++ /* stack_ptr */ ++ memcpy(&arch_hdr.dha_stack_ptr, (void *)&hdr_buf[offset], ++ nr_cpus * sizeof(&arch_hdr.dha_stack_ptr[0])); ++ offset += nr_cpus * sizeof(&arch_hdr.dha_stack_ptr[0]); ++ ++ if (arch_hdr.dha_magic_number != DUMP_ASM_MAGIC_NUMBER) { ++ fprintf(stderr, "Invalid magic number for x86_64\n"); ++ goto err; ++ } ++ ++ /* ++ * read the kernel load address on IA64 -- other architectures have ++ * no relocatable kernel at the lifetime of LKCD ++ */ ++#ifdef IA64 ++ memcpy(&arch_hdr.dha_kernel_addr, (void *)&hdr_buf[offset], sizeof(uint64_t)); ++#endif ++ ++ memcpy(&dump_header_asm_v8, &arch_hdr, sizeof(dump_header_asm_t)); ++ ++ return 0; ++ ++err: ++ free(hdr_buf); ++ return -1; ++} ++ ++#else /* architecture that has no lkcd_dump_init_v8 */ ++ ++int ++lkcd_dump_init_v8_arch(dump_header_t *dh) ++{ ++ return 0; ++} ++ ++#endif ++ ++ + + /* + * Verify and initialize the LKCD environment, storing the common data +@@ -56,17 +232,26 @@ + if (read(lkcd->fd, dh, sizeof(dump_header_t)) != + sizeof(dump_header_t)) + return FALSE; +- if ((dh->dh_version & LKCD_DUMP_VERSION_NUMBER_MASK) == LKCD_DUMP_V9) ++ if ((dh->dh_version & LKCD_DUMP_VERSION_NUMBER_MASK) == LKCD_DUMP_V9){ + if (read(lkcd->fd, &dh_dump_buffer_size, sizeof(dh_dump_buffer_size)) != + sizeof(dh_dump_buffer_size)) + return FALSE; ++ lkcd_offset_to_first_page = dh_dump_buffer_size; ++ } else ++ lkcd_offset_to_first_page = LKCD_OFFSET_TO_FIRST_PAGE; + + lkcd->dump_page = dp; + lkcd->dump_header = dh; + if (lkcd->debug) + dump_lkcd_environment(LKCD_DUMP_HEADER_ONLY); ++ ++ if (lkcd_dump_init_v8_arch(dh) != 0) { ++ fprintf(stderr, "Warning: Failed to initialise " ++ "arch specific dump code\n"); ++ } ++ + #ifdef IA64 +- if ( (fix_addr_v8(fd) == -1) ) ++ if ( (fix_addr_v8(&dump_header_asm_v8) == -1) ) + return FALSE; + #endif + +@@ -146,7 +331,7 @@ + lkcd->compression = dh->dh_dump_compress; + lkcd->page_header_size = sizeof(dump_page_t); + +- lseek(lkcd->fd, LKCD_OFFSET_TO_FIRST_PAGE, SEEK_SET); ++ lseek(lkcd->fd, lkcd_offset_to_first_page, SEEK_SET); + + /* + * Read all of the pages and save the page offsets for lkcd_lseek(). +@@ -483,7 +668,7 @@ + /* + * Determine the granularity between offsets. + */ +- if (lseek(lkcd->fd, page_headers[0] + LKCD_OFFSET_TO_FIRST_PAGE, ++ if (lseek(lkcd->fd, page_headers[0] + lkcd_offset_to_first_page, + SEEK_SET) == -1) + return; + if (read(lkcd->fd, dp, lkcd->page_header_size) != +@@ -491,7 +676,7 @@ + return; + physaddr1 = (dp->dp_address - lkcd->kvbase) << lkcd->page_shift; + +- if (lseek(lkcd->fd, page_headers[1] + LKCD_OFFSET_TO_FIRST_PAGE, ++ if (lseek(lkcd->fd, page_headers[1] + lkcd_offset_to_first_page, + SEEK_SET) == -1) + return; + if (read(lkcd->fd, dp, lkcd->page_header_size) +@@ -508,7 +693,7 @@ + for (i = 0; i < (MCLX_PAGE_HEADERS-1); i++) { + if (!page_headers[i]) + break; +- lkcd->curhdroffs = page_headers[i] + LKCD_OFFSET_TO_FIRST_PAGE; ++ lkcd->curhdroffs = page_headers[i] + lkcd_offset_to_first_page; + set_mb_benchmark((granularity * (i+1))/lkcd->page_size); + } + } +--- crash/lkcd_x86_trace.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_x86_trace.c 2009-01-26 14:54:36.000000000 -0500 +@@ -5,8 +5,8 @@ + /* + * lkcd_x86_trace.c + * +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * + * Adapted as noted from the following LKCD files: + * +@@ -21,6 +21,9 @@ + + #include "lkcd_x86_trace.h" + ++#undef XEN_HYPER_MODE ++static int XEN_HYPER_MODE(void) { return (pc->flags & XEN_HYPER) != 0; } ++ + static void *kl_alloc_block(int, int); + static void kl_free_block(void *); + static void GET_BLOCK(kaddr_t, unsigned, void *); +@@ -47,12 +50,13 @@ + static int setup_trace_rec(kaddr_t, kaddr_t, int, trace_t *); + static int valid_ra(kaddr_t); + static int valid_ra_function(kaddr_t, char *); ++static int eframe_incr(kaddr_t, char *); + static int find_trace(kaddr_t, kaddr_t, kaddr_t, kaddr_t, trace_t *, int); + static void dump_stack_frame(trace_t *, sframe_t *, FILE *); + static void print_trace(trace_t *, int, FILE *); +-struct pt_regs; +-static int eframe_type(struct pt_regs *); +-static void print_eframe(FILE *, struct pt_regs *); ++static int eframe_type(uaddr_t *); ++char *funcname_display(char *); ++static void print_eframe(FILE *, uaddr_t *); + static void trace_banner(FILE *); + static void print_kaddr(kaddr_t, FILE *, int); + int do_text_list(kaddr_t, int, FILE *); +@@ -505,7 +509,9 @@ + { "receive_chars", NULL, + COMPILER_VERSION_EQUAL, GCC(2,96,0), 0, 0, 48 }, + { "default_idle", NULL, +- COMPILER_VERSION_START, GCC(3,3,2), 0, -4, 0 }, ++ COMPILER_VERSION_START, GCC(2,96,0), 0, -4, 0 }, ++ { "hidinput_hid_event", NULL, ++ COMPILER_VERSION_START, GCC(4,1,2), 0, 0, 28 }, + { NULL, NULL, 0, 0, 0, 0, 0 }, + }; + +@@ -1117,8 +1123,9 @@ + return(0); + } + +-#include ++#ifndef REDHAT + #include ++#endif + #define KERNEL_EFRAME 0 + #define USER_EFRAME 1 + #define KERNEL_EFRAME_SZ 13 /* no ss and esp */ +@@ -1141,31 +1148,34 @@ + * Check if the exception frame is of kernel or user type + * Is checking only DS and CS values sufficient ? + */ +-int eframe_type(struct pt_regs *regs) ++ ++int eframe_type(uaddr_t *int_eframe) + { +- if (((regs->xcs & 0xffff) == __KERNEL_CS) && +- ((regs->xds & 0xffff) == __KERNEL_DS)) ++ ushort xcs, xds; ++ ++ xcs = (ushort)(int_eframe[INT_EFRAME_CS] & 0xffff); ++ xds = (ushort)(int_eframe[INT_EFRAME_DS] & 0xffff); ++ ++ if ((xcs == __KERNEL_CS) && (xds == __KERNEL_DS)) + return KERNEL_EFRAME; + #ifdef REDHAT +- else if (((regs->xcs & 0xffff) == 0x60) && +- ((regs->xds & 0xffff) == 0x68)) ++ else if ((xcs == 0x60) && (xds == 0x68)) ++ return KERNEL_EFRAME; ++ else if ((xcs == 0x60) && (xds == 0x7b)) ++ return KERNEL_EFRAME; ++ else if (XEN() && (xcs == 0x61) && (xds == 0x7b)) + return KERNEL_EFRAME; +- else if (((regs->xcs & 0xffff) == 0x60) && +- ((regs->xds & 0xffff) == 0x7b)) +- return KERNEL_EFRAME; + #endif +- else if (((regs->xcs & 0xffff) == __USER_CS) && +- ((regs->xds & 0xffff) == __USER_DS)) ++ else if ((xcs == __USER_CS) && (xds == __USER_DS)) + return USER_EFRAME; + #ifdef REDHAT +- else if (((regs->xcs & 0xffff) == 0x73) && +- ((regs->xds & 0xffff) == 0x7b)) ++ else if ((xcs == 0x73) && (xds == 0x7b)) + return USER_EFRAME; + #endif + return -1; + } + +-void print_eframe(FILE *ofp, struct pt_regs *regs) ++void print_eframe(FILE *ofp, uaddr_t *regs) + { + int type = eframe_type(regs); + +@@ -1206,6 +1216,93 @@ + } \ + } + #endif ++ ++/* ++ * Determine how much to increment the stack pointer to find the ++ * exception frame associated with a generic "error_code" or "nmi" ++ * exception. ++ * ++ * The incoming addr is that of the call to the generic error_code ++ * or nmi exception handler function. Until later 2.6 kernels, the next ++ * instruction had always been an "addl $8,%esp". However, with later ++ * 2.6 kernels, that esp adjustment is no long valid, and there will be ++ * an immediate "jmp" instruction. Returns 4 or 12, whichever is appropriate. ++ * Cache the value the first time, and allow for future changes or additions. ++ */ ++ ++#define NMI_ADJ (0) ++#define ERROR_CODE_ADJ (1) ++#define EFRAME_ADJUSTS (ERROR_CODE_ADJ+1) ++ ++static int eframe_adjust[EFRAME_ADJUSTS] = { 0 }; ++ ++static int ++eframe_incr(kaddr_t addr, char *funcname) ++{ ++ instr_rec_t irp; ++ kaddr_t next; ++ int size, adj, val; ++ ++ if (STRNEQ(funcname, "nmi")) { ++ adj = NMI_ADJ; ++ val = eframe_adjust[NMI_ADJ]; ++ } else if (strstr(funcname, "error_code")) { ++ adj = ERROR_CODE_ADJ; ++ val = eframe_adjust[ERROR_CODE_ADJ]; ++ } else { ++ adj = -1; ++ val = 0; ++ error(INFO, ++ "unexpected exception frame marker: %lx (%s)\n", ++ addr, funcname); ++ } ++ ++ if (val) { ++ console("eframe_incr(%lx, %s): eframe_adjust[%d]: %d\n", ++ addr, funcname, adj, val); ++ return val; ++ } ++ ++ console("eframe_incr(%lx, %s): TBD:\n", addr, funcname); ++ ++ bzero(&irp, sizeof(irp)); ++ irp.aflag = 1; ++ irp.dflag = 1; ++ if (!(size = get_instr_info(addr, &irp))) { ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "eframe_incr(%lx, %s): get_instr_info(%lx) failed\n", ++ addr, funcname, addr); ++ return((THIS_KERNEL_VERSION > LINUX(2,6,9)) ? 4 : 12); ++ } ++ console(" addr: %lx size: %d opcode: 0x%x insn: \"%s\"\n", ++ addr, size, irp.opcode, irp.opcodep->name); ++ ++ next = addr + size; ++ bzero(&irp, sizeof(irp)); ++ irp.aflag = 1; ++ irp.dflag = 1; ++ if (!(size = get_instr_info(next, &irp))) { ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "eframe_incr(%lx, %s): get_instr_info(%lx) failed\n", ++ addr, funcname, next); ++ return((THIS_KERNEL_VERSION > LINUX(2,6,9)) ? 4 : 12); ++ } ++ console(" next: %lx size: %d opcode: 0x%x insn: \"%s\"\n", ++ next, size, irp.opcode, irp.opcodep->name); ++ ++ if (STREQ(irp.opcodep->name, "jmp")) ++ val = 4; ++ else ++ val = 12; ++ ++ if (adj >= 0) ++ eframe_adjust[adj] = val; ++ ++ return val; ++} ++ + /* + * find_trace() + * +@@ -1253,6 +1350,7 @@ + int flag; + int interrupted_system_call = FALSE; + struct bt_info *bt = trace->bt; ++ uaddr_t *pt; + #endif + sbp = trace->stack[curstkidx].ptr; + sbase = trace->stack[curstkidx].addr; +@@ -1322,7 +1420,20 @@ + } + } + asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - (saddr - sp))); ++ + #ifdef REDHAT ++ if (XEN_HYPER_MODE()) { ++ func_name = kl_funcname(pc); ++ if (STREQ(func_name, "idle_loop") || STREQ(func_name, "hypercall") ++ || STREQ(func_name, "process_softirqs") ++ || STREQ(func_name, "tracing_off") ++ || STREQ(func_name, "page_fault") ++ || STREQ(func_name, "handle_exception")) { ++ UPDATE_FRAME(func_name, pc, 0, sp, bp, asp, 0, 0, bp - sp, 0); ++ return(trace->nframes); ++ } ++ } ++ + ra = GET_STACK_ULONG(bp + 4); + /* + * HACK: The get_framesize() function can return the proper +@@ -1447,7 +1558,8 @@ + bp = curframe->fp + frame_size; + } + #endif +- if ((func_name = kl_funcname(pc))) { ++ func_name = kl_funcname(pc); ++ if (func_name && !XEN_HYPER_MODE()) { + if (strstr(func_name, "kernel_thread")) { + ra = 0; + bp = saddr - 4; +@@ -1503,25 +1615,26 @@ + return(trace->nframes); + #ifdef REDHAT + } else if (strstr(func_name, "error_code") ++ || STREQ(func_name, "nmi_stack_correct") + || STREQ(func_name, "nmi")) { + #else + } else if (strstr(func_name, "error_code")) { + #endif + /* an exception frame */ +- sp = curframe->fp+12; ++ sp = curframe->fp + eframe_incr(pc, func_name); + + bp = sp + (KERNEL_EFRAME_SZ-1)*4; + asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - + (saddr - sp))); + curframe = alloc_sframe(trace, flags); +- ra = ((struct pt_regs *)asp)->eip; +- frame_type = eframe_type((struct pt_regs*)asp); ++ ra = asp[INT_EFRAME_EIP]; ++ frame_type = eframe_type(asp); + UPDATE_FRAME(func_name, pc, ra, sp, bp, asp, + 0, 0, (bp - sp + 4), EX_FRAME); + + /* prepare for next kernel frame, if present */ + if (frame_type == KERNEL_EFRAME) { +- pc = ((struct pt_regs *)asp)->eip; ++ pc = asp[INT_EFRAME_EIP]; + sp = curframe->fp+4; + #ifdef REDHAT + bp = sp + get_framesize(pc, bt); +@@ -1540,20 +1653,20 @@ + sp = curframe->fp + 4; + asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - + (saddr - sp))); +- frame_type = eframe_type((struct pt_regs*)asp); ++ frame_type = eframe_type(asp); + if (frame_type == KERNEL_EFRAME) + bp = curframe->fp+(KERNEL_EFRAME_SZ-1)*4; + else + bp = curframe->fp+(USER_EFRAME_SZ-1)*4; + curframe = alloc_sframe(trace, flags); +- ra = ((struct pt_regs *)asp)->eip; ++ ra = asp[INT_EFRAME_EIP]; + UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, + 0, 0, curframe->fp - curframe->sp+4, EX_FRAME); + + /* prepare for next kernel frame, if present */ + if (frame_type == KERNEL_EFRAME) { + sp = curframe->fp + 4; +- pc = ((struct pt_regs *)asp)->eip; ++ pc = asp[INT_EFRAME_EIP]; + #ifdef REDHAT + bp = sp + get_framesize(pc, bt); + #else +@@ -1571,6 +1684,47 @@ + } + } + } ++ if (func_name && XEN_HYPER_MODE()) { ++ if (STREQ(func_name, "continue_nmi") || ++ STREQ(func_name, "vmx_asm_vmexit_handler") || ++ STREQ(func_name, "handle_nmi_mce") || ++ STREQ(func_name, "deferred_nmi")) { ++ /* Interrupt frame */ ++ sp = curframe->fp + 4; ++ asp = (uaddr_t*)((uaddr_t)sbp + (STACK_SIZE - ++ (saddr - sp))); ++ bp = curframe->fp + (12 * 4); ++ curframe = alloc_sframe(trace, flags); ++ ra = *(asp + 9); ++ UPDATE_FRAME(func_name, pc, ra, sp, bp + 4, asp, ++ 0, 0, curframe->fp - curframe->sp+4, 12 * 4); ++ ++ /* contunue next frame */ ++ pc = ra; ++ sp = curframe->fp + 4; ++ bp = sp + get_framesize(pc, bt); ++ func_name = kl_funcname(pc); ++ if (!func_name) ++ return trace->nframes; ++ continue; ++ } ++ } ++ ++ /* ++ * Check for hypervisor_callback from user-space. ++ */ ++ if ((bt->flags & BT_XEN_STOP_THIS_CPU) && bt->tc->mm_struct && ++ STREQ(kl_funcname(curframe->pc), "hypervisor_callback")) { ++ pt = curframe->asp+1; ++ if (eframe_type(pt) == USER_EFRAME) { ++ if (program_context.debug >= 1) /* pc above */ ++ error(INFO, ++ "hypervisor_callback from user space\n"); ++ curframe->asp++; ++ curframe->flag |= EX_FRAME; ++ return(trace->nframes); ++ } ++ } + + /* Make sure our next frame pointer is valid (in the stack). + */ +@@ -1653,7 +1807,7 @@ + #ifdef REDHAT + kaddr_t fp = 0; + kaddr_t last_fp, last_pc, next_fp, next_pc; +- struct pt_regs *pt; ++ uaddr_t *pt; + struct bt_info *bt; + + bt = trace->bt; +@@ -1684,8 +1838,15 @@ + (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ))) + return; + +- print_stack_entry(trace->bt, +- trace->bt->flags & BT_BUMP_FRAME_LEVEL ? ++ if ((frmp->level == 0) && (bt->flags & BT_XEN_STOP_THIS_CPU)) { ++ print_stack_entry(trace->bt, 0, trace->bt->stkptr, ++ symbol_value("stop_this_cpu"), ++ value_symbol(symbol_value("stop_this_cpu")), ++ frmp, ofp); ++ } ++ ++ print_stack_entry(trace->bt, (trace->bt->flags & ++ (BT_BUMP_FRAME_LEVEL|BT_XEN_STOP_THIS_CPU)) ? + frmp->level + 1 : frmp->level, + fp ? (ulong)fp : trace->bt->stkptr, + (ulong)frmp->pc, frmp->funcname, frmp, ofp); +@@ -1707,7 +1868,11 @@ + fprintf(ofp, " [0x%x]\n", frmp->pc); + #endif + if (frmp->flag & EX_FRAME) { +- pt = (struct pt_regs *)frmp->asp; ++ pt = frmp->asp; ++ if (CRASHDEBUG(1)) ++ fprintf(ofp, ++ " EXCEPTION FRAME: %lx\n", ++ (unsigned long)frmp->sp); + print_eframe(ofp, pt); + } + #ifdef REDHAT +@@ -1789,6 +1954,114 @@ + if (kt->flags & RA_SEEK) + bt->flags |= BT_SPECULATE; + ++ if (XENDUMP_DUMPFILE() && XEN() && is_task_active(bt->task) && ++ STREQ(kl_funcname(bt->instptr), "stop_this_cpu")) { ++ /* ++ * bt->instptr of "stop_this_cpu" is not a return ++ * address -- replace it with the actual return ++ * address found at the bt->stkptr location. ++ */ ++ if (readmem((ulong)bt->stkptr, KVADDR, &eip, ++ sizeof(ulong), "xendump eip", RETURN_ON_ERROR)) ++ bt->instptr = eip; ++ bt->flags |= BT_XEN_STOP_THIS_CPU; ++ if (CRASHDEBUG(1)) ++ error(INFO, "replacing stop_this_cpu with %s\n", ++ kl_funcname(bt->instptr)); ++ } ++ ++ if (XENDUMP_DUMPFILE() && XEN() && is_idle_thread(bt->task) && ++ is_task_active(bt->task) && ++ !(kt->xen_flags & XEN_SUSPEND) && ++ STREQ(kl_funcname(bt->instptr), "schedule")) { ++ /* ++ * This is an invalid (stale) schedule reference ++ * left in the task->thread. Move down the stack ++ * until the smp_call_function_interrupt return ++ * address is found. ++ */ ++ saddr = bt->stkptr; ++ while (readmem(saddr, KVADDR, &eip, ++ sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) { ++ if (STREQ(kl_funcname(eip), "smp_call_function_interrupt")) { ++ bt->instptr = eip; ++ bt->stkptr = saddr; ++ bt->flags |= BT_XEN_STOP_THIS_CPU; ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "switch schedule to smp_call_function_interrupt\n"); ++ break; ++ } ++ saddr -= sizeof(void *); ++ if (saddr <= bt->stackbase) ++ break; ++ } ++ } ++ ++ if (XENDUMP_DUMPFILE() && XEN() && is_idle_thread(bt->task) && ++ is_task_active(bt->task) && ++ (kt->xen_flags & XEN_SUSPEND) && ++ STREQ(kl_funcname(bt->instptr), "schedule")) { ++ int framesize = 0; ++ /* ++ * This is an invalid (stale) schedule reference ++ * left in the task->thread. Move down the stack ++ * until the hypercall_page() return address is ++ * found, and fix up its framesize as we go. ++ */ ++ saddr = bt->stacktop; ++ while (readmem(saddr, KVADDR, &eip, ++ sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) { ++ ++ if (STREQ(kl_funcname(eip), "xen_idle")) ++ framesize += sizeof(ulong); ++ else if (framesize) ++ framesize += sizeof(ulong); ++ ++ if (STREQ(kl_funcname(eip), "hypercall_page")) { ++ int framesize = 24; ++ bt->instptr = eip; ++ bt->stkptr = saddr; ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "switch schedule to hypercall_page (framesize: %d)\n", ++ framesize); ++ FRAMESIZE_CACHE_ENTER(eip, &framesize); ++ break; ++ } ++ saddr -= sizeof(void *); ++ if (saddr <= bt->stackbase) ++ break; ++ } ++ } ++ ++ if (XENDUMP_DUMPFILE() && XEN() && !is_idle_thread(bt->task) && ++ is_task_active(bt->task) && ++ STREQ(kl_funcname(bt->instptr), "schedule")) { ++ /* ++ * This is an invalid (stale) schedule reference ++ * left in the task->thread. Move down the stack ++ * until the smp_call_function_interrupt return ++ * address is found. ++ */ ++ saddr = bt->stacktop; ++ while (readmem(saddr, KVADDR, &eip, ++ sizeof(ulong), "xendump esp", RETURN_ON_ERROR)) { ++ if (STREQ(kl_funcname(eip), "smp_call_function_interrupt")) { ++ bt->instptr = eip; ++ bt->stkptr = saddr; ++ bt->flags |= BT_XEN_STOP_THIS_CPU; ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "switch schedule to smp_call_function_interrupt\n"); ++ break; ++ } ++ saddr -= sizeof(void *); ++ if (saddr <= bt->stackbase) ++ break; ++ } ++ } ++ + if (!verify_back_trace(bt) && !recoverable(bt, ofp) && + !BT_REFERENCE_CHECK(bt)) + error(INFO, "cannot resolve stack trace:\n"); +@@ -1797,12 +2070,14 @@ + return(0); + #endif + +- if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) { +- return(1); +- } +- if (kl_get_task_struct(task, 2, tsp)) { +- kl_free_block(tsp); +- return(1); ++ if (!XEN_HYPER_MODE()) { ++ if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) { ++ return(1); ++ } ++ if (kl_get_task_struct(task, 2, tsp)) { ++ kl_free_block(tsp); ++ return(1); ++ } + } + trace = (trace_t *)alloc_trace_rec(C_TEMP); + if (!trace) { +@@ -1874,7 +2149,9 @@ + #endif + print_trace(trace, flags, ofp); + } +- kl_free_block(tsp); ++ if (!XEN_HYPER_MODE()) ++ kl_free_block(tsp); ++ + free_trace_rec(trace); + #ifdef REDHAT + if (KL_ERROR == KLE_PRINT_TRACE_ERROR) { +@@ -1901,13 +2178,15 @@ + errcnt = 0; + KL_ERROR = 0; + +- if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) +- return FALSE; +- +- if (kl_get_task_struct(bt->task, 2, tsp)) { +- kl_free_block(tsp); +- return FALSE; +- } ++ if (!XEN_HYPER_MODE()) { ++ if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) ++ return FALSE; ++ ++ if (kl_get_task_struct(bt->task, 2, tsp)) { ++ kl_free_block(tsp); ++ return FALSE; ++ } ++ } + + trace = (trace_t *)alloc_trace_rec(C_TEMP); + if (!trace) +@@ -1952,7 +2231,9 @@ + } while (frmp != trace->frame); + } + +- kl_free_block(tsp); ++ if (!XEN_HYPER_MODE()) ++ kl_free_block(tsp); ++ + free_trace_rec(trace); + return (errcnt ? FALSE : TRUE); + } +@@ -1982,7 +2263,7 @@ + (sp && (bt->ref->hexval == sp->value))) + bt->ref->cmdflags |= BT_REF_FOUND; + if (frmp->flag & EX_FRAME) { +- type = eframe_type((struct pt_regs *)frmp->asp); ++ type = eframe_type(frmp->asp); + x86_dump_eframe_common(bt, (ulong *)frmp->asp, + (type == KERNEL_EFRAME)); + } +@@ -2163,12 +2444,14 @@ + bt->flags |= BT_TEXT_SYMBOLS_PRINT|BT_ERROR_MASK; + back_trace(bt); + +- bt->flags = BT_EFRAME_COUNT; +- if ((cnt = machdep->eframe_search(bt))) { +- error(INFO, "possible exception frame%s:\n", +- cnt > 1 ? "s" : ""); +- bt->flags &= ~(ulonglong)BT_EFRAME_COUNT; +- machdep->eframe_search(bt); ++ if (!XEN_HYPER_MODE()) { ++ bt->flags = BT_EFRAME_COUNT; ++ if ((cnt = machdep->eframe_search(bt))) { ++ error(INFO, "possible exception frame%s:\n", ++ cnt > 1 ? "s" : ""); ++ bt->flags &= ~(ulonglong)BT_EFRAME_COUNT; ++ machdep->eframe_search(bt); ++ } + } + } + +@@ -2192,11 +2475,12 @@ + else + buf[0] = NULLCHAR; + +- if ((sp = eframe_label(funcname, eip))) ++ if ((sp = eframe_label(funcname, eip))) + funcname = sp->name; + + fprintf(ofp, "%s#%d [%8lx] %s%s at %lx\n", +- level < 10 ? " " : "", level, esp, funcname, ++ level < 10 ? " " : "", level, esp, ++ funcname_display(funcname), + strlen(buf) ? buf : "", eip); + + if (bt->flags & BT_LINE_NUMBERS) { +@@ -2236,6 +2520,9 @@ + struct eframe_labels *efp; + struct syment *sp; + ++ if (XEN_HYPER_MODE()) ++ return NULL; /* ODA: need support ? */ ++ + efp = &eframe_labels; + + if (!efp->init) { +@@ -2255,7 +2542,8 @@ + efp->tracesys_exit = symbol_search("tracesys_exit"); + } + +- if ((efp->sysenter = symbol_search("sysenter_entry"))) { ++ if ((efp->sysenter = symbol_search("sysenter_entry")) || ++ (efp->sysenter = symbol_search("ia32_sysenter_target"))) { + if ((sp = symbol_search("sysexit_ret_end_marker"))) + efp->sysenter_end = sp; + else if ((sp = symbol_search("system_call"))) +@@ -2325,6 +2613,25 @@ + } + + /* ++ * If it makes sense to display a different function/label name ++ * in a stack entry, it can be done here. Unlike eframe_label(), ++ * this routine won't cause the passed-in function name pointer ++ * to be changed -- this is strictly for display purposes only. ++ */ ++char * ++funcname_display(char *funcname) ++{ ++ struct syment *sp; ++ ++ if (STREQ(funcname, "nmi_stack_correct") && ++ (sp = symbol_search("nmi"))) ++ return sp->name; ++ ++ return funcname; ++} ++ ++ ++/* + * Cache 2k starting from the passed-in text address. This sits on top + * of the instrbuf 256-byte cache, but we don't want to extend its size + * because we can run off the end of a module segment -- if this routine +@@ -4858,6 +5165,8 @@ + } else { + codeptr++; + } ++ if (STREQ(op->name, "ud2a")) ++ codeptr += kt->BUG_bytes; + } else { + opcode = *codeptr; + op = &op_386[*codeptr]; +--- crash/lkcd_dump_v7.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_dump_v7.h 2006-10-11 09:14:35.000000000 -0400 +@@ -1,8 +1,8 @@ + /* lkcd_dump_v5.h - core analysis suite + * + * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 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 +@@ -35,7 +35,7 @@ + #ifndef _DUMP_H + #define _DUMP_H + +-#include ++//#include + + /* define TRUE and FALSE for use in our dump modules */ + #ifndef FALSE +--- crash/xen_hyper_global_data.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/xen_hyper_global_data.c 2008-09-29 16:38:45.000000000 -0400 +@@ -0,0 +1,399 @@ ++/* ++ * xen_hyper_global_data.c ++ * ++ * Portions Copyright (C) 2006-2007 Fujitsu Limited ++ * Portions Copyright (C) 2006-2007 VA Linux Systems Japan K.K. ++ * ++ * Authors: Itsuro Oda ++ * Fumihiko Kakuma ++ * ++ * This file is part of Xencrash. ++ * ++ * Xencrash 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 (version 2 of the License). ++ * ++ * Xencrash 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with Xencrash; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "defs.h" ++ ++#ifdef XEN_HYPERVISOR_ARCH ++#include "xen_hyper_defs.h" ++ ++/* ++ * Global data for Xen hypervisor. ++ */ ++ ++struct xen_hyper_machdep_table xen_hyper_machdep_table = { 0 }; ++struct xen_hyper_machdep_table *xhmachdep = &xen_hyper_machdep_table; ++ ++struct xen_hyper_table xen_hyper_table = { 0 }; ++struct xen_hyper_table *xht = &xen_hyper_table; ++ ++struct xen_hyper_dumpinfo_table xen_hyper_dumpinfo_table = { 0 }; ++struct xen_hyper_dumpinfo_table *xhdit = &xen_hyper_dumpinfo_table; ++ ++struct xen_hyper_domain_table xen_hyper_domain_table = { 0 }; ++struct xen_hyper_domain_table *xhdt = &xen_hyper_domain_table; ++ ++struct xen_hyper_vcpu_table xen_hyper_vcpu_table = { 0 }; ++struct xen_hyper_vcpu_table *xhvct = &xen_hyper_vcpu_table; ++ ++struct xen_hyper_pcpu_table xen_hyper_pcpu_table = { 0 }; ++struct xen_hyper_pcpu_table *xhpct = &xen_hyper_pcpu_table; ++ ++struct xen_hyper_sched_table xen_hyper_sched_table = { 0 }; ++struct xen_hyper_sched_table *xhscht = &xen_hyper_sched_table; ++ ++struct xen_hyper_symbol_table_data xen_hyper_symbol_table_data = { 0 }; ++struct xen_hyper_symbol_table_data *xhsymt = &xen_hyper_symbol_table_data; ++ ++/* ++ * The following commands are for Xen hypervisor. ++ */ ++ ++struct command_table_entry xen_hyper_command_table[] = { ++ {"*", cmd_pointer, help_pointer, 0}, ++ {"alias", cmd_alias, help_alias, 0}, ++ {"ascii", cmd_ascii, help_ascii, 0}, ++ {"bt", cmd_bt, help_bt, 0}, ++ {"dis", cmd_dis, help_dis, 0}, ++ {"domain", xen_hyper_cmd_domain, xen_hyper_help_domain, REFRESH_TASK_TABLE}, ++ {"doms", xen_hyper_cmd_doms, xen_hyper_help_doms, REFRESH_TASK_TABLE}, ++#if defined(X86) || defined(X86_64) ++ {"dumpinfo",xen_hyper_cmd_dumpinfo, xen_hyper_help_dumpinfo,0}, ++#endif ++ {"eval", cmd_eval, help_eval, 0}, ++ {"exit", cmd_quit, help_exit, 0}, ++ {"extend", cmd_extend, help_extend, 0}, ++ {"gdb", cmd_gdb, help_gdb, 0}, ++ {"help", xen_hyper_cmd_help, help_help, 0}, ++ {"list", cmd_list, help__list, 0}, ++ {"log", xen_hyper_cmd_log, xen_hyper_help_log, 0}, ++ {"p", cmd_p, help_p, 0}, ++ {"pcpus", xen_hyper_cmd_pcpus, xen_hyper_help_pcpus, 0}, ++ {"pte", cmd_pte, help_pte, 0}, ++ {"q", cmd_quit, help_quit, 0}, ++ {"rd", cmd_rd, help_rd, 0}, ++ {"repeat", cmd_repeat, help_repeat, 0}, ++ {"sched", xen_hyper_cmd_sched, xen_hyper_help_sched, 0}, ++ {"search", cmd_search, help_search, 0}, ++ {"set", cmd_set, help_set, 0}, ++ {"struct", cmd_struct, help_struct, 0}, ++ {"sym", cmd_sym, help_sym, 0}, ++ {"sys", xen_hyper_cmd_sys, xen_hyper_help_sys, 0}, ++ {"test", cmd_test, NULL, HIDDEN_COMMAND}, ++ {"union", cmd_union, help_union, 0}, ++ {"vcpu", xen_hyper_cmd_vcpu, xen_hyper_help_vcpu, REFRESH_TASK_TABLE}, ++ {"vcpus", xen_hyper_cmd_vcpus, xen_hyper_help_vcpus, REFRESH_TASK_TABLE}, ++ {"whatis", cmd_whatis, help_whatis, 0}, ++ {"wr", cmd_wr, help_wr, 0}, ++ {(char *)NULL} ++}; ++ ++/* ++ * ++ */ ++struct xen_hyper_offset_table xen_hyper_offset_table = { 0 }; ++struct xen_hyper_size_table xen_hyper_size_table = { 0 }; ++ ++/* ++ * help data ++ */ ++ ++char *xen_hyper_help_domain[] = { ++"domain", ++"display contents of domain struct", ++"[domain-id | domainp] ...", ++" This command displays contents of domain struct for selected, or all, domains", ++" domain-id a domain id.", ++" domainp a domain pointer.", ++NULL ++}; ++ ++char *xen_hyper_help_doms[] = { ++"doms", ++"display domain status information", ++"[domain-id | domainp] ...", ++" This command displays domain status for selected, or all, domains" , ++" domain-id a domain id.", ++" domainp a domain pointer.", ++" ", ++" 1. the DOMAIN-ID.", ++" 2. the struct domain pointer.", ++" 3. the domain state", ++" (SF:fully shut down, SH:shutting down, DY:dying,", ++" CP:pause by controller software, PO:polling event channels,", ++" PA:pause by the hypervisor, RU:running).", ++" 4. the TYPE of domain", ++" (O:dom_io, X:dom_xen, I:idle domain, 0:domain 0, U:domain U).", ++" 5. displays max_pages member of domain.", ++" 6. displays tot_pages member of domain.", ++" 7. a number of vcpu that domain is assigned.", ++" 8. the shared_info pointer of domain.", ++" 9. frame containing list of mfns containing list of mfns" , ++" containing p2m.", ++" ", ++" The active domain on each CPU will be highlighted by an angle ", ++" bracket (\">\") preceding its information.", ++" The crashing domain on each CPU will be highlighted by an aster ", ++" (\"*\") preceding its information.", ++"\nEXAMPLES", ++" Show the domain status of all:\n", ++" %s> doms", ++" DID DOMAIN ST T MAXPAGE TOTPAGE VCPU SHARED_I P2M_MFN", ++" 32753 ffbf8080 RU O 0 0 0 0 ----", ++" 32754 ffbfa080 RU X 0 0 0 0 ----", ++" 32767 ffbfc080 RU I 0 0 2 0 ----", ++" >* 0 ff198080 RU 0 ffffffff 32900 2 ff194000 18d0", ++" 4 ffbee080 RU U 4000 4000 2 ff18d000 3eb92", ++" 5 ff186080 RU U 4000 4000 2 ff184000 298d3", ++" %s>", ++NULL ++}; ++ ++char *xen_hyper_help_dumpinfo[] = { ++"dumpinfo", ++"display Xen dump information", ++"[-t | -r] [pcpu-id | enotep] ...", ++" This command displays Xen dump information for selected, or all, cpus" , ++" pcpu-id a physical cpu id.", ++" enotep a ELF Note pointer.", ++" -t display time information.", ++" -r display register information.", ++NULL ++}; ++ ++char *xen_hyper_help_log[] = { ++"log", ++"dump system message buffer", ++" ", ++" This command dumps the xen conring contents in chronological order." , ++" ", ++"EXAMPLES", ++" Dump the Xen message buffer:\n", ++" %s> log", ++" __ __ _____ ___ _ _ _", ++" \\ \\/ /___ _ __ |___ / / _ \\ _ _ _ __ ___| |_ __ _| |__ | | ___", ++" \\ // _ \\ '_ \\ |_ \\| | | |__| | | | '_ \\/ __| __/ _` | '_ \\| |/ _ \\", ++" / \\ __/ | | | ___) | |_| |__| |_| | | | \\__ \\ || (_| | |_) | | __/", ++" /_/\\_\\___|_| |_| |____(_)___/ \\__,_|_| |_|___/\\__\\__,_|_.__/|_|\\___|", ++" ", ++" http://www.cl.cam.ac.uk/netos/xen", ++" University of Cambridge Computer Laboratory", ++" ", ++" Xen version 3.0-unstable (damm@) (gcc version 3.4.6 (Gentoo 3.4.6-r1, ssp-3.4.5-1.0,", ++" pie-8.7.9)) Wed Dec 6 17:34:32 JST 2006", ++" Latest ChangeSet: unavailable", ++" ", ++" (XEN) Console output is synchronous.", ++" (XEN) Command line: 12733-i386-pae/xen.gz console=com1 sync_console conswitch=bb com1", ++" =115200,8n1,0x3f8 dom0_mem=480000 crashkernel=64M@32M", ++" (XEN) Physical RAM map:", ++" (XEN) 0000000000000000 - 0000000000098000 (usable)", ++" (XEN) 0000000000098000 - 00000000000a0000 (reserved)", ++" (XEN) 00000000000f0000 - 0000000000100000 (reserved)", ++" (XEN) 0000000000100000 - 000000003f7f0000 (usable)", ++" (XEN) 000000003f7f0000 - 000000003f7f3000 (ACPI NVS)", ++" (XEN) 000000003f7f3000 - 000000003f800000 (ACPI data)", ++" (XEN) 00000000e0000000 - 00000000f0000000 (reserved)", ++" (XEN) 00000000fec00000 - 0000000100000000 (reserved)", ++" (XEN) Kdump: 64MB (65536kB) at 0x2000000", ++" (XEN) System RAM: 1015MB (1039904kB)", ++" (XEN) ACPI: RSDP (v000 XPC ) @ 0x000f9250", ++" ...", ++NULL ++}; ++ ++char *xen_hyper_help_pcpus[] = { ++"pcpus", ++"display physical cpu information", ++"[-r][-t] [pcpu-id | pcpup] ...", ++" This command displays physical cpu information for selected, or all, cpus" , ++" pcpu-id a physical cpu id.", ++" pcpup a physical cpu pointer.", ++" cur-vcpu a current virtual cpu pointer.", ++" -r display register information.", ++" -t display init_tss information.", ++" ", ++" The crashing physical cpu will be highlighted by an aster ", ++" (\"*\") preceding its information.", ++"\nEXAMPLES", ++" Show the physical cpu status of all:\n", ++" %s> pcpus", ++" PCID PCPU CUR-VCPU", ++" 0 ff1a3fb4 ffbf9080", ++" * 1 ff1dbfb4 ffbf8080", ++" %s>", ++" ", ++" Show the physical cpu status of all with register information:\n", ++" %s> pcpus -r", ++" PCID PCPU CUR-VCPU", ++" * 0 ff1b7fb4 ffbef080", ++" Register information:", ++" struct cpu_user_regs {", ++" ebx = 0x0,", ++" ecx = 0xdcf4bed8,", ++" edx = 0xc0326887,", ++" esi = 0x63,", ++" edi = 0x0,", ++" ebp = 0xdcf4bee0,", ++" eax = 0x25,", ++" error_code = 0x6,", ++" entry_vector = 0xe,", ++" eip = 0xc01014a7,", ++" cs = 0x61,", ++" saved_upcall_mask = 0x0,", ++" _pad0 = 0x0,", ++" eflags = 0x202,", ++" esp = 0xdcf4bed0,", ++" ss = 0x69,", ++" _pad1 = 0x0,", ++" es = 0x7b,", ++" _pad2 = 0x0,", ++" ds = 0x7b,", ++" _pad3 = 0x0,", ++" fs = 0x0,", ++" _pad4 = 0x0,", ++" gs = 0x0,", ++" _pad5 = 0x0", ++" }", ++" ", ++" Show the physical cpu status of all with init_tss information:\n", ++" %s> pcpus -t", ++" PCID PCPU CUR-VCPU", ++" * 0 ff1b7fb4 ffbef080", ++" init_tss information:", ++" struct tss_struct {", ++" back_link = 0x0,", ++" __blh = 0x0,", ++" esp0 = 0xff1b7fe8,", ++" ss0 = 0xe010,", ++" __ss0h = 0x0,", ++" esp1 = 0xdcf4bff8,", ++" ss1 = 0x69,", ++" __ss1h = 0x0,", ++" esp2 = 0x0,", ++" ss2 = 0x0,", ++" __ss2h = 0x0,", ++" __cr3 = 0x0,", ++" eip = 0x0,", ++" eflags = 0x0,", ++" eax = 0x0,", ++" ecx = 0x0,", ++" edx = 0x0,", ++" ebx = 0x0,", ++" esp = 0x0,", ++" ebp = 0x0,", ++" esi = 0x0,", ++" edi = 0x0,", ++" es = 0x0,", ++" __esh = 0x0,", ++" cs = 0x0,", ++" __csh = 0x0,", ++" ss = 0x0,", ++" __ssh = 0x0,", ++" ds = 0x0,", ++" __dsh = 0x0,", ++" fs = 0x0,", ++" __fsh = 0x0,", ++" gs = 0x0,", ++" __gsh = 0x0,", ++" ldt = 0x0,", ++" __ldth = 0x0,", ++" trace = 0x0,", ++" bitmap = 0x8000,", ++" __cacheline_filler = \"\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\"", ++" }", ++NULL ++}; ++ ++char *xen_hyper_help_sched[] = { ++"pcpus", ++"display scheduler information", ++"[-v] [pcpu-id] ...", ++" This command displays scheduler information for selected, or all, cpus" , ++" pcpu-id a physical cpu id.", ++" -v display verbosely scheduler information.", ++" ", ++NULL ++}; ++ ++char *xen_hyper_help_sys[] = { ++"sys", ++"system data", ++"[-c [name|number]] config", ++" This command displays system-specific data. If no arguments are entered,\n" ++" the same system data shown during %s invocation is shown.\n", ++"\nEXAMPLES", ++" Display essential system information:\n", ++" %s> sys", ++" DEBUG KERNEL: xen-syms", ++" DUMPFILE: vmcore", ++" CPUS: 2", ++" DOMAINS: 2", ++" MACHINE: Pentium III (Coppermine) (866 Mhz)", ++" MEMORY: 2 GB", ++" %s>", ++NULL ++}; ++ ++char *xen_hyper_help_vcpu[] = { ++"vcpu", ++"display contents of vcpu struct", ++"[vcpup] ...", ++" This command displays contents of vcpu struct for selected, or all, vcpus", ++" vcpu-id a virtual cpu id.", ++" vcpup a virtual cpu pointer.", ++NULL ++}; ++ ++char *xen_hyper_help_vcpus[] = { ++"vcpus", ++"display vcpu status information", ++"[-i domain-id vcpu-id | vcpup] ...", ++" This command displays vcpu status for selected, or all, vcpus" , ++" domain-id a domain id.", ++" vcpu-id a VCPU-ID.", ++" vcpup a hexadecimal struct vcpu pointer.", ++" -i specify vcpu id as an argument.", ++" ", ++" 1. the VCPU-ID.", ++" 2. the physical CPU-ID.", ++" 3. the struct vcpu pointer.", ++" 4. the vcpu state (RU, BL, OF).", ++" 5. the TYPE of domain that vcpu is assigned(I, 0, G).", ++" 6. the DOMAIN-ID of domain that vcpu is assigned.", ++" 7. the struct domain pointer of domain that vcpu is assigned.", ++" ", ++" The active vcpu on each CPU will be highlighted by an angle ", ++" bracket (\">\") preceding its information.", ++" The crashing vcpu on each CPU will be highlighted by an aster ", ++" (\"*\") preceding its information.", ++"\nEXAMPLES", ++" Show the vcpu status of all:\n", ++" %s> vcpus", ++" VCID PCID VCPU ST T DOMID DOMAIN", ++" 0 0 ffbfe080 RU I 32767 ffbfc080", ++" 1 1 ff1df080 RU I 32767 ffbfc080", ++" >* 0 0 ff195180 RU 0 0 ff198080", ++" > 1 1 ff190080 BL 0 0 ff198080", ++" 0 1 ff18a080 BL G 4 ffbee080", ++" 1 0 ff189080 BL G 4 ffbee080", ++" 0 1 ff1f3080 BL G 5 ff186080", ++" 1 0 ff1f2080 BL G 5 ff186080", ++" %s>", ++NULL ++}; ++ ++struct task_context fake_tc = { 0 }; ++ ++#endif +--- crash/xen_hyper_defs.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/xen_hyper_defs.h 2008-12-03 12:03:24.000000000 -0500 +@@ -0,0 +1,976 @@ ++/* ++ * xen_hyper_defs.h ++ * ++ * Portions Copyright (C) 2006-2007 Fujitsu Limited ++ * Portions Copyright (C) 2006-2007 VA Linux Systems Japan K.K. ++ * ++ * Authors: Itsuro Oda ++ * Fumihiko Kakuma ++ * ++ * This file is part of Xencrash. ++ * ++ * Xencrash 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 (version 2 of the License). ++ * ++ * Xencrash 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with Xencrash; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifdef XEN_HYPERVISOR_ARCH ++ ++#include ++#include ++ ++#ifdef X86 ++/* Xen Hypervisor address space layout */ ++#define IOREMAP_VIRT_END (0UL) ++#define IOREMAP_VIRT_START (0xFFC00000UL) ++#define DIRECTMAP_VIRT_END IOREMAP_VIRT_START ++#define DIRECTMAP_VIRT_START (0xFF000000UL) ++#define MAPCACHE_VIRT_END DIRECTMAP_VIRT_START ++#define MAPCACHE_VIRT_START (0xFFC00000UL) ++#define PERDOMAIN_VIRT_END DIRECTMAP_VIRT_START ++#define PERDOMAIN_VIRT_START (0xFE800000UL) ++#define SH_LINEAR_PT_VIRT_END PERDOMAIN_VIRT_START ++#define SH_LINEAR_PT_VIRT_START (0xFE400000UL) ++#define SH_LINEAR_PT_VIRT_START_PAE (0xFE000000UL) ++#define LINEAR_PT_VIRT_END SH_LINEAR_PT_VIRT_START ++#define LINEAR_PT_VIRT_START (0xFE000000UL) ++#define LINEAR_PT_VIRT_START_PAE (0xFD800000UL) ++#define RDWR_MPT_VIRT_END LINEAR_PT_VIRT_START ++#define RDWR_MPT_VIRT_START (0xFDC00000UL) ++#define RDWR_MPT_VIRT_START_PAE (0xFC800000UL) ++#define FRAMETABLE_VIRT_END RDWR_MPT_VIRT_START ++#define FRAMETABLE_VIRT_START (0xFC400000UL) ++#define FRAMETABLE_VIRT_START_PAE (0xF6800000UL) ++#define RO_MPT_VIRT_END FRAMETABLE_VIRT_START ++#define RO_MPT_VIRT_START (0xFC000000UL) ++#define RO_MPT_VIRT_START_PAE (0xF5800000UL) ++ ++#define HYPERVISOR_VIRT_START RO_MPT_VIRT_START ++#define HYPERVISOR_VIRT_START_PAE RO_MPT_VIRT_START_PAE ++#endif ++ ++#ifdef X86_64 ++#define HYPERVISOR_VIRT_START (0xffff800000000000) ++#define HYPERVISOR_VIRT_END (0xffff880000000000) ++#define DIRECTMAP_VIRT_START (0xffff830000000000) ++#define DIRECTMAP_VIRT_END (0xffff840000000000) ++#define PAGE_OFFSET_XEN_HYPER DIRECTMAP_VIRT_START ++#define XEN_VIRT_START (0xffff828c80000000) ++#define XEN_VIRT_ADDR(vaddr) \ ++ (((vaddr) >= XEN_VIRT_START) && ((vaddr) < DIRECTMAP_VIRT_START)) ++#endif ++ ++#ifdef IA64 ++#define HYPERVISOR_VIRT_START (0xe800000000000000) ++#define HYPERVISOR_VIRT_END (0xf800000000000000) ++#define DEFAULT_SHAREDINFO_ADDR (0xf100000000000000) ++#define PERCPU_PAGE_SIZE 65536 ++#define PERCPU_ADDR (DEFAULT_SHAREDINFO_ADDR - PERCPU_PAGE_SIZE) ++#define DIRECTMAP_VIRT_START (0xf000000000000000) ++#define DIRECTMAP_VIRT_END PERCPU_ADDR ++#define VIRT_FRAME_TABLE_SIZE (0x0100000000000000) ++ ++#define PERCPU_VIRT_ADDR(vaddr) \ ++ (((vaddr) >= PERCPU_ADDR) && ((vaddr) < PERCPU_ADDR + PERCPU_PAGE_SIZE)) ++ ++#define FRAME_TABLE_VIRT_ADDR(vaddr) \ ++ ((vaddr) >= xhmachdep->frame_table && (vaddr) < xhmachdep->frame_table + VIRT_FRAME_TABLE_SIZE) ++ ++#undef IA64_RBS_OFFSET ++#define IA64_RBS_OFFSET ((XEN_HYPER_SIZE(vcpu) + 15) & ~15) ++ ++#endif /* IA64 */ ++ ++#define DIRECTMAP_VIRT_ADDR(vaddr) \ ++ (((vaddr) >= DIRECTMAP_VIRT_START) && ((vaddr) < DIRECTMAP_VIRT_END)) ++ ++typedef uint16_t domid_t; ++typedef uint32_t Elf_Word; ++ ++/* ++ * NOTE kakuma: The following defines are temporary version for ++ * elf note format which is used only in crash. ++ */ ++#define XEN_HYPER_ELF_NOTE_V1 1 ++#define XEN_HYPER_ELF_NOTE_V2 2 ++#define XEN_HYPER_ELF_NOTE_V3 3 ++#define XEN_HYPER_ELF_NOTE_V4 4 ++ ++#ifdef X86 ++#define XEN_HYPER_ELF_NOTE_V4_NOTE_SIZE 0x100 ++#endif ++#if defined(X86_64) || defined(IA64) ++#define XEN_HYPER_ELF_NOTE_V4_NOTE_SIZE 0x200 ++#endif ++ ++/* ++ * Xen Hyper ++ */ ++#define XEN_HYPER_SMP (0x400) ++ ++#ifdef X86 ++#define XEN_HYPER_MAX_VIRT_CPUS (32) ++#define XEN_HYPER_HZ 100 ++#endif ++#ifdef X86_64 ++#define XEN_HYPER_MAX_VIRT_CPUS (32) ++#define XEN_HYPER_HZ 100 ++#endif ++#ifdef IA64 ++#define XEN_HYPER_MAX_VIRT_CPUS (64) ++#define XEN_HYPER_HZ 100 ++#endif ++#ifndef XEN_HYPER_MAX_VIRT_CPUS ++#define XEN_HYPER_MAX_VIRT_CPUS (1) ++#endif ++ ++#if defined(X86) || defined(X86_64) ++#define xen_hyper_per_cpu(var, cpu) \ ++ ((ulong)(var) + (((ulong)(cpu))<percpu_shift)) ++#elif defined(IA64) ++#define xen_hyper_per_cpu(var, cpu) \ ++ ((xht->flags & XEN_HYPER_SMP) ? \ ++ (ulong)(var) + (xht->__per_cpu_offset[cpu]) : \ ++ (ulong)(var)) ++#endif ++ ++#if defined(X86) || defined(X86_64) ++#define XEN_HYPER_STACK_ORDER 2 ++#if 0 ++#define XEN_HYPER_STACK_SIZE (machdep->pagesize << XEN_HYPER_STACK_ORDER) ++#endif ++#define XEN_HYPER_GET_CPU_INFO(sp) \ ++ ((sp & ~(STACKSIZE()-1)) | \ ++ (STACKSIZE() - XEN_HYPER_SIZE(cpu_info))) ++#endif ++ ++#define XEN_HYPER_CONRING_SIZE 16384 ++ ++/* system time */ ++#define XEN_HYPER_NANO_TO_SEC(ns) ((ulonglong)((ns) / 1000000000ULL)) ++#define XEN_HYPER_MICR_TO_SEC(us) ((ulonglong)((us) / 1000000ULL)) ++#define XEN_HYPER_MILI_TO_SEC(ms) ((ulonglong)((ms) / 1000ULL)) ++ ++/* ++ * Domain ++ */ ++/* Prepared domain ID. */ ++#define XEN_HYPER_DOMID_IO (0x7FF1U) ++#define XEN_HYPER_DOMID_XEN (0x7FF2U) ++#define XEN_HYPER_DOMID_IDLE (0x7FFFU) ++ ++/* Domain flags (domain_flags). */ ++ /* Is this domain privileged? */ ++#define XEN_HYPER__DOMF_privileged 0 ++#define XEN_HYPER_DOMF_privileged (1UL<= 0) ++#define XEN_HYPER_VALID_STRUCT(X) (xen_hyper_size_table.X >= 0) ++#define XEN_HYPER_VALID_MEMBER(X) (xen_hyper_offset_table.X >= 0) ++ ++#define XEN_HYPER_ASSIGN_SIZE(X) (xen_hyper_size_table.X) ++#define XEN_HYPER_ASSIGN_OFFSET(X) (xen_hyper_offset_table.X) ++ ++#define XEN_HYPER_STRUCT_SIZE_INIT(X, Y) (XEN_HYPER_ASSIGN_SIZE(X) = STRUCT_SIZE(Y)) ++#define XEN_HYPER_MEMBER_SIZE_INIT(X, Y, Z) (XEN_HYPER_ASSIGN_SIZE(X) = MEMBER_SIZE(Y, Z)) ++#define XEN_HYPER_MEMBER_OFFSET_INIT(X, Y, Z) (XEN_HYPER_ASSIGN_OFFSET(X) = MEMBER_OFFSET(Y, Z)) ++ ++/* ++ * System ++ */ ++#define XEN_HYPER_MAX_CPUS() (xht->max_cpus) ++#define XEN_HYPER_CRASHING_CPU() (xht->crashing_cpu) ++ ++/* ++ * Dump information ++ */ ++#define XEN_HYPER_X86_NOTE_EIP(regs) (regs[12]) ++#define XEN_HYPER_X86_NOTE_ESP(regs) (regs[15]) ++#define XEN_HYPER_X86_64_NOTE_RIP(regs) (regs[16]) ++#define XEN_HYPER_X86_64_NOTE_RSP(regs) (regs[19]) ++ ++/* ++ * Domain ++ */ ++#define XEN_HYPER_DOMAIN_F_INIT 0x1 ++ ++#define XEN_HYPER_NR_DOMAINS() (xht->domains) ++#define XEN_HYPER_RUNNING_DOMAINS() (xhdt->running_domains) ++ ++/* ++ * Phisycal CPU ++ */ ++#define XEN_HYPER_NR_PCPUS() (xht->pcpus) ++#define for_cpu_indexes(i, cpuid) \ ++ for (i = 0, cpuid = xht->cpu_idxs[i]; \ ++ i < XEN_HYPER_NR_PCPUS(); \ ++ cpuid = xht->cpu_idxs[++i]) ++#define XEN_HYPER_CURR_VCPU(pcpuid) \ ++ (xen_hyper_get_active_vcpu_from_pcpuid(pcpuid)) ++ ++/* ++ * VCPU ++ */ ++#define XEN_HYPER_VCPU_F_INIT 0x1 ++ ++#define XEN_HYPER_NR_VCPUS_IN_DOM(domain_context) (domain_context->vcpu_cnt) ++#define XEN_HYPER_VCPU_LAST_CONTEXT() (xhvct->last) ++ ++/* ++ * tools ++ */ ++#define XEN_HYPER_PRI(fp, len, str, buf, flag, args) \ ++ sprintf args; \ ++ xen_hyper_fpr_indent(fp, len, str, buf, flag); ++#define XEN_HYPER_PRI_CONST(fp, len, str, flag) \ ++ xen_hyper_fpr_indent(fp, len, str, NULL, flag); ++ ++#define XEN_HYPER_PRI_L (0x0) ++#define XEN_HYPER_PRI_R (0x1) ++#define XEN_HYPER_PRI_LF (0x2) ++ ++/* ++ * Global data ++ */ ++extern struct xen_hyper_machdep_table *xhmachdep; ++extern struct xen_hyper_table *xht; ++extern struct xen_hyper_dumpinfo_table *xhdit; ++extern struct xen_hyper_domain_table *xhdt; ++extern struct xen_hyper_vcpu_table *xhvct; ++extern struct xen_hyper_pcpu_table *xhpct; ++extern struct xen_hyper_sched_table *xhscht; ++extern struct xen_hyper_symbol_table_data *xhsymt; ++ ++extern struct xen_hyper_offset_table xen_hyper_offset_table; ++extern struct xen_hyper_size_table xen_hyper_size_table; ++ ++extern struct command_table_entry xen_hyper_command_table[]; ++extern struct task_context fake_tc; ++ ++/* ++ * Xen Hyper command help ++ */ ++extern char *xen_hyper_help_domain[]; ++extern char *xen_hyper_help_doms[]; ++extern char *xen_hyper_help_dumpinfo[]; ++extern char *xen_hyper_help_log[]; ++extern char *xen_hyper_help_pcpus[]; ++extern char *xen_hyper_help_sched[]; ++extern char *xen_hyper_help_sys[]; ++extern char *xen_hyper_help_vcpu[]; ++extern char *xen_hyper_help_vcpus[]; ++ ++/* ++ * Prototype ++ */ ++ulonglong xen_hyper_get_uptime_hyper(void); ++ ++/* ++ * x86 ++ */ ++int xen_hyper_x86_get_smp_cpus(void); ++uint64_t xen_hyper_x86_memory_size(void); ++ ++/* ++ * IA64 ++ */ ++int xen_hyper_ia64_get_smp_cpus(void); ++uint64_t xen_hyper_ia64_memory_size(void); ++ulong xen_hyper_ia64_processor_speed(void); ++ ++/* ++ * Xen Hyper ++ */ ++void xen_hyper_init(void); ++void xen_hyper_domain_init(void); ++void xen_hyper_vcpu_init(void); ++void xen_hyper_dumpinfo_init(void); ++void xen_hyper_misc_init(void); ++void xen_hyper_post_init(void); ++struct xen_hyper_dumpinfo_context *xen_hyper_id_to_dumpinfo_context(uint id); ++struct xen_hyper_dumpinfo_context *xen_hyper_note_to_dumpinfo_context(ulong note); ++char *xen_hyper_fill_elf_notes(ulong note, char *note_buf, int type); ++ ++/* domain */ ++void xen_hyper_refresh_domain_context_space(void); ++int xen_hyper_get_domains(void); ++char *xen_hyper_get_domain_next(int mod, ulong *next); ++domid_t xen_hyper_domain_to_id(ulong domain); ++char *xen_hyper_id_to_domain_struct(domid_t id); ++struct xen_hyper_domain_context * ++xen_hyper_domain_to_domain_context(ulong domain); ++struct xen_hyper_domain_context * ++xen_hyper_id_to_domain_context(domid_t id); ++struct xen_hyper_domain_context * ++xen_hyper_store_domain_context(struct xen_hyper_domain_context *dc, ++ ulong domain, char *dp); ++char *xen_hyper_read_domain_from_context(struct xen_hyper_domain_context *dc); ++char *xen_hyper_read_domain(ulong domain); ++char *xen_hyper_read_domain_verify(ulong domain); ++char *xen_hyper_fill_domain_struct(ulong domain, char *domain_struct); ++void xen_hyper_alloc_domain_context_space(int domains); ++ulong xen_hyper_domain_state(struct xen_hyper_domain_context *dc); ++ ++/* vcpu */ ++void xen_hyper_refresh_vcpu_context_space(void); ++struct xen_hyper_vcpu_context * ++xen_hyper_vcpu_to_vcpu_context(ulong vcpu); ++struct xen_hyper_vcpu_context * ++xen_hyper_id_to_vcpu_context(ulong domain, domid_t did, int vcid); ++struct xen_hyper_vcpu_context_array * ++xen_hyper_domain_to_vcpu_context_array(ulong domain); ++struct xen_hyper_vcpu_context_array * ++xen_hyper_domid_to_vcpu_context_array(domid_t id); ++struct xen_hyper_vcpu_context * ++xen_hyper_store_vcpu_context(struct xen_hyper_vcpu_context *vcc, ++ ulong vcpu, char *vcp); ++char * ++xen_hyper_read_vcpu_from_context(struct xen_hyper_vcpu_context *vcc); ++char *xen_hyper_read_vcpu(ulong vcpu); ++char *xen_hyper_read_vcpu_verify(ulong vcpu); ++char *xen_hyper_fill_vcpu_struct(ulong vcpu, char *vcpu_struct); ++void xen_hyper_alloc_vcpu_context_arrays_space(int domains); ++void xen_hyper_alloc_vcpu_context_space(struct xen_hyper_vcpu_context_array *vcca, int vcpus); ++int xen_hyper_vcpu_state(struct xen_hyper_vcpu_context *vcc); ++ ++/* pcpu */ ++#if defined(X86) || defined(X86_64) ++void xen_hyper_x86_pcpu_init(void); ++#elif defined(IA64) ++void xen_hyper_ia64_pcpu_init(void); ++#endif ++struct xen_hyper_pcpu_context *xen_hyper_id_to_pcpu_context(uint id); ++struct xen_hyper_pcpu_context *xen_hyper_pcpu_to_pcpu_context(ulong pcpu); ++struct xen_hyper_pcpu_context *xen_hyper_store_pcpu_context(struct xen_hyper_pcpu_context *pcc, ++ ulong pcpu, char *pcp); ++struct xen_hyper_pcpu_context *xen_hyper_store_pcpu_context_tss(struct xen_hyper_pcpu_context *pcc, ++ ulong init_tss, char *tss); ++char *xen_hyper_read_pcpu(ulong pcpu); ++char *xen_hyper_fill_pcpu_struct(ulong pcpu, char *pcpu_struct); ++void xen_hyper_alloc_pcpu_context_space(int pcpus); ++ ++/* others */ ++char *xen_hyper_x86_fill_cpu_data(int idx, char *cpuinfo_x86); ++char *xen_hyper_ia64_fill_cpu_data(int idx, char *cpuinfo_ia64); ++int xen_hyper_is_vcpu_crash(struct xen_hyper_vcpu_context *vcc); ++void xen_hyper_print_bt_header(FILE *out, ulong pcpu, int newline); ++ulong xen_hyper_get_active_vcpu_from_pcpuid(ulong pcpu); ++ulong xen_hyper_pcpu_to_active_vcpu(ulong pcpu); ++void xen_hyper_get_cpu_info(void); ++int xen_hyper_test_pcpu_id(uint pcpu_id); ++ ++/* ++ * Xen Hyper command ++ */ ++void xen_hyper_cmd_help(void); ++void xen_hyper_cmd_domain(void); ++void xen_hyper_cmd_doms(void); ++void xen_hyper_cmd_dumpinfo(void); ++void xen_hyper_cmd_log(void); ++void xen_hyper_dump_log(void); ++void xen_hyper_cmd_pcpus(void); ++void xen_hyper_cmd_sched(void); ++void xen_hyper_cmd_sys(void); ++void xen_hyper_cmd_vcpu(void); ++void xen_hyper_cmd_vcpus(void); ++void xen_hyper_display_sys_stats(void); ++ ++void xen_hyper_show_vcpu_context(struct xen_hyper_vcpu_context *vcc); ++char *xen_hyper_domain_state_string(struct xen_hyper_domain_context *dc, ++ char *buf, int verbose); ++char *xen_hyper_vcpu_state_string(struct xen_hyper_vcpu_context *vcc, ++ char *buf, int verbose); ++ ++/* tools */ ++void xen_hyper_fpr_indent(FILE *fp, int len, char *str1, char *str2, int flag); ++ ++#else ++ ++#define XEN_HYPERVISOR_NOT_SUPPORTED \ ++ "Xen hypervisor mode not supported on this architecture\n" ++ ++#endif +--- crash/x86_64.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/x86_64.c 2009-01-26 14:56:02.000000000 -0500 +@@ -1,7 +1,7 @@ + /* x86_64.c -- core analysis suite + * +- * Copyright (C) 2004, 2005 David Anderson +- * Copyright (C) 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -14,11 +14,16 @@ + * GNU General Public License for more details. + */ + #include "defs.h" ++#include "xen_hyper_defs.h" + + #ifdef X86_64 + + static int x86_64_kvtop(struct task_context *, ulong, physaddr_t *, int); ++static int x86_64_kvtop_xen_wpt(struct task_context *, ulong, physaddr_t *, int); + static int x86_64_uvtop(struct task_context *, ulong, physaddr_t *, int); ++static int x86_64_uvtop_level4(struct task_context *, ulong, physaddr_t *, int); ++static int x86_64_uvtop_level4_xen_wpt(struct task_context *, ulong, physaddr_t *, int); ++static int x86_64_uvtop_level4_rhel4_xen_wpt(struct task_context *, ulong, physaddr_t *, int); + static ulong x86_64_vmalloc_start(void); + static int x86_64_is_task_addr(ulong); + static int x86_64_verify_symbol(const char *, ulong, char); +@@ -32,14 +37,17 @@ + #define EFRAME_VERIFY (0x2) + #define EFRAME_CS (0x4) + #define EFRAME_SEARCH (0x8) ++static int x86_64_print_eframe_location(ulong, int, FILE *); + static void x86_64_back_trace_cmd(struct bt_info *); + static ulong x86_64_in_exception_stack(struct bt_info *); + static ulong x86_64_in_irqstack(struct bt_info *); + static void x86_64_low_budget_back_trace_cmd(struct bt_info *); ++static void x86_64_dwarf_back_trace_cmd(struct bt_info *); + static void x86_64_get_dumpfile_stack_frame(struct bt_info *, ulong *, ulong *); + static struct syment *x86_64_function_called_by(ulong); + static int is_direct_call_target(struct bt_info *); + static void get_x86_64_frame(struct bt_info *, ulong *, ulong *); ++static ulong text_lock_function(char *, struct bt_info *, ulong); + static int x86_64_print_stack_entry(struct bt_info *, FILE *, int, int, ulong); + static void x86_64_display_full_frame(struct bt_info *, ulong, FILE *); + static void x86_64_do_bt_reference_check(struct bt_info *, ulong,char *); +@@ -56,6 +64,8 @@ + static void x86_64_display_memmap(void); + static void x86_64_dump_line_number(ulong); + static struct line_number_hook x86_64_line_number_hooks[]; ++static void x86_64_calc_phys_base(void); ++static int x86_64_is_module_addr(ulong); + static int x86_64_is_kvaddr(ulong); + static int x86_64_is_uvaddr(ulong, struct task_context *); + void x86_64_compiler_warning_stub(void); +@@ -63,7 +73,25 @@ + static void x86_64_cpu_pda_init(void); + static void x86_64_ist_init(void); + static void x86_64_post_init(void); +- ++static void parse_cmdline_arg(void); ++static void x86_64_clear_machdep_cache(void); ++static void x86_64_irq_eframe_link_init(void); ++static int x86_64_xendump_p2m_create(struct xendump_data *); ++static char *x86_64_xendump_load_page(ulong, struct xendump_data *); ++static int x86_64_xendump_page_index(ulong, struct xendump_data *); ++static int x86_64_xen_kdump_p2m_create(struct xen_kdump_data *); ++static char *x86_64_xen_kdump_load_page(ulong, char *); ++static ulong x86_64_xen_kdump_page_mfn(ulong); ++static void x86_64_debug_dump_page(FILE *, char *, char *); ++static void x86_64_get_xendump_regs(struct xendump_data *, struct bt_info *, ulong *, ulong *); ++static ulong x86_64_xendump_panic_task(struct xendump_data *); ++static void x86_64_init_hyper(int); ++static ulong x86_64_get_stackbase_hyper(ulong); ++static ulong x86_64_get_stacktop_hyper(ulong); ++static int x86_64_framesize_cache_resize(void); ++static int x86_64_framesize_cache_func(int, ulong, int *); ++static int x86_64_get_framesize(struct bt_info *, ulong); ++static void x86_64_framesize_debug(struct bt_info *); + + struct machine_specific x86_64_machine_specific = { 0 }; + +@@ -74,6 +102,11 @@ + void + x86_64_init(int when) + { ++ if (XEN_HYPER_MODE()) { ++ x86_64_init_hyper(when); ++ return; ++ } ++ + switch (when) + { + case PRE_SYMTAB: +@@ -86,6 +119,8 @@ + machdep->pageoffset = machdep->pagesize - 1; + machdep->pagemask = ~((ulonglong)machdep->pageoffset); + machdep->stacksize = machdep->pagesize * 2; ++ if ((machdep->machspec->upml = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc upml space."); + if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pgd space."); + if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) +@@ -93,17 +128,99 @@ + if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc ptbl space."); + if ((machdep->machspec->pml4 = +- (char *)malloc(PAGESIZE())) == NULL) ++ (char *)malloc(PAGESIZE()*2)) == NULL) + error(FATAL, "cannot malloc pml4 space."); ++ machdep->machspec->last_upml_read = 0; ++ machdep->machspec->last_pml4_read = 0; + machdep->last_pgd_read = 0; + machdep->last_pmd_read = 0; + machdep->last_ptbl_read = 0; + machdep->verify_paddr = generic_verify_paddr; + machdep->ptrs_per_pgd = PTRS_PER_PGD; + machdep->flags |= MACHDEP_BT_TEXT; ++ machdep->flags |= FRAMESIZE_DEBUG; ++ machdep->machspec->irq_eframe_link = UNINITIALIZED; ++ if (machdep->cmdline_arg) ++ parse_cmdline_arg(); + break; + + case PRE_GDB: ++ if (!(machdep->flags & VM_FLAGS)) { ++ if (symbol_exists("xen_start_info")) { ++ if (PVOPS()) ++ machdep->flags |= VM_2_6_11; ++ else if (symbol_exists("low_pml4") && ++ symbol_exists("swap_low_mappings")) ++ machdep->flags |= VM_XEN_RHEL4; ++ else ++ machdep->flags |= VM_XEN; ++ } else if (symbol_exists("boot_vmalloc_pgt")) ++ machdep->flags |= VM_ORIG; ++ else ++ machdep->flags |= VM_2_6_11; ++ } ++ ++ switch (machdep->flags & VM_FLAGS) ++ { ++ case VM_ORIG: ++ /* pre-2.6.11 layout */ ++ machdep->machspec->userspace_top = USERSPACE_TOP_ORIG; ++ machdep->machspec->page_offset = PAGE_OFFSET_ORIG; ++ machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_ORIG; ++ machdep->machspec->vmalloc_end = VMALLOC_END_ORIG; ++ machdep->machspec->modules_vaddr = MODULES_VADDR_ORIG; ++ machdep->machspec->modules_end = MODULES_END_ORIG; ++ ++ free(machdep->machspec->upml); ++ machdep->machspec->upml = NULL; ++ ++ machdep->uvtop = x86_64_uvtop; ++ break; ++ ++ case VM_2_6_11: ++ /* 2.6.11 layout */ ++ machdep->machspec->userspace_top = USERSPACE_TOP_2_6_11; ++ machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_2_6_11; ++ machdep->machspec->vmalloc_end = VMALLOC_END_2_6_11; ++ machdep->machspec->modules_vaddr = MODULES_VADDR_2_6_11; ++ machdep->machspec->modules_end = MODULES_END_2_6_11; ++ ++ /* 2.6.24 layout */ ++ machdep->machspec->vmemmap_vaddr = VMEMMAP_VADDR_2_6_24; ++ machdep->machspec->vmemmap_end = VMEMMAP_END_2_6_24; ++ if (symbol_exists("vmemmap_populate")) ++ machdep->flags |= VMEMMAP; ++ ++ if (kernel_symbol_exists("end_pfn")) ++ /* 2.6.11 layout */ ++ machdep->machspec->page_offset = PAGE_OFFSET_2_6_11; ++ else ++ /* 2.6.27 layout */ ++ machdep->machspec->page_offset = PAGE_OFFSET_2_6_27; ++ ++ machdep->uvtop = x86_64_uvtop_level4; ++ break; ++ ++ case VM_XEN: ++ /* Xen layout */ ++ machdep->machspec->userspace_top = USERSPACE_TOP_XEN; ++ machdep->machspec->page_offset = PAGE_OFFSET_XEN; ++ machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_XEN; ++ machdep->machspec->vmalloc_end = VMALLOC_END_XEN; ++ machdep->machspec->modules_vaddr = MODULES_VADDR_XEN; ++ machdep->machspec->modules_end = MODULES_END_XEN; ++ break; ++ ++ case VM_XEN_RHEL4: ++ /* RHEL4 Xen layout */ ++ machdep->machspec->userspace_top = USERSPACE_TOP_XEN_RHEL4; ++ machdep->machspec->page_offset = PAGE_OFFSET_XEN_RHEL4; ++ machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_XEN_RHEL4; ++ machdep->machspec->vmalloc_end = VMALLOC_END_XEN_RHEL4; ++ machdep->machspec->modules_vaddr = MODULES_VADDR_XEN_RHEL4; ++ machdep->machspec->modules_end = MODULES_END_XEN_RHEL4; ++ break; ++ } + machdep->kvbase = (ulong)PAGE_OFFSET; + machdep->identity_map_base = (ulong)PAGE_OFFSET; + machdep->is_kvaddr = x86_64_is_kvaddr; +@@ -111,7 +228,6 @@ + machdep->eframe_search = x86_64_eframe_search; + machdep->back_trace = x86_64_low_budget_back_trace_cmd; + machdep->processor_speed = x86_64_processor_speed; +- machdep->uvtop = x86_64_uvtop; + machdep->kvtop = x86_64_kvtop; + machdep->get_task_pgd = x86_64_get_task_pgd; + machdep->get_stack_frame = x86_64_get_stack_frame; +@@ -126,6 +242,12 @@ + machdep->line_number_hooks = x86_64_line_number_hooks; + machdep->value_to_symbol = generic_machdep_value_to_symbol; + machdep->init_kernel_pgd = x86_64_init_kernel_pgd; ++ machdep->clear_machdep_cache = x86_64_clear_machdep_cache; ++ machdep->xendump_p2m_create = x86_64_xendump_p2m_create; ++ machdep->get_xendump_regs = x86_64_get_xendump_regs; ++ machdep->xen_kdump_p2m_create = x86_64_xen_kdump_p2m_create; ++ machdep->xendump_panic_task = x86_64_xendump_panic_task; ++ x86_64_calc_phys_base(); + break; + + case POST_GDB: +@@ -140,8 +262,23 @@ + MEMBER_OFFSET_INIT(thread_struct_rip, "thread_struct", "rip"); + MEMBER_OFFSET_INIT(thread_struct_rsp, "thread_struct", "rsp"); + MEMBER_OFFSET_INIT(thread_struct_rsp0, "thread_struct", "rsp0"); ++ if (INVALID_MEMBER(thread_struct_rip)) ++ MEMBER_OFFSET_INIT(thread_struct_rip, "thread_struct", "ip"); ++ if (INVALID_MEMBER(thread_struct_rsp)) ++ MEMBER_OFFSET_INIT(thread_struct_rsp, "thread_struct", "sp"); ++ if (INVALID_MEMBER(thread_struct_rsp0)) ++ MEMBER_OFFSET_INIT(thread_struct_rsp0, "thread_struct", "sp0"); + STRUCT_SIZE_INIT(tss_struct, "tss_struct"); + MEMBER_OFFSET_INIT(tss_struct_ist, "tss_struct", "ist"); ++ if (INVALID_MEMBER(tss_struct_ist)) { ++ long x86_tss_offset, ist_offset; ++ x86_tss_offset = MEMBER_OFFSET("tss_struct", "x86_tss"); ++ ist_offset = MEMBER_OFFSET("x86_hw_tss", "ist"); ++ if ((x86_tss_offset != INVALID_OFFSET) && ++ (ist_offset != INVALID_OFFSET)) ++ ASSIGN_OFFSET(tss_struct_ist) = x86_tss_offset + ++ ist_offset; ++ } + MEMBER_OFFSET_INIT(user_regs_struct_rip, + "user_regs_struct", "rip"); + MEMBER_OFFSET_INIT(user_regs_struct_rsp, +@@ -158,16 +295,49 @@ + if ((machdep->machspec->irqstack = (char *) + malloc(machdep->machspec->stkinfo.isize)) == NULL) + error(FATAL, "cannot malloc irqstack space."); +- if (symbol_exists("irq_desc")) +- ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc, +- "irq_desc", NULL, 0); +- else +- machdep->nr_irqs = 224; /* NR_IRQS (at least) */ ++ if (symbol_exists("irq_desc")) { ++ if (LKCD_KERNTYPES()) ++ ARRAY_LENGTH_INIT_ALT(machdep->nr_irqs, ++ "irq_desc", "kernel_stat.irqs", NULL, 0); ++ else ++ ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc, ++ "irq_desc", NULL, 0); ++ } else ++ machdep->nr_irqs = 224; /* NR_IRQS (at least) */ + machdep->vmalloc_start = x86_64_vmalloc_start; + machdep->dump_irq = x86_64_dump_irq; +- machdep->hz = HZ; +- if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) +- machdep->hz = 1000; ++ if (!machdep->hz) { ++ machdep->hz = HZ; ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) ++ machdep->hz = 1000; ++ } ++ machdep->section_size_bits = _SECTION_SIZE_BITS; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; ++ if (XEN()) { ++ if (kt->xen_flags & WRITABLE_PAGE_TABLES) { ++ switch (machdep->flags & VM_FLAGS) ++ { ++ case VM_XEN: ++ machdep->uvtop = x86_64_uvtop_level4_xen_wpt; ++ break; ++ case VM_XEN_RHEL4: ++ machdep->uvtop = x86_64_uvtop_level4_rhel4_xen_wpt; ++ break; ++ } ++ } else ++ machdep->uvtop = x86_64_uvtop_level4; ++ MEMBER_OFFSET_INIT(vcpu_guest_context_user_regs, ++ "vcpu_guest_context", "user_regs"); ++ ASSIGN_OFFSET(cpu_user_regs_rsp) = ++ MEMBER_OFFSET("cpu_user_regs", "ss") - sizeof(ulong); ++ ASSIGN_OFFSET(cpu_user_regs_rip) = ++ MEMBER_OFFSET("cpu_user_regs", "cs") - sizeof(ulong); ++ } ++ x86_64_irq_eframe_link_init(); ++ break; ++ ++ case POST_VM: ++ init_unwind_table(); + break; + + case POST_INIT: +@@ -191,10 +361,26 @@ + fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); + if (machdep->flags & PT_REGS_INIT) + fprintf(fp, "%sPT_REGS_INIT", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + if (machdep->flags & MACHDEP_BT_TEXT) + fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : ""); ++ if (machdep->flags & VM_ORIG) ++ fprintf(fp, "%sVM_ORIG", others++ ? "|" : ""); ++ if (machdep->flags & VM_2_6_11) ++ fprintf(fp, "%sVM_2_6_11", others++ ? "|" : ""); ++ if (machdep->flags & VM_XEN) ++ fprintf(fp, "%sVM_XEN", others++ ? "|" : ""); ++ if (machdep->flags & VM_XEN_RHEL4) ++ fprintf(fp, "%sVM_XEN_RHEL4", others++ ? "|" : ""); ++ if (machdep->flags & VMEMMAP) ++ fprintf(fp, "%sVMEMMAP", others++ ? "|" : ""); ++ if (machdep->flags & NO_TSS) ++ fprintf(fp, "%sNO_TSS", others++ ? "|" : ""); ++ if (machdep->flags & SCHED_TEXT) ++ fprintf(fp, "%sSCHED_TEXT", others++ ? "|" : ""); ++ if (machdep->flags & PHYS_BASE) ++ fprintf(fp, "%sPHYS_BASE", others++ ? "|" : ""); ++ if (machdep->flags & FRAMESIZE_DEBUG) ++ fprintf(fp, "%sFRAMESIZE_DEBUG", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); +@@ -215,13 +401,32 @@ + fprintf(fp, " back_trace: x86_64_back_trace_cmd()\n"); + else if (machdep->back_trace == x86_64_low_budget_back_trace_cmd) + fprintf(fp, +- " back_trace: x86_64_low_budget_back_trace_cmd()\n"); ++ " back_trace: x86_64_low_budget_back_trace_cmd() %s\n", ++ kt->flags & DWARF_UNWIND ? ++ "-> x86_64_dwarf_back_trace_cmd()" : ""); ++ else if (machdep->back_trace == x86_64_dwarf_back_trace_cmd) ++ fprintf(fp, ++ " back_trace: x86_64_dwarf_back_trace_cmd() %s\n", ++ kt->flags & DWARF_UNWIND ? ++ "" : "->x86_64_low_budget_back_trace_cmd()"); + else + fprintf(fp, " back_trace: %lx\n", + (ulong)machdep->back_trace); + fprintf(fp, " processor_speed: x86_64_processor_speed()\n"); +- fprintf(fp, " uvtop: x86_64_uvtop()\n"); +- fprintf(fp, " kvtop: x86_64_kvtop()\n"); ++ if (machdep->uvtop == x86_64_uvtop) ++ fprintf(fp, " uvtop: x86_64_uvtop()\n"); ++ else if (machdep->uvtop == x86_64_uvtop_level4) ++ fprintf(fp, " uvtop: x86_64_uvtop_level4()\n"); ++ else if (machdep->uvtop == x86_64_uvtop_level4_xen_wpt) ++ fprintf(fp, " uvtop: x86_64_uvtop_level4_xen_wpt()\n"); ++ else if (machdep->uvtop == x86_64_uvtop_level4_rhel4_xen_wpt) ++ fprintf(fp, " uvtop: x86_64_uvtop_level4_rhel4_xen_wpt()\n"); ++ else ++ fprintf(fp, " uvtop: %lx\n", (ulong)machdep->uvtop); ++ fprintf(fp, " kvtop: x86_64_kvtop()"); ++ if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) ++ fprintf(fp, " -> x86_64_kvtop_xen_wpt()"); ++ fprintf(fp, "\n"); + fprintf(fp, " get_task_pgd: x86_64_get_task_pgd()\n"); + fprintf(fp, " dump_irq: x86_64_dump_irq()\n"); + fprintf(fp, " get_stack_frame: x86_64_get_stack_frame()\n"); +@@ -239,6 +444,11 @@ + fprintf(fp, " is_uvaddr: x86_64_is_uvaddr()\n"); + fprintf(fp, " verify_paddr: generic_verify_paddr()\n"); + fprintf(fp, " init_kernel_pgd: x86_64_init_kernel_pgd()\n"); ++ fprintf(fp, "clear_machdep_cache: x86_64_clear_machdep_cache()\n"); ++ fprintf(fp, " xendump_p2m_create: x86_64_xendump_p2m_create()\n"); ++ fprintf(fp, " get_xendump_regs: x86_64_get_xendump_regs()\n"); ++ fprintf(fp, " xendump_panic_task: x86_64_xendump_panic_task()\n"); ++ fprintf(fp, "xen_kdump_p2m_create: x86_64_xen_kdump_p2m_create()\n"); + fprintf(fp, " line_number_hooks: x86_64_line_number_hooks\n"); + fprintf(fp, " value_to_symbol: generic_machdep_value_to_symbol()\n"); + fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); +@@ -248,9 +458,33 @@ + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); +- fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); ++ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); ++ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); ++ fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); ++ ++ fprintf(fp, " machspec: %016lx\n", (ulong)machdep->machspec); ++ fprintf(fp, " userspace_top: %016lx\n", (ulong)ms->userspace_top); ++ fprintf(fp, " page_offset: %016lx\n", (ulong)ms->page_offset); ++ fprintf(fp, " vmalloc_start_addr: %016lx\n", (ulong)ms->vmalloc_start_addr); ++ fprintf(fp, " vmalloc_end: %016lx\n", (ulong)ms->vmalloc_end); ++ fprintf(fp, " modules_vaddr: %016lx\n", (ulong)ms->modules_vaddr); ++ fprintf(fp, " modules_end: %016lx\n", (ulong)ms->modules_end); ++ fprintf(fp, " vmemmap_vaddr: %016lx %s\n", (ulong)ms->vmemmap_vaddr, ++ machdep->flags & VMEMMAP ? "" : "(unused)"); ++ fprintf(fp, " vmemmap_end: %016lx %s\n", (ulong)ms->vmemmap_end, ++ machdep->flags & VMEMMAP ? "" : "(unused)"); ++ fprintf(fp, " phys_base: %lx\n", (ulong)ms->phys_base); + fprintf(fp, " pml4: %lx\n", (ulong)ms->pml4); ++ fprintf(fp, " last_pml4_read: %lx\n", (ulong)ms->last_pml4_read); ++ if (ms->upml) { ++ fprintf(fp, " upml: %lx\n", (ulong)ms->upml); ++ fprintf(fp, " last_upml_read: %lx\n", (ulong)ms->last_upml_read); ++ } else { ++ fprintf(fp, " upml: (unused)\n"); ++ fprintf(fp, " last_upml_read: (unused)\n"); ++ } + fprintf(fp, " irqstack: %lx\n", (ulong)ms->irqstack); ++ fprintf(fp, " irq_eframe_link: %ld\n", ms->irq_eframe_link); + fprintf(fp, " pto: %s", + machdep->flags & PT_REGS_INIT ? "\n" : "(uninitialized)\n"); + if (machdep->flags & PT_REGS_INIT) { +@@ -276,8 +510,10 @@ + fprintf(fp, " rsp: %ld\n", ms->pto.rsp); + fprintf(fp, " ss: %ld\n", ms->pto.ss); + } +- fprintf(fp, " stkinfo: esize: %d isize: %d\n", +- ms->stkinfo.esize, ms->stkinfo.isize); ++ fprintf(fp, " stkinfo: esize: %d%sisize: %d\n", ++ ms->stkinfo.esize, ++ machdep->flags & NO_TSS ? " (NO TSS) " : " ", ++ ms->stkinfo.isize); + fprintf(fp, " ebase[%s][7]:", + arg ? "NR_CPUS" : "cpus"); + cpus = arg ? NR_CPUS : kt->cpus; +@@ -306,9 +542,9 @@ + static void + x86_64_cpu_pda_init(void) + { +- int i, cpus, nr_pda, cpunumber; ++ int i, cpus, nr_pda, cpunumber, _cpu_pda, _boot_cpu_pda; + char *cpu_pda_buf; +- ulong level4_pgt, data_offset; ++ ulong level4_pgt, data_offset, cpu_pda_addr; + struct syment *sp, *nsp; + ulong offset, istacksize; + +@@ -320,18 +556,54 @@ + MEMBER_OFFSET_INIT(x8664_pda_irqstackptr, "x8664_pda", "irqstackptr"); + MEMBER_OFFSET_INIT(x8664_pda_level4_pgt, "x8664_pda", "level4_pgt"); + MEMBER_OFFSET_INIT(x8664_pda_cpunumber, "x8664_pda", "cpunumber"); ++ MEMBER_OFFSET_INIT(x8664_pda_me, "x8664_pda", "me"); + + cpu_pda_buf = GETBUF(SIZE(x8664_pda)); + +- if (!(nr_pda = get_array_length("cpu_pda", NULL, 0))) +- nr_pda = NR_CPUS; +- ++ if (LKCD_KERNTYPES()) { ++ if (symbol_exists("_cpu_pda")) ++ _cpu_pda = TRUE; ++ else ++ _cpu_pda = FALSE; ++ nr_pda = get_cpus_possible(); ++ } else { ++ if (symbol_exists("_cpu_pda")) { ++ if (!(nr_pda = get_array_length("_cpu_pda", NULL, 0))) ++ nr_pda = NR_CPUS; ++ _cpu_pda = TRUE; ++ } else { ++ if (!(nr_pda = get_array_length("cpu_pda", NULL, 0))) ++ nr_pda = NR_CPUS; ++ _cpu_pda = FALSE; ++ } ++ } ++ if (_cpu_pda) { ++ if (symbol_exists("_boot_cpu_pda")) ++ _boot_cpu_pda = TRUE; ++ else ++ _boot_cpu_pda = FALSE; ++ } + for (i = cpus = 0; i < nr_pda; i++) { +- if (!CPU_PDA_READ(i, cpu_pda_buf)) +- break; +- level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt)); ++ if (_cpu_pda) { ++ if (_boot_cpu_pda) { ++ if (!_CPU_PDA_READ2(i, cpu_pda_buf)) ++ break; ++ } else { ++ if (!_CPU_PDA_READ(i, cpu_pda_buf)) ++ break; ++ } ++ } else { ++ if (!CPU_PDA_READ(i, cpu_pda_buf)) ++ break; ++ } ++ ++ if (VALID_MEMBER(x8664_pda_level4_pgt)) { ++ level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt)); ++ if (!VALID_LEVEL4_PGT_ADDR(level4_pgt)) ++ break; ++ } + cpunumber = INT(cpu_pda_buf + OFFSET(x8664_pda_cpunumber)); +- if (!VALID_LEVEL4_PGT_ADDR(level4_pgt) || (cpunumber != cpus)) ++ if (cpunumber != cpus) + break; + cpus++; + +@@ -351,8 +623,8 @@ + i, level4_pgt, data_offset); + } + +- +- if ((i = get_array_length("boot_cpu_stack", NULL, 0))) { ++ if (!LKCD_KERNTYPES() && ++ (i = get_array_length("boot_cpu_stack", NULL, 0))) { + istacksize = i; + } else if ((sp = symbol_search("boot_cpu_stack")) && + (nsp = next_symbol(NULL, sp))) { +@@ -381,8 +653,9 @@ + * the address of &boot_cpu_stack[0]. + */ + sp = value_search(machdep->machspec->stkinfo.ibase[0], &offset); +- if (!sp || offset || !STREQ(sp->name, "boot_cpu_stack")) { +- if (symbol_value("boot_cpu_stack")) { ++ nsp = symbol_search("boot_cpu_stack"); ++ if (!sp || offset || !nsp || (sp->value != nsp->value)) { ++ if (symbol_exists("boot_cpu_stack")) { + error(WARNING, + "cpu 0 IRQ stack: %lx\n boot_cpu_stack: %lx\n\n", + machdep->machspec->stkinfo.ibase[0], +@@ -448,6 +721,13 @@ + if (ms->stkinfo.ebase[c][0] == 0) + break; + } ++ } else if (!symbol_exists("boot_exception_stacks")) { ++ machdep->flags |= NO_TSS; ++ ++ if (CRASHDEBUG(1)) ++ error(NOTE, "CONFIG_X86_NO_TSS\n"); ++ ++ return; + } + + if (ms->stkinfo.ebase[0][0] && ms->stkinfo.ebase[0][1]) +@@ -535,6 +815,10 @@ + if (clues >= 2) + kt->cpu_flags[c] |= NMI; + } ++ ++ if (symbol_exists("__sched_text_start") && ++ (symbol_value("__sched_text_start") == symbol_value("schedule"))) ++ machdep->flags |= SCHED_TEXT; + } + + /* +@@ -576,7 +860,7 @@ + ulong x86_64_VTOP(ulong vaddr) + { + if (vaddr >= __START_KERNEL_map) +- return ((vaddr) - (ulong)__START_KERNEL_map); ++ return ((vaddr) - (ulong)__START_KERNEL_map + machdep->machspec->phys_base); + else + return ((vaddr) - PAGE_OFFSET); + } +@@ -584,12 +868,21 @@ + /* + * Include both vmalloc'd and module address space as VMALLOC space. + */ +-int x86_64_IS_VMALLOC_ADDR(ulong vaddr) ++int ++x86_64_IS_VMALLOC_ADDR(ulong vaddr) + { + return ((vaddr >= VMALLOC_START && vaddr <= VMALLOC_END) || ++ ((machdep->flags & VMEMMAP) && ++ (vaddr >= VMEMMAP_VADDR && vaddr <= VMEMMAP_END)) || + (vaddr >= MODULES_VADDR && vaddr <= MODULES_END)); + } + ++static int ++x86_64_is_module_addr(ulong vaddr) ++{ ++ return (vaddr >= MODULES_VADDR && vaddr <= MODULES_END); ++} ++ + /* + * Refining this may cause more problems than just doing it this way. + */ +@@ -616,43 +909,52 @@ + */ + + static int +-x86_64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) ++x86_64_uvtop_level4(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) + { +- ulong mm; +- ulong *pgd; ++ ulong mm; ++ ulong *pml; ++ ulong pml_paddr; ++ ulong pml_pte; ++ ulong *pgd; + ulong pgd_paddr; + ulong pgd_pte; + ulong *pmd; + ulong pmd_paddr; + ulong pmd_pte; +- ulong *ptep; +- ulong pte_paddr; +- ulong pte; +- physaddr_t physpage; ++ ulong *ptep; ++ ulong pte_paddr; ++ ulong pte; ++ physaddr_t physpage; + +- if (!tc) +- error(FATAL, "current context invalid\n"); ++ if (!tc) ++ error(FATAL, "current context invalid\n"); + +- *paddr = 0; ++ *paddr = 0; + +- if (IS_KVADDR(uvaddr)) +- return x86_64_kvtop(tc, uvaddr, paddr, verbose); ++ if (IS_KVADDR(uvaddr)) ++ return x86_64_kvtop(tc, uvaddr, paddr, verbose); + +- /* +- * pgd = pgd_offset(mm, address); +- */ +- if ((mm = task_mm(tc->task, TRUE))) +- pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); +- else +- readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, +- sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); ++ if ((mm = task_mm(tc->task, TRUE))) ++ pml = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); ++ else ++ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pml, ++ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); + +- pgd_paddr = x86_64_VTOP((ulong)pgd); +- FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); ++ pml_paddr = x86_64_VTOP((ulong)pml); ++ FILL_UPML(pml_paddr, PHYSADDR, PAGESIZE()); ++ pml = ((ulong *)pml_paddr) + pml4_index(uvaddr); ++ pml_pte = ULONG(machdep->machspec->upml + PAGEOFFSET(pml)); ++ if (verbose) ++ fprintf(fp, " PML: %lx => %lx\n", (ulong)pml, pml_pte); ++ if (!(pml_pte & _PAGE_PRESENT)) ++ goto no_upage; ++ ++ pgd_paddr = pml_pte & PHYSICAL_PAGE_MASK; ++ FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); + pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); + pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); +- if (verbose) +- fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd, pgd_pte); ++ if (verbose) ++ fprintf(fp, " PUD: %lx => %lx\n", (ulong)pgd, pgd_pte); + if (!(pgd_pte & _PAGE_PRESENT)) + goto no_upage; + +@@ -682,29 +984,31 @@ + + /* + * ptep = pte_offset_map(pmd, address); +- * pte = *ptep; ++ * pte = *ptep; + */ +- pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; +- FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); +- ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); +- pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); +- if (verbose) +- fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); +- if (!(pte & (_PAGE_PRESENT))) { +- if (pte && verbose) { +- fprintf(fp, "\n"); +- x86_64_translate_pte(pte, 0, 0); +- } +- goto no_upage; +- } ++ pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; ++ FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); ++ ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); ++ if (!(pte & (_PAGE_PRESENT))) { ++ *paddr = pte; ++ ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_64_translate_pte(pte, 0, 0); ++ } ++ goto no_upage; ++ } + +- *paddr = (PAGEBASE(pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(uvaddr); ++ *paddr = (PAGEBASE(pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(uvaddr); + +- if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx\n\n", + PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK); +- x86_64_translate_pte(pte, 0, 0); +- } ++ x86_64_translate_pte(pte, 0, 0); ++ } + + return TRUE; + +@@ -713,1982 +1017,4872 @@ + return FALSE; + } + +- +-/* +- * Translates a kernel virtual address to its physical address. cmd_vtop() +- * sets the verbose flag so that the pte translation gets displayed; all +- * other callers quietly accept the translation. +- */ + static int +-x86_64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) ++x86_64_uvtop_level4_xen_wpt(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) + { +- ulong *pml4; +- ulong *pgd; ++ ulong mm; ++ ulong *pml; ++ ulong pml_paddr; ++ ulong pml_pte; ++ ulong *pgd; + ulong pgd_paddr; + ulong pgd_pte; + ulong *pmd; + ulong pmd_paddr; + ulong pmd_pte; ++ ulong pseudo_pmd_pte; + ulong *ptep; + ulong pte_paddr; + ulong pte; ++ ulong pseudo_pte; + physaddr_t physpage; ++ char buf[BUFSIZE]; + +- if (!IS_KVADDR(kvaddr)) +- return FALSE; ++ if (!tc) ++ error(FATAL, "current context invalid\n"); + +- if (!vt->vmalloc_start) { +- *paddr = x86_64_VTOP(kvaddr); +- return TRUE; +- } ++ *paddr = 0; + +- if (!IS_VMALLOC_ADDR(kvaddr)) { +- *paddr = x86_64_VTOP(kvaddr); +- if (!verbose) +- return TRUE; +- } +- +- /* +- * pgd = pgd_offset_k(addr); +- */ +- FILL_PML4(); +- pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); +- if (verbose) { +- fprintf(fp, "PML4 DIRECTORY: %lx\n", vt->kernel_pgd[0]); +- fprintf(fp, "PAGE DIRECTORY: %lx\n", *pml4); +- } +- if (!(*pml4) & _PAGE_PRESENT) +- goto no_kpage; +- pgd_paddr = (*pml4) & PHYSICAL_PAGE_MASK; ++ if (IS_KVADDR(uvaddr)) ++ return x86_64_kvtop(tc, uvaddr, paddr, verbose); ++ ++ if ((mm = task_mm(tc->task, TRUE))) ++ pml = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); ++ else ++ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pml, ++ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); ++ ++ pml_paddr = x86_64_VTOP((ulong)pml); ++ FILL_UPML(pml_paddr, PHYSADDR, PAGESIZE()); ++ pml = ((ulong *)pml_paddr) + pml4_index(uvaddr); ++ pml_pte = ULONG(machdep->machspec->upml + PAGEOFFSET(pml)); ++ if (verbose) ++ fprintf(fp, " PML: %lx => %lx [machine]\n", (ulong)pml, pml_pte); ++ if (!(pml_pte & _PAGE_PRESENT)) ++ goto no_upage; ++ ++ pgd_paddr = pml_pte & PHYSICAL_PAGE_MASK; ++ pgd_paddr = xen_m2p(pgd_paddr); ++ if (verbose) ++ fprintf(fp, " PML: %lx\n", pgd_paddr); + FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); +- pgd = ((ulong *)pgd_paddr) + pgd_index(kvaddr); ++ pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); + pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); +- if (verbose) +- fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd, pgd_pte); ++ if (verbose) ++ fprintf(fp, " PUD: %lx => %lx [machine]\n", (ulong)pgd, pgd_pte); + if (!(pgd_pte & _PAGE_PRESENT)) +- goto no_kpage; ++ goto no_upage; + + /* +- * pmd = pmd_offset(pgd, addr); ++ * pmd = pmd_offset(pgd, address); + */ + pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; ++ pmd_paddr = xen_m2p(pmd_paddr); ++ if (verbose) ++ fprintf(fp, " PUD: %lx\n", pmd_paddr); + FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); +- pmd = ((ulong *)pmd_paddr) + pmd_index(kvaddr); ++ pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr); + pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); + if (verbose) +- fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); ++ fprintf(fp, " PMD: %lx => %lx [machine]\n", (ulong)pmd, pmd_pte); + if (!(pmd_pte & _PAGE_PRESENT)) +- goto no_kpage; +- if (pmd_pte & _PAGE_PSE) { +- if (verbose) { +- fprintf(fp, " PAGE: %lx (2MB)\n\n", ++ goto no_upage; ++ if (pmd_pte & _PAGE_PSE) { ++ if (verbose) ++ fprintf(fp, " PAGE: %lx (2MB) [machine]\n", + PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK); +- x86_64_translate_pte(pmd_pte, 0, 0); ++ ++ pseudo_pmd_pte = xen_m2p(PAGEBASE(pmd_pte)); ++ ++ if (pseudo_pmd_pte == XEN_MACHADDR_NOT_FOUND) { ++ if (verbose) ++ fprintf(fp, " PAGE: page not available\n"); ++ *paddr = PADDR_NOT_AVAILABLE; ++ return FALSE; + } + +- physpage = (PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK) + +- (kvaddr & ~_2MB_PAGE_MASK); ++ pseudo_pmd_pte |= PAGEOFFSET(pmd_pte); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s (2MB)\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(PAGEBASE(pseudo_pmd_pte) & ++ PHYSICAL_PAGE_MASK))); ++ ++ x86_64_translate_pte(pseudo_pmd_pte, 0, 0); ++ } ++ ++ physpage = (PAGEBASE(pseudo_pmd_pte) & PHYSICAL_PAGE_MASK) + ++ (uvaddr & ~_2MB_PAGE_MASK); ++ + *paddr = physpage; + return TRUE; +- } ++ } + +- /* +- * ptep = pte_offset_map(pmd, addr); ++ /* ++ * ptep = pte_offset_map(pmd, address); + * pte = *ptep; + */ + pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; ++ pte_paddr = xen_m2p(pte_paddr); ++ if (verbose) ++ fprintf(fp, " PMD: %lx\n", pte_paddr); + FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); +- ptep = ((ulong *)pte_paddr) + pte_index(kvaddr); ++ ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); + pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); +- if (verbose) +- fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); +- if (!(pte & (_PAGE_PRESENT))) { +- if (pte && verbose) { +- fprintf(fp, "\n"); +- x86_64_translate_pte(pte, 0, 0); +- } +- goto no_kpage; +- } +- +- *paddr = (PAGEBASE(pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(kvaddr); +- +- if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx [machine]\n", (ulong)ptep, pte); ++ if (!(pte & (_PAGE_PRESENT))) { ++ *paddr = pte; ++ ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_64_translate_pte(pte, 0, 0); ++ } ++ goto no_upage; ++ } ++ ++ pseudo_pte = xen_m2p(pte & PHYSICAL_PAGE_MASK); ++ if (verbose) ++ fprintf(fp, " PTE: %lx\n", pseudo_pte + PAGEOFFSET(pte)); ++ ++ *paddr = (PAGEBASE(pseudo_pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(uvaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx [machine]\n", ++ PAGEBASE(pte) & PHYSICAL_PAGE_MASK); ++ fprintf(fp, " PAGE: %lx\n\n", + PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK); +- x86_64_translate_pte(pte, 0, 0); +- } ++ x86_64_translate_pte(pseudo_pte + PAGEOFFSET(pte), 0, 0); ++ } + +- return TRUE; ++ return TRUE; + +-no_kpage: +- return FALSE; +-} ++no_upage: + +-/* +- * Determine where vmalloc'd memory starts. +- */ +-static ulong +-x86_64_vmalloc_start(void) +-{ +- return ((ulong)VMALLOC_START); ++ return FALSE; + } + +-/* +- * thread_info implementation makes for less accurate results here. +- */ + static int +-x86_64_is_task_addr(ulong task) ++x86_64_uvtop_level4_rhel4_xen_wpt(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) + { +- if (tt->flags & THREAD_INFO) +- return IS_KVADDR(task); +- else +- return (IS_KVADDR(task) && (ALIGNED_STACK_OFFSET(task) == 0)); +-} ++ ulong mm; ++ ulong *pgd; ++ ulong pgd_paddr; ++ ulong pgd_pte; ++ ulong *pmd; ++ ulong pmd_paddr; ++ ulong pmd_pte; ++ ulong pseudo_pmd_pte; ++ ulong *ptep; ++ ulong pte_paddr; ++ ulong pte; ++ ulong pseudo_pte; ++ physaddr_t physpage; ++ char buf[BUFSIZE]; + ++ if (!tc) ++ error(FATAL, "current context invalid\n"); + +-/* +- * easy enough... +- */ +-static ulong +-x86_64_processor_speed(void) +-{ +- unsigned long cpu_khz; ++ *paddr = 0; + +- if (machdep->mhz) +- return (machdep->mhz); ++ if (IS_KVADDR(uvaddr)) ++ return x86_64_kvtop(tc, uvaddr, paddr, verbose); + +- if (symbol_exists("cpu_khz")) { +- get_symbol_data("cpu_khz", sizeof(long), &cpu_khz); +- if (cpu_khz) +- return(machdep->mhz = cpu_khz/1000); +- } ++ if ((mm = task_mm(tc->task, TRUE))) ++ pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); ++ else ++ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, ++ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); + +- return 0; +-} ++ pgd_paddr = x86_64_VTOP((ulong)pgd); ++ FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); ++ pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); ++ if (verbose) ++ fprintf(fp, " PGD: %lx => %lx [machine]\n", (ulong)pgd, pgd_pte); ++ if (!(pgd_pte & _PAGE_PRESENT)) ++ goto no_upage; ++ ++ /* ++ * pmd = pmd_offset(pgd, address); ++ */ ++ pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; ++ pmd_paddr = xen_m2p(pmd_paddr); ++ if (verbose) ++ fprintf(fp, " PGD: %lx\n", pmd_paddr); ++ FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); ++ pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx [machine]\n", (ulong)pmd, pmd_pte); ++ if (!(pmd_pte & _PAGE_PRESENT)) ++ goto no_upage; ++ if (pmd_pte & _PAGE_PSE) { ++ if (verbose) ++ fprintf(fp, " PAGE: %lx (2MB) [machine]\n", ++ PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK); + ++ pseudo_pmd_pte = xen_m2p(PAGEBASE(pmd_pte)); + +-/* +- * Accept or reject a symbol from the kernel namelist. +- */ +-static int +-x86_64_verify_symbol(const char *name, ulong value, char type) +-{ +- if (STREQ(name, "_text") || STREQ(name, "_stext")) +- machdep->flags |= KSYMS_START; ++ if (pseudo_pmd_pte == XEN_MACHADDR_NOT_FOUND) { ++ if (verbose) ++ fprintf(fp, " PAGE: page not available\n"); ++ *paddr = PADDR_NOT_AVAILABLE; ++ return FALSE; ++ } + +- if (!name || !strlen(name) || !(machdep->flags & KSYMS_START)) +- return FALSE; ++ pseudo_pmd_pte |= PAGEOFFSET(pmd_pte); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s (2MB)\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(PAGEBASE(pseudo_pmd_pte) & ++ PHYSICAL_PAGE_MASK))); ++ ++ x86_64_translate_pte(pseudo_pmd_pte, 0, 0); ++ } ++ ++ physpage = (PAGEBASE(pseudo_pmd_pte) & PHYSICAL_PAGE_MASK) + ++ (uvaddr & ~_2MB_PAGE_MASK); ++ ++ *paddr = physpage; ++ return TRUE; ++ } ++ ++ /* ++ * ptep = pte_offset_map(pmd, address); ++ * pte = *ptep; ++ */ ++ pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; ++ pte_paddr = xen_m2p(pte_paddr); ++ if (verbose) ++ fprintf(fp, " PMD: %lx\n", pte_paddr); ++ FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); ++ ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx [machine]\n", (ulong)ptep, pte); ++ if (!(pte & (_PAGE_PRESENT))) { ++ *paddr = pte; ++ ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_64_translate_pte(pte, 0, 0); ++ } ++ goto no_upage; ++ } ++ ++ pseudo_pte = xen_m2p(pte & PHYSICAL_PAGE_MASK); ++ if (verbose) ++ fprintf(fp, " PTE: %lx\n", pseudo_pte + PAGEOFFSET(pte)); ++ ++ *paddr = (PAGEBASE(pseudo_pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(uvaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx [machine]\n", ++ PAGEBASE(pte) & PHYSICAL_PAGE_MASK); ++ fprintf(fp, " PAGE: %lx\n\n", ++ PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK); ++ x86_64_translate_pte(pseudo_pte + PAGEOFFSET(pte), 0, 0); ++ } + + return TRUE; +-} + ++no_upage: + +-/* +- * Get the relevant page directory pointer from a task structure. +- */ +-static ulong +-x86_64_get_task_pgd(ulong task) +-{ +- return (error(FATAL, "x86_64_get_task_pgd: N/A\n")); ++ return FALSE; + } + +- +-/* +- * Translate a PTE, returning TRUE if the page is present. +- * If a physaddr pointer is passed in, don't print anything. +- */ + static int +-x86_64_translate_pte(ulong pte, void *physaddr, ulonglong unused) ++x86_64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) + { +- int c, others, len1, len2, len3; +- ulong paddr; +- char buf[BUFSIZE]; +- char buf2[BUFSIZE]; +- char buf3[BUFSIZE]; +- char ptebuf[BUFSIZE]; +- char physbuf[BUFSIZE]; +- char *arglist[MAXARGS]; +- int page_present; ++ ulong mm; ++ ulong *pgd; ++ ulong pgd_paddr; ++ ulong pgd_pte; ++ ulong *pmd; ++ ulong pmd_paddr; ++ ulong pmd_pte; ++ ulong *ptep; ++ ulong pte_paddr; ++ ulong pte; ++ physaddr_t physpage; + +- paddr = pte & PHYSICAL_PAGE_MASK; +- page_present = pte & _PAGE_PRESENT; ++ if (!tc) ++ error(FATAL, "current context invalid\n"); + +- if (physaddr) { +- *((ulong *)physaddr) = paddr; +- return page_present; +- } +- +- sprintf(ptebuf, "%lx", pte); +- len1 = MAX(strlen(ptebuf), strlen("PTE")); +- fprintf(fp, "%s ", mkstring(buf, len1, CENTER|LJUST, "PTE")); ++ *paddr = 0; + +- if (!page_present && pte) { +- swap_location(pte, buf); +- if ((c = parse_line(buf, arglist)) != 3) +- error(FATAL, "cannot determine swap location\n"); ++ if (IS_KVADDR(uvaddr)) ++ return x86_64_kvtop(tc, uvaddr, paddr, verbose); + +- len2 = MAX(strlen(arglist[0]), strlen("SWAP")); +- len3 = MAX(strlen(arglist[2]), strlen("OFFSET")); ++ /* ++ * pgd = pgd_offset(mm, address); ++ */ ++ if ((mm = task_mm(tc->task, TRUE))) ++ pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); ++ else ++ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, ++ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); + +- fprintf(fp, "%s %s\n", +- mkstring(buf2, len2, CENTER|LJUST, "SWAP"), +- mkstring(buf3, len3, CENTER|LJUST, "OFFSET")); ++ pgd_paddr = x86_64_VTOP((ulong)pgd); ++ FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); ++ pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); ++ if (verbose) ++ fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd, pgd_pte); ++ if (!(pgd_pte & _PAGE_PRESENT)) ++ goto no_upage; + +- strcpy(buf2, arglist[0]); +- strcpy(buf3, arglist[2]); +- fprintf(fp, "%s %s %s\n", +- mkstring(ptebuf, len1, CENTER|RJUST, NULL), +- mkstring(buf2, len2, CENTER|RJUST, NULL), +- mkstring(buf3, len3, CENTER|RJUST, NULL)); ++ /* ++ * pmd = pmd_offset(pgd, address); ++ */ ++ pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; ++ FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); ++ pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); ++ if (!(pmd_pte & _PAGE_PRESENT)) ++ goto no_upage; ++ if (pmd_pte & _PAGE_PSE) { ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx (2MB)\n\n", ++ PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK); ++ x86_64_translate_pte(pmd_pte, 0, 0); ++ } + +- return page_present; ++ physpage = (PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK) + ++ (uvaddr & ~_2MB_PAGE_MASK); ++ *paddr = physpage; ++ return TRUE; + } + +- sprintf(physbuf, "%lx", paddr); +- len2 = MAX(strlen(physbuf), strlen("PHYSICAL")); +- fprintf(fp, "%s ", mkstring(buf, len2, CENTER|LJUST, "PHYSICAL")); ++ /* ++ * ptep = pte_offset_map(pmd, address); ++ * pte = *ptep; ++ */ ++ pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; ++ FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); ++ ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); ++ if (!(pte & (_PAGE_PRESENT))) { ++ *paddr = pte; + +- fprintf(fp, "FLAGS\n"); ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_64_translate_pte(pte, 0, 0); ++ } ++ goto no_upage; ++ } + +- fprintf(fp, "%s %s ", +- mkstring(ptebuf, len1, CENTER|RJUST, NULL), +- mkstring(physbuf, len2, CENTER|RJUST, NULL)); +- fprintf(fp, "("); +- others = 0; ++ *paddr = (PAGEBASE(pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(uvaddr); + +- if (pte) { +- if (pte & _PAGE_PRESENT) +- fprintf(fp, "%sPRESENT", others++ ? "|" : ""); +- if (pte & _PAGE_RW) +- fprintf(fp, "%sRW", others++ ? "|" : ""); +- if (pte & _PAGE_USER) +- fprintf(fp, "%sUSER", others++ ? "|" : ""); +- if (pte & _PAGE_PWT) +- fprintf(fp, "%sPWT", others++ ? "|" : ""); +- if (pte & _PAGE_PCD) +- fprintf(fp, "%sPCD", others++ ? "|" : ""); +- if (pte & _PAGE_ACCESSED) +- fprintf(fp, "%sACCESSED", others++ ? "|" : ""); +- if (pte & _PAGE_DIRTY) +- fprintf(fp, "%sDIRTY", others++ ? "|" : ""); +- if ((pte & _PAGE_PSE) && (pte & _PAGE_PRESENT)) +- fprintf(fp, "%sPSE", others++ ? "|" : ""); +- if ((pte & _PAGE_PROTNONE) && !(pte & _PAGE_PRESENT)) +- fprintf(fp, "%sPROTNONE", others++ ? "|" : ""); +- if (pte & _PAGE_GLOBAL) +- fprintf(fp, "%sGLOBAL", others++ ? "|" : ""); +- if (pte & _PAGE_NX) +- fprintf(fp, "%sNX", others++ ? "|" : ""); +- } else { +- fprintf(fp, "no mapping"); ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx\n\n", ++ PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK); ++ x86_64_translate_pte(pte, 0, 0); + } + +- fprintf(fp, ")\n"); ++ return TRUE; + +- return (page_present); ++no_upage: ++ ++ return FALSE; + } + +-static char * +-x86_64_exception_stacks[7] = { +- "STACKFAULT", +- "DOUBLEFAULT", +- "NMI", +- "DEBUG", +- "MCE", +- "(unknown)", +- "(unknown)" +-}; + + /* +- * Look for likely exception frames in a stack. ++ * Translates a kernel virtual address to its physical address. cmd_vtop() ++ * sets the verbose flag so that the pte translation gets displayed; all ++ * other callers quietly accept the translation. + */ +-static int +-x86_64_eframe_search(struct bt_info *bt) ++static int ++x86_64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) + { +- int i, c, cnt; +- ulong estack, irqstack, stacksize; +- ulong *up; +- struct machine_specific *ms; +- struct bt_info bt_local; ++ ulong *pml4; ++ ulong *pgd; ++ ulong pgd_paddr; ++ ulong pgd_pte; ++ ulong *pmd; ++ ulong pmd_paddr; ++ ulong pmd_pte; ++ ulong *ptep; ++ ulong pte_paddr; ++ ulong pte; ++ physaddr_t physpage; + +- if (bt->flags & BT_EFRAME_SEARCH2) { +- BCOPY(bt, &bt_local, sizeof(struct bt_info)); ++ if (!IS_KVADDR(kvaddr)) ++ return FALSE; ++ ++ if (XEN_HYPER_MODE()) { ++ if (XEN_VIRT_ADDR(kvaddr)) { ++ *paddr = kvaddr - XEN_VIRT_START + xen_phys_start(); ++ return TRUE; ++ } ++ if (DIRECTMAP_VIRT_ADDR(kvaddr)) { ++ *paddr = kvaddr - DIRECTMAP_VIRT_START; ++ return TRUE; ++ } ++ FILL_PML4_HYPER(); ++ pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); ++ if (verbose) { ++ fprintf(fp, "PML4 DIRECTORY: %lx\n", vt->kernel_pgd[0]); ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", *pml4); ++ } ++ } else { ++ if (!vt->vmalloc_start) { ++ *paddr = x86_64_VTOP(kvaddr); ++ return TRUE; ++ } ++ ++ if (!IS_VMALLOC_ADDR(kvaddr)) { ++ *paddr = x86_64_VTOP(kvaddr); ++ if (!verbose) ++ return TRUE; ++ } ++ ++ if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) ++ return (x86_64_kvtop_xen_wpt(tc, kvaddr, paddr, verbose)); ++ ++ /* ++ * pgd = pgd_offset_k(addr); ++ */ ++ FILL_PML4(); ++ pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); ++ if (verbose) { ++ fprintf(fp, "PML4 DIRECTORY: %lx\n", vt->kernel_pgd[0]); ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", *pml4); ++ } ++ } ++ if (!(*pml4) & _PAGE_PRESENT) ++ goto no_kpage; ++ pgd_paddr = (*pml4) & PHYSICAL_PAGE_MASK; ++ FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); ++ pgd = ((ulong *)pgd_paddr) + pgd_index(kvaddr); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); ++ if (verbose) ++ fprintf(fp, " PUD: %lx => %lx\n", (ulong)pgd, pgd_pte); ++ if (!(pgd_pte & _PAGE_PRESENT)) ++ goto no_kpage; ++ ++ /* ++ * pmd = pmd_offset(pgd, addr); ++ */ ++ pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; ++ FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); ++ pmd = ((ulong *)pmd_paddr) + pmd_index(kvaddr); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); ++ if (!(pmd_pte & _PAGE_PRESENT)) ++ goto no_kpage; ++ if (pmd_pte & _PAGE_PSE) { ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx (2MB)\n\n", ++ PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK); ++ x86_64_translate_pte(pmd_pte, 0, 0); ++ } ++ ++ physpage = (PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK) + ++ (kvaddr & ~_2MB_PAGE_MASK); ++ *paddr = physpage; ++ return TRUE; ++ } ++ ++ /* ++ * ptep = pte_offset_map(pmd, addr); ++ * pte = *ptep; ++ */ ++ pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; ++ FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); ++ ptep = ((ulong *)pte_paddr) + pte_index(kvaddr); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); ++ if (!(pte & (_PAGE_PRESENT))) { ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_64_translate_pte(pte, 0, 0); ++ } ++ goto no_kpage; ++ } ++ ++ *paddr = (PAGEBASE(pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(kvaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx\n\n", ++ PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK); ++ x86_64_translate_pte(pte, 0, 0); ++ } ++ ++ return TRUE; ++ ++no_kpage: ++ return FALSE; ++} ++ ++ ++static int ++x86_64_kvtop_xen_wpt(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) ++{ ++ ulong *pml4; ++ ulong *pgd; ++ ulong pgd_paddr; ++ ulong pgd_pte; ++ ulong *pmd; ++ ulong pmd_paddr; ++ ulong pmd_pte; ++ ulong pseudo_pmd_pte; ++ ulong *ptep; ++ ulong pte_paddr; ++ ulong pte; ++ ulong pseudo_pte; ++ physaddr_t physpage; ++ char buf[BUFSIZE]; ++ ++ /* ++ * pgd = pgd_offset_k(addr); ++ */ ++ FILL_PML4(); ++ pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); ++ if (verbose) { ++ fprintf(fp, "PML4 DIRECTORY: %lx\n", vt->kernel_pgd[0]); ++ fprintf(fp, "PAGE DIRECTORY: %lx [machine]\n", *pml4); ++ } ++ if (!(*pml4) & _PAGE_PRESENT) ++ goto no_kpage; ++ pgd_paddr = (*pml4) & PHYSICAL_PAGE_MASK; ++ pgd_paddr = xen_m2p(pgd_paddr); ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", pgd_paddr); ++ FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); ++ pgd = ((ulong *)pgd_paddr) + pgd_index(kvaddr); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); ++ if (verbose) ++ fprintf(fp, " PUD: %lx => %lx [machine]\n", (ulong)pgd, pgd_pte); ++ if (!(pgd_pte & _PAGE_PRESENT)) ++ goto no_kpage; ++ ++ /* ++ * pmd = pmd_offset(pgd, addr); ++ */ ++ pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; ++ pmd_paddr = xen_m2p(pmd_paddr); ++ if (verbose) ++ fprintf(fp, " PUD: %lx\n", pmd_paddr); ++ FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); ++ pmd = ((ulong *)pmd_paddr) + pmd_index(kvaddr); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx [machine]\n", (ulong)pmd, pmd_pte); ++ if (!(pmd_pte & _PAGE_PRESENT)) ++ goto no_kpage; ++ if (pmd_pte & _PAGE_PSE) { ++ if (verbose) ++ fprintf(fp, " PAGE: %lx (2MB) [machine]\n", ++ PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK); ++ ++ pseudo_pmd_pte = xen_m2p(PAGEBASE(pmd_pte)); ++ ++ if (pseudo_pmd_pte == XEN_MACHADDR_NOT_FOUND) { ++ if (verbose) ++ fprintf(fp, " PAGE: page not available\n"); ++ *paddr = PADDR_NOT_AVAILABLE; ++ return FALSE; ++ } ++ ++ pseudo_pmd_pte |= PAGEOFFSET(pmd_pte); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %s (2MB)\n\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, ++ MKSTR(PAGEBASE(pseudo_pmd_pte) & ++ PHYSICAL_PAGE_MASK))); ++ ++ x86_64_translate_pte(pseudo_pmd_pte, 0, 0); ++ } ++ ++ physpage = (PAGEBASE(pseudo_pmd_pte) & PHYSICAL_PAGE_MASK) + ++ (kvaddr & ~_2MB_PAGE_MASK); ++ ++ *paddr = physpage; ++ return TRUE; ++ } ++ ++ /* ++ * ptep = pte_offset_map(pmd, addr); ++ * pte = *ptep; ++ */ ++ pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; ++ pte_paddr = xen_m2p(pte_paddr); ++ if (verbose) ++ fprintf(fp, " PMD: %lx\n", pte_paddr); ++ FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); ++ ptep = ((ulong *)pte_paddr) + pte_index(kvaddr); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx [machine]\n", (ulong)ptep, pte); ++ if (!(pte & (_PAGE_PRESENT))) { ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ x86_64_translate_pte(pte, 0, 0); ++ } ++ goto no_kpage; ++ } ++ ++ pseudo_pte = xen_m2p(pte & PHYSICAL_PAGE_MASK); ++ if (verbose) ++ fprintf(fp, " PTE: %lx\n", pseudo_pte + PAGEOFFSET(pte)); ++ ++ *paddr = (PAGEBASE(pseudo_pte) & PHYSICAL_PAGE_MASK) + PAGEOFFSET(kvaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx [machine]\n", ++ PAGEBASE(pte) & PHYSICAL_PAGE_MASK); ++ fprintf(fp, " PAGE: %lx\n\n", ++ PAGEBASE(*paddr) & PHYSICAL_PAGE_MASK); ++ x86_64_translate_pte(pseudo_pte + PAGEOFFSET(pte), 0, 0); ++ } ++ ++ return TRUE; ++ ++no_kpage: ++ return FALSE; ++} ++ ++ ++/* ++ * Determine where vmalloc'd memory starts. ++ */ ++static ulong ++x86_64_vmalloc_start(void) ++{ ++ return ((ulong)VMALLOC_START); ++} ++ ++/* ++ * thread_info implementation makes for less accurate results here. ++ */ ++static int ++x86_64_is_task_addr(ulong task) ++{ ++ if (tt->flags & THREAD_INFO) ++ return IS_KVADDR(task); ++ else ++ return (IS_KVADDR(task) && (ALIGNED_STACK_OFFSET(task) == 0)); ++} ++ ++ ++/* ++ * easy enough... ++ */ ++static ulong ++x86_64_processor_speed(void) ++{ ++ unsigned long cpu_khz = 0; ++ ++ if (machdep->mhz) ++ return (machdep->mhz); ++ ++ if (symbol_exists("cpu_khz")) { ++ get_symbol_data("cpu_khz", sizeof(int), &cpu_khz); ++ if (cpu_khz) ++ return(machdep->mhz = cpu_khz/1000); ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * Accept or reject a symbol from the kernel namelist. ++ */ ++static int ++x86_64_verify_symbol(const char *name, ulong value, char type) ++{ ++ if (STREQ(name, "_text") || STREQ(name, "_stext")) ++ machdep->flags |= KSYMS_START; ++ ++ if (!name || !strlen(name) || !(machdep->flags & KSYMS_START)) ++ return FALSE; ++ return TRUE; ++} ++ ++ ++/* ++ * Get the relevant page directory pointer from a task structure. ++ */ ++static ulong ++x86_64_get_task_pgd(ulong task) ++{ ++ return (error(FATAL, "x86_64_get_task_pgd: N/A\n")); ++} ++ ++ ++/* ++ * Translate a PTE, returning TRUE if the page is present. ++ * If a physaddr pointer is passed in, don't print anything. ++ */ ++static int ++x86_64_translate_pte(ulong pte, void *physaddr, ulonglong unused) ++{ ++ int c, others, len1, len2, len3; ++ ulong paddr; ++ char buf[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; ++ char ptebuf[BUFSIZE]; ++ char physbuf[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ int page_present; ++ ++ paddr = pte & PHYSICAL_PAGE_MASK; ++ page_present = pte & _PAGE_PRESENT; ++ ++ if (physaddr) { ++ *((ulong *)physaddr) = paddr; ++ return page_present; ++ } ++ ++ sprintf(ptebuf, "%lx", pte); ++ len1 = MAX(strlen(ptebuf), strlen("PTE")); ++ fprintf(fp, "%s ", mkstring(buf, len1, CENTER|LJUST, "PTE")); ++ ++ if (!page_present && pte) { ++ swap_location(pte, buf); ++ if ((c = parse_line(buf, arglist)) != 3) ++ error(FATAL, "cannot determine swap location\n"); ++ ++ len2 = MAX(strlen(arglist[0]), strlen("SWAP")); ++ len3 = MAX(strlen(arglist[2]), strlen("OFFSET")); ++ ++ fprintf(fp, "%s %s\n", ++ mkstring(buf2, len2, CENTER|LJUST, "SWAP"), ++ mkstring(buf3, len3, CENTER|LJUST, "OFFSET")); ++ ++ strcpy(buf2, arglist[0]); ++ strcpy(buf3, arglist[2]); ++ fprintf(fp, "%s %s %s\n", ++ mkstring(ptebuf, len1, CENTER|RJUST, NULL), ++ mkstring(buf2, len2, CENTER|RJUST, NULL), ++ mkstring(buf3, len3, CENTER|RJUST, NULL)); ++ ++ return page_present; ++ } ++ ++ sprintf(physbuf, "%lx", paddr); ++ len2 = MAX(strlen(physbuf), strlen("PHYSICAL")); ++ fprintf(fp, "%s ", mkstring(buf, len2, CENTER|LJUST, "PHYSICAL")); ++ ++ fprintf(fp, "FLAGS\n"); ++ ++ fprintf(fp, "%s %s ", ++ mkstring(ptebuf, len1, CENTER|RJUST, NULL), ++ mkstring(physbuf, len2, CENTER|RJUST, NULL)); ++ fprintf(fp, "("); ++ others = 0; ++ ++ if (pte) { ++ if (pte & _PAGE_PRESENT) ++ fprintf(fp, "%sPRESENT", others++ ? "|" : ""); ++ if (pte & _PAGE_RW) ++ fprintf(fp, "%sRW", others++ ? "|" : ""); ++ if (pte & _PAGE_USER) ++ fprintf(fp, "%sUSER", others++ ? "|" : ""); ++ if (pte & _PAGE_PWT) ++ fprintf(fp, "%sPWT", others++ ? "|" : ""); ++ if (pte & _PAGE_PCD) ++ fprintf(fp, "%sPCD", others++ ? "|" : ""); ++ if (pte & _PAGE_ACCESSED) ++ fprintf(fp, "%sACCESSED", others++ ? "|" : ""); ++ if (pte & _PAGE_DIRTY) ++ fprintf(fp, "%sDIRTY", others++ ? "|" : ""); ++ if ((pte & _PAGE_PSE) && (pte & _PAGE_PRESENT)) ++ fprintf(fp, "%sPSE", others++ ? "|" : ""); ++ if ((pte & _PAGE_PROTNONE) && !(pte & _PAGE_PRESENT)) ++ fprintf(fp, "%sPROTNONE", others++ ? "|" : ""); ++ if (pte & _PAGE_GLOBAL) ++ fprintf(fp, "%sGLOBAL", others++ ? "|" : ""); ++ if (pte & _PAGE_NX) ++ fprintf(fp, "%sNX", others++ ? "|" : ""); ++ } else { ++ fprintf(fp, "no mapping"); ++ } ++ ++ fprintf(fp, ")\n"); ++ ++ return (page_present); ++} ++ ++static char * ++x86_64_exception_stacks[7] = { ++ "STACKFAULT", ++ "DOUBLEFAULT", ++ "NMI", ++ "DEBUG", ++ "MCE", ++ "(unknown)", ++ "(unknown)" ++}; ++ ++/* ++ * Look for likely exception frames in a stack. ++ */ ++static int ++x86_64_eframe_search(struct bt_info *bt) ++{ ++ int i, c, cnt; ++ ulong estack, irqstack, stacksize; ++ ulong *up; ++ struct machine_specific *ms; ++ struct bt_info bt_local; ++ ++ if (bt->flags & BT_EFRAME_SEARCH2) { ++ BCOPY(bt, &bt_local, sizeof(struct bt_info)); + bt->flags &= ~(ulonglong)BT_EFRAME_SEARCH2; + +- ms = machdep->machspec; ++ ms = machdep->machspec; ++ ++ for (c = 0; c < kt->cpus; c++) { ++ if (ms->stkinfo.ibase[c] == 0) ++ break; ++ bt->hp->esp = ms->stkinfo.ibase[c]; ++ fprintf(fp, "CPU %d IRQ STACK:\n", c); ++ if ((cnt = x86_64_eframe_search(bt))) ++ fprintf(fp, "\n"); ++ else ++ fprintf(fp, "(none found)\n\n"); ++ } ++ ++ for (c = 0; c < kt->cpus; c++) { ++ for (i = 0; i < 7; i++) { ++ if (ms->stkinfo.ebase[c][i] == 0) ++ break; ++ bt->hp->esp = ms->stkinfo.ebase[c][i]; ++ fprintf(fp, "CPU %d %s EXCEPTION STACK:\n", ++ c, x86_64_exception_stacks[i]); ++ if ((cnt = x86_64_eframe_search(bt))) ++ fprintf(fp, "\n"); ++ else ++ fprintf(fp, "(none found)\n\n"); ++ } ++ } ++ ++ return 0; ++ } ++ ++ if (bt->hp && bt->hp->esp) { ++ ms = machdep->machspec; ++ bt->stkptr = bt->hp->esp; ++ if ((estack = x86_64_in_exception_stack(bt))) { ++ stacksize = ms->stkinfo.esize; ++ bt->stackbase = estack; ++ bt->stacktop = estack + ms->stkinfo.esize; ++ bt->stackbuf = ms->irqstack; ++ alter_stackbuf(bt); ++ } else if ((irqstack = x86_64_in_irqstack(bt))) { ++ stacksize = ms->stkinfo.isize; ++ bt->stackbase = irqstack; ++ bt->stacktop = irqstack + ms->stkinfo.isize; ++ bt->stackbuf = ms->irqstack; ++ alter_stackbuf(bt); ++ } else if (!INSTACK(bt->stkptr, bt)) ++ error(FATAL, ++ "unrecognized stack address for this task: %lx\n", ++ bt->hp->esp); ++ } ++ ++ stacksize = bt->stacktop - bt->stackbase - SIZE(pt_regs); ++ ++ if (bt->stkptr) ++ i = (bt->stkptr - bt->stackbase)/sizeof(ulong); ++ else ++ i = 0; ++ ++ for (cnt = 0; i <= stacksize/sizeof(ulong); i++) { ++ up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++ ++ if (x86_64_exception_frame(EFRAME_SEARCH|EFRAME_PRINT| ++ EFRAME_VERIFY, 0, (char *)up, bt, fp)) ++ cnt++; ++ } ++ ++ return cnt; ++} ++ ++static void ++x86_64_display_full_frame(struct bt_info *bt, ulong rsp, FILE *ofp) ++{ ++ int i, u_idx; ++ ulong *up; ++ ulong words, addr; ++ ++ if (rsp < bt->frameptr) ++ return; ++ ++ words = (rsp - bt->frameptr) / sizeof(ulong) + 1; ++ ++ addr = bt->frameptr; ++ u_idx = (bt->frameptr - bt->stackbase)/sizeof(ulong); ++ for (i = 0; i < words; i++, u_idx++) { ++ if (!(i & 1)) ++ fprintf(ofp, "%s %lx: ", i ? "\n" : "", addr); ++ ++ up = (ulong *)(&bt->stackbuf[u_idx*sizeof(ulong)]); ++ fprintf(ofp, "%016lx ", *up); ++ addr += sizeof(ulong); ++ } ++ fprintf(ofp, "\n"); ++} ++ ++/* ++ * Check a frame for a requested reference. ++ */ ++static void ++x86_64_do_bt_reference_check(struct bt_info *bt, ulong text, char *name) ++{ ++ struct syment *sp; ++ ulong offset; ++ ++ if (!name) ++ sp = value_search(text, &offset); ++ else if (!text) ++ sp = symbol_search(name); ++ ++ switch (bt->ref->cmdflags & (BT_REF_SYMBOL|BT_REF_HEXVAL)) ++ { ++ case BT_REF_SYMBOL: ++ if (name) { ++ if (STREQ(name, bt->ref->str)) ++ bt->ref->cmdflags |= BT_REF_FOUND; ++ } else { ++ if (sp && !offset && STREQ(sp->name, bt->ref->str)) ++ bt->ref->cmdflags |= BT_REF_FOUND; ++ } ++ break; ++ ++ case BT_REF_HEXVAL: ++ if (text) { ++ if (bt->ref->hexval == text) ++ bt->ref->cmdflags |= BT_REF_FOUND; ++ } else if (sp && (bt->ref->hexval == sp->value)) ++ bt->ref->cmdflags |= BT_REF_FOUND; ++ else if (!name && !text && (bt->ref->hexval == 0)) ++ bt->ref->cmdflags |= BT_REF_FOUND; ++ break; ++ } ++} ++ ++/* ++ * Determine the function containing a .text.lock. reference. ++ */ ++static ulong ++text_lock_function(char *name, struct bt_info *bt, ulong locktext) ++{ ++ int c, reterror, instr, arg; ++ char buf[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ char *p1; ++ ulong locking_func; ++ ++ instr = arg = -1; ++ locking_func = 0; ++ ++ open_tmpfile2(); ++ ++ if (STREQ(name, ".text.lock.spinlock")) ++ sprintf(buf, "x/4i 0x%lx", locktext); ++ else ++ sprintf(buf, "x/1i 0x%lx", locktext); ++ ++ if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { ++ close_tmpfile2(); ++ bt->flags |= BT_FRAMESIZE_DISABLE; ++ return 0; ++ } ++ ++ rewind(pc->tmpfile2); ++ while (fgets(buf, BUFSIZE, pc->tmpfile2)) { ++ c = parse_line(buf, arglist); ++ ++ if (instr == -1) { ++ /* ++ * Check whether are ++ * in the output string. ++ */ ++ if (LASTCHAR(arglist[0]) == ':') { ++ instr = 1; ++ arg = 2; ++ } else { ++ instr = 2; ++ arg = 3; ++ } ++ } ++ ++ if (c < (arg+1)) ++ break; ++ ++ if (STREQ(arglist[instr], "jmpq") || STREQ(arglist[instr], "jmp")) { ++ p1 = arglist[arg]; ++ reterror = 0; ++ locking_func = htol(p1, RETURN_ON_ERROR, &reterror); ++ if (reterror) ++ locking_func = 0; ++ break; ++ } ++ } ++ close_tmpfile2(); ++ ++ if (!locking_func) ++ bt->flags |= BT_FRAMESIZE_DISABLE; ++ ++ return locking_func; ++ ++} ++ ++ ++/* ++ * print one entry of a stack trace ++ */ ++#define BACKTRACE_COMPLETE (1) ++#define BACKTRACE_ENTRY_IGNORED (2) ++#define BACKTRACE_ENTRY_DISPLAYED (3) ++#define BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED (4) ++ ++static int ++x86_64_print_stack_entry(struct bt_info *bt, FILE *ofp, int level, ++ int stkindex, ulong text) ++{ ++ ulong rsp, offset, locking_func; ++ struct syment *sp, *spl; ++ char *name; ++ int result; ++ long eframe_check; ++ char buf[BUFSIZE]; ++ ++ eframe_check = -1; ++ offset = 0; ++ sp = value_search(text, &offset); ++ if (!sp) ++ return BACKTRACE_ENTRY_IGNORED; ++ ++ name = sp->name; ++ ++ if (bt->flags & BT_TEXT_SYMBOLS) { ++ if (bt->flags & BT_EXCEPTION_FRAME) ++ rsp = bt->stkptr; ++ else ++ rsp = bt->stackbase + (stkindex * sizeof(long)); ++ fprintf(ofp, " [%s] %s at %lx\n", ++ mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(rsp)), ++ name, text); ++ if (BT_REFERENCE_CHECK(bt)) ++ x86_64_do_bt_reference_check(bt, text, name); ++ return BACKTRACE_ENTRY_DISPLAYED; ++ } ++ ++ if (!offset && !(bt->flags & BT_EXCEPTION_FRAME) && ++ !(bt->flags & BT_START)) { ++ if (STREQ(name, "child_rip")) { ++ if (symbol_exists("kernel_thread")) ++ name = "kernel_thread"; ++ else if (symbol_exists("arch_kernel_thread")) ++ name = "arch_kernel_thread"; ++ } ++ else if (!(bt->flags & BT_SCHEDULE)) { ++ if (STREQ(name, "error_exit")) ++ eframe_check = 8; ++ else { ++ if (CRASHDEBUG(2)) ++ fprintf(ofp, ++ "< ignoring text symbol with no offset: %s() >\n", ++ sp->name); ++ return BACKTRACE_ENTRY_IGNORED; ++ } ++ } ++ } ++ ++ if (bt->flags & BT_SCHEDULE) ++ name = "schedule"; ++ ++ if (STREQ(name, "child_rip")) { ++ if (symbol_exists("kernel_thread")) ++ name = "kernel_thread"; ++ else if (symbol_exists("arch_kernel_thread")) ++ name = "arch_kernel_thread"; ++ result = BACKTRACE_COMPLETE; ++ } else if (STREQ(name, "cpu_idle")) ++ result = BACKTRACE_COMPLETE; ++ else ++ result = BACKTRACE_ENTRY_DISPLAYED; ++ ++ if (bt->flags & BT_EXCEPTION_FRAME) ++ rsp = bt->stkptr; ++ else if (bt->flags & BT_START) ++ rsp = bt->stkptr; ++ else ++ rsp = bt->stackbase + (stkindex * sizeof(long)); ++ ++ if ((bt->flags & BT_FULL)) { ++ if (bt->frameptr) ++ x86_64_display_full_frame(bt, rsp, ofp); ++ bt->frameptr = rsp + sizeof(ulong); ++ } ++ ++ fprintf(ofp, "%s#%d [%8lx] %s at %lx", level < 10 ? " " : "", level, ++ rsp, name, text); ++ ++ if (STREQ(name, "tracesys")) ++ fprintf(ofp, " (via system_call)"); ++ else if (STRNEQ(name, ".text.lock.")) { ++ if ((locking_func = text_lock_function(name, bt, text)) && ++ (spl = value_search(locking_func, &offset))) ++ fprintf(ofp, " (via %s)", spl->name); ++ } ++ ++ if (bt->flags & BT_FRAMESIZE_DISABLE) ++ fprintf(ofp, " *"); ++ ++ fprintf(ofp, "\n"); ++ ++ if (bt->flags & BT_LINE_NUMBERS) { ++ get_line_number(text, buf, FALSE); ++ if (strlen(buf)) ++ fprintf(ofp, " %s\n", buf); ++ } ++ ++ if (eframe_check >= 0) { ++ if (x86_64_exception_frame(EFRAME_PRINT|EFRAME_VERIFY, ++ bt->stackbase + (stkindex*sizeof(long)) + eframe_check, ++ NULL, bt, ofp)) ++ result = BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED; ++ } ++ ++ if (BT_REFERENCE_CHECK(bt)) ++ x86_64_do_bt_reference_check(bt, text, name); ++ ++ bt->call_target = name; ++ ++ if (is_direct_call_target(bt)) { ++ if (CRASHDEBUG(2)) ++ fprintf(ofp, "< enable BT_CHECK_CALLER for %s >\n", ++ bt->call_target); ++ bt->flags |= BT_CHECK_CALLER; ++ } else { ++ if (CRASHDEBUG(2) && (bt->flags & BT_CHECK_CALLER)) ++ fprintf(ofp, "< disable BT_CHECK_CALLER for %s >\n", ++ bt->call_target); ++ if (bt->flags & BT_CHECK_CALLER) { ++ if (CRASHDEBUG(2)) ++ fprintf(ofp, "< set BT_NO_CHECK_CALLER >\n"); ++ bt->flags |= BT_NO_CHECK_CALLER; ++ } ++ bt->flags &= ~(ulonglong)BT_CHECK_CALLER; ++ } ++ ++ return result; ++} ++ ++/* ++ * Unroll a kernel stack. ++ */ ++static void ++x86_64_back_trace_cmd(struct bt_info *bt) ++{ ++ error(FATAL, "x86_64_back_trace_cmd: TBD\n"); ++} ++ ++ ++ ++/* ++ * Determine whether the initial stack pointer is located in one of the ++ * exception stacks. ++ */ ++static ulong ++x86_64_in_exception_stack(struct bt_info *bt) ++{ ++ int c, i; ++ ulong rsp; ++ ulong estack; ++ struct machine_specific *ms; ++ ++ rsp = bt->stkptr; ++ ms = machdep->machspec; ++ estack = 0; ++ ++ for (c = 0; !estack && (c < kt->cpus); c++) { ++ for (i = 0; i < 7; i++) { ++ if (ms->stkinfo.ebase[c][i] == 0) ++ break; ++ if ((rsp >= ms->stkinfo.ebase[c][i]) && ++ (rsp < (ms->stkinfo.ebase[c][i] + ++ ms->stkinfo.esize))) { ++ estack = ms->stkinfo.ebase[c][i]; ++ if (CRASHDEBUG(1) && (c != bt->tc->processor)) ++ error(INFO, ++ "task cpu: %d exception stack cpu: %d\n", ++ bt->tc->processor, c); ++ break; ++ } ++ } ++ } ++ ++ return estack; ++} ++ ++/* ++ * Determine whether the current stack pointer is in a cpu's irqstack. ++ */ ++static ulong ++x86_64_in_irqstack(struct bt_info *bt) ++{ ++ int c; ++ ulong rsp; ++ ulong irqstack; ++ struct machine_specific *ms; ++ ++ rsp = bt->stkptr; ++ ms = machdep->machspec; ++ irqstack = 0; ++ ++ for (c = 0; !irqstack && (c < kt->cpus); c++) { ++ if (ms->stkinfo.ibase[c] == 0) ++ break; ++ if ((rsp >= ms->stkinfo.ibase[c]) && ++ (rsp < (ms->stkinfo.ibase[c] + ms->stkinfo.isize))) { ++ irqstack = ms->stkinfo.ibase[c]; ++ if (CRASHDEBUG(1) && (c != bt->tc->processor)) ++ error(INFO, ++ "task cpu: %d IRQ stack cpu: %d\n", ++ bt->tc->processor, c); ++ break; ++ } ++ } ++ ++ return irqstack; ++} ++ ++#define STACK_TRANSITION_ERRMSG_E_I_P \ ++"cannot transition from exception stack to IRQ stack to current process stack:\n exception stack pointer: %lx\n IRQ stack pointer: %lx\n process stack pointer: %lx\n current stack base: %lx\n" ++#define STACK_TRANSITION_ERRMSG_E_P \ ++"cannot transition from exception stack to current process stack:\n exception stack pointer: %lx\n process stack pointer: %lx\n current_stack_base: %lx\n" ++#define STACK_TRANSITION_ERRMSG_I_P \ ++"cannot transition from IRQ stack to current process stack:\n IRQ stack pointer: %lx\n process stack pointer: %lx\n current stack base: %lx\n" ++ ++/* ++ * Low-budget back tracer -- dump text return addresses, following call chain ++ * when possible, along with any verifiable exception frames. ++ */ ++static void ++x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in) ++{ ++ int i, level, done, framesize; ++ ulong rsp, offset, stacktop; ++ ulong *up; ++ long cs; ++ struct syment *sp, *spt; ++ FILE *ofp; ++ ulong estack, irqstack; ++ ulong irq_eframe; ++ struct bt_info bt_local, *bt; ++ struct machine_specific *ms; ++ ulong last_process_stack_eframe; ++ ulong user_mode_eframe; ++ ++ /* ++ * User may have made a run-time switch. ++ */ ++ if (kt->flags & DWARF_UNWIND) { ++ machdep->back_trace = x86_64_dwarf_back_trace_cmd; ++ x86_64_dwarf_back_trace_cmd(bt_in); ++ return; ++ } ++ ++ bt = &bt_local; ++ BCOPY(bt_in, bt, sizeof(struct bt_info)); ++ ++ if (bt->flags & BT_FRAMESIZE_DEBUG) { ++ x86_64_framesize_debug(bt); ++ return; ++ } ++ ++ level = 0; ++ done = FALSE; ++ irq_eframe = 0; ++ last_process_stack_eframe = 0; ++ bt->call_target = NULL; ++ rsp = bt->stkptr; ++ if (!rsp) { ++ error(INFO, "cannot determine starting stack pointer\n"); ++ return; ++ } ++ ms = machdep->machspec; ++ if (BT_REFERENCE_CHECK(bt)) ++ ofp = pc->nullfp; ++ else ++ ofp = fp; ++ ++ if (bt->flags & BT_TEXT_SYMBOLS) { ++ if (!(bt->flags & BT_TEXT_SYMBOLS_ALL)) ++ fprintf(ofp, "%sSTART: %s%s at %lx\n", ++ space(VADDR_PRLEN > 8 ? 14 : 6), ++ closest_symbol(bt->instptr), ++ STREQ(closest_symbol(bt->instptr), "thread_return") ? ++ " (schedule)" : "", ++ bt->instptr); ++ } else if (bt->flags & BT_START) { ++ x86_64_print_stack_entry(bt, ofp, level, ++ 0, bt->instptr); ++ bt->flags &= ~BT_START; ++ level++; ++ } ++ ++ ++ if ((estack = x86_64_in_exception_stack(bt))) { ++in_exception_stack: ++ bt->flags |= BT_EXCEPTION_STACK; ++ /* ++ * The stack buffer will have been loaded with the process ++ * stack, so switch to the indicated exception stack. ++ */ ++ bt->stackbase = estack; ++ bt->stacktop = estack + ms->stkinfo.esize; ++ bt->stackbuf = ms->irqstack; ++ ++ if (!readmem(bt->stackbase, KVADDR, bt->stackbuf, ++ bt->stacktop - bt->stackbase, ++ bt->hp && (bt->hp->esp == bt->stkptr) ? ++ "irqstack contents via hook" : "irqstack contents", ++ RETURN_ON_ERROR)) ++ error(FATAL, "read of exception stack at %lx failed\n", ++ bt->stackbase); ++ ++ /* ++ * If irq_eframe is set, we've jumped back here from the ++ * IRQ stack dump below. Do basically the same thing as if ++ * had come from the processor stack, but presume that we ++ * must have been in kernel mode, i.e., took an exception ++ * while operating on an IRQ stack. (untested) ++ */ ++ if (irq_eframe) { ++ bt->flags |= BT_EXCEPTION_FRAME; ++ i = (irq_eframe - bt->stackbase)/sizeof(ulong); ++ x86_64_print_stack_entry(bt, ofp, level, i, ++ bt->instptr); ++ bt->flags &= ~(ulonglong)BT_EXCEPTION_FRAME; ++ cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, ++ bt->stackbuf + (irq_eframe - bt->stackbase), ++ bt, ofp); ++ rsp += SIZE(pt_regs); /* guaranteed kernel mode */ ++ level++; ++ irq_eframe = 0; ++ } ++ ++ stacktop = bt->stacktop - SIZE(pt_regs); ++ ++ bt->flags &= ~BT_FRAMESIZE_DISABLE; ++ ++ for (i = (rsp - bt->stackbase)/sizeof(ulong); ++ !done && (rsp < stacktop); i++, rsp += sizeof(ulong)) { ++ ++ up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++ ++ if (!is_kernel_text(*up)) ++ continue; ++ ++ switch (x86_64_print_stack_entry(bt, ofp, level, i,*up)) ++ { ++ case BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED: ++ rsp += SIZE(pt_regs); ++ i += SIZE(pt_regs)/sizeof(ulong); ++ case BACKTRACE_ENTRY_DISPLAYED: ++ level++; ++ if ((framesize = x86_64_get_framesize(bt, *up)) >= 0) { ++ rsp += framesize; ++ i += framesize/sizeof(ulong); ++ } ++ break; ++ case BACKTRACE_ENTRY_IGNORED: ++ break; ++ case BACKTRACE_COMPLETE: ++ done = TRUE; ++ break; ++ } ++ } ++ ++ cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, ++ bt->stackbuf + (bt->stacktop - bt->stackbase) - ++ SIZE(pt_regs), bt, ofp); ++ ++ if (!BT_REFERENCE_CHECK(bt)) ++ fprintf(fp, "--- ---\n"); ++ ++ /* ++ * stack = (unsigned long *) estack_end[-2]; ++ */ ++ up = (ulong *)(&bt->stackbuf[bt->stacktop - bt->stackbase]); ++ up -= 2; ++ rsp = bt->stkptr = *up; ++ up -= 3; ++ bt->instptr = *up; ++ if (cs & 3) ++ done = TRUE; /* user-mode exception */ ++ else ++ done = FALSE; /* kernel-mode exception */ ++ bt->frameptr = 0; ++ ++ /* ++ * Print the return values from the estack end. ++ */ ++ if (!done) { ++ bt->flags |= BT_START; ++ x86_64_print_stack_entry(bt, ofp, level, ++ 0, bt->instptr); ++ bt->flags &= ~(BT_START|BT_FRAMESIZE_DISABLE); ++ level++; ++ if ((framesize = x86_64_get_framesize(bt, bt->instptr)) >= 0) ++ rsp += framesize; ++ } ++ } ++ ++ /* ++ * IRQ stack entry always comes in via the process stack, regardless ++ * whether it happened while running in user or kernel space. ++ */ ++ if (!done && (irqstack = x86_64_in_irqstack(bt))) { ++ bt->flags |= BT_IRQSTACK; ++ /* ++ * Until coded otherwise, the stackbase will be pointing to ++ * either the exception stack or, more likely, the process ++ * stack base. Switch it to the IRQ stack. ++ */ ++ bt->stackbase = irqstack; ++ bt->stacktop = irqstack + ms->stkinfo.isize; ++ bt->stackbuf = ms->irqstack; ++ ++ if (!readmem(bt->stackbase, KVADDR, ++ bt->stackbuf, bt->stacktop - bt->stackbase, ++ bt->hp && (bt->hp->esp == bt_in->stkptr) ? ++ "irqstack contents via hook" : "irqstack contents", ++ RETURN_ON_ERROR)) ++ error(FATAL, "read of IRQ stack at %lx failed\n", ++ bt->stackbase); ++ ++ stacktop = bt->stacktop - 64; /* from kernel code */ ++ ++ bt->flags &= ~BT_FRAMESIZE_DISABLE; ++ ++ for (i = (rsp - bt->stackbase)/sizeof(ulong); ++ !done && (rsp < stacktop); i++, rsp += sizeof(ulong)) { ++ ++ up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++ ++ if (!is_kernel_text(*up)) ++ continue; ++ ++ switch (x86_64_print_stack_entry(bt, ofp, level, i,*up)) ++ { ++ case BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED: ++ rsp += SIZE(pt_regs); ++ i += SIZE(pt_regs)/sizeof(ulong); ++ case BACKTRACE_ENTRY_DISPLAYED: ++ level++; ++ if ((framesize = x86_64_get_framesize(bt, *up)) >= 0) { ++ rsp += framesize; ++ i += framesize/sizeof(ulong); ++ } ++ break; ++ case BACKTRACE_ENTRY_IGNORED: ++ break; ++ case BACKTRACE_COMPLETE: ++ done = TRUE; ++ break; ++ } ++ } ++ ++ if (!BT_REFERENCE_CHECK(bt)) ++ fprintf(fp, "--- ---\n"); ++ ++ /* ++ * stack = (unsigned long *) (irqstack_end[-1]); ++ * (where irqstack_end is 64 bytes below page end) ++ */ ++ up = (ulong *)(&bt->stackbuf[stacktop - bt->stackbase]); ++ up -= 1; ++ irq_eframe = rsp = bt->stkptr = (*up) - ms->irq_eframe_link; ++ up -= 1; ++ bt->instptr = *up; ++ /* ++ * No exception frame when coming from call_softirq. ++ */ ++ if ((sp = value_search(bt->instptr, &offset)) && ++ STREQ(sp->name, "call_softirq")) ++ irq_eframe = 0; ++ bt->frameptr = 0; ++ done = FALSE; ++ } else ++ irq_eframe = 0; ++ ++ if (!done && (estack = x86_64_in_exception_stack(bt))) ++ goto in_exception_stack; ++ ++ if (!done && (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK))) { ++ /* ++ * Verify that the rsp pointer taken from either the ++ * exception or IRQ stack points into the process stack. ++ */ ++ bt->stackbase = GET_STACKBASE(bt->tc->task); ++ bt->stacktop = GET_STACKTOP(bt->tc->task); ++ ++ if (!INSTACK(rsp, bt)) { ++ switch (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK)) ++ { ++ case (BT_EXCEPTION_STACK|BT_IRQSTACK): ++ error(FATAL, STACK_TRANSITION_ERRMSG_E_I_P, ++ bt_in->stkptr, bt->stkptr, rsp, ++ bt->stackbase); ++ ++ case BT_EXCEPTION_STACK: ++ error(FATAL, STACK_TRANSITION_ERRMSG_E_P, ++ bt_in->stkptr, rsp, bt->stackbase); ++ ++ case BT_IRQSTACK: ++ error(FATAL, STACK_TRANSITION_ERRMSG_I_P, ++ bt_in->stkptr, rsp, bt->stackbase); ++ } ++ } ++ ++ /* ++ * Now fill the local stack buffer from the process stack. ++ */ ++ if (!readmem(bt->stackbase, KVADDR, bt->stackbuf, ++ bt->stacktop - bt->stackbase, ++ "irqstack contents", RETURN_ON_ERROR)) ++ error(FATAL, "read of process stack at %lx failed\n", ++ bt->stackbase); ++ } ++ ++ /* ++ * For a normally blocked task, hand-create the first level. ++ */ ++ if (!done && ++ !(bt->flags & (BT_TEXT_SYMBOLS|BT_EXCEPTION_STACK|BT_IRQSTACK)) && ++ STREQ(closest_symbol(bt->instptr), "thread_return")) { ++ bt->flags |= BT_SCHEDULE; ++ i = (rsp - bt->stackbase)/sizeof(ulong); ++ x86_64_print_stack_entry(bt, ofp, level, ++ i, bt->instptr); ++ bt->flags &= ~(ulonglong)BT_SCHEDULE; ++ rsp += sizeof(ulong); ++ level++; ++ } ++ ++ /* ++ * Dump the IRQ exception frame from the process stack. ++ * If the CS register indicates a user exception frame, ++ * then set done to TRUE to avoid the process stack walk-through. ++ * Otherwise, bump up the rsp past the kernel-mode eframe. ++ */ ++ if (irq_eframe) { ++ bt->flags |= BT_EXCEPTION_FRAME; ++ i = (irq_eframe - bt->stackbase)/sizeof(ulong); ++ x86_64_print_stack_entry(bt, ofp, level, i, bt->instptr); ++ bt->flags &= ~(ulonglong)BT_EXCEPTION_FRAME; ++ cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, ++ bt->stackbuf + (irq_eframe - bt->stackbase), bt, ofp); ++ if (cs & 3) ++ done = TRUE; /* IRQ from user-mode */ ++ else { ++ if (x86_64_print_eframe_location(rsp, level, ofp)) ++ level++; ++ rsp += SIZE(pt_regs); ++ irq_eframe = 0; ++ } ++ level++; ++ } ++ ++ /* ++ * Walk the process stack. ++ */ ++ ++ bt->flags &= ~BT_FRAMESIZE_DISABLE; ++ ++ for (i = (rsp - bt->stackbase)/sizeof(ulong); ++ !done && (rsp < bt->stacktop); i++, rsp += sizeof(ulong)) { ++ ++ up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++ ++ if (!is_kernel_text(*up)) ++ continue; ++ ++ if ((bt->flags & BT_CHECK_CALLER)) { ++ /* ++ * A non-zero offset value from the value_search() ++ * lets us know if it's a real text return address. ++ */ ++ spt = value_search(*up, &offset); ++ if (!offset && !(bt->flags & BT_FRAMESIZE_DISABLE)) ++ continue; ++ ++ /* ++ * sp gets the syment of the function that the text ++ * routine above called before leaving its return ++ * address on the stack -- if it can be determined. ++ */ ++ sp = x86_64_function_called_by((*up)-5); ++ ++ if (sp == NULL) { ++ /* ++ * We were unable to get the called function. ++ * If the text address had an offset, then ++ * it must have made an indirect call, and ++ * can't have called our target function. ++ */ ++ if (offset) { ++ if (CRASHDEBUG(1)) ++ fprintf(ofp, ++ "< ignoring %s() -- makes indirect call and NOT %s()>\n", ++ spt->name, ++ bt->call_target); ++ continue; ++ } ++ } else if ((machdep->flags & SCHED_TEXT) && ++ STREQ(bt->call_target, "schedule") && ++ STREQ(sp->name, "__sched_text_start")) { ++ ; /* bait and switch */ ++ } else if (!STREQ(sp->name, bt->call_target)) { ++ /* ++ * We got function called by the text routine, ++ * but it's not our target function. ++ */ ++ if (CRASHDEBUG(2)) ++ fprintf(ofp, ++ "< ignoring %s() -- calls %s() and NOT %s()>\n", ++ spt->name, sp->name, ++ bt->call_target); ++ continue; ++ } ++ } ++ ++ switch (x86_64_print_stack_entry(bt, ofp, level, i,*up)) ++ { ++ case BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED: ++ last_process_stack_eframe = rsp + 8; ++ if (x86_64_print_eframe_location(last_process_stack_eframe, level, ofp)) ++ level++; ++ rsp += SIZE(pt_regs); ++ i += SIZE(pt_regs)/sizeof(ulong); ++ case BACKTRACE_ENTRY_DISPLAYED: ++ level++; ++ if ((framesize = x86_64_get_framesize(bt, *up)) >= 0) { ++ rsp += framesize; ++ i += framesize/sizeof(ulong); ++ } ++ break; ++ case BACKTRACE_ENTRY_IGNORED: ++ break; ++ case BACKTRACE_COMPLETE: ++ done = TRUE; ++ break; ++ } ++ } ++ ++ if (!irq_eframe && !is_kernel_thread(bt->tc->task) && ++ (GET_STACKBASE(bt->tc->task) == bt->stackbase)) { ++ user_mode_eframe = bt->stacktop - SIZE(pt_regs); ++ if (last_process_stack_eframe < user_mode_eframe) ++ x86_64_exception_frame(EFRAME_PRINT, 0, bt->stackbuf + ++ (bt->stacktop - bt->stackbase) - SIZE(pt_regs), ++ bt, ofp); ++ } ++ ++ if (bt->flags & BT_TEXT_SYMBOLS) { ++ if (BT_REFERENCE_FOUND(bt)) { ++ print_task_header(fp, task_to_context(bt->task), 0); ++ BCOPY(bt_in, bt, sizeof(struct bt_info)); ++ bt->ref = NULL; ++ machdep->back_trace(bt); ++ fprintf(fp, "\n"); ++ } ++ } ++} ++ ++/* ++ * Use dwarf CFI encodings to correctly follow the call chain. ++ */ ++static void ++x86_64_dwarf_back_trace_cmd(struct bt_info *bt_in) ++{ ++ int i, level, done; ++ ulong rsp, offset, stacktop; ++ ulong *up; ++ long cs; ++ struct syment *sp; ++ FILE *ofp; ++ ulong estack, irqstack; ++ ulong irq_eframe; ++ struct bt_info bt_local, *bt; ++ struct machine_specific *ms; ++ ulong last_process_stack_eframe; ++ ulong user_mode_eframe; ++ ++ /* ++ * User may have made a run-time switch. ++ */ ++ if (!(kt->flags & DWARF_UNWIND)) { ++ machdep->back_trace = x86_64_low_budget_back_trace_cmd; ++ x86_64_low_budget_back_trace_cmd(bt_in); ++ return; ++ } ++ ++ bt = &bt_local; ++ BCOPY(bt_in, bt, sizeof(struct bt_info)); ++ ++ if (bt->flags & BT_FRAMESIZE_DEBUG) { ++ dwarf_debug(bt); ++ return; ++ } ++ ++ level = 0; ++ done = FALSE; ++ irq_eframe = 0; ++ last_process_stack_eframe = 0; ++ bt->call_target = NULL; ++ bt->bptr = 0; ++ rsp = bt->stkptr; ++ if (!rsp) { ++ error(INFO, "cannot determine starting stack pointer\n"); ++ return; ++ } ++ ms = machdep->machspec; ++ if (BT_REFERENCE_CHECK(bt)) ++ ofp = pc->nullfp; ++ else ++ ofp = fp; ++ ++ if (bt->flags & BT_TEXT_SYMBOLS) { ++ if (!(bt->flags & BT_TEXT_SYMBOLS_ALL)) ++ fprintf(ofp, "%sSTART: %s%s at %lx\n", ++ space(VADDR_PRLEN > 8 ? 14 : 6), ++ closest_symbol(bt->instptr), ++ STREQ(closest_symbol(bt->instptr), "thread_return") ? ++ " (schedule)" : "", ++ bt->instptr); ++ } else if (bt->flags & BT_START) { ++ x86_64_print_stack_entry(bt, ofp, level, ++ 0, bt->instptr); ++ bt->flags &= ~BT_START; ++ level++; ++ } ++ ++ ++ if ((estack = x86_64_in_exception_stack(bt))) { ++in_exception_stack: ++ bt->flags |= BT_EXCEPTION_STACK; ++ /* ++ * The stack buffer will have been loaded with the process ++ * stack, so switch to the indicated exception stack. ++ */ ++ bt->stackbase = estack; ++ bt->stacktop = estack + ms->stkinfo.esize; ++ bt->stackbuf = ms->irqstack; ++ ++ if (!readmem(bt->stackbase, KVADDR, bt->stackbuf, ++ bt->stacktop - bt->stackbase, ++ bt->hp && (bt->hp->esp == bt->stkptr) ? ++ "irqstack contents via hook" : "irqstack contents", ++ RETURN_ON_ERROR)) ++ error(FATAL, "read of exception stack at %lx failed\n", ++ bt->stackbase); ++ ++ /* ++ * If irq_eframe is set, we've jumped back here from the ++ * IRQ stack dump below. Do basically the same thing as if ++ * had come from the processor stack, but presume that we ++ * must have been in kernel mode, i.e., took an exception ++ * while operating on an IRQ stack. (untested) ++ */ ++ if (irq_eframe) { ++ bt->flags |= BT_EXCEPTION_FRAME; ++ i = (irq_eframe - bt->stackbase)/sizeof(ulong); ++ x86_64_print_stack_entry(bt, ofp, level, i, ++ bt->instptr); ++ bt->flags &= ~(ulonglong)BT_EXCEPTION_FRAME; ++ cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, ++ bt->stackbuf + (irq_eframe - bt->stackbase), ++ bt, ofp); ++ rsp += SIZE(pt_regs); /* guaranteed kernel mode */ ++ level++; ++ irq_eframe = 0; ++ } ++ ++ stacktop = bt->stacktop - SIZE(pt_regs); ++ ++ if (!done) { ++ level = dwarf_backtrace(bt, level, stacktop); ++ done = TRUE; ++ } ++ ++ cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, ++ bt->stackbuf + (bt->stacktop - bt->stackbase) - ++ SIZE(pt_regs), bt, ofp); ++ ++ if (!BT_REFERENCE_CHECK(bt)) ++ fprintf(fp, "--- ---\n"); ++ ++ /* ++ * stack = (unsigned long *) estack_end[-2]; ++ */ ++ up = (ulong *)(&bt->stackbuf[bt->stacktop - bt->stackbase]); ++ up -= 2; ++ rsp = bt->stkptr = *up; ++ up -= 3; ++ bt->instptr = *up; ++ if (cs & 3) ++ done = TRUE; /* user-mode exception */ ++ else ++ done = FALSE; /* kernel-mode exception */ ++ bt->frameptr = 0; ++ ++ /* ++ * Print the return values from the estack end. ++ */ ++ if (!done) { ++ bt->flags |= BT_START; ++ x86_64_print_stack_entry(bt, ofp, level, ++ 0, bt->instptr); ++ bt->flags &= ~BT_START; ++ level++; ++ } ++ } ++ ++ /* ++ * IRQ stack entry always comes in via the process stack, regardless ++ * whether it happened while running in user or kernel space. ++ */ ++ if (!done && (irqstack = x86_64_in_irqstack(bt))) { ++ bt->flags |= BT_IRQSTACK; ++ /* ++ * Until coded otherwise, the stackbase will be pointing to ++ * either the exception stack or, more likely, the process ++ * stack base. Switch it to the IRQ stack. ++ */ ++ bt->stackbase = irqstack; ++ bt->stacktop = irqstack + ms->stkinfo.isize; ++ bt->stackbuf = ms->irqstack; ++ ++ if (!readmem(bt->stackbase, KVADDR, ++ bt->stackbuf, bt->stacktop - bt->stackbase, ++ bt->hp && (bt->hp->esp == bt_in->stkptr) ? ++ "irqstack contents via hook" : "irqstack contents", ++ RETURN_ON_ERROR)) ++ error(FATAL, "read of IRQ stack at %lx failed\n", ++ bt->stackbase); ++ ++ stacktop = bt->stacktop - 64; /* from kernel code */ ++ ++ if (!done) { ++ level = dwarf_backtrace(bt, level, stacktop); ++ done = TRUE; ++ } ++ ++ if (!BT_REFERENCE_CHECK(bt)) ++ fprintf(fp, "--- ---\n"); ++ ++ /* ++ * stack = (unsigned long *) (irqstack_end[-1]); ++ * (where irqstack_end is 64 bytes below page end) ++ */ ++ up = (ulong *)(&bt->stackbuf[stacktop - bt->stackbase]); ++ up -= 1; ++ irq_eframe = rsp = bt->stkptr = (*up) - ms->irq_eframe_link; ++ up -= 1; ++ bt->instptr = *up; ++ /* ++ * No exception frame when coming from call_softirq. ++ */ ++ if ((sp = value_search(bt->instptr, &offset)) && ++ STREQ(sp->name, "call_softirq")) ++ irq_eframe = 0; ++ bt->frameptr = 0; ++ done = FALSE; ++ } else ++ irq_eframe = 0; ++ ++ if (!done && (estack = x86_64_in_exception_stack(bt))) ++ goto in_exception_stack; ++ ++ if (!done && (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK))) { ++ /* ++ * Verify that the rsp pointer taken from either the ++ * exception or IRQ stack points into the process stack. ++ */ ++ bt->stackbase = GET_STACKBASE(bt->tc->task); ++ bt->stacktop = GET_STACKTOP(bt->tc->task); ++ ++ if (!INSTACK(rsp, bt)) { ++ switch (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK)) ++ { ++ case (BT_EXCEPTION_STACK|BT_IRQSTACK): ++ error(FATAL, STACK_TRANSITION_ERRMSG_E_I_P, ++ bt_in->stkptr, bt->stkptr, rsp, ++ bt->stackbase); ++ ++ case BT_EXCEPTION_STACK: ++ error(FATAL, STACK_TRANSITION_ERRMSG_E_P, ++ bt_in->stkptr, rsp, bt->stackbase); ++ ++ case BT_IRQSTACK: ++ error(FATAL, STACK_TRANSITION_ERRMSG_I_P, ++ bt_in->stkptr, rsp, bt->stackbase); ++ } ++ } ++ ++ /* ++ * Now fill the local stack buffer from the process stack. ++ */ ++ if (!readmem(bt->stackbase, KVADDR, bt->stackbuf, ++ bt->stacktop - bt->stackbase, ++ "irqstack contents", RETURN_ON_ERROR)) ++ error(FATAL, "read of process stack at %lx failed\n", ++ bt->stackbase); ++ } ++ ++ /* ++ * Dump the IRQ exception frame from the process stack. ++ * If the CS register indicates a user exception frame, ++ * then set done to TRUE to avoid the process stack walk-through. ++ * Otherwise, bump up the rsp past the kernel-mode eframe. ++ */ ++ if (irq_eframe) { ++ bt->flags |= BT_EXCEPTION_FRAME; ++ level = dwarf_print_stack_entry(bt, level); ++ bt->flags &= ~(ulonglong)BT_EXCEPTION_FRAME; ++ cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, ++ bt->stackbuf + (irq_eframe - bt->stackbase), bt, ofp); ++ if (cs & 3) ++ done = TRUE; /* IRQ from user-mode */ ++ else { ++ if (x86_64_print_eframe_location(rsp, level, ofp)) ++ level++; ++ rsp += SIZE(pt_regs); ++ irq_eframe = 0; ++ } ++ level++; ++ } ++ ++ /* ++ * Walk the process stack. ++ */ ++ if (!done) { ++ level = dwarf_backtrace(bt, level, bt->stacktop); ++ done = TRUE; ++ } ++ ++ if (!irq_eframe && !is_kernel_thread(bt->tc->task) && ++ (GET_STACKBASE(bt->tc->task) == bt->stackbase)) { ++ user_mode_eframe = bt->stacktop - SIZE(pt_regs); ++ if (last_process_stack_eframe < user_mode_eframe) ++ x86_64_exception_frame(EFRAME_PRINT, 0, bt->stackbuf + ++ (bt->stacktop - bt->stackbase) - SIZE(pt_regs), ++ bt, ofp); ++ } ++ ++ if (bt->flags & BT_TEXT_SYMBOLS) { ++ if (BT_REFERENCE_FOUND(bt)) { ++ print_task_header(fp, task_to_context(bt->task), 0); ++ BCOPY(bt_in, bt, sizeof(struct bt_info)); ++ bt->ref = NULL; ++ machdep->back_trace(bt); ++ fprintf(fp, "\n"); ++ } ++ } ++} ++ ++/* ++ * Functions that won't be called indirectly. ++ * Add more to this as they are discovered. ++ */ ++static const char *direct_call_targets[] = { ++ "schedule", ++ "schedule_timeout", ++ NULL ++}; ++ ++static int ++is_direct_call_target(struct bt_info *bt) ++{ ++ int i; ++ ++ if (!bt->call_target || (bt->flags & BT_NO_CHECK_CALLER)) ++ return FALSE; ++ ++ if (strstr(bt->call_target, "schedule") && ++ is_task_active(bt->task)) ++ return FALSE; ++ ++ for (i = 0; direct_call_targets[i]; i++) { ++ if (STREQ(direct_call_targets[i], bt->call_target)) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static struct syment * ++x86_64_function_called_by(ulong rip) ++{ ++ struct syment *sp; ++ char buf[BUFSIZE], *p1; ++ ulong value, offset; ++ unsigned char byte; ++ ++ value = 0; ++ sp = NULL; ++ ++ if (!readmem(rip, KVADDR, &byte, sizeof(unsigned char), "call byte", ++ RETURN_ON_ERROR)) ++ return sp; ++ ++ if (byte != 0xe8) ++ return sp; ++ ++ sprintf(buf, "x/i 0x%lx", rip); ++ ++ open_tmpfile2(); ++ if (gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { ++ rewind(pc->tmpfile2); ++ while (fgets(buf, BUFSIZE, pc->tmpfile2)) { ++ if ((p1 = strstr(buf, "callq")) && ++ whitespace(*(p1-1))) { ++ if (extract_hex(p1, &value, NULLCHAR, TRUE)) ++ break; ++ } ++ } ++ } ++ close_tmpfile2(); ++ ++ if (value) ++ sp = value_search(value, &offset); ++ ++ /* ++ * Functions that jmp to schedule() or schedule_timeout(). ++ */ ++ if (sp) { ++ if ((STREQ(sp->name, "schedule_timeout_interruptible") || ++ STREQ(sp->name, "schedule_timeout_uninterruptible"))) ++ sp = symbol_search("schedule_timeout"); ++ ++ if (STREQ(sp->name, "__cond_resched")) ++ sp = symbol_search("schedule"); ++ } ++ ++ return sp; ++} ++ ++/* ++ * Unroll the kernel stack using a minimal amount of gdb services. ++ */ ++static void ++x86_64_back_trace(struct gnu_request *req, struct bt_info *bt) ++{ ++ error(FATAL, "x86_64_back_trace: unused\n"); ++} ++ ++ ++/* ++ * Print exception frame information for x86_64. ++ * ++ * Pid: 0, comm: swapper Not tainted 2.6.5-1.360phro.rootsmp ++ * RIP: 0010:[] {default_idle+36} ++ * RSP: 0018:ffffffff8048bfd8 EFLAGS: 00000246 ++ * RAX: 0000000000000000 RBX: ffffffff8010f510 RCX: 0000000000000018 ++ * RDX: 0000010001e37280 RSI: ffffffff803ac0a0 RDI: 000001007f43c400 ++ * RBP: 0000000000000000 R08: ffffffff8048a000 R09: 0000000000000000 ++ * R10: ffffffff80482188 R11: 0000000000000001 R12: 0000000000000000 ++ * R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 ++ * FS: 0000002a96e14fc0(0000) GS:ffffffff80481d80(0000) GS:0000000055578aa0 ++ * CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b ++ * CR2: 0000002a9556b000 CR3: 0000000000101000 CR4: 00000000000006e0 ++ * ++ */ ++ ++static long ++x86_64_exception_frame(ulong flags, ulong kvaddr, char *local, ++ struct bt_info *bt, FILE *ofp) ++{ ++ long rip, rsp, cs, ss, rflags, orig_rax, rbp; ++ long rax, rbx, rcx, rdx, rsi, rdi; ++ long r8, r9, r10, r11, r12, r13, r14, r15; ++ struct machine_specific *ms; ++ struct syment *sp; ++ ulong offset; ++ char *pt_regs_buf; ++ long verified; ++ int err; ++ ++ ms = machdep->machspec; ++ ++ if (!(machdep->flags & PT_REGS_INIT)) { ++ err = 0; ++ err |= ((ms->pto.r15 = MEMBER_OFFSET("pt_regs", "r15")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.r14 = MEMBER_OFFSET("pt_regs", "r14")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.r13 = MEMBER_OFFSET("pt_regs", "r13")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.r12 = MEMBER_OFFSET("pt_regs", "r12")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.r11 = MEMBER_OFFSET("pt_regs", "r11")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.r10 = MEMBER_OFFSET("pt_regs", "r10")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.r9 = MEMBER_OFFSET("pt_regs", "r9")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.r8 = MEMBER_OFFSET("pt_regs", "r8")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.cs = MEMBER_OFFSET("pt_regs", "cs")) == ++ INVALID_OFFSET); ++ err |= ((ms->pto.ss = MEMBER_OFFSET("pt_regs", "ss")) == ++ INVALID_OFFSET); ++ /* ++ * x86/x86_64 merge changed traditional register names. ++ */ ++ if (((ms->pto.rbp = MEMBER_OFFSET("pt_regs", "rbp")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rbp = MEMBER_OFFSET("pt_regs", "bp")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rax = MEMBER_OFFSET("pt_regs", "rax")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rax = MEMBER_OFFSET("pt_regs", "ax")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rbx = MEMBER_OFFSET("pt_regs", "rbx")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rbx = MEMBER_OFFSET("pt_regs", "bx")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rcx = MEMBER_OFFSET("pt_regs", "rcx")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rcx = MEMBER_OFFSET("pt_regs", "cx")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rdx = MEMBER_OFFSET("pt_regs", "rdx")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rdx = MEMBER_OFFSET("pt_regs", "dx")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rsi = MEMBER_OFFSET("pt_regs", "rsi")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rsi = MEMBER_OFFSET("pt_regs", "si")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rdi = MEMBER_OFFSET("pt_regs", "rdi")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rdi = MEMBER_OFFSET("pt_regs", "di")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rip = MEMBER_OFFSET("pt_regs", "rip")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rip = MEMBER_OFFSET("pt_regs", "ip")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.rsp = MEMBER_OFFSET("pt_regs", "rsp")) == ++ INVALID_OFFSET) && ++ ((ms->pto.rsp = MEMBER_OFFSET("pt_regs", "sp")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.eflags = MEMBER_OFFSET("pt_regs", "eflags")) == ++ INVALID_OFFSET) && ++ ((ms->pto.eflags = MEMBER_OFFSET("pt_regs", "flags")) == ++ INVALID_OFFSET)) ++ err++; ++ if (((ms->pto.orig_rax = MEMBER_OFFSET("pt_regs", "orig_rax")) == ++ INVALID_OFFSET) && ++ ((ms->pto.orig_rax = MEMBER_OFFSET("pt_regs", "orig_ax")) == ++ INVALID_OFFSET)) ++ err++; ++ ++ if (err) ++ error(WARNING, "pt_regs structure has changed\n"); ++ ++ machdep->flags |= PT_REGS_INIT; ++ } ++ ++ if (kvaddr) { ++ pt_regs_buf = GETBUF(SIZE(pt_regs)); ++ readmem(kvaddr, KVADDR, pt_regs_buf, ++ SIZE(pt_regs), "pt_regs", FAULT_ON_ERROR); ++ } else ++ pt_regs_buf = local; ++ ++ rip = ULONG(pt_regs_buf + ms->pto.rip); ++ rsp = ULONG(pt_regs_buf + ms->pto.rsp); ++ cs = ULONG(pt_regs_buf + ms->pto.cs); ++ ss = ULONG(pt_regs_buf + ms->pto.ss); ++ rflags = ULONG(pt_regs_buf + ms->pto.eflags); ++ orig_rax = ULONG(pt_regs_buf + ms->pto.orig_rax); ++ rbp = ULONG(pt_regs_buf + ms->pto.rbp); ++ rax = ULONG(pt_regs_buf + ms->pto.rax); ++ rbx = ULONG(pt_regs_buf + ms->pto.rbx); ++ rcx = ULONG(pt_regs_buf + ms->pto.rcx); ++ rdx = ULONG(pt_regs_buf + ms->pto.rdx); ++ rsi = ULONG(pt_regs_buf + ms->pto.rsi); ++ rdi = ULONG(pt_regs_buf + ms->pto.rdi); ++ r8 = ULONG(pt_regs_buf + ms->pto.r8); ++ r9 = ULONG(pt_regs_buf + ms->pto.r9); ++ r10 = ULONG(pt_regs_buf + ms->pto.r10); ++ r11 = ULONG(pt_regs_buf + ms->pto.r11); ++ r12 = ULONG(pt_regs_buf + ms->pto.r12); ++ r13 = ULONG(pt_regs_buf + ms->pto.r13); ++ r14 = ULONG(pt_regs_buf + ms->pto.r14); ++ r15 = ULONG(pt_regs_buf + ms->pto.r15); ++ ++ verified = x86_64_eframe_verify(bt, ++ kvaddr ? kvaddr : (local - bt->stackbuf) + bt->stackbase, ++ cs, ss, rip, rsp, rflags); ++ ++ /* ++ * If it's print-if-verified request, don't print bogus eframes. ++ */ ++ if (!verified && ((flags & (EFRAME_VERIFY|EFRAME_PRINT)) == ++ (EFRAME_VERIFY|EFRAME_PRINT))) ++ flags &= ~EFRAME_PRINT; ++ ++ if (CRASHDEBUG(2)) ++ fprintf(ofp, "< exception frame at: %lx >\n", kvaddr ? kvaddr : ++ (local - bt->stackbuf) + bt->stackbase); ++ ++ if (flags & EFRAME_PRINT) { ++ if (flags & EFRAME_SEARCH) { ++ fprintf(ofp, "\n %s-MODE EXCEPTION FRAME AT: %lx\n", ++ cs & 3 ? "USER" : "KERNEL", ++ kvaddr ? kvaddr : ++ (local - bt->stackbuf) + bt->stackbase); ++ } else if (!(cs & 3)) { ++ fprintf(ofp, " [exception RIP: "); ++ if ((sp = value_search(rip, &offset))) { ++ fprintf(ofp, "%s", sp->name); ++ if (offset) ++ fprintf(ofp, (output_radix == 16) ? ++ "+0x%lx" : "+%ld", offset); ++ } else ++ fprintf(ofp, "unknown or invalid address"); ++ fprintf(ofp, "]\n"); ++ } ++ fprintf(ofp, " RIP: %016lx RSP: %016lx RFLAGS: %08lx\n", ++ rip, rsp, rflags); ++ fprintf(ofp, " RAX: %016lx RBX: %016lx RCX: %016lx\n", ++ rax, rbx, rcx); ++ fprintf(ofp, " RDX: %016lx RSI: %016lx RDI: %016lx\n", ++ rdx, rsi, rdi); ++ fprintf(ofp, " RBP: %016lx R8: %016lx R9: %016lx\n", ++ rbp, r8, r9); ++ fprintf(ofp, " R10: %016lx R11: %016lx R12: %016lx\n", ++ r10, r11, r12); ++ fprintf(ofp, " R13: %016lx R14: %016lx R15: %016lx\n", ++ r13, r14, r15); ++ fprintf(ofp, " ORIG_RAX: %016lx CS: %04lx SS: %04lx\n", ++ orig_rax, cs, ss); ++ ++ if (!verified && CRASHDEBUG((pc->flags & RUNTIME) ? 0 : 1)) ++ error(WARNING, "possibly bogus exception frame\n"); ++ } ++ ++ if ((flags & EFRAME_PRINT) && BT_REFERENCE_CHECK(bt)) { ++ x86_64_do_bt_reference_check(bt, rip, NULL); ++ x86_64_do_bt_reference_check(bt, rsp, NULL); ++ x86_64_do_bt_reference_check(bt, cs, NULL); ++ x86_64_do_bt_reference_check(bt, ss, NULL); ++ x86_64_do_bt_reference_check(bt, rflags, NULL); ++ x86_64_do_bt_reference_check(bt, orig_rax, NULL); ++ x86_64_do_bt_reference_check(bt, rbp, NULL); ++ x86_64_do_bt_reference_check(bt, rax, NULL); ++ x86_64_do_bt_reference_check(bt, rbx, NULL); ++ x86_64_do_bt_reference_check(bt, rcx, NULL); ++ x86_64_do_bt_reference_check(bt, rdx, NULL); ++ x86_64_do_bt_reference_check(bt, rsi, NULL); ++ x86_64_do_bt_reference_check(bt, rdi, NULL); ++ x86_64_do_bt_reference_check(bt, r8, NULL); ++ x86_64_do_bt_reference_check(bt, r9, NULL); ++ x86_64_do_bt_reference_check(bt, r10, NULL); ++ x86_64_do_bt_reference_check(bt, r11, NULL); ++ x86_64_do_bt_reference_check(bt, r12, NULL); ++ x86_64_do_bt_reference_check(bt, r13, NULL); ++ x86_64_do_bt_reference_check(bt, r14, NULL); ++ x86_64_do_bt_reference_check(bt, r15, NULL); ++ } ++ ++ /* Remember the rip and rsp for unwinding the process stack */ ++ if (kt->flags & DWARF_UNWIND){ ++ bt->instptr = rip; ++ bt->stkptr = rsp; ++ bt->bptr = rbp; ++ } ++ ++ if (kvaddr) ++ FREEBUF(pt_regs_buf); ++ ++ if (flags & EFRAME_CS) ++ return cs; ++ else if (flags & EFRAME_VERIFY) ++ return verified; ++ ++ return 0; ++} ++ ++static int ++x86_64_print_eframe_location(ulong eframe, int level, FILE *ofp) ++{ ++ return FALSE; ++ ++#ifdef NOTDEF ++ ulong rip; ++ char *pt_regs_buf; ++ struct machine_specific *ms; ++ struct syment *sp; ++ ++ ms = machdep->machspec; ++ ++ pt_regs_buf = GETBUF(SIZE(pt_regs)); ++ if (!readmem(eframe, KVADDR, pt_regs_buf, SIZE(pt_regs), ++ "pt_regs", RETURN_ON_ERROR|QUIET)) { ++ FREEBUF(pt_regs_buf); ++ return FALSE; ++ } ++ ++ rip = ULONG(pt_regs_buf + ms->pto.rip); ++ FREEBUF(pt_regs_buf); ++ ++ if (!(sp = value_search(rip, NULL))) ++ return FALSE; ++ ++ fprintf(ofp, "%s#%d [%8lx] %s at %lx\n", level < 10 ? " " : "", level+1, ++ eframe, sp->name, rip); ++ ++ return TRUE; ++#endif ++} ++ ++/* ++ * Check that the verifiable registers contain reasonable data. ++ */ ++#define RAZ_MASK 0xffffffffffc08028 /* return-as-zero bits */ ++ ++static int ++x86_64_eframe_verify(struct bt_info *bt, long kvaddr, long cs, long ss, ++ long rip, long rsp, long rflags) ++{ ++ if ((rflags & RAZ_MASK) || !(rflags & 0x2)) ++ return FALSE; ++ ++ if ((cs == 0x10) && (ss == 0x18)) { ++ if (is_kernel_text(rip) && IS_KVADDR(rsp)) ++ return TRUE; ++ ++ if (x86_64_is_module_addr(rip) && ++ IS_KVADDR(rsp) && ++ (rsp == (kvaddr + SIZE(pt_regs)))) ++ return TRUE; ++ } ++ ++ if ((cs == 0x10) && kvaddr) { ++ if (is_kernel_text(rip) && IS_KVADDR(rsp) && ++ (rsp == (kvaddr + SIZE(pt_regs) + 8))) ++ return TRUE; ++ } ++ ++ if ((cs == 0x10) && kvaddr) { ++ if (is_kernel_text(rip) && IS_KVADDR(rsp) && ++ (rsp == (kvaddr + SIZE(pt_regs)))) ++ return TRUE; ++ } ++ ++ if ((cs == 0x10) && kvaddr) { ++ if (is_kernel_text(rip) && IS_KVADDR(rsp) && ++ x86_64_in_exception_stack(bt)) ++ return TRUE; ++ } ++ ++ if ((cs == 0x33) && (ss == 0x2b)) { ++ if (IS_UVADDR(rip, bt->tc) && IS_UVADDR(rsp, bt->tc)) ++ return TRUE; ++ } ++ ++ if (XEN() && ((cs == 0x33) || (cs == 0xe033)) && ++ ((ss == 0x2b) || (ss == 0xe02b))) { ++ if (IS_UVADDR(rip, bt->tc) && IS_UVADDR(rsp, bt->tc)) ++ return TRUE; ++ } ++ ++ if (XEN() && ((cs == 0x10000e030) || (cs == 0xe030)) && ++ (ss == 0xe02b)) { ++ if (is_kernel_text(rip) && IS_KVADDR(rsp)) ++ return TRUE; ++ } ++ ++ /* ++ * 32-bit segments ++ */ ++ if ((cs == 0x23) && (ss == 0x2b)) { ++ if (IS_UVADDR(rip, bt->tc) && IS_UVADDR(rsp, bt->tc)) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/* ++ * Get a stack frame combination of pc and ra from the most relevent spot. ++ */ ++static void ++x86_64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp) ++{ ++ if (bt->flags & BT_DUMPFILE_SEARCH) ++ return x86_64_get_dumpfile_stack_frame(bt, pcp, spp); ++ ++ if (pcp) ++ *pcp = x86_64_get_pc(bt); ++ if (spp) ++ *spp = x86_64_get_sp(bt); ++} ++ ++/* ++ * Get the starting point for the active cpus in a diskdump/netdump. ++ */ ++static void ++x86_64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *rip, ulong *rsp) ++{ ++ int panic_task; ++ int i, estack, panic, stage; ++ char *sym; ++ struct syment *sp; ++ ulong *up; ++ struct bt_info bt_local, *bt; ++ struct machine_specific *ms; ++ char *user_regs; ++ ulong ur_rip, ur_rsp; ++ ulong halt_rip, halt_rsp; ++ ulong crash_kexec_rip, crash_kexec_rsp; ++ ++ bt = &bt_local; ++ BCOPY(bt_in, bt, sizeof(struct bt_info)); ++ ms = machdep->machspec; ++ ur_rip = ur_rsp = 0; ++ halt_rip = halt_rsp = 0; ++ crash_kexec_rip = crash_kexec_rsp = 0; ++ stage = 0; ++ estack = -1; ++ ++ panic_task = tt->panic_task == bt->task ? TRUE : FALSE; ++ ++ if (panic_task && bt->machdep) { ++ user_regs = bt->machdep; ++ ++ if (x86_64_eframe_verify(bt, ++ 0, ++ ULONG(user_regs + OFFSET(user_regs_struct_cs)), ++ ULONG(user_regs + OFFSET(user_regs_struct_ss)), ++ ULONG(user_regs + OFFSET(user_regs_struct_rip)), ++ ULONG(user_regs + OFFSET(user_regs_struct_rsp)), ++ ULONG(user_regs + OFFSET(user_regs_struct_eflags)))) { ++ bt->stkptr = ULONG(user_regs + ++ OFFSET(user_regs_struct_rsp)); ++ if (x86_64_in_irqstack(bt)) { ++ ur_rip = ULONG(user_regs + ++ OFFSET(user_regs_struct_rip)); ++ ur_rsp = ULONG(user_regs + ++ OFFSET(user_regs_struct_rsp)); ++ goto skip_stage; ++ } ++ } ++ } ++ ++ panic = FALSE; ++ ++ /* ++ * Check the process stack first. ++ */ ++next_stack: ++ for (i = 0, up = (ulong *)bt->stackbuf; ++ i < (bt->stacktop - bt->stackbase)/sizeof(ulong); i++, up++) { ++ sym = closest_symbol(*up); ++ if (XEN_CORE_DUMPFILE()) { ++ if (STREQ(sym, "crash_kexec")) { ++ sp = x86_64_function_called_by((*up)-5); ++ if (sp && STREQ(sp->name, "machine_kexec")) { ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ return; ++ } ++ } ++ if (STREQ(sym, "xen_machine_kexec")) { ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ return; ++ } ++ } else if (STREQ(sym, "netconsole_netdump") || ++ STREQ(sym, "netpoll_start_netdump") || ++ STREQ(sym, "start_disk_dump") || ++ STREQ(sym, "disk_dump") || ++ STREQ(sym, "crash_kexec") || ++ STREQ(sym, "machine_kexec") || ++ STREQ(sym, "try_crashdump")) { ++ if (STREQ(sym, "crash_kexec")) { ++ sp = x86_64_function_called_by((*up)-5); ++ if (sp && STREQ(sp->name, "machine_kexec")) { ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ return; ++ } ++ } ++ /* ++ * Use second instance of crash_kexec if it exists. ++ */ ++ if (!(bt->flags & BT_TEXT_SYMBOLS) && ++ STREQ(sym, "crash_kexec") && !crash_kexec_rip) { ++ crash_kexec_rip = *up; ++ crash_kexec_rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ continue; ++ } ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ return; ++ } ++ ++ if ((estack >= 0) && ++ (STREQ(sym, "nmi_watchdog_tick") || ++ STREQ(sym, "default_do_nmi"))) { ++ sp = x86_64_function_called_by((*up)-5); ++ if (!sp || !STREQ(sp->name, "die_nmi")) ++ continue; ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ bt_in->flags |= BT_START; ++ *rip = symbol_value("die_nmi"); ++ *rsp = (*rsp) - (7*sizeof(ulong)); ++ return; ++ } ++ ++ if (STREQ(sym, "panic")) { ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ panic = TRUE; ++ continue; /* keep looking for die */ ++ } ++ ++ if (STREQ(sym, "die")) { ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ for (i++, up++; i < LONGS_PER_STACK; i++, up++) { ++ sym = closest_symbol(*up); ++ if (STREQ(sym, "sysrq_handle_crash")) ++ goto next_sysrq; ++ } ++ return; ++ } ++ ++ if (STREQ(sym, "sysrq_handle_crash")) { ++next_sysrq: ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ pc->flags |= SYSRQ; ++ for (i++, up++; i < LONGS_PER_STACK; i++, up++) { ++ sym = closest_symbol(*up); ++ if (STREQ(sym, "sysrq_handle_crash")) ++ goto next_sysrq; ++ } ++ return; ++ } ++ ++ if (!panic_task && (stage > 0) && ++ STREQ(sym, "smp_call_function_interrupt")) { ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ return; ++ } ++ ++ if (!panic_task && STREQ(sym, "crash_nmi_callback")) { ++ *rip = *up; ++ *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ return; ++ } ++ ++ if (XEN_CORE_DUMPFILE() && !panic_task && (bt->tc->pid == 0) && ++ (stage == 0) && STREQ(sym, "safe_halt")) { ++ halt_rip = *up; ++ halt_rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ } ++ ++ if (XEN_CORE_DUMPFILE() && !panic_task && (bt->tc->pid == 0) && ++ !halt_rip && (stage == 0) && STREQ(sym, "xen_idle")) { ++ halt_rip = *up; ++ halt_rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ } ++ ++ if (!XEN_CORE_DUMPFILE() && !panic_task && (bt->tc->pid == 0) && ++ !halt_rip && (stage == 0) && STREQ(sym, "cpu_idle")) { ++ halt_rip = *up; ++ halt_rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); ++ } ++ } ++ ++ if (panic) ++ return; ++ ++ if (crash_kexec_rip) { ++ *rip = crash_kexec_rip; ++ *rsp = crash_kexec_rsp; ++ return; ++ } ++ ++skip_stage: ++ switch (stage) ++ { ++ /* ++ * Now check the processor's interrupt stack. ++ */ ++ case 0: ++ bt->stackbase = ms->stkinfo.ibase[bt->tc->processor]; ++ bt->stacktop = ms->stkinfo.ibase[bt->tc->processor] + ++ ms->stkinfo.isize; ++ console("x86_64_get_dumpfile_stack_frame: searching IRQ stack at %lx\n", ++ bt->stackbase); ++ bt->stackbuf = ms->irqstack; ++ alter_stackbuf(bt); ++ stage = 1; ++ goto next_stack; ++ ++ /* ++ * Check the exception stacks. ++ */ ++ case 1: ++ if (++estack == 7) ++ break; ++ bt->stackbase = ms->stkinfo.ebase[bt->tc->processor][estack]; ++ bt->stacktop = ms->stkinfo.ebase[bt->tc->processor][estack] + ++ ms->stkinfo.esize; ++ console("x86_64_get_dumpfile_stack_frame: searching %s estack at %lx\n", ++ x86_64_exception_stacks[estack], bt->stackbase); ++ if (!(bt->stackbase)) ++ goto skip_stage; ++ bt->stackbuf = ms->irqstack; ++ alter_stackbuf(bt); ++ goto next_stack; ++ ++ } ++ ++ /* ++ * We didn't find what we were looking for, so just use what was ++ * passed in from the ELF header. ++ */ ++ if (ur_rip && ur_rsp) { ++ *rip = ur_rip; ++ *rsp = ur_rsp; ++ return; ++ } ++ ++ if (halt_rip && halt_rsp) { ++ *rip = halt_rip; ++ *rsp = halt_rsp; ++ return; ++ } ++ ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "x86_64_get_dumpfile_stack_frame: cannot find anything useful (task: %lx)\n", ++ bt->task); ++ ++ bt->flags &= ~(ulonglong)BT_DUMPFILE_SEARCH; ++ ++ machdep->get_stack_frame(bt, rip, rsp); ++} ++ ++/* ++ * Get the saved RSP from the task's thread_struct. ++ */ ++static ulong ++x86_64_get_sp(struct bt_info *bt) ++{ ++ ulong offset, rsp; ++ ++ if (tt->flags & THREAD_INFO) { ++ readmem(bt->task + OFFSET(task_struct_thread) + ++ OFFSET(thread_struct_rsp), KVADDR, ++ &rsp, sizeof(void *), ++ "thread_struct rsp", FAULT_ON_ERROR); ++ return rsp; ++ } ++ ++ offset = OFFSET(task_struct_thread) + OFFSET(thread_struct_rsp); ++ ++ return GET_STACK_ULONG(offset); ++} ++ ++/* ++ * Get the saved PC from the task's thread_struct if it exists; ++ * otherwise just use the "thread_return" label value. ++ */ ++static ulong ++x86_64_get_pc(struct bt_info *bt) ++{ ++ ulong offset, rip; ++ ++ if (INVALID_MEMBER(thread_struct_rip)) ++ return symbol_value("thread_return"); ++ ++ if (tt->flags & THREAD_INFO) { ++ readmem(bt->task + OFFSET(task_struct_thread) + ++ OFFSET(thread_struct_rip), KVADDR, ++ &rip, sizeof(void *), ++ "thread_struct rip", FAULT_ON_ERROR); ++ if (rip) ++ return rip; ++ else ++ return symbol_value("thread_return"); ++ } ++ ++ offset = OFFSET(task_struct_thread) + OFFSET(thread_struct_rip); ++ ++ return GET_STACK_ULONG(offset); ++} ++ ++ ++/* ++ * Do the work for x86_64_get_sp() and x86_64_get_pc(). ++ */ ++static void ++get_x86_64_frame(struct bt_info *bt, ulong *getpc, ulong *getsp) ++{ ++ error(FATAL, "get_x86_64_frame: TBD\n"); ++} ++ ++/* ++ * Do the work for cmd_irq(). ++ */ ++static void ++x86_64_dump_irq(int irq) ++{ ++ if (symbol_exists("irq_desc")) { ++ machdep->dump_irq = generic_dump_irq; ++ return(generic_dump_irq(irq)); ++ } ++ ++ error(FATAL, "x86_64_dump_irq: irq_desc[] does not exist?\n"); ++} ++ ++/* ++ * Do the work for irq -d ++ */ ++void ++x86_64_display_idt_table(void) ++{ ++ int i; ++ char *idt_table_buf; ++ char buf[BUFSIZE]; ++ ulong *ip; ++ ++ if (INVALID_SIZE(gate_struct)) { ++ option_not_supported('d'); ++ return; ++ } ++ idt_table_buf = GETBUF(SIZE(gate_struct) * 256); ++ readmem(symbol_value("idt_table"), KVADDR, idt_table_buf, ++ SIZE(gate_struct) * 256, "idt_table", FAULT_ON_ERROR); ++ ip = (ulong *)idt_table_buf; ++ ++ for (i = 0; i < 256; i++, ip += 2) { ++ if (i < 10) ++ fprintf(fp, " "); ++ else if (i < 100) ++ fprintf(fp, " "); ++ fprintf(fp, "[%d] %s\n", ++ i, x86_64_extract_idt_function(ip, buf, NULL)); ++ } ++ ++ FREEBUF(idt_table_buf); ++} ++ ++/* ++ * Extract the function name out of the IDT entry. ++ */ ++static char * ++x86_64_extract_idt_function(ulong *ip, char *buf, ulong *retaddr) ++{ ++ ulong i1, i2, addr; ++ char locbuf[BUFSIZE]; ++ physaddr_t phys; ++ ++ if (buf) ++ BZERO(buf, BUFSIZE); ++ ++ i1 = *ip; ++ i2 = *(ip+1); ++ ++ i2 <<= 32; ++ addr = i2 & 0xffffffff00000000; ++ addr |= (i1 & 0xffff); ++ i1 >>= 32; ++ addr |= (i1 & 0xffff0000); ++ ++ if (retaddr) ++ *retaddr = addr; ++ ++ if (!buf) ++ return NULL; ++ ++ value_to_symstr(addr, locbuf, 0); ++ if (strlen(locbuf)) ++ sprintf(buf, locbuf); ++ else { ++ sprintf(buf, "%016lx", addr); ++ if (kvtop(NULL, addr, &phys, 0)) { ++ addr = machdep->kvbase + (ulong)phys; ++ if (value_to_symstr(addr, locbuf, 0)) { ++ strcat(buf, " <"); ++ strcat(buf, locbuf); ++ strcat(buf, ">"); ++ } ++ } ++ } ++ ++ return buf; ++} ++ ++/* ++ * Filter disassembly output if the output radix is not gdb's default 10 ++ */ ++static int ++x86_64_dis_filter(ulong vaddr, char *inbuf) ++{ ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char *colon, *p1; ++ int argc; ++ char *argv[MAXARGS]; ++ ulong value; ++ ++ if (!inbuf) ++ return TRUE; ++/* ++ * For some reason gdb can go off into the weeds translating text addresses, ++ * (on alpha -- not necessarily seen on x86_64) so this routine both fixes the ++ * references as well as imposing the current output radix on the translations. ++ */ ++ console("IN: %s", inbuf); + +- for (c = 0; c < kt->cpus; c++) { +- if (ms->stkinfo.ibase[c] == 0) +- break; +- bt->hp->esp = ms->stkinfo.ibase[c]; +- fprintf(fp, "CPU %d IRQ STACK:\n", c); +- if ((cnt = x86_64_eframe_search(bt))) +- fprintf(fp, "\n"); +- else +- fprintf(fp, "(none found)\n\n"); ++ colon = strstr(inbuf, ":"); ++ ++ if (colon) { ++ sprintf(buf1, "0x%lx <%s>", vaddr, ++ value_to_symstr(vaddr, buf2, pc->output_radix)); ++ sprintf(buf2, "%s%s", buf1, colon); ++ strcpy(inbuf, buf2); ++ } ++ ++ strcpy(buf1, inbuf); ++ argc = parse_line(buf1, argv); ++ ++ if ((FIRSTCHAR(argv[argc-1]) == '<') && ++ (LASTCHAR(argv[argc-1]) == '>')) { ++ p1 = rindex(inbuf, '<'); ++ while ((p1 > inbuf) && !STRNEQ(p1, " 0x")) ++ p1--; ++ ++ if (!STRNEQ(p1, " 0x")) ++ return FALSE; ++ p1++; ++ ++ if (!extract_hex(p1, &value, NULLCHAR, TRUE)) ++ return FALSE; ++ ++ sprintf(buf1, "0x%lx <%s>\n", value, ++ value_to_symstr(value, buf2, pc->output_radix)); ++ ++ sprintf(p1, buf1); ++ ++ } else if (STREQ(argv[argc-2], "callq") && ++ hexadecimal(argv[argc-1], 0)) { ++ /* ++ * Update module code of the form: ++ * ++ * callq 0xffffffffa0017aa0 ++ * ++ * to show a bracketed direct call target. ++ */ ++ p1 = &LASTCHAR(inbuf); ++ ++ if (extract_hex(argv[argc-1], &value, NULLCHAR, TRUE)) { ++ sprintf(buf1, " <%s>\n", ++ value_to_symstr(value, buf2, ++ pc->output_radix)); ++ if (IS_MODULE_VADDR(value) && ++ !strstr(buf2, "+")) ++ sprintf(p1, buf1); + } ++ } + +- for (c = 0; c < kt->cpus; c++) { +- for (i = 0; i < 7; i++) { +- if (ms->stkinfo.ebase[c][i] == 0) +- break; +- bt->hp->esp = ms->stkinfo.ebase[c][i]; +- fprintf(fp, "CPU %d %s EXCEPTION STACK:\n", +- c, x86_64_exception_stacks[i]); +- if ((cnt = x86_64_eframe_search(bt))) +- fprintf(fp, "\n"); +- else +- fprintf(fp, "(none found)\n\n"); +- } +- } ++ console(" %s", inbuf); + +- return 0; ++ return TRUE; ++} ++ ++ ++/* ++ * Override smp_num_cpus if possible and necessary. ++ */ ++int ++x86_64_get_smp_cpus(void) ++{ ++ int i, cpus, nr_pda, cpunumber, _cpu_pda, _boot_cpu_pda; ++ char *cpu_pda_buf; ++ ulong level4_pgt, cpu_pda_addr; ++ ++ if (!VALID_STRUCT(x8664_pda)) ++ return 1; ++ ++ cpu_pda_buf = GETBUF(SIZE(x8664_pda)); ++ ++ if (LKCD_KERNTYPES()) { ++ if (symbol_exists("_cpu_pda")) ++ _cpu_pda = TRUE; ++ else ++ _cpu_pda = FALSE; ++ nr_pda = get_cpus_possible(); ++ } else { ++ if (symbol_exists("_cpu_pda")) { ++ if (!(nr_pda = get_array_length("_cpu_pda", NULL, 0))) ++ nr_pda = NR_CPUS; ++ _cpu_pda = TRUE; ++ } else { ++ if (!(nr_pda = get_array_length("cpu_pda", NULL, 0))) ++ nr_pda = NR_CPUS; ++ _cpu_pda = FALSE; ++ } ++ } ++ if (_cpu_pda) { ++ if (symbol_exists("_boot_cpu_pda")) ++ _boot_cpu_pda = TRUE; ++ else ++ _boot_cpu_pda = FALSE; ++ } ++ for (i = cpus = 0; i < nr_pda; i++) { ++ if (_cpu_pda) { ++ if (_boot_cpu_pda) { ++ if (!_CPU_PDA_READ2(i, cpu_pda_buf)) ++ break; ++ } else { ++ if (!_CPU_PDA_READ(i, cpu_pda_buf)) ++ break; ++ } ++ } else { ++ if (!CPU_PDA_READ(i, cpu_pda_buf)) ++ break; ++ } ++ if (VALID_MEMBER(x8664_pda_level4_pgt)) { ++ level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt)); ++ if (!VALID_LEVEL4_PGT_ADDR(level4_pgt)) ++ break; ++ } ++ cpunumber = INT(cpu_pda_buf + OFFSET(x8664_pda_cpunumber)); ++ if (cpunumber != cpus) ++ break; ++ cpus++; ++ } ++ ++ FREEBUF(cpu_pda_buf); ++ ++ return cpus; ++} ++ ++/* ++ * Machine dependent command. ++ */ ++void ++x86_64_cmd_mach(void) ++{ ++ int c; ++ ++ while ((c = getopt(argcnt, args, "cm")) != EOF) { ++ switch(c) ++ { ++ case 'c': ++ x86_64_display_cpu_data(); ++ return; ++ ++ case 'm': ++ x86_64_display_memmap(); ++ return; ++ ++ default: ++ argerrs++; ++ break; ++ } + } + +- if (bt->hp && bt->hp->esp) { +- ms = machdep->machspec; +- bt->stkptr = bt->hp->esp; +- if ((estack = x86_64_in_exception_stack(bt))) { +- stacksize = ms->stkinfo.esize; +- bt->stackbase = estack; +- bt->stacktop = estack + ms->stkinfo.esize; +- bt->stackbuf = ms->irqstack; +- alter_stackbuf(bt); +- } else if ((irqstack = x86_64_in_irqstack(bt))) { +- stacksize = ms->stkinfo.isize; +- bt->stackbase = irqstack; +- bt->stacktop = irqstack + ms->stkinfo.isize; +- bt->stackbuf = ms->irqstack; +- alter_stackbuf(bt); +- } else if (!INSTACK(bt->stkptr, bt)) +- error(FATAL, +- "unrecognized stack address for this task: %lx\n", +- bt->hp->esp); +- } ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ x86_64_display_machine_stats(); ++} ++ ++/* ++ * "mach" command output. ++ */ ++static void ++x86_64_display_machine_stats(void) ++{ ++ struct new_utsname *uts; ++ char buf[BUFSIZE]; ++ ulong mhz; ++ ++ uts = &kt->utsname; ++ ++ fprintf(fp, " MACHINE TYPE: %s\n", uts->machine); ++ fprintf(fp, " MEMORY SIZE: %s\n", get_memory_size(buf)); ++ fprintf(fp, " CPUS: %d\n", kt->cpus); ++ fprintf(fp, " PROCESSOR SPEED: "); ++ if ((mhz = machdep->processor_speed())) ++ fprintf(fp, "%ld Mhz\n", mhz); ++ else ++ fprintf(fp, "(unknown)\n"); ++ fprintf(fp, " HZ: %d\n", machdep->hz); ++ fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); ++// fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); ++ fprintf(fp, "KERNEL VIRTUAL BASE: %lx\n", machdep->kvbase); ++ fprintf(fp, "KERNEL VMALLOC BASE: %lx\n", vt->vmalloc_start); ++ if (machdep->flags & VMEMMAP) ++ fprintf(fp, "KERNEL VMEMMAP BASE: %lx\n", machdep->machspec->vmemmap_vaddr); ++ fprintf(fp, " KERNEL START MAP: %lx\n", __START_KERNEL_map); ++ fprintf(fp, "KERNEL MODULES BASE: %lx\n", MODULES_VADDR); ++ fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); ++} ++ ++/* ++ * "mach -c" ++ */ ++static void ++x86_64_display_cpu_data(void) ++{ ++ int cpu, cpus, boot_cpu, _cpu_pda; ++ ulong cpu_data; ++ ulong cpu_pda, cpu_pda_addr; ++ ++ if (symbol_exists("cpu_data")) { ++ cpu_data = symbol_value("cpu_data"); ++ cpus = kt->cpus; ++ boot_cpu = FALSE; ++ } else if (symbol_exists("boot_cpu_data")) { ++ cpu_data = symbol_value("boot_cpu_data"); ++ boot_cpu = TRUE; ++ cpus = 1; ++ } ++ if (symbol_exists("_cpu_pda")) { ++ cpu_pda = symbol_value("_cpu_pda"); ++ _cpu_pda = TRUE; ++ } else if (symbol_exists("cpu_pda")) { ++ cpu_pda = symbol_value("cpu_pda"); ++ _cpu_pda = FALSE; ++ } ++ ++ for (cpu = 0; cpu < cpus; cpu++) { ++ if (boot_cpu) ++ fprintf(fp, "BOOT CPU:\n"); ++ else ++ fprintf(fp, "%sCPU %d:\n", cpu ? "\n" : "", cpu); ++ ++ dump_struct("cpuinfo_x86", cpu_data, 0); ++ fprintf(fp, "\n"); ++ ++ if (_cpu_pda) { ++ readmem(cpu_pda, KVADDR, &cpu_pda_addr, ++ sizeof(unsigned long), "_cpu_pda addr", FAULT_ON_ERROR); ++ dump_struct("x8664_pda", cpu_pda_addr, 0); ++ cpu_pda += sizeof(void *); ++ } else { ++ dump_struct("x8664_pda", cpu_pda, 0); ++ cpu_pda += SIZE(x8664_pda); ++ } ++ cpu_data += SIZE(cpuinfo_x86); ++ } ++} ++ ++/* ++ * "mach -m" ++ */ ++static char *e820type[] = { ++ "(invalid type)", ++ "E820_RAM", ++ "E820_RESERVED", ++ "E820_ACPI", ++ "E820_NVS", ++}; ++ ++static void ++x86_64_display_memmap(void) ++{ ++ ulong e820; ++ int nr_map, i; ++ char *buf, *e820entry_ptr; ++ ulonglong addr, size; ++ uint type; + +- stacksize = bt->stacktop - bt->stackbase - SIZE(pt_regs); ++ e820 = symbol_value("e820"); ++ if (CRASHDEBUG(1)) ++ dump_struct("e820map", e820, RADIX(16)); ++ buf = (char *)GETBUF(SIZE(e820map)); + +- if (bt->stkptr) +- i = (bt->stkptr - bt->stackbase)/sizeof(ulong); +- else +- i = 0; ++ readmem(e820, KVADDR, &buf[0], SIZE(e820map), ++ "e820map", FAULT_ON_ERROR); + +- for (cnt = 0; i <= stacksize/sizeof(ulong); i++) { +- up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++ nr_map = INT(buf + OFFSET(e820map_nr_map)); + +- if (x86_64_exception_frame(EFRAME_SEARCH|EFRAME_PRINT| +- EFRAME_VERIFY, 0, (char *)up, bt, fp)) +- cnt++; +- } ++ fprintf(fp, " PHYSICAL ADDRESS RANGE TYPE\n"); + +- return cnt; ++ for (i = 0; i < nr_map; i++) { ++ e820entry_ptr = buf + sizeof(int) + (SIZE(e820entry) * i); ++ addr = ULONGLONG(e820entry_ptr + OFFSET(e820entry_addr)); ++ size = ULONGLONG(e820entry_ptr + OFFSET(e820entry_size)); ++ type = UINT(e820entry_ptr + OFFSET(e820entry_type)); ++ fprintf(fp, "%016llx - %016llx %s\n", addr, addr+size, ++ e820type[type]); ++ } + } + +-static void +-x86_64_display_full_frame(struct bt_info *bt, ulong rsp, FILE *ofp) +-{ +- int i, u_idx; +- ulong *up; +- ulong words, addr; + +- words = (rsp - bt->frameptr) / sizeof(ulong) + 1; ++static const char *hook_files[] = { ++ "arch/x86_64/kernel/entry.S", ++ "arch/x86_64/kernel/head.S", ++ "arch/x86_64/kernel/semaphore.c" ++}; + +- addr = bt->frameptr; +- u_idx = (bt->frameptr - bt->stackbase)/sizeof(ulong); +- for (i = 0; i < words; i++, u_idx++) { +- if (!(i & 1)) +- fprintf(ofp, "%s %lx: ", i ? "\n" : "", addr); +- +- up = (ulong *)(&bt->stackbuf[u_idx*sizeof(ulong)]); +- fprintf(ofp, "%016lx ", *up); +- addr += sizeof(ulong); +- } +- fprintf(ofp, "\n"); +-} ++#define ENTRY_S ((char **)&hook_files[0]) ++#define HEAD_S ((char **)&hook_files[1]) ++#define SEMAPHORE_C ((char **)&hook_files[2]) ++ ++static struct line_number_hook x86_64_line_number_hooks[] = { ++ {"ret_from_fork", ENTRY_S}, ++ {"system_call", ENTRY_S}, ++ {"int_ret_from_sys_call", ENTRY_S}, ++ {"ptregscall_common", ENTRY_S}, ++ {"stub_execve", ENTRY_S}, ++ {"stub_rt_sigreturn", ENTRY_S}, ++ {"common_interrupt", ENTRY_S}, ++ {"ret_from_intr", ENTRY_S}, ++ {"load_gs_index", ENTRY_S}, ++ {"arch_kernel_thread", ENTRY_S}, ++ {"execve", ENTRY_S}, ++ {"page_fault", ENTRY_S}, ++ {"coprocessor_error", ENTRY_S}, ++ {"simd_coprocessor_error", ENTRY_S}, ++ {"device_not_available", ENTRY_S}, ++ {"debug", ENTRY_S}, ++ {"nmi", ENTRY_S}, ++ {"int3", ENTRY_S}, ++ {"overflow", ENTRY_S}, ++ {"bounds", ENTRY_S}, ++ {"invalid_op", ENTRY_S}, ++ {"coprocessor_segment_overrun", ENTRY_S}, ++ {"reserved", ENTRY_S}, ++ {"double_fault", ENTRY_S}, ++ {"invalid_TSS", ENTRY_S}, ++ {"segment_not_present", ENTRY_S}, ++ {"stack_segment", ENTRY_S}, ++ {"general_protection", ENTRY_S}, ++ {"alignment_check", ENTRY_S}, ++ {"divide_error", ENTRY_S}, ++ {"spurious_interrupt_bug", ENTRY_S}, ++ {"machine_check", ENTRY_S}, ++ {"call_debug", ENTRY_S}, ++ ++ {NULL, NULL} /* list must be NULL-terminated */ ++}; + +-/* +- * Check a frame for a requested reference. +- */ + static void +-x86_64_do_bt_reference_check(struct bt_info *bt, ulong text, char *name) ++x86_64_dump_line_number(ulong callpc) + { +- struct syment *sp; +- ulong offset; +- +- if (!name) +- sp = value_search(text, &offset); +- else if (!text) +- sp = symbol_search(name); ++ error(FATAL, "x86_64_dump_line_number: TBD\n"); ++} + +- switch (bt->ref->cmdflags & (BT_REF_SYMBOL|BT_REF_HEXVAL)) +- { +- case BT_REF_SYMBOL: +- if (name) { +- if (STREQ(name, bt->ref->str)) +- bt->ref->cmdflags |= BT_REF_FOUND; +- } else { +- if (sp && !offset && STREQ(sp->name, bt->ref->str)) +- bt->ref->cmdflags |= BT_REF_FOUND; +- } +- break; ++void ++x86_64_compiler_warning_stub(void) ++{ ++ struct line_number_hook *lhp; ++ char **p; + +- case BT_REF_HEXVAL: +- if (text) { +- if (bt->ref->hexval == text) +- bt->ref->cmdflags |= BT_REF_FOUND; +- } else if (sp && (bt->ref->hexval == sp->value)) +- bt->ref->cmdflags |= BT_REF_FOUND; +- else if (!name && !text && (bt->ref->hexval == 0)) +- bt->ref->cmdflags |= BT_REF_FOUND; +- break; +- } ++ lhp = &x86_64_line_number_hooks[0]; lhp++; ++ p = ENTRY_S; ++ x86_64_back_trace(NULL, NULL); ++ get_x86_64_frame(NULL, NULL, NULL); ++ x86_64_dump_line_number(0); + } + + /* +- * print one entry of a stack trace ++ * Force the VM address-range selection via: ++ * ++ * --machdep vm=orig ++ * --machdep vm=2.6.11 ++ * ++ * Force the phys_base address via: ++ * ++ * --machdep phys_base=
++ * ++ * Force the IRQ stack back-link via: ++ * ++ * --machdep irq_eframe_link= + */ +-#define BACKTRACE_COMPLETE (1) +-#define BACKTRACE_ENTRY_IGNORED (2) +-#define BACKTRACE_ENTRY_DISPLAYED (3) +-#define BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED (4) + +-static int +-x86_64_print_stack_entry(struct bt_info *bt, FILE *ofp, int level, +- int stkindex, ulong text) ++void ++parse_cmdline_arg(void) + { +- ulong rsp, offset; +- struct syment *sp; +- char *name; +- int result; +- long eframe_check; ++ int i, c, errflag; ++ char *p; + char buf[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ int megabytes; ++ int lines = 0; ++ int vm_flag; ++ ulong value; ++ ++ if (!strstr(machdep->cmdline_arg, "=")) { ++ error(WARNING, "ignoring --machdep option: %s\n\n", ++ machdep->cmdline_arg); ++ return; ++ } + +- eframe_check = -1; +- offset = 0; +- sp = value_search(text, &offset); +- if (!sp) +- return BACKTRACE_ENTRY_IGNORED; +- +- name = sp->name; ++ strcpy(buf, machdep->cmdline_arg); + +- if (bt->flags & BT_TEXT_SYMBOLS) { +- if (bt->flags & BT_EXCEPTION_FRAME) +- rsp = bt->stkptr; +- else +- rsp = bt->stackbase + (stkindex * sizeof(long)); +- fprintf(ofp, " [%s] %s at %lx\n", +- mkstring(buf, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(rsp)), +- name, text); +- if (BT_REFERENCE_CHECK(bt)) +- x86_64_do_bt_reference_check(bt, text, name); +- return BACKTRACE_ENTRY_DISPLAYED; ++ for (p = buf; *p; p++) { ++ if (*p == ',') ++ *p = ' '; + } + +- if (!offset && !(bt->flags & BT_EXCEPTION_FRAME) && +- !(bt->flags & BT_START)) { +- if (STREQ(name, "child_rip")) { +- if (symbol_exists("kernel_thread")) +- name = "kernel_thread"; +- else if (symbol_exists("arch_kernel_thread")) +- name = "arch_kernel_thread"; +- } +- else if (!(bt->flags & BT_SCHEDULE)) { +- if (STREQ(name, "error_exit")) +- eframe_check = 8; +- else { +- if (CRASHDEBUG(2)) +- fprintf(ofp, +- "< ignoring text symbol with no offset: %s() >\n", +- sp->name); +- return BACKTRACE_ENTRY_IGNORED; ++ c = parse_line(buf, arglist); ++ ++ for (i = vm_flag = 0; i < c; i++) { ++ errflag = 0; ++ ++ if (STRNEQ(arglist[i], "vm=")) { ++ vm_flag++; ++ p = arglist[i] + strlen("vm="); ++ if (strlen(p)) { ++ if (STREQ(p, "orig")) { ++ machdep->flags |= VM_ORIG; ++ continue; ++ } else if (STREQ(p, "2.6.11")) { ++ machdep->flags |= VM_2_6_11; ++ continue; ++ } else if (STREQ(p, "xen")) { ++ machdep->flags |= VM_XEN; ++ continue; ++ } else if (STREQ(p, "xen-rhel4")) { ++ machdep->flags |= VM_XEN_RHEL4; ++ continue; ++ } ++ } ++ } else if (STRNEQ(arglist[i], "phys_base=")) { ++ megabytes = FALSE; ++ if ((LASTCHAR(arglist[i]) == 'm') || ++ (LASTCHAR(arglist[i]) == 'M')) { ++ LASTCHAR(arglist[i]) = NULLCHAR; ++ megabytes = TRUE; ++ } ++ p = arglist[i] + strlen("phys_base="); ++ if (strlen(p)) { ++ if (megabytes) { ++ value = dtol(p, RETURN_ON_ERROR|QUIET, ++ &errflag); ++ } else ++ value = htol(p, RETURN_ON_ERROR|QUIET, ++ &errflag); ++ if (!errflag) { ++ if (megabytes) ++ value = MEGABYTES(value); ++ machdep->machspec->phys_base = value; ++ error(NOTE, ++ "setting phys_base to: 0x%lx\n\n", ++ machdep->machspec->phys_base); ++ machdep->flags |= PHYS_BASE; ++ continue; ++ } ++ } ++ } else if (STRNEQ(arglist[i], "irq_eframe_link=")) { ++ p = arglist[i] + strlen("irq_eframe_link="); ++ if (strlen(p)) { ++ value = stol(p, RETURN_ON_ERROR|QUIET, &errflag); ++ if (!errflag) { ++ machdep->machspec->irq_eframe_link = value; ++ continue; ++ } + } + } ++ ++ error(WARNING, "ignoring --machdep option: %s\n", arglist[i]); ++ lines++; ++ } ++ ++ if (vm_flag) { ++ switch (machdep->flags & VM_FLAGS) ++ { ++ case 0: ++ break; ++ ++ case VM_ORIG: ++ error(NOTE, "using original x86_64 VM address ranges\n"); ++ lines++; ++ break; ++ ++ case VM_2_6_11: ++ error(NOTE, "using 2.6.11 x86_64 VM address ranges\n"); ++ lines++; ++ break; ++ ++ case VM_XEN: ++ error(NOTE, "using xen x86_64 VM address ranges\n"); ++ lines++; ++ break; ++ ++ case VM_XEN_RHEL4: ++ error(NOTE, "using RHEL4 xen x86_64 VM address ranges\n"); ++ lines++; ++ break; ++ ++ default: ++ error(WARNING, "cannot set multiple vm values\n"); ++ lines++; ++ machdep->flags &= ~VM_FLAGS; ++ break; ++ } + } + +- if (bt->flags & BT_SCHEDULE) +- name = "schedule"; ++ if (lines) ++ fprintf(fp, "\n"); ++} + +- if (STREQ(name, "child_rip")) { +- if (symbol_exists("kernel_thread")) +- name = "kernel_thread"; +- else if (symbol_exists("arch_kernel_thread")) +- name = "arch_kernel_thread"; +- result = BACKTRACE_COMPLETE; +- } else if (STREQ(name, "cpu_idle")) +- result = BACKTRACE_COMPLETE; +- else +- result = BACKTRACE_ENTRY_DISPLAYED; ++void ++x86_64_clear_machdep_cache(void) ++{ ++ machdep->machspec->last_upml_read = 0; ++} + +- if (bt->flags & BT_EXCEPTION_FRAME) +- rsp = bt->stkptr; +- else if (bt->flags & BT_START) +- rsp = bt->stkptr; ++static void ++x86_64_irq_eframe_link_init(void) ++{ ++ int c; ++ struct syment *sp, *spn; ++ char buf[BUFSIZE]; ++ char link_register[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ ulong max_instructions; ++ ++ if (machdep->machspec->irq_eframe_link == UNINITIALIZED) ++ machdep->machspec->irq_eframe_link = 0; + else +- rsp = bt->stackbase + (stkindex * sizeof(long)); ++ return; ++ ++ if (THIS_KERNEL_VERSION < LINUX(2,6,9)) ++ return; + +- if ((bt->flags & BT_FULL)) { +- if (bt->frameptr) +- x86_64_display_full_frame(bt, rsp, ofp); +- bt->frameptr = rsp + sizeof(ulong); ++ if (!(sp = symbol_search("common_interrupt")) || ++ !(spn = next_symbol(NULL, sp))) { ++ return; + } + +- fprintf(ofp, "%s#%d [%8lx] %s at %lx\n", level < 10 ? " " : "", level, +- rsp, name, text); ++ max_instructions = spn->value - sp->value; + +- if (bt->flags & BT_LINE_NUMBERS) { +- get_line_number(text, buf, FALSE); +- if (strlen(buf)) +- fprintf(ofp, " %s\n", buf); +- } ++ open_tmpfile(); + +- if (eframe_check >= 0) { +- if (x86_64_exception_frame(EFRAME_PRINT|EFRAME_VERIFY, +- bt->stackbase + (stkindex*sizeof(long)) + eframe_check, +- NULL, bt, ofp)) +- result = BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED; +- } ++ sprintf(buf, "x/%ldi 0x%lx", ++ max_instructions, sp->value); + +- if (BT_REFERENCE_CHECK(bt)) +- x86_64_do_bt_reference_check(bt, text, name); ++ if (!gdb_pass_through(buf, pc->tmpfile, GNU_RETURN_ON_ERROR)) ++ return; + +- bt->call_target = name; ++ link_register[0] = NULLCHAR; + +- if (is_direct_call_target(bt)) { +- if (CRASHDEBUG(2)) +- fprintf(ofp, "< enable BT_CHECK_CALLER for %s >\n", +- bt->call_target); +- bt->flags |= BT_CHECK_CALLER; +- } else { +- if (CRASHDEBUG(2) && (bt->flags & BT_CHECK_CALLER)) +- fprintf(ofp, "< disable BT_CHECK_CALLER for %s >\n", +- bt->call_target); +- if (bt->flags & BT_CHECK_CALLER) { +- if (CRASHDEBUG(2)) +- fprintf(ofp, "< set BT_NO_CHECK_CALLER >\n"); +- bt->flags |= BT_NO_CHECK_CALLER; +- } +- bt->flags &= ~(ulonglong)BT_CHECK_CALLER; ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (!strstr(buf, sp->name)) ++ break; ++ if ((c = parse_line(buf, arglist)) < 4) ++ continue; ++ if (strstr(arglist[2], "push")) ++ strcpy(link_register, arglist[3]); + } ++ close_tmpfile(); + +- return result; +-} ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "IRQ stack link register: %s\n", ++ strlen(link_register) ? ++ link_register : "undetermined"); + +-/* +- * Unroll a kernel stack. +- */ +-static void +-x86_64_back_trace_cmd(struct bt_info *bt) +-{ +- error(FATAL, "x86_64_back_trace_cmd: TBD\n"); ++ if (STREQ(link_register, "%rbp")) ++ machdep->machspec->irq_eframe_link = 40; ++ + } + +- ++#include "netdump.h" + + /* +- * Determine whether the initial stack pointer is located in one of the +- * exception stacks. ++ * From the xen vmcore, create an index of mfns for each page that makes ++ * up the dom0 kernel's complete phys_to_machine_mapping[max_pfn] array. + */ +-static ulong +-x86_64_in_exception_stack(struct bt_info *bt) +-{ +- int c, i; +- ulong rsp; +- ulong estack; +- struct machine_specific *ms; +- +- rsp = bt->stkptr; +- ms = machdep->machspec; +- estack = 0; + +- for (c = 0; !estack && (c < kt->cpus); c++) { +- for (i = 0; i < 7; i++) { +- if (ms->stkinfo.ebase[c][i] == 0) +- break; +- if ((rsp >= ms->stkinfo.ebase[c][i]) && +- (rsp < (ms->stkinfo.ebase[c][i] + +- ms->stkinfo.esize))) { +- estack = ms->stkinfo.ebase[c][i]; +- if (c != bt->tc->processor) +- error(INFO, +- "task cpu: %d exception stack cpu: %d\n", +- bt->tc->processor, c); +- break; +- } +- } +- } +- +- return estack; +-} ++#define MAX_X86_64_FRAMES (512) ++#define MFNS_PER_FRAME (PAGESIZE()/sizeof(ulong)) + +-/* +- * Determine whether the current stack pointer is in a cpu's irqstack. +- */ +-static ulong +-x86_64_in_irqstack(struct bt_info *bt) ++static int ++x86_64_xen_kdump_p2m_create(struct xen_kdump_data *xkd) + { +- int c; +- ulong rsp; +- ulong irqstack; +- struct machine_specific *ms; ++ int i, j; ++ ulong kvaddr; ++ ulong *up; ++ ulong frames; ++ ulong frame_mfn[MAX_X86_64_FRAMES] = { 0 }; ++ int mfns[MAX_X86_64_FRAMES] = { 0 }; + +- rsp = bt->stkptr; +- ms = machdep->machspec; +- irqstack = 0; ++ /* ++ * Temporarily read physical (machine) addresses from vmcore by ++ * going directly to read_netdump() instead of via read_kdump(). ++ */ ++ pc->readmem = read_netdump; + +- for (c = 0; !irqstack && (c < kt->cpus); c++) { +- if (ms->stkinfo.ibase[c] == 0) +- break; +- if ((rsp >= ms->stkinfo.ibase[c]) && +- (rsp < (ms->stkinfo.ibase[c] + ms->stkinfo.isize))) { +- irqstack = ms->stkinfo.ibase[c]; +- if (c != bt->tc->processor) +- error(INFO, +- "task cpu: %d IRQ stack cpu: %d\n", +- bt->tc->processor, c); +- break; +- } +- } ++ if (xkd->flags & KDUMP_CR3) ++ goto use_cr3; + +- return irqstack; +-} ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "x86_64_xen_kdump_p2m_create: p2m_mfn: %lx\n", ++ xkd->p2m_mfn); + +-#define STACK_TRANSITION_ERRMSG_E_I_P \ +-"cannot transition from exception stack to IRQ stack to current process stack:\n exception stack pointer: %lx\n IRQ stack pointer: %lx\n process stack pointer: %lx\n current stack base: %lx\n" +-#define STACK_TRANSITION_ERRMSG_E_P \ +-"cannot transition from exception stack to current process stack:\n exception stack pointer: %lx\n process stack pointer: %lx\n current_stack_base: %lx\n" +-#define STACK_TRANSITION_ERRMSG_I_P \ +-"cannot transition from IRQ stack to current process stack:\n IRQ stack pointer: %lx\n process stack pointer: %lx\n current stack base: %lx" ++ if (!readmem(PTOB(xkd->p2m_mfn), PHYSADDR, xkd->page, PAGESIZE(), ++ "xen kdump p2m mfn page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump p2m mfn page\n"); + +-/* +- * Low-budget back tracer -- dump text return addresses, following call chain +- * when possible, along with any verifiable exception frames. +- */ +-static void +-x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in) +-{ +- int i, level, done; +- ulong rsp, offset, stacktop; +- ulong *up; +- long cs; +- struct syment *sp, *spt; +- FILE *ofp; +- ulong estack, irqstack; +- ulong irq_eframe; +- struct bt_info bt_local, *bt; +- struct machine_specific *ms; +- ulong last_process_stack_eframe; +- ulong user_mode_eframe; ++ if (CRASHDEBUG(2)) ++ x86_64_debug_dump_page(fp, xkd->page, "pfn_to_mfn_frame_list"); + +- bt = &bt_local; +- BCOPY(bt_in, bt, sizeof(struct bt_info)); ++ for (i = 0, up = (ulong *)xkd->page; i < MAX_X86_64_FRAMES; i++, up++) ++ frame_mfn[i] = *up; + +- level = 0; +- done = FALSE; +- irq_eframe = 0; +- last_process_stack_eframe = 0; +- bt->call_target = NULL; +- rsp = bt->stkptr; +- if (!rsp) { +- error(INFO, "cannot determine starting stack pointer\n"); +- return; +- } +- ms = machdep->machspec; +- if (BT_REFERENCE_CHECK(bt)) +- ofp = pc->nullfp; +- else +- ofp = fp; ++ for (i = 0; i < MAX_X86_64_FRAMES; i++) { ++ if (!frame_mfn[i]) ++ break; + +- if (bt->flags & BT_TEXT_SYMBOLS) { +- fprintf(ofp, "%sSTART: %s%s at %lx\n", +- space(VADDR_PRLEN > 8 ? 14 : 6), +- closest_symbol(bt->instptr), +- STREQ(closest_symbol(bt->instptr), "thread_return") ? +- " (schedule)" : "", +- bt->instptr); +- } else if (bt->flags & BT_START) { +- x86_64_print_stack_entry(bt, ofp, level, +- 0, bt->instptr); +- bt->flags &= ~BT_START; +- level++; ++ if (!readmem(PTOB(frame_mfn[i]), PHYSADDR, xkd->page, ++ PAGESIZE(), "xen kdump p2m mfn list page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump p2m mfn list page\n"); ++ ++ for (j = 0, up = (ulong *)xkd->page; j < MFNS_PER_FRAME; j++, up++) ++ if (*up) ++ mfns[i]++; ++ ++ xkd->p2m_frames += mfns[i]; ++ ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(fp, xkd->page, "pfn_to_mfn_frame_list page"); + } + ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "p2m_frames: %d\n", xkd->p2m_frames); + +- if ((estack = x86_64_in_exception_stack(bt))) { +-in_exception_stack: +- bt->flags |= BT_EXCEPTION_STACK; +- /* +- * The stack buffer will have been loaded with the process +- * stack, so switch to the indicated exception stack. +- */ +- bt->stackbase = estack; +- bt->stacktop = estack + ms->stkinfo.esize; +- bt->stackbuf = ms->irqstack; ++ if ((xkd->p2m_mfn_frame_list = (ulong *) ++ malloc(xkd->p2m_frames * sizeof(ulong))) == NULL) ++ error(FATAL, "cannot malloc p2m_frame_index_list"); + +- if (!readmem(bt->stackbase, KVADDR, bt->stackbuf, +- bt->stacktop - bt->stackbase, +- bt->hp && (bt->hp->esp == bt->stkptr) ? +- "irqstack contents via hook" : "irqstack contents", ++ for (i = 0, frames = xkd->p2m_frames; frames; i++) { ++ if (!readmem(PTOB(frame_mfn[i]), PHYSADDR, ++ &xkd->p2m_mfn_frame_list[i * MFNS_PER_FRAME], ++ mfns[i] * sizeof(ulong), "xen kdump p2m mfn list page", + RETURN_ON_ERROR)) +- error(FATAL, "read of exception stack at %lx failed\n", +- bt->stackbase); ++ error(FATAL, "cannot read xen kdump p2m mfn list page\n"); + +- /* +- * If irq_eframe is set, we've jumped back here from the +- * IRQ stack dump below. Do basically the same thing as if +- * had come from the processor stack, but presume that we +- * must have been in kernel mode, i.e., took an exception +- * while operating on an IRQ stack. (untested) +- */ +- if (irq_eframe) { +- bt->flags |= BT_EXCEPTION_FRAME; +- i = (irq_eframe - bt->stackbase)/sizeof(ulong); +- x86_64_print_stack_entry(bt, ofp, level, i, +- bt->instptr); +- bt->flags &= ~(ulonglong)BT_EXCEPTION_FRAME; +- cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, +- bt->stackbuf + (irq_eframe - bt->stackbase), +- bt, ofp); +- rsp += SIZE(pt_regs); /* guaranteed kernel mode */ +- level++; +- irq_eframe = 0; +- } ++ frames -= mfns[i]; ++ } + +- stacktop = bt->stacktop - SIZE(pt_regs); ++ if (CRASHDEBUG(2)) { ++ for (i = 0; i < xkd->p2m_frames; i++) ++ fprintf(fp, "%lx ", xkd->p2m_mfn_frame_list[i]); ++ fprintf(fp, "\n"); ++ } + +- for (i = (rsp - bt->stackbase)/sizeof(ulong); +- !done && (rsp < stacktop); i++, rsp += sizeof(ulong)) { ++ pc->readmem = read_kdump; ++ return TRUE; + +- up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++use_cr3: + +- if (!is_kernel_text(*up)) +- continue; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "x86_64_xen_kdump_p2m_create: cr3: %lx\n", xkd->cr3); + +- switch (x86_64_print_stack_entry(bt, ofp, level, i,*up)) +- { +- case BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED: +- rsp += SIZE(pt_regs); +- i += SIZE(pt_regs)/sizeof(ulong); +- case BACKTRACE_ENTRY_DISPLAYED: +- level++; +- break; +- case BACKTRACE_ENTRY_IGNORED: +- break; +- case BACKTRACE_COMPLETE: +- done = TRUE; +- break; +- } +- } ++ if (!readmem(PTOB(xkd->cr3), PHYSADDR, machdep->machspec->pml4, ++ PAGESIZE(), "xen kdump cr3 page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump cr3 page\n"); + +- cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, +- bt->stackbuf + (bt->stacktop - bt->stackbase) - +- SIZE(pt_regs), bt, ofp); ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(fp, machdep->machspec->pml4, ++ "contents of PML4 page:"); + +- if (!BT_REFERENCE_CHECK(bt)) +- fprintf(fp, "--- ---\n"); ++ kvaddr = symbol_value("end_pfn"); ++ if (!x86_64_xen_kdump_load_page(kvaddr, xkd->page)) ++ return FALSE; ++ up = (ulong *)(xkd->page + PAGEOFFSET(kvaddr)); + +- /* +- * stack = (unsigned long *) estack_end[-2]; +- */ +- up = (ulong *)(&bt->stackbuf[bt->stacktop - bt->stackbase]); +- up -= 2; +- rsp = bt->stkptr = *up; +- up -= 3; +- bt->instptr = *up; +- if (cs & 3) +- done = TRUE; /* user-mode exception */ +- else +- done = FALSE; /* kernel-mode exception */ +- bt->frameptr = 0; ++ xkd->p2m_frames = (*up/(PAGESIZE()/sizeof(ulong))) + ++ ((*up%(PAGESIZE()/sizeof(ulong))) ? 1 : 0); + +- /* +- * Print the return values from the estack end. +- */ +- if (!done) { +- bt->flags |= BT_START; +- x86_64_print_stack_entry(bt, ofp, level, +- 0, bt->instptr); +- bt->flags &= ~BT_START; +- level++; +- } +- } ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "end_pfn at %lx: %lx (%ld) -> %d p2m_frames\n", ++ kvaddr, *up, *up, xkd->p2m_frames); ++ ++ if ((xkd->p2m_mfn_frame_list = (ulong *) ++ malloc(xkd->p2m_frames * sizeof(ulong))) == NULL) ++ error(FATAL, "cannot malloc p2m_frame_index_list"); ++ ++ kvaddr = symbol_value("phys_to_machine_mapping"); ++ if (!x86_64_xen_kdump_load_page(kvaddr, xkd->page)) ++ return FALSE; ++ up = (ulong *)(xkd->page + PAGEOFFSET(kvaddr)); ++ kvaddr = *up; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "phys_to_machine_mapping: %lx\n", kvaddr); ++ ++ machdep->last_pgd_read = BADADDR; ++ machdep->last_pmd_read = BADADDR; ++ machdep->last_ptbl_read = BADADDR; ++ ++ for (i = 0; i < xkd->p2m_frames; i++) { ++ xkd->p2m_mfn_frame_list[i] = x86_64_xen_kdump_page_mfn(kvaddr); ++ kvaddr += PAGESIZE(); ++ } + +- /* +- * IRQ stack entry always comes in via the process stack, regardless +- * whether it happened while running in user or kernel space. +- */ +- if (!done && (irqstack = x86_64_in_irqstack(bt))) { +- bt->flags |= BT_IRQSTACK; +- /* +- * Until coded otherwise, the stackbase will be pointing to +- * either the exception stack or, more likely, the process +- * stack base. Switch it to the IRQ stack. +- */ +- bt->stackbase = irqstack; +- bt->stacktop = irqstack + ms->stkinfo.isize; +- bt->stackbuf = ms->irqstack; ++ if (CRASHDEBUG(1)) { ++ for (i = 0; i < xkd->p2m_frames; i++) ++ fprintf(fp, "%lx ", xkd->p2m_mfn_frame_list[i]); ++ fprintf(fp, "\n"); ++ } + +- if (!readmem(bt->stackbase, KVADDR, +- bt->stackbuf, bt->stacktop - bt->stackbase, +- bt->hp && (bt->hp->esp == bt_in->stkptr) ? +- "irqstack contents via hook" : "irqstack contents", +- RETURN_ON_ERROR)) +- error(FATAL, "read of IRQ stack at %lx failed\n", +- bt->stackbase); ++ machdep->last_pgd_read = 0; ++ machdep->last_ptbl_read = 0; ++ machdep->last_pmd_read = 0; ++ pc->readmem = read_kdump; + +- stacktop = bt->stacktop - 64; /* from kernel code */ ++ return TRUE; ++} + +- for (i = (rsp - bt->stackbase)/sizeof(ulong); +- !done && (rsp < stacktop); i++, rsp += sizeof(ulong)) { ++static char * ++x86_64_xen_kdump_load_page(ulong kvaddr, char *pgbuf) ++{ ++ ulong mfn; ++ ulong *pml4, *pgd, *pmd, *ptep; + +- up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++ pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); ++ mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- if (!is_kernel_text(*up)) +- continue; ++ if (CRASHDEBUG(3)) ++ fprintf(fp, ++ "[%lx] pml4: %lx mfn: %lx pml4_index: %lx\n", ++ kvaddr, *pml4, mfn, pml4_index(kvaddr)); ++ ++ if (!readmem(PTOB(mfn), PHYSADDR, machdep->pgd, PAGESIZE(), ++ "xen kdump pud page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read/find pud page\n"); ++ ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(fp, machdep->pgd, ++ "contents of page upper directory page:"); + +- switch (x86_64_print_stack_entry(bt, ofp, level, i,*up)) +- { +- case BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED: +- rsp += SIZE(pt_regs); +- i += SIZE(pt_regs)/sizeof(ulong); +- case BACKTRACE_ENTRY_DISPLAYED: +- level++; +- break; +- case BACKTRACE_ENTRY_IGNORED: +- break; +- case BACKTRACE_COMPLETE: +- done = TRUE; +- break; +- } +- } ++ pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); ++ mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- if (!BT_REFERENCE_CHECK(bt)) +- fprintf(fp, "--- ---\n"); ++ if (CRASHDEBUG(3)) ++ fprintf(fp, ++ "[%lx] pgd: %lx mfn: %lx pgd_index: %lx\n", ++ kvaddr, *pgd, mfn, pgd_index(kvaddr)); + +- /* +- * stack = (unsigned long *) (irqstack_end[-1]); +- * (where irqstack_end is 64 bytes below page end) +- */ +- up = (ulong *)(&bt->stackbuf[stacktop - bt->stackbase]); +- up -= 1; +- irq_eframe = rsp = bt->stkptr = *up; +- up -= 1; +- bt->instptr = *up; +- bt->frameptr = 0; +- done = FALSE; +- } else +- irq_eframe = 0; ++ if (!readmem(PTOB(mfn), PHYSADDR, machdep->pmd, PAGESIZE(), ++ "xen kdump pmd page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read/find pmd page\n"); + +- if (!done && (estack = x86_64_in_exception_stack(bt))) +- goto in_exception_stack; ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(fp, machdep->pmd, ++ "contents of page middle directory page:"); + +- if (!done && (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK))) { +- /* +- * Verify that the rsp pointer taken from either the +- * exception or IRQ stack points into the process stack. +- */ +- bt->stackbase = GET_STACKBASE(bt->tc->task); +- bt->stacktop = GET_STACKTOP(bt->tc->task); ++ pmd = ((ulong *)machdep->pmd) + pmd_index(kvaddr); ++ mfn = ((*pmd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- if (!INSTACK(rsp, bt)) { +- switch (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK)) +- { +- case (BT_EXCEPTION_STACK|BT_IRQSTACK): +- error(FATAL, STACK_TRANSITION_ERRMSG_E_I_P, +- bt_in->stkptr, bt->stkptr, rsp, +- bt->stackbase); ++ if (CRASHDEBUG(3)) ++ fprintf(fp, ++ "[%lx] pmd: %lx mfn: %lx pmd_index: %lx\n", ++ kvaddr, *pmd, mfn, pmd_index(kvaddr)); + +- case BT_EXCEPTION_STACK: +- error(FATAL, STACK_TRANSITION_ERRMSG_E_P, +- bt_in->stkptr, rsp, bt->stackbase); ++ if (!readmem(PTOB(mfn), PHYSADDR, machdep->ptbl, PAGESIZE(), ++ "xen kdump page table page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read/find page table page\n"); + +- case BT_IRQSTACK: +- error(FATAL, STACK_TRANSITION_ERRMSG_I_P, +- bt_in->stkptr, rsp, bt->stackbase); +- } +- } ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(fp, machdep->ptbl, ++ "contents of page table page:"); + +- /* +- * Now fill the local stack buffer from the process stack. +- */ +- if (!readmem(bt->stackbase, KVADDR, bt->stackbuf, +- bt->stacktop - bt->stackbase, +- "irqstack contents", RETURN_ON_ERROR)) +- error(FATAL, "read of process stack at %lx failed\n", +- bt->stackbase); +- } ++ ptep = ((ulong *)machdep->ptbl) + pte_index(kvaddr); ++ mfn = ((*ptep) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- /* +- * For a normally blocked task, hand-create the first level. +- */ +- if (!done && +- !(bt->flags & (BT_TEXT_SYMBOLS|BT_EXCEPTION_STACK|BT_IRQSTACK)) && +- STREQ(closest_symbol(bt->instptr), "thread_return")) { +- bt->flags |= BT_SCHEDULE; +- i = (rsp - bt->stackbase)/sizeof(ulong); +- x86_64_print_stack_entry(bt, ofp, level, +- i, bt->instptr); +- bt->flags &= ~(ulonglong)BT_SCHEDULE; +- rsp += sizeof(ulong); +- level++; +- } ++ if (CRASHDEBUG(3)) ++ fprintf(fp, ++ "[%lx] ptep: %lx mfn: %lx pte_index: %lx\n", ++ kvaddr, *ptep, mfn, pte_index(kvaddr)); + +- /* +- * Dump the IRQ exception frame from the process stack. +- * If the CS register indicates a user exception frame, +- * then set done to TRUE to avoid the process stack walk-through. +- * Otherwise, bump up the rsp past the kernel-mode eframe. +- */ +- if (irq_eframe) { +- bt->flags |= BT_EXCEPTION_FRAME; +- i = (irq_eframe - bt->stackbase)/sizeof(ulong); +- x86_64_print_stack_entry(bt, ofp, level, i, bt->instptr); +- bt->flags &= ~(ulonglong)BT_EXCEPTION_FRAME; +- cs = x86_64_exception_frame(EFRAME_PRINT|EFRAME_CS, 0, +- bt->stackbuf + (irq_eframe - bt->stackbase), bt, ofp); +- if (cs & 3) +- done = TRUE; /* IRQ from user-mode */ +- else +- rsp += SIZE(pt_regs); +- level++; +- } ++ if (!readmem(PTOB(mfn), PHYSADDR, pgbuf, PAGESIZE(), ++ "xen kdump page table page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read/find pte page\n"); + +- /* +- * Walk the process stack. +- */ +- for (i = (rsp - bt->stackbase)/sizeof(ulong); +- !done && (rsp < bt->stacktop); i++, rsp += sizeof(ulong)) { ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(fp, pgbuf, ++ "contents of page:"); + +- up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); ++ return pgbuf; ++} + +- if (!is_kernel_text(*up)) +- continue; ++static ulong ++x86_64_xen_kdump_page_mfn(ulong kvaddr) ++{ ++ ulong mfn; ++ ulong *pml4, *pgd, *pmd, *ptep; + +- if ((bt->flags & BT_CHECK_CALLER)) { +- /* +- * A non-zero offset value from the value_search() +- * lets us know if it's a real text return address. +- */ +- spt = value_search(*up, &offset); +- /* +- * sp gets the syment of the function that the text +- * routine above called before leaving its return +- * address on the stack -- if it can be determined. +- */ +- sp = x86_64_function_called_by((*up)-5); ++ pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); ++ mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- if (sp == NULL) { +- /* +- * We were unable to get the called function. +- * If the text address had an offset, then +- * it must have made an indirect call, and +- * can't have called our target function. +- */ +- if (offset) { +- if (CRASHDEBUG(1)) +- fprintf(ofp, +- "< ignoring %s() -- makes indirect call and NOT %s()>\n", +- spt->name, +- bt->call_target); +- continue; +- } +- } else if (!STREQ(sp->name, bt->call_target)) { +- /* +- * We got function called by the text routine, +- * but it's not our target function. +- */ +- if (CRASHDEBUG(2)) +- fprintf(ofp, +- "< ignoring %s() -- calls %s() and NOT %s()>\n", +- spt->name, sp->name, +- bt->call_target); +- continue; +- } +- } ++ if ((mfn != machdep->last_pgd_read) && ++ !readmem(PTOB(mfn), PHYSADDR, machdep->pgd, PAGESIZE(), ++ "xen kdump pud entry", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read/find pud page\n"); ++ machdep->last_pgd_read = mfn; + +- switch (x86_64_print_stack_entry(bt, ofp, level, i,*up)) +- { +- case BACKTRACE_ENTRY_AND_EFRAME_DISPLAYED: +- last_process_stack_eframe = rsp + 8; +- rsp += SIZE(pt_regs); +- i += SIZE(pt_regs)/sizeof(ulong); +- case BACKTRACE_ENTRY_DISPLAYED: +- level++; +- break; +- case BACKTRACE_ENTRY_IGNORED: +- break; +- case BACKTRACE_COMPLETE: +- done = TRUE; +- break; +- } +- } ++ pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); ++ mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- if (!irq_eframe && !is_kernel_thread(bt->tc->task) && +- (GET_STACKBASE(bt->tc->task) == bt->stackbase)) { +- user_mode_eframe = bt->stacktop - SIZE(pt_regs); +- if (last_process_stack_eframe < user_mode_eframe) +- x86_64_exception_frame(EFRAME_PRINT, 0, bt->stackbuf + +- (bt->stacktop - bt->stackbase) - SIZE(pt_regs), +- bt, ofp); +- } ++ if ((mfn != machdep->last_pmd_read) && ++ !readmem(PTOB(mfn), PHYSADDR, machdep->pmd, PAGESIZE(), ++ "xen kdump pmd entry", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read/find pmd page\n"); ++ machdep->last_pmd_read = mfn; + +- if (bt->flags & BT_TEXT_SYMBOLS) { +- if (BT_REFERENCE_FOUND(bt)) { +- print_task_header(fp, task_to_context(bt->task), 0); +- BCOPY(bt_in, bt, sizeof(struct bt_info)); +- bt->ref = NULL; +- machdep->back_trace(bt); +- fprintf(fp, "\n"); +- } +- } ++ pmd = ((ulong *)machdep->pmd) + pmd_index(kvaddr); ++ mfn = ((*pmd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ if ((mfn != machdep->last_ptbl_read) && ++ !readmem(PTOB(mfn), PHYSADDR, machdep->ptbl, PAGESIZE(), ++ "xen kdump page table page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read/find page table page\n"); ++ machdep->last_ptbl_read = mfn; ++ ++ ptep = ((ulong *)machdep->ptbl) + pte_index(kvaddr); ++ mfn = ((*ptep) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ return mfn; + } + ++#include "xendump.h" ++ + /* +- * Functions that won't be called indirectly. +- * Add more to this as they are discovered. ++ * Determine the physical address base for relocatable kernels. + */ +-static const char *direct_call_targets[] = { +- "schedule", +- "schedule_timeout", +- NULL +-}; +- +-static int +-is_direct_call_target(struct bt_info *bt) ++static void ++x86_64_calc_phys_base(void) + { + int i; ++ FILE *iomem; ++ char buf[BUFSIZE]; ++ char *p1; ++ ulong phys_base, text_start, kernel_code_start; ++ int errflag; ++ struct vmcore_data *vd; ++ static struct xendump_data *xd; ++ Elf64_Phdr *phdr; + +- if (!bt->call_target || (bt->flags & BT_NO_CHECK_CALLER)) +- return FALSE; ++ if (machdep->flags & PHYS_BASE) /* --machdep override */ ++ return; + +- for (i = 0; direct_call_targets[i]; i++) { +- if (STREQ(direct_call_targets[i], bt->call_target)) +- return TRUE; +- } ++ machdep->machspec->phys_base = 0; /* default/traditional */ + +- return FALSE; +-} ++ if (!kernel_symbol_exists("phys_base")) ++ return; + +-static struct syment * +-x86_64_function_called_by(ulong rip) +-{ +- struct syment *sp; +- char buf[BUFSIZE], *p1; +- ulong value, offset; +- unsigned char byte; ++ if (!symbol_exists("_text")) ++ return; ++ else ++ text_start = symbol_value("_text"); + +- value = 0; +- sp = NULL; ++ if (ACTIVE()) { ++ if ((iomem = fopen("/proc/iomem", "r")) == NULL) ++ return; ++ ++ errflag = 1; ++ while (fgets(buf, BUFSIZE, iomem)) { ++ if (strstr(buf, ": Kernel code")) { ++ clean_line(buf); ++ errflag = 0; ++ break; ++ } ++ } ++ fclose(iomem); ++ ++ if (errflag) ++ return; ++ ++ if (!(p1 = strstr(buf, "-"))) ++ return; ++ else ++ *p1 = NULLCHAR; ++ ++ errflag = 0; ++ kernel_code_start = htol(buf, RETURN_ON_ERROR|QUIET, &errflag); ++ if (errflag) ++ return; ++ ++ machdep->machspec->phys_base = kernel_code_start - ++ (text_start - __START_KERNEL_map); ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "_text: %lx ", text_start); ++ fprintf(fp, "Kernel code: %lx -> ", kernel_code_start); ++ fprintf(fp, "phys_base: %lx\n\n", ++ machdep->machspec->phys_base); ++ } + +- if (!readmem(rip, KVADDR, &byte, sizeof(unsigned char), "call byte", +- RETURN_ON_ERROR)) +- return sp; ++ return; ++ } + +- if (byte != 0xe8) +- return sp; ++ /* ++ * Get relocation value from whatever dumpfile format is being used. ++ */ + +- sprintf(buf, "x/i 0x%lx", rip); ++ if (DISKDUMP_DUMPFILE()) { ++ if (diskdump_phys_base(&phys_base)) { ++ machdep->machspec->phys_base = phys_base; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "compressed kdump: phys_base: %lx\n", ++ phys_base); ++ } ++ return; ++ } + +- open_tmpfile2(); +- if (gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { +- rewind(pc->tmpfile2); +- while (fgets(buf, BUFSIZE, pc->tmpfile2)) { +- if ((p1 = strstr(buf, "callq")) && +- whitespace(*(p1-1))) { +- if (extract_hex(p1, &value, NULLCHAR, TRUE)) +- break; ++ if ((vd = get_kdump_vmcore_data())) { ++ for (i = 0; i < vd->num_pt_load_segments; i++) { ++ phdr = vd->load64 + i; ++ if ((phdr->p_vaddr >= __START_KERNEL_map) && ++ !(IS_VMALLOC_ADDR(phdr->p_vaddr))) { ++ ++ machdep->machspec->phys_base = phdr->p_paddr - ++ (phdr->p_vaddr & ~(__START_KERNEL_map)); ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "p_vaddr: %lx p_paddr: %lx -> ", ++ phdr->p_vaddr, phdr->p_paddr); ++ fprintf(fp, "phys_base: %lx\n\n", ++ machdep->machspec->phys_base); ++ } ++ break; + } + } ++ ++ return; + } +- close_tmpfile2(); + +- if (value) +- sp = value_search(value, &offset); ++ if ((xd = get_xendump_data())) { ++ if (text_start == __START_KERNEL_map) { ++ /* ++ * Xen kernels are not relocable (yet) and don't have ++ * the "phys_base" entry point, so this is most likely ++ * a xendump of a fully-virtualized relocatable kernel. ++ * No clues exist in the xendump header, so hardwire ++ * phys_base to 2MB and hope for the best. ++ */ ++ machdep->machspec->phys_base = 0x200000; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "default relocatable phys_base: %lx\n", ++ machdep->machspec->phys_base); + +- return sp; +-} ++ } else if (text_start > __START_KERNEL_map) { ++ switch (xd->flags & (XC_CORE_ELF|XC_CORE_NO_P2M)) ++ { ++ /* ++ * If this is a new ELF-style xendump with no ++ * p2m information, then it also must be a ++ * fully-virtualized relocatable kernel. Again, ++ * the xendump header is useless, and we don't ++ * have /proc/iomem, so presume that the kernel ++ * code starts at 2MB. ++ */ ++ case (XC_CORE_ELF|XC_CORE_NO_P2M): ++ machdep->machspec->phys_base = 0x200000 - ++ (text_start - __START_KERNEL_map); ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "default relocatable " ++ "phys_base: %lx\n", ++ machdep->machspec->phys_base); ++ break; + +-/* +- * Unroll the kernel stack using a minimal amount of gdb services. +- */ +-static void +-x86_64_back_trace(struct gnu_request *req, struct bt_info *bt) +-{ +- error(FATAL, "x86_64_back_trace: unused\n"); ++ default: ++ break; ++ } ++ } ++ } + } + + + /* +- * Print exception frame information for x86_64. +- * +- * Pid: 0, comm: swapper Not tainted 2.6.5-1.360phro.rootsmp +- * RIP: 0010:[] {default_idle+36} +- * RSP: 0018:ffffffff8048bfd8 EFLAGS: 00000246 +- * RAX: 0000000000000000 RBX: ffffffff8010f510 RCX: 0000000000000018 +- * RDX: 0000010001e37280 RSI: ffffffff803ac0a0 RDI: 000001007f43c400 +- * RBP: 0000000000000000 R08: ffffffff8048a000 R09: 0000000000000000 +- * R10: ffffffff80482188 R11: 0000000000000001 R12: 0000000000000000 +- * R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 +- * FS: 0000002a96e14fc0(0000) GS:ffffffff80481d80(0000) GS:0000000055578aa0 +- * CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b +- * CR2: 0000002a9556b000 CR3: 0000000000101000 CR4: 00000000000006e0 +- * ++ * Create an index of mfns for each page that makes up the ++ * kernel's complete phys_to_machine_mapping[max_pfn] array. + */ +- +-static long +-x86_64_exception_frame(ulong flags, ulong kvaddr, char *local, +- struct bt_info *bt, FILE *ofp) ++static int ++x86_64_xendump_p2m_create(struct xendump_data *xd) + { +- long rip, rsp, cs, ss, rflags, orig_rax, rbp; +- long rax, rbx, rcx, rdx, rsi, rdi; +- long r8, r9, r10, r11, r12, r13, r14, r15; +- struct machine_specific *ms; +- char *pt_regs_buf; +- long verified; +- int err; +- +- ms = machdep->machspec; +- +- if (!(machdep->flags & PT_REGS_INIT)) { +- err = 0; +- err |= ((ms->pto.r15 = MEMBER_OFFSET("pt_regs", "r15")) == +- INVALID_OFFSET); +- err |= ((ms->pto.r14 = MEMBER_OFFSET("pt_regs", "r14")) == +- INVALID_OFFSET); +- err |= ((ms->pto.r13 = MEMBER_OFFSET("pt_regs", "r13")) == +- INVALID_OFFSET); +- err |= ((ms->pto.r12 = MEMBER_OFFSET("pt_regs", "r12")) == +- INVALID_OFFSET); +- err |= ((ms->pto.r11 = MEMBER_OFFSET("pt_regs", "r11")) == +- INVALID_OFFSET); +- err |= ((ms->pto.r10 = MEMBER_OFFSET("pt_regs", "r10")) == +- INVALID_OFFSET); +- err |= ((ms->pto.r9 = MEMBER_OFFSET("pt_regs", "r9")) == +- INVALID_OFFSET); +- err |= ((ms->pto.r8 = MEMBER_OFFSET("pt_regs", "r8")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rax = MEMBER_OFFSET("pt_regs", "rax")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rbx = MEMBER_OFFSET("pt_regs", "rbx")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rcx = MEMBER_OFFSET("pt_regs", "rcx")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rdx = MEMBER_OFFSET("pt_regs", "rdx")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rsi = MEMBER_OFFSET("pt_regs", "rsi")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rdi = MEMBER_OFFSET("pt_regs", "rdi")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rip = MEMBER_OFFSET("pt_regs", "rip")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rsp = MEMBER_OFFSET("pt_regs", "rsp")) == +- INVALID_OFFSET); +- err |= ((ms->pto.cs = MEMBER_OFFSET("pt_regs", "cs")) == +- INVALID_OFFSET); +- err |= ((ms->pto.ss = MEMBER_OFFSET("pt_regs", "ss")) == +- INVALID_OFFSET); +- err |= ((ms->pto.eflags = MEMBER_OFFSET("pt_regs", "eflags")) == +- INVALID_OFFSET); +- err |= ((ms->pto.orig_rax = +- MEMBER_OFFSET("pt_regs", "orig_rax")) == +- INVALID_OFFSET); +- err |= ((ms->pto.rbp = MEMBER_OFFSET("pt_regs", "rbp")) == +- INVALID_OFFSET); ++ int i, idx; ++ ulong mfn, kvaddr, ctrlreg[8], ctrlreg_offset; ++ ulong *up; ++ off_t offset; + +- if (err) +- error(WARNING, "pt_regs structure has changed\n"); ++ if (!symbol_exists("phys_to_machine_mapping")) { ++ xd->flags |= XC_CORE_NO_P2M; ++ return TRUE; ++ } + +- machdep->flags |= PT_REGS_INIT; +- } ++ if ((ctrlreg_offset = MEMBER_OFFSET("vcpu_guest_context", "ctrlreg")) == ++ INVALID_OFFSET) ++ error(FATAL, ++ "cannot determine vcpu_guest_context.ctrlreg offset\n"); ++ else if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, ++ "MEMBER_OFFSET(vcpu_guest_context, ctrlreg): %ld\n", ++ ctrlreg_offset); ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)ctrlreg_offset; ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ error(FATAL, "cannot lseek to xch_ctxt_offset\n"); ++ ++ if (read(xd->xfd, &ctrlreg, sizeof(ctrlreg)) != ++ sizeof(ctrlreg)) ++ error(FATAL, "cannot read vcpu_guest_context ctrlreg[8]\n"); ++ ++ for (i = 0; CRASHDEBUG(1) && (i < 8); i++) ++ fprintf(xd->ofp, "ctrlreg[%d]: %lx\n", i, ctrlreg[i]); ++ ++ mfn = ctrlreg[3] >> PAGESHIFT(); ++ ++ if (!xc_core_mfn_to_page(mfn, machdep->machspec->pml4)) ++ error(FATAL, "cannot read/find cr3 page\n"); ++ ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(xd->ofp, machdep->machspec->pml4, ++ "contents of PML4 page:"); + +- if (kvaddr) { +- pt_regs_buf = GETBUF(SIZE(pt_regs)); +- readmem(kvaddr, KVADDR, pt_regs_buf, +- SIZE(pt_regs), "pt_regs", FAULT_ON_ERROR); +- } else +- pt_regs_buf = local; ++ kvaddr = symbol_value("end_pfn"); ++ if (!x86_64_xendump_load_page(kvaddr, xd)) ++ return FALSE; + +- rip = ULONG(pt_regs_buf + ms->pto.rip); +- rsp = ULONG(pt_regs_buf + ms->pto.rsp); +- cs = ULONG(pt_regs_buf + ms->pto.cs); +- ss = ULONG(pt_regs_buf + ms->pto.ss); +- rflags = ULONG(pt_regs_buf + ms->pto.eflags); +- orig_rax = ULONG(pt_regs_buf + ms->pto.orig_rax); +- rbp = ULONG(pt_regs_buf + ms->pto.rbp); +- rax = ULONG(pt_regs_buf + ms->pto.rax); +- rbx = ULONG(pt_regs_buf + ms->pto.rbx); +- rcx = ULONG(pt_regs_buf + ms->pto.rcx); +- rdx = ULONG(pt_regs_buf + ms->pto.rdx); +- rsi = ULONG(pt_regs_buf + ms->pto.rsi); +- rdi = ULONG(pt_regs_buf + ms->pto.rdi); +- r8 = ULONG(pt_regs_buf + ms->pto.r8); +- r9 = ULONG(pt_regs_buf + ms->pto.r9); +- r10 = ULONG(pt_regs_buf + ms->pto.r10); +- r11 = ULONG(pt_regs_buf + ms->pto.r11); +- r12 = ULONG(pt_regs_buf + ms->pto.r12); +- r13 = ULONG(pt_regs_buf + ms->pto.r13); +- r14 = ULONG(pt_regs_buf + ms->pto.r14); +- r15 = ULONG(pt_regs_buf + ms->pto.r15); ++ up = (ulong *)(xd->page + PAGEOFFSET(kvaddr)); ++ if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, "end_pfn: %lx\n", *up); + +- verified = x86_64_eframe_verify(bt, +- kvaddr ? kvaddr : (local - bt->stackbuf) + bt->stackbase, +- cs, ss, rip, rsp, rflags); ++ xd->xc_core.p2m_frames = (*up/(PAGESIZE()/sizeof(ulong))) + ++ ((*up%(PAGESIZE()/sizeof(ulong))) ? 1 : 0); + +- /* +- * If it's print-if-verified request, don't print bogus eframes. +- */ +- if (!verified && ((flags & (EFRAME_VERIFY|EFRAME_PRINT)) == +- (EFRAME_VERIFY|EFRAME_PRINT))) +- flags &= ~EFRAME_PRINT; ++ if ((xd->xc_core.p2m_frame_index_list = (ulong *) ++ malloc(xd->xc_core.p2m_frames * sizeof(ulong))) == NULL) ++ error(FATAL, "cannot malloc p2m_frame_list"); + +- if (CRASHDEBUG(2)) +- fprintf(ofp, "< exception frame at: %lx >\n", kvaddr ? kvaddr : +- (local - bt->stackbuf) + bt->stackbase); ++ kvaddr = symbol_value("phys_to_machine_mapping"); ++ if (!x86_64_xendump_load_page(kvaddr, xd)) ++ return FALSE; + +- if (flags & EFRAME_PRINT) { +- if (flags & EFRAME_SEARCH) { +- fprintf(ofp, "\n %s-MODE EXCEPTION FRAME AT: %lx\n", +- cs & 3 ? "USER" : "KERNEL", +- kvaddr ? kvaddr : +- (local - bt->stackbuf) + bt->stackbase); +- } ++ up = (ulong *)(xd->page + PAGEOFFSET(kvaddr)); ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "phys_to_machine_mapping: %lx\n", *up); + +- fprintf(ofp, " RIP: %016lx RSP: %016lx RFLAGS: %08lx\n", +- rip, rsp, rflags); +- fprintf(ofp, " RAX: %016lx RBX: %016lx RCX: %016lx\n", +- rax, rbx, rcx); +- fprintf(ofp, " RDX: %016lx RSI: %016lx RDI: %016lx\n", +- rdx, rsi, rdi); +- fprintf(ofp, " RBP: %016lx R8: %016lx R9: %016lx\n", +- rbp, r8, r9); +- fprintf(ofp, " R10: %016lx R11: %016lx R12: %016lx\n", +- r10, r11, r12); +- fprintf(ofp, " R13: %016lx R14: %016lx R15: %016lx\n", +- r13, r14, r15); +- fprintf(ofp, " ORIG_RAX: %016lx CS: %04lx SS: %04lx\n", +- orig_rax, cs, ss); ++ kvaddr = *up; ++ machdep->last_ptbl_read = BADADDR; + +- if (!verified) +- error(WARNING, "possibly bogus exception frame\n"); ++ for (i = 0; i < xd->xc_core.p2m_frames; i++) { ++ if ((idx = x86_64_xendump_page_index(kvaddr, xd)) == MFN_NOT_FOUND) ++ return FALSE; ++ xd->xc_core.p2m_frame_index_list[i] = idx; ++ kvaddr += PAGESIZE(); + } + +- if ((flags & EFRAME_PRINT) && BT_REFERENCE_CHECK(bt)) { +- x86_64_do_bt_reference_check(bt, rip, NULL); +- x86_64_do_bt_reference_check(bt, rsp, NULL); +- x86_64_do_bt_reference_check(bt, cs, NULL); +- x86_64_do_bt_reference_check(bt, ss, NULL); +- x86_64_do_bt_reference_check(bt, rflags, NULL); +- x86_64_do_bt_reference_check(bt, orig_rax, NULL); +- x86_64_do_bt_reference_check(bt, rbp, NULL); +- x86_64_do_bt_reference_check(bt, rax, NULL); +- x86_64_do_bt_reference_check(bt, rbx, NULL); +- x86_64_do_bt_reference_check(bt, rcx, NULL); +- x86_64_do_bt_reference_check(bt, rdx, NULL); +- x86_64_do_bt_reference_check(bt, rsi, NULL); +- x86_64_do_bt_reference_check(bt, rdi, NULL); +- x86_64_do_bt_reference_check(bt, r8, NULL); +- x86_64_do_bt_reference_check(bt, r9, NULL); +- x86_64_do_bt_reference_check(bt, r10, NULL); +- x86_64_do_bt_reference_check(bt, r11, NULL); +- x86_64_do_bt_reference_check(bt, r12, NULL); +- x86_64_do_bt_reference_check(bt, r13, NULL); +- x86_64_do_bt_reference_check(bt, r14, NULL); +- x86_64_do_bt_reference_check(bt, r15, NULL); +- } ++ machdep->last_ptbl_read = 0; + +- if (kvaddr) +- FREEBUF(pt_regs_buf); ++ return TRUE; ++} + +- if (flags & EFRAME_CS) +- return cs; +- else if (flags & EFRAME_VERIFY) +- return verified; ++static void ++x86_64_debug_dump_page(FILE *ofp, char *page, char *name) ++{ ++ int i; ++ ulong *up; + +- return 0; ++ fprintf(ofp, "%s\n", name); ++ ++ up = (ulong *)page; ++ for (i = 0; i < 256; i++) { ++ fprintf(ofp, "%016lx: %016lx %016lx\n", ++ (ulong)((i * 2) * sizeof(ulong)), ++ *up, *(up+1)); ++ up += 2; ++ } + } + + /* +- * Check that the verifiable registers contain reasonable data. ++ * Find the page associate with the kvaddr, and read its contents ++ * into the passed-in buffer. + */ +-#define RAZ_MASK 0xffffffffffc08028 /* return-as-zero bits */ +- +-static int +-x86_64_eframe_verify(struct bt_info *bt, long kvaddr, long cs, long ss, +- long rip, long rsp, long rflags) ++static char * ++x86_64_xendump_load_page(ulong kvaddr, struct xendump_data *xd) + { +- if ((rflags & RAZ_MASK) || !(rflags & 0x2)) +- return FALSE; ++ ulong mfn; ++ ulong *pml4, *pgd, *pmd, *ptep; + +- if ((cs == 0x10) && (ss == 0x18)) { +- if (is_kernel_text(rip) && IS_KVADDR(rsp)) +- return TRUE; +- } ++ pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); ++ mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- if ((cs == 0x10) && kvaddr) { +- if (is_kernel_text(rip) && IS_KVADDR(rsp) && +- (rsp == (kvaddr + SIZE(pt_regs) + 8))) +- return TRUE; +- } ++ if (CRASHDEBUG(3)) ++ fprintf(xd->ofp, ++ "[%lx] pml4: %lx mfn: %lx pml4_index: %lx\n", ++ kvaddr, *pml4, mfn, pml4_index(kvaddr)); + +- if ((cs == 0x10) && kvaddr) { +- if (is_kernel_text(rip) && IS_KVADDR(rsp) && +- (rsp == (kvaddr + SIZE(pt_regs)))) +- return TRUE; +- } ++ if (!xc_core_mfn_to_page(mfn, machdep->pgd)) ++ error(FATAL, "cannot read/find pud page\n"); ++ ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(xd->ofp, machdep->pgd, ++ "contents of page upper directory page:"); ++ ++ pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); ++ mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ if (CRASHDEBUG(3)) ++ fprintf(xd->ofp, ++ "[%lx] pgd: %lx mfn: %lx pgd_index: %lx\n", ++ kvaddr, *pgd, mfn, pgd_index(kvaddr)); ++ ++ if (!xc_core_mfn_to_page(mfn, machdep->pmd)) ++ error(FATAL, "cannot read/find pmd page\n"); ++ ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(xd->ofp, machdep->pmd, ++ "contents of page middle directory page:"); ++ ++ pmd = ((ulong *)machdep->pmd) + pmd_index(kvaddr); ++ mfn = ((*pmd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ if (CRASHDEBUG(3)) ++ fprintf(xd->ofp, ++ "[%lx] pmd: %lx mfn: %lx pmd_index: %lx\n", ++ kvaddr, *pmd, mfn, pmd_index(kvaddr)); ++ ++ if (!xc_core_mfn_to_page(mfn, machdep->ptbl)) ++ error(FATAL, "cannot read/find page table page\n"); ++ ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(xd->ofp, machdep->ptbl, ++ "contents of page table page:"); + +- if ((cs == 0x33) && (ss == 0x2b)) { +- if (IS_UVADDR(rip, bt->tc) && IS_UVADDR(rsp, bt->tc)) +- return TRUE; +- } ++ ptep = ((ulong *)machdep->ptbl) + pte_index(kvaddr); ++ mfn = ((*ptep) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + +- return FALSE; ++ if (CRASHDEBUG(3)) ++ fprintf(xd->ofp, ++ "[%lx] ptep: %lx mfn: %lx pte_index: %lx\n", ++ kvaddr, *ptep, mfn, pte_index(kvaddr)); ++ ++ if (!xc_core_mfn_to_page(mfn, xd->page)) ++ error(FATAL, "cannot read/find pte page\n"); ++ ++ if (CRASHDEBUG(7)) ++ x86_64_debug_dump_page(xd->ofp, xd->page, ++ "contents of page:"); ++ ++ return xd->page; + } + + /* +- * Get a stack frame combination of pc and ra from the most relevent spot. ++ * Find the dumpfile page index associated with the kvaddr. + */ +-static void +-x86_64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp) ++static int ++x86_64_xendump_page_index(ulong kvaddr, struct xendump_data *xd) + { +- if (bt->flags & BT_DUMPFILE_SEARCH) +- return x86_64_get_dumpfile_stack_frame(bt, pcp, spp); ++ int idx; ++ ulong mfn; ++ ulong *pml4, *pgd, *pmd, *ptep; + +- if (pcp) +- *pcp = x86_64_get_pc(bt); +- if (spp) +- *spp = x86_64_get_sp(bt); ++ pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); ++ mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ if ((mfn != machdep->last_pgd_read) && ++ !xc_core_mfn_to_page(mfn, machdep->pgd)) ++ error(FATAL, "cannot read/find pud page\n"); ++ machdep->last_pgd_read = mfn; ++ ++ pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); ++ mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ if ((mfn != machdep->last_pmd_read) && ++ !xc_core_mfn_to_page(mfn, machdep->pmd)) ++ error(FATAL, "cannot read/find pmd page\n"); ++ ++ machdep->last_pmd_read = mfn; ++ ++ pmd = ((ulong *)machdep->pmd) + pmd_index(kvaddr); ++ mfn = ((*pmd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ if ((mfn != machdep->last_ptbl_read) && ++ !xc_core_mfn_to_page(mfn, machdep->ptbl)) ++ error(FATAL, "cannot read/find page table page\n"); ++ machdep->last_ptbl_read = mfn; ++ ++ ptep = ((ulong *)machdep->ptbl) + pte_index(kvaddr); ++ mfn = ((*ptep) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); ++ ++ if ((idx = xc_core_mfn_to_page_index(mfn)) == MFN_NOT_FOUND) ++ error(INFO, "cannot determine page index for %lx\n", ++ kvaddr); ++ ++ return idx; + } + + /* +- * Get the starting point for the active cpus in a diskdump/netdump. ++ * Pull the rsp from the cpu_user_regs struct in the header ++ * turn it into a task, and match it with the active_set. ++ * Unfortunately, the registers in the vcpu_guest_context ++ * are not necessarily those of the panic task, so for now ++ * let get_active_set_panic_task() get the right task. + */ +-static void +-x86_64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *rip, ulong *rsp) ++static ulong ++x86_64_xendump_panic_task(struct xendump_data *xd) + { +- int panic_task; +- int i, panic, stage; +- char *sym; +- struct syment *sp; +- ulong *up; +- struct bt_info bt_local, *bt; +- struct machine_specific *ms; +- char *user_regs; +- ulong ur_rip; +- ulong ur_rsp; ++ int i; ++ ulong rsp; ++ off_t offset; ++ ulong task; + +- bt = &bt_local; +- BCOPY(bt_in, bt, sizeof(struct bt_info)); +- ms = machdep->machspec; +- ur_rip = ur_rsp = 0; +- stage = 0; ++ if (INVALID_MEMBER(vcpu_guest_context_user_regs) || ++ INVALID_MEMBER(cpu_user_regs_esp)) ++ return NO_TASK; ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)OFFSET(vcpu_guest_context_user_regs) + ++ (off_t)OFFSET(cpu_user_regs_rsp); ++ ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ return NO_TASK; ++ ++ if (read(xd->xfd, &rsp, sizeof(ulong)) != sizeof(ulong)) ++ return NO_TASK; ++ ++ if (IS_KVADDR(rsp) && (task = stkptr_to_task(rsp))) { ++ ++ for (i = 0; i < NR_CPUS; i++) { ++ if (task == tt->active_set[i]) { ++ if (CRASHDEBUG(0)) ++ error(INFO, ++ "x86_64_xendump_panic_task: rsp: %lx -> task: %lx\n", ++ rsp, task); ++ return task; ++ } ++ } + +- panic_task = tt->panic_task == bt->task ? TRUE : FALSE; ++ error(WARNING, ++ "x86_64_xendump_panic_task: rsp: %lx -> task: %lx (not active)\n", ++ rsp); ++ } + +- if (panic_task && bt->machdep) { +- user_regs = bt->machdep; ++ return NO_TASK; ++} + +- if (x86_64_eframe_verify(bt, +- 0, +- ULONG(user_regs + OFFSET(user_regs_struct_cs)), +- ULONG(user_regs + OFFSET(user_regs_struct_ss)), +- ULONG(user_regs + OFFSET(user_regs_struct_rip)), +- ULONG(user_regs + OFFSET(user_regs_struct_rsp)), +- ULONG(user_regs + OFFSET(user_regs_struct_eflags)))) { +- bt->stkptr = ULONG(user_regs + +- OFFSET(user_regs_struct_rsp)); +- if (x86_64_in_irqstack(bt)) { +- ur_rip = ULONG(user_regs + +- OFFSET(user_regs_struct_rip)); +- ur_rsp = ULONG(user_regs + +- OFFSET(user_regs_struct_rsp)); +- goto skip_stage; +- } +- } +- } ++/* ++ * Because of an off-by-one vcpu bug in early xc_domain_dumpcore() ++ * instantiations, the registers in the vcpu_guest_context are not ++ * necessarily those of the panic task. Furthermore, the rsp is ++ * seemingly unassociated with the task, presumably due a hypervisor ++ * callback, so only accept the contents if they retfer to the panic ++ * task's stack. ++ */ ++static void ++x86_64_get_xendump_regs(struct xendump_data *xd, struct bt_info *bt, ulong *rip, ulong *rsp) ++{ ++ ulong task, xrip, xrsp; ++ off_t offset; ++ struct syment *sp; ++ int cpu; + +- panic = FALSE; ++ if (INVALID_MEMBER(vcpu_guest_context_user_regs) || ++ INVALID_MEMBER(cpu_user_regs_rip) || ++ INVALID_MEMBER(cpu_user_regs_rsp)) ++ goto generic; ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)OFFSET(vcpu_guest_context_user_regs) + ++ (off_t)OFFSET(cpu_user_regs_rsp); ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ goto generic; ++ if (read(xd->xfd, &xrsp, sizeof(ulong)) != sizeof(ulong)) ++ goto generic; ++ ++ offset = (off_t)xd->xc_core.header.xch_ctxt_offset + ++ (off_t)OFFSET(vcpu_guest_context_user_regs) + ++ (off_t)OFFSET(cpu_user_regs_rip); ++ if (lseek(xd->xfd, offset, SEEK_SET) == -1) ++ goto generic; ++ if (read(xd->xfd, &xrip, sizeof(ulong)) != sizeof(ulong)) ++ goto generic; + + /* +- * Check the process stack first. ++ * This works -- comes from smp_send_stop call in panic. ++ * But xendump_panic_hook() will forestall this function ++ * from being called (for now). + */ +-next_stack: +- for (i = 0, up = (ulong *)bt->stackbuf; +- i < (bt->stacktop - bt->stackbase)/sizeof(ulong); i++, up++) { +- sym = closest_symbol(*up); +- +- if (STREQ(sym, "netconsole_netdump") || +- STREQ(sym, "netpoll_start_netdump") || +- STREQ(sym, "start_disk_dump") || +- STREQ(sym, "disk_dump") || +- STREQ(sym, "try_crashdump")) { +- *rip = *up; +- *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); +- return; +- } ++ if (IS_KVADDR(xrsp) && (task = stkptr_to_task(xrsp)) && ++ (task == bt->task)) { ++ if (CRASHDEBUG(1)) ++ fprintf(xd->ofp, ++ "hooks from vcpu_guest_context: rip: %lx rsp: %lx\n", xrip, xrsp); ++ *rip = xrip; ++ *rsp = xrsp; ++ return; ++ } + +- if ((stage == 2) && +- (STREQ(sym, "nmi_watchdog_tick") || +- STREQ(sym, "default_do_nmi"))) { +- sp = x86_64_function_called_by((*up)-5); +- if (!sp || !STREQ(sp->name, "die_nmi")) +- continue; +- *rip = *up; +- *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); +- bt_in->flags |= BT_START; +- *rip = symbol_value("die_nmi"); +- *rsp = (*rsp) - (7*sizeof(ulong)); +- return; +- } ++generic: + +- if (STREQ(sym, "panic")) { +- *rip = *up; +- *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); +- panic = TRUE; +- continue; /* keep looking for die */ +- } ++ machdep->get_stack_frame(bt, rip, rsp); + +- if (STREQ(sym, "die")) { +- *rip = *up; +- *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); +- for (i++, up++; i < LONGS_PER_STACK; i++, up++) { +- sym = closest_symbol(*up); +- if (STREQ(sym, "sysrq_handle_crash")) +- goto next_sysrq; ++ /* ++ * If this is an active task showing itself in schedule(), ++ * then the thread_struct rsp is stale. It has to be coming ++ * from a callback via the interrupt stack. ++ */ ++ if (is_task_active(bt->task) && (symbol_value("thread_return") == *rip)) { ++ cpu = bt->tc->processor; ++ xrsp = machdep->machspec->stkinfo.ibase[cpu] + ++ machdep->machspec->stkinfo.isize - sizeof(ulong); ++ ++ while (readmem(xrsp, KVADDR, &xrip, ++ sizeof(ulong), "xendump rsp", RETURN_ON_ERROR)) { ++ if ((sp = value_search(xrip, (ulong *)&offset)) && ++ STREQ(sp->name, "smp_really_stop_cpu") && offset) { ++ *rip = xrip; ++ *rsp = xrsp; ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "switch thread_return to smp_call_function_interrupt\n"); ++ break; + } +- return; ++ xrsp -= sizeof(ulong); ++ if (xrsp <= machdep->machspec->stkinfo.ibase[cpu]) ++ break; + } ++ } ++} + +- if (STREQ(sym, "sysrq_handle_crash")) { +-next_sysrq: +- *rip = *up; +- *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); +- machdep->flags |= SYSRQ; +- for (i++, up++; i < LONGS_PER_STACK; i++, up++) { +- sym = closest_symbol(*up); +- if (STREQ(sym, "sysrq_handle_crash")) +- goto next_sysrq; +- } +- return; +- } ++/* for XEN Hypervisor analysis */ + +- if (!panic_task && (stage > 0) && +- STREQ(sym, "smp_call_function_interrupt")) { +- *rip = *up; +- *rsp = bt->stackbase + ((char *)(up) - bt->stackbuf); +- return; +- } +- } ++static int ++x86_64_is_kvaddr_hyper(ulong addr) ++{ ++ return (addr >= HYPERVISOR_VIRT_START && addr < HYPERVISOR_VIRT_END); ++} + +- if (panic) +- return; ++static ulong ++x86_64_get_stackbase_hyper(ulong task) ++{ ++ struct xen_hyper_vcpu_context *vcc; ++ struct xen_hyper_pcpu_context *pcc; ++ ulong rsp0, base; ++ ++ /* task means vcpu here */ ++ vcc = xen_hyper_vcpu_to_vcpu_context(task); ++ if (!vcc) ++ error(FATAL, "invalid vcpu\n"); ++ ++ pcc = xen_hyper_id_to_pcpu_context(vcc->processor); ++ if (!pcc) ++ error(FATAL, "invalid pcpu number\n"); ++ ++ rsp0 = pcc->sp.rsp0; ++ base = rsp0 & (~(STACKSIZE() - 1)); ++ return base; ++} + +-skip_stage: +- switch (stage) +- { +- /* +- * Now check the processor's interrupt stack. +- */ +- case 0: +- bt->stackbase = ms->stkinfo.ibase[bt->tc->processor]; +- bt->stacktop = ms->stkinfo.ibase[bt->tc->processor] + +- ms->stkinfo.isize; +- bt->stackbuf = ms->irqstack; +- alter_stackbuf(bt); +- stage = 1; +- goto next_stack; ++static ulong ++x86_64_get_stacktop_hyper(ulong task) ++{ ++ return x86_64_get_stackbase_hyper(task) + STACKSIZE(); ++} + +- /* +- * Check the NMI exception stack. +- */ +- case 1: +- bt->stackbase = ms->stkinfo.ebase[bt->tc->processor][NMI_STACK]; +- bt->stacktop = ms->stkinfo.ebase[bt->tc->processor][NMI_STACK] + +- ms->stkinfo.esize; +- bt->stackbuf = ms->irqstack; +- alter_stackbuf(bt); +- stage = 2; +- goto next_stack; ++#define EXCEPTION_STACKSIZE_HYPER (1024UL) + +- case 2: +- break; +- } ++static ulong ++x86_64_in_exception_stack_hyper(ulong vcpu, ulong rsp) ++{ ++ struct xen_hyper_vcpu_context *vcc; ++ struct xen_hyper_pcpu_context *pcc; ++ int i; ++ ulong stackbase; + +- /* +- * We didn't find what we were looking for, so just use what was +- * passed in from the ELF header. +- */ +- if (ur_rip && ur_rsp) { +- *rip = ur_rip; +- *rsp = ur_rsp; ++ vcc = xen_hyper_vcpu_to_vcpu_context(vcpu); ++ if (!vcc) ++ error(FATAL, "invalid vcpu\n"); ++ ++ pcc = xen_hyper_id_to_pcpu_context(vcc->processor); ++ if (!pcc) ++ error(FATAL, "invalid pcpu number\n"); ++ ++ for (i = 0; i < XEN_HYPER_TSS_IST_MAX; i++) { ++ if (pcc->ist[i] == 0) { ++ continue; ++ } ++ stackbase = pcc->ist[i] - EXCEPTION_STACKSIZE_HYPER; ++ if ((rsp & ~(EXCEPTION_STACKSIZE_HYPER - 1)) == stackbase) { ++ return stackbase; ++ } + } + +- console("x86_64_get_dumpfile_stack_frame: cannot find anything useful\n"); ++ return 0; ++} + +- bt->flags &= ~(ulonglong)BT_DUMPFILE_SEARCH; ++static void ++x86_64_get_stack_frame_hyper(struct bt_info *bt, ulong *pcp, ulong *spp) ++{ ++ struct xen_hyper_vcpu_context *vcc; ++ int pcpu; ++ ulong *regs; ++ ulong rsp, rip; ++ ++ /* task means vcpu here */ ++ vcc = xen_hyper_vcpu_to_vcpu_context(bt->task); ++ if (!vcc) ++ error(FATAL, "invalid vcpu\n"); ++ ++ pcpu = vcc->processor; ++ if (!xen_hyper_test_pcpu_id(pcpu)) { ++ error(FATAL, "invalid pcpu number\n"); ++ } ++ ++ if (bt->flags & BT_TEXT_SYMBOLS_ALL) { ++ if (spp) ++ *spp = x86_64_get_stackbase_hyper(bt->task); ++ if (pcp) ++ *pcp = 0; ++ bt->flags &= ~BT_TEXT_SYMBOLS_ALL; ++ return; ++ } + +- machdep->get_stack_frame(bt, rip, rsp); ++ regs = (ulong *)xen_hyper_id_to_dumpinfo_context(pcpu)->pr_reg_ptr; ++ rsp = XEN_HYPER_X86_64_NOTE_RSP(regs); ++ rip = XEN_HYPER_X86_64_NOTE_RIP(regs); ++ ++ if (spp) { ++ if (x86_64_in_exception_stack_hyper(bt->task, rsp)) ++ *spp = rsp; ++ else if (rsp < x86_64_get_stackbase_hyper(bt->task) || ++ rsp >= x86_64_get_stacktop_hyper(bt->task)) ++ *spp = x86_64_get_stackbase_hyper(bt->task); ++ else ++ *spp = rsp; ++ } ++ if (pcp) { ++ if (is_kernel_text(rip)) ++ *pcp = rip; ++ else ++ *pcp = 0; ++ } + } + +-/* +- * Get the saved RSP from the task's thread_struct. +- */ +-static ulong +-x86_64_get_sp(struct bt_info *bt) ++static int ++x86_64_print_stack_entry_hyper(struct bt_info *bt, FILE *ofp, int level, ++ int stkindex, ulong text) + { +- ulong offset, rsp; ++ ulong rsp, offset; ++ struct syment *sp; ++ char *name; ++ int result; ++ char buf[BUFSIZE]; + +- if (tt->flags & THREAD_INFO) { +- readmem(bt->task + OFFSET(task_struct_thread) + +- OFFSET(thread_struct_rsp), KVADDR, +- &rsp, sizeof(void *), +- "thread_struct rsp", FAULT_ON_ERROR); +- return rsp; +- } ++ offset = 0; ++ sp = value_search(text, &offset); ++ if (!sp) ++ return BACKTRACE_ENTRY_IGNORED; + +- offset = OFFSET(task_struct_thread) + OFFSET(thread_struct_rsp); ++ name = sp->name; + +- return GET_STACK_ULONG(offset); +-} ++ if (STREQ(name, "syscall_enter")) ++ result = BACKTRACE_COMPLETE; ++ else ++ result = BACKTRACE_ENTRY_DISPLAYED; + +-/* +- * Get the saved PC from the task's thread_struct if it exists; +- * otherwise just use the "thread_return" label value. +- */ +-static ulong +-x86_64_get_pc(struct bt_info *bt) +-{ +- ulong offset, rip; ++ rsp = bt->stackbase + (stkindex * sizeof(long)); + +- if (INVALID_MEMBER(thread_struct_rip)) +- return symbol_value("thread_return"); ++ if ((bt->flags & BT_FULL)) { ++ if (bt->frameptr) ++ x86_64_display_full_frame(bt, rsp, ofp); ++ bt->frameptr = rsp + sizeof(ulong); ++ } + +- if (tt->flags & THREAD_INFO) { +- readmem(bt->task + OFFSET(task_struct_thread) + +- OFFSET(thread_struct_rip), KVADDR, +- &rip, sizeof(void *), +- "thread_struct rip", FAULT_ON_ERROR); +- return rip; +- } ++ fprintf(ofp, "%s#%d [%8lx] %s at %lx\n", level < 10 ? " " : "", level, ++ rsp, name, text); ++ ++ if (bt->flags & BT_LINE_NUMBERS) { ++ get_line_number(text, buf, FALSE); ++ if (strlen(buf)) ++ fprintf(ofp, " %s\n", buf); ++ } + +- offset = OFFSET(task_struct_thread) + OFFSET(thread_struct_rip); ++ if (BT_REFERENCE_CHECK(bt)) ++ x86_64_do_bt_reference_check(bt, text, name); + +- return GET_STACK_ULONG(offset); ++ return result; + } + +- +-/* +- * Do the work for x86_64_get_sp() and x86_64_get_pc(). +- */ + static void +-get_x86_64_frame(struct bt_info *bt, ulong *getpc, ulong *getsp) +-{ +- error(FATAL, "get_x86_64_frame: TBD\n"); +-} +- +-/* +- * Do the work for cmd_irq(). +- */ +-static void +-x86_64_dump_irq(int irq) ++x86_64_print_eframe_regs_hyper(struct bt_info *bt) + { +- if (symbol_exists("irq_desc")) { +- machdep->dump_irq = generic_dump_irq; +- return(generic_dump_irq(irq)); +- } ++ ulong *up; ++ ulong offset; ++ struct syment *sp; + +- error(FATAL, "ia64_dump_irq: irq_desc[] does not exist?\n"); +-} + +-/* +- * Do the work for irq -d +- */ +-void +-x86_64_display_idt_table(void) +-{ +- int i; +- char *idt_table_buf; +- char buf[BUFSIZE]; +- ulong *ip; ++ up = (ulong *)(&bt->stackbuf[bt->stacktop - bt->stackbase]); ++ up -= 21; + +- idt_table_buf = GETBUF(SIZE(gate_struct) * 256); +- readmem(symbol_value("idt_table"), KVADDR, idt_table_buf, +- SIZE(gate_struct) * 256, "idt_table", FAULT_ON_ERROR); +- ip = (ulong *)idt_table_buf; ++ fprintf(fp, " [exception RIP: "); ++ if ((sp = value_search(up[16], &offset))) { ++ fprintf(fp, "%s", sp->name); ++ if (offset) ++ fprintf(fp, (output_radix == 16) ? ++ "+0x%lx" : "+%ld", offset); ++ } else ++ fprintf(fp, "unknown or invalid address"); ++ fprintf(fp, "]\n"); + +- for (i = 0; i < 256; i++, ip += 2) { +- if (i < 10) +- fprintf(fp, " "); +- else if (i < 100) +- fprintf(fp, " "); +- fprintf(fp, "[%d] %s\n", +- i, x86_64_extract_idt_function(ip, buf, NULL)); +- } ++ fprintf(fp, " RIP: %016lx RSP: %016lx RFLAGS: %08lx\n", ++ up[16], up[19], up[18]); ++ fprintf(fp, " RAX: %016lx RBX: %016lx RCX: %016lx\n", ++ up[10], up[5], up[11]); ++ fprintf(fp, " RDX: %016lx RSI: %016lx RDI: %016lx\n", ++ up[11], up[13], up[14]); ++ fprintf(fp, " RBP: %016lx R8: %016lx R9: %016lx\n", ++ up[4], up[9], up[8]); ++ fprintf(fp, " R10: %016lx R11: %016lx R12: %016lx\n", ++ up[7], up[6], up[3]); ++ fprintf(fp, " R13: %016lx R14: %016lx R15: %016lx\n", ++ up[2], up[1], up[0]); ++ fprintf(fp, " ORIG_RAX: %016lx CS: %04lx SS: %04lx\n", ++ up[15], up[17], up[20]); + +- FREEBUF(idt_table_buf); ++ fprintf(fp, "--- ---\n"); + } + + /* +- * Extract the function name out of the IDT entry. ++ * simple back tracer for xen hypervisor ++ * irq stack does not exist. so relative easy. + */ +-static char * +-x86_64_extract_idt_function(ulong *ip, char *buf, ulong *retaddr) ++static void ++x86_64_simple_back_trace_cmd_hyper(struct bt_info *bt_in) + { +- ulong i1, i2, addr; +- char locbuf[BUFSIZE]; +- physaddr_t phys; ++ int i, level, done; ++ ulong rsp, estack, stacktop; ++ ulong *up; ++ FILE *ofp; ++ struct bt_info bt_local, *bt; ++ char ebuf[EXCEPTION_STACKSIZE_HYPER]; + +- if (buf) +- BZERO(buf, BUFSIZE); ++ bt = &bt_local; ++ BCOPY(bt_in, bt, sizeof(struct bt_info)); + +- i1 = *ip; +- i2 = *(ip+1); ++ if (bt->flags & BT_FRAMESIZE_DEBUG) { ++ error(INFO, "-F not support\n"); ++ return; ++ } + +- i2 <<= 32; +- addr = i2 & 0xffffffff00000000; +- addr |= (i1 & 0xffff); +- i1 >>= 32; +- addr |= (i1 & 0xffff0000); ++ level = 0; ++ done = FALSE; ++ bt->call_target = NULL; ++ rsp = bt->stkptr; ++ if (!rsp) { ++ error(INFO, "cannot determine starting stack pointer\n"); ++ return; ++ } ++ if (BT_REFERENCE_CHECK(bt)) ++ ofp = pc->nullfp; ++ else ++ ofp = fp; + +- if (retaddr) +- *retaddr = addr; ++ while ((estack = x86_64_in_exception_stack_hyper(bt->task, rsp))) { ++ bt->flags |= BT_EXCEPTION_STACK; ++ bt->stackbase = estack; ++ bt->stacktop = estack + EXCEPTION_STACKSIZE_HYPER; ++ bt->stackbuf = ebuf; + +- if (!buf) +- return NULL; ++ if (!readmem(bt->stackbase, KVADDR, bt->stackbuf, ++ bt->stacktop - bt->stackbase, "exception stack contents", ++ RETURN_ON_ERROR)) ++ error(FATAL, "read of exception stack at %lx failed\n", ++ bt->stackbase); + +- value_to_symstr(addr, locbuf, 0); +- if (strlen(locbuf)) +- sprintf(buf, locbuf); +- else { +- sprintf(buf, "%016lx", addr); +- if (kvtop(NULL, addr, &phys, 0)) { +- addr = machdep->kvbase + (ulong)phys; +- if (value_to_symstr(addr, locbuf, 0)) { +- strcat(buf, " <"); +- strcat(buf, locbuf); +- strcat(buf, ">"); +- } +- } +- } ++ stacktop = bt->stacktop - 168; + +- return buf; +-} ++ for (i = (rsp - bt->stackbase)/sizeof(ulong); ++ !done && (rsp < stacktop); i++, rsp += sizeof(ulong)) { ++ ++ up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); + +-/* +- * Filter disassembly output if the output radix is not gdb's default 10 +- */ +-static int +-x86_64_dis_filter(ulong vaddr, char *inbuf) +-{ +- char buf1[BUFSIZE]; +- char buf2[BUFSIZE]; +- char *colon, *p1; +- int argc; +- char *argv[MAXARGS]; +- ulong value; ++ if (!is_kernel_text(*up)) ++ continue; + +- if (!inbuf) +- return TRUE; +-/* +- * For some reason gdb can go off into the weeds translating text addresses, +- * (on alpha -- not necessarily seen on x86_64) so this routine both fixes the +- * references as well as imposing the current output radix on the translations. +- */ +- console("IN: %s", inbuf); ++ switch (x86_64_print_stack_entry_hyper(bt, ofp, level, i,*up)) ++ { ++ case BACKTRACE_ENTRY_DISPLAYED: ++ level++; ++ break; ++ case BACKTRACE_ENTRY_IGNORED: ++ break; ++ case BACKTRACE_COMPLETE: ++ done = TRUE; ++ break; ++ } ++ } + +- colon = strstr(inbuf, ":"); ++ if (!BT_REFERENCE_CHECK(bt)) ++ x86_64_print_eframe_regs_hyper(bt); + +- if (colon) { +- sprintf(buf1, "0x%lx <%s>", vaddr, +- value_to_symstr(vaddr, buf2, pc->output_radix)); +- sprintf(buf2, "%s%s", buf1, colon); +- strcpy(inbuf, buf2); ++ up = (ulong *)(&bt->stackbuf[bt->stacktop - bt->stackbase]); ++ up -= 2; ++ rsp = bt->stkptr = *up; ++ up -= 3; ++ bt->instptr = *up; ++ done = FALSE; ++ bt->frameptr = 0; + } + +- strcpy(buf1, inbuf); +- argc = parse_line(buf1, argv); ++ if (bt->flags & BT_EXCEPTION_STACK) { ++ bt->flags &= ~BT_EXCEPTION_STACK; ++ bt->stackbase = bt_in->stackbase; ++ bt->stacktop = bt_in->stacktop; ++ bt->stackbuf = bt_in->stackbuf; ++ } + +- if ((FIRSTCHAR(argv[argc-1]) == '<') && +- (LASTCHAR(argv[argc-1]) == '>')) { +- p1 = rindex(inbuf, '<'); +- while ((p1 > inbuf) && !STRNEQ(p1, " 0x")) +- p1--; ++ for (i = (rsp - bt->stackbase)/sizeof(ulong); ++ !done && (rsp < bt->stacktop); i++, rsp += sizeof(ulong)) { + +- if (!STRNEQ(p1, " 0x")) +- return FALSE; +- p1++; ++ up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); + +- if (!extract_hex(p1, &value, NULLCHAR, TRUE)) +- return FALSE; ++ if (!is_kernel_text(*up)) ++ continue; + +- sprintf(buf1, "0x%lx <%s>\n", value, +- value_to_symstr(value, buf2, pc->output_radix)); ++ switch (x86_64_print_stack_entry_hyper(bt, ofp, level, i,*up)) ++ { ++ case BACKTRACE_ENTRY_DISPLAYED: ++ level++; ++ break; ++ case BACKTRACE_ENTRY_IGNORED: ++ break; ++ case BACKTRACE_COMPLETE: ++ done = TRUE; ++ break; ++ } ++ } ++} + +- sprintf(p1, buf1); +- +- } else if (STREQ(argv[argc-2], "callq") && +- hexadecimal(argv[argc-1], 0)) { +- /* +- * Update module code of the form: +- * +- * callq 0xffffffffa0017aa0 +- * +- * to show a bracketed direct call target. +- */ +- p1 = &LASTCHAR(inbuf); ++static void ++x86_64_init_hyper(int when) ++{ ++ switch (when) ++ { ++ case PRE_SYMTAB: ++ machdep->verify_symbol = x86_64_verify_symbol; ++ machdep->machspec = &x86_64_machine_specific; ++ if (pc->flags & KERNEL_DEBUG_QUERY) ++ return; ++ machdep->pagesize = memory_page_size(); ++ machdep->pageshift = ffs(machdep->pagesize) - 1; ++ machdep->pageoffset = machdep->pagesize - 1; ++ machdep->pagemask = ~((ulonglong)machdep->pageoffset); ++ machdep->stacksize = machdep->pagesize * 2; ++ if ((machdep->machspec->upml = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc upml space."); ++ if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pgd space."); ++ if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pmd space."); ++ if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc ptbl space."); ++ if ((machdep->machspec->pml4 = ++ (char *)malloc(PAGESIZE()*2)) == NULL) ++ error(FATAL, "cannot malloc pml4 space."); ++ machdep->machspec->last_upml_read = 0; ++ machdep->machspec->last_pml4_read = 0; ++ machdep->last_pgd_read = 0; ++ machdep->last_pmd_read = 0; ++ machdep->last_ptbl_read = 0; ++ machdep->verify_paddr = generic_verify_paddr; ++ machdep->ptrs_per_pgd = PTRS_PER_PGD; ++ if (machdep->cmdline_arg) ++ parse_cmdline_arg(); ++ break; + +- if (extract_hex(argv[argc-1], &value, NULLCHAR, TRUE)) { +- sprintf(buf1, " <%s>\n", +- value_to_symstr(value, buf2, +- pc->output_radix)); +- if (IS_MODULE_VADDR(value) && +- !strstr(buf2, "+")) +- sprintf(p1, buf1); +- } +- } ++ case PRE_GDB: ++ machdep->machspec->page_offset = PAGE_OFFSET_XEN_HYPER; ++ machdep->kvbase = (ulong)HYPERVISOR_VIRT_START; ++ machdep->identity_map_base = (ulong)PAGE_OFFSET_XEN_HYPER; ++ machdep->is_kvaddr = x86_64_is_kvaddr_hyper; ++ machdep->is_uvaddr = x86_64_is_uvaddr; ++ machdep->eframe_search = x86_64_eframe_search; ++ machdep->back_trace = x86_64_simple_back_trace_cmd_hyper; ++ machdep->processor_speed = x86_64_processor_speed; ++ machdep->kvtop = x86_64_kvtop; ++ machdep->get_task_pgd = x86_64_get_task_pgd; ++ machdep->get_stack_frame = x86_64_get_stack_frame_hyper; ++ machdep->get_stackbase = x86_64_get_stackbase_hyper; ++ machdep->get_stacktop = x86_64_get_stacktop_hyper; ++ machdep->translate_pte = x86_64_translate_pte; ++ machdep->memory_size = xen_hyper_x86_memory_size; /* KAK add */ ++ machdep->is_task_addr = x86_64_is_task_addr; ++ machdep->dis_filter = x86_64_dis_filter; ++ machdep->cmd_mach = x86_64_cmd_mach; ++ machdep->get_smp_cpus = xen_hyper_x86_get_smp_cpus; /* KAK add */ ++ machdep->line_number_hooks = x86_64_line_number_hooks; ++ machdep->value_to_symbol = generic_machdep_value_to_symbol; ++ machdep->init_kernel_pgd = x86_64_init_kernel_pgd; ++ machdep->clear_machdep_cache = x86_64_clear_machdep_cache; + +- console(" %s", inbuf); ++ /* machdep table for Xen Hypervisor */ ++ xhmachdep->pcpu_init = xen_hyper_x86_pcpu_init; ++ break; ++ ++ case POST_GDB: ++ XEN_HYPER_STRUCT_SIZE_INIT(cpuinfo_x86, "cpuinfo_x86"); ++ XEN_HYPER_STRUCT_SIZE_INIT(tss_struct, "tss_struct"); ++ XEN_HYPER_ASSIGN_OFFSET(tss_struct_rsp0) = MEMBER_OFFSET("tss_struct", "__blh") + sizeof(short unsigned int); ++ XEN_HYPER_MEMBER_OFFSET_INIT(tss_struct_ist, "tss_struct", "ist"); ++ if (symbol_exists("cpu_data")) { ++ xht->cpu_data_address = symbol_value("cpu_data"); ++ } ++/* KAK Can this be calculated? */ ++ if (!machdep->hz) { ++ machdep->hz = XEN_HYPER_HZ; ++ } ++ break; + +- return TRUE; ++ case POST_INIT: ++ break; ++ } + } + + +-/* +- * Override smp_num_cpus if possible and necessary. +- */ +-int +-x86_64_get_smp_cpus(void) +-{ +- int i, cpus, nr_pda, cpunumber; +- char *cpu_pda_buf; +- ulong level4_pgt; ++struct framesize_cache { ++ ulong textaddr; ++ int framesize; ++}; + +- if (!VALID_STRUCT(x8664_pda)) +- return 1; ++static struct framesize_cache *x86_64_framesize_cache = NULL; ++static int framesize_cache_entries = 0; + +- cpu_pda_buf = GETBUF(SIZE(x8664_pda)); ++#define FRAMESIZE_QUERY (1) ++#define FRAMESIZE_ENTER (2) ++#define FRAMESIZE_DUMP (3) + +- if (!(nr_pda = get_array_length("cpu_pda", NULL, 0))) +- nr_pda = NR_CPUS; ++#define FRAMESIZE_CACHE_INCR (50) + +- for (i = cpus = 0; i < nr_pda; i++) { +- if (!CPU_PDA_READ(i, cpu_pda_buf)) +- break; +- level4_pgt = ULONG(cpu_pda_buf + OFFSET(x8664_pda_level4_pgt)); +- cpunumber = INT(cpu_pda_buf + OFFSET(x8664_pda_cpunumber)); +- if (!VALID_LEVEL4_PGT_ADDR(level4_pgt) || (cpunumber != cpus)) +- break; +- cpus++; +- } ++static int ++x86_64_framesize_cache_resize(void) ++{ ++ int i; ++ struct framesize_cache *new_fc, *fc; + +- FREEBUF(cpu_pda_buf); ++ if ((new_fc = realloc(x86_64_framesize_cache, ++ (framesize_cache_entries+FRAMESIZE_CACHE_INCR) * ++ sizeof(struct framesize_cache))) == NULL) { ++ error(INFO, "cannot realloc x86_64_framesize_cache space!\n"); ++ return FALSE; ++ } + +- return cpus; ++ fc = new_fc + framesize_cache_entries; ++ for (i = framesize_cache_entries; ++ i < (framesize_cache_entries+FRAMESIZE_CACHE_INCR); ++ fc++, i++) { ++ fc->textaddr = 0; ++ fc->framesize = 0; ++ } ++ ++ x86_64_framesize_cache = new_fc; ++ framesize_cache_entries += FRAMESIZE_CACHE_INCR; ++ ++ return TRUE; + } + +-/* +- * Machine dependent command. +- */ +-void +-x86_64_cmd_mach(void) ++static int ++x86_64_framesize_cache_func(int cmd, ulong textaddr, int *framesize) + { +- int c; ++ int i; ++ struct framesize_cache *fc; ++ char buf[BUFSIZE]; + +- while ((c = getopt(argcnt, args, "cm")) != EOF) { +- switch(c) +- { +- case 'c': +- x86_64_display_cpu_data(); +- return; ++ if (!x86_64_framesize_cache) { ++ framesize_cache_entries = FRAMESIZE_CACHE_INCR; ++ if ((x86_64_framesize_cache = calloc(framesize_cache_entries, ++ sizeof(struct framesize_cache))) == NULL) ++ error(FATAL, ++ "cannot calloc x86_64_framesize_cache space!\n"); ++ } + +- case 'm': +- x86_64_display_memmap(); +- return; ++ switch (cmd) ++ { ++ case FRAMESIZE_QUERY: ++ fc = &x86_64_framesize_cache[0]; ++ for (i = 0; i < framesize_cache_entries; i++, fc++) { ++ if (fc->textaddr == textaddr) { ++ *framesize = fc->framesize; ++ return TRUE; ++ } ++ } ++ return FALSE; + +- default: +- argerrs++; +- break; +- } +- } ++ case FRAMESIZE_ENTER: ++retry: ++ fc = &x86_64_framesize_cache[0]; ++ for (i = 0; i < framesize_cache_entries; i++, fc++) { ++ if ((fc->textaddr == 0) || ++ (fc->textaddr == textaddr)) { ++ fc->textaddr = textaddr; ++ fc->framesize = *framesize; ++ return fc->framesize; ++ } ++ } + +- if (argerrs) +- cmd_usage(pc->curcmd, SYNOPSIS); ++ if (x86_64_framesize_cache_resize()) ++ goto retry; + +- x86_64_display_machine_stats(); +-} ++ return *framesize; + +-/* +- * "mach" command output. +- */ +-static void +-x86_64_display_machine_stats(void) +-{ +- struct new_utsname *uts; +- char buf[BUFSIZE]; +- ulong mhz; ++ case FRAMESIZE_DUMP: ++ fc = &x86_64_framesize_cache[0]; ++ for (i = 0; i < framesize_cache_entries; i++, fc++) { ++ if (fc->textaddr == 0) { ++ if (i < (framesize_cache_entries-1)) { ++ fprintf(fp, "[%d-%d]: (unused)\n", ++ i, framesize_cache_entries-1); ++ } ++ break; ++ } + +- uts = &kt->utsname; ++ fprintf(fp, "[%3d]: %lx %3d (%s)\n", i, ++ fc->textaddr, fc->framesize, ++ value_to_symstr(fc->textaddr, buf, 0)); ++ } ++ break; ++ } + +- fprintf(fp, " MACHINE TYPE: %s\n", uts->machine); +- fprintf(fp, " MEMORY SIZE: %s\n", get_memory_size(buf)); +- fprintf(fp, " CPUS: %d\n", kt->cpus); +- fprintf(fp, " PROCESSOR SPEED: "); +- if ((mhz = machdep->processor_speed())) +- fprintf(fp, "%ld Mhz\n", mhz); +- else +- fprintf(fp, "(unknown)\n"); +- fprintf(fp, " HZ: %d\n", machdep->hz); +- fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); +- fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); +- fprintf(fp, "KERNEL VIRTUAL BASE: %lx\n", machdep->kvbase); +- fprintf(fp, "KERNEL VMALLOC BASE: %lx\n", vt->vmalloc_start); +- fprintf(fp, " KERNEL START MAP: %lx\n", __START_KERNEL_map); +- fprintf(fp, "KERNEL MODULES BASE: %lx\n", MODULES_VADDR); +- fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); ++ return TRUE; + } + +-/* +- * "mach -c" +- */ +-static void +-x86_64_display_cpu_data(void) ++#define BT_FRAMESIZE_IGNORE_MASK \ ++ (BT_OLD_BACK_TRACE|BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_ALL|BT_FRAMESIZE_DISABLE) ++ ++static int ++x86_64_get_framesize(struct bt_info *bt, ulong textaddr) + { +- int cpu, cpus, boot_cpu; +- ulong cpu_data; +- ulong cpu_pda; +- +- if (symbol_exists("cpu_data")) { +- cpu_data = symbol_value("cpu_data"); +- cpus = kt->cpus; +- boot_cpu = FALSE; +- } else if (symbol_exists("boot_cpu_data")) { +- cpu_data = symbol_value("boot_cpu_data"); +- boot_cpu = TRUE; +- cpus = 1; ++ int c, framesize, instr, arg; ++ struct syment *sp; ++ long max_instructions; ++ ulong offset; ++ char buf[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ ulong locking_func, textaddr_save, current; ++ char *p1, *p2; ++ int reterror; ++ ++ if (!(bt->flags & BT_FRAMESIZE_DEBUG)) { ++ if ((bt->flags & BT_FRAMESIZE_IGNORE_MASK) || ++ (kt->flags & USE_OLD_BT)) ++ return 0; ++ } ++ ++ if (!(sp = value_search(textaddr, &offset))) { ++ if (!(bt->flags & BT_FRAMESIZE_DEBUG)) ++ bt->flags |= BT_FRAMESIZE_DISABLE; ++ return 0; ++ } ++ ++ if (!(bt->flags & BT_FRAMESIZE_DEBUG) && ++ x86_64_framesize_cache_func(FRAMESIZE_QUERY, textaddr, &framesize)) { ++ if (framesize == -1) ++ bt->flags |= BT_FRAMESIZE_DISABLE; ++ return framesize; + } +- cpu_pda = symbol_value("cpu_pda"); +- +- for (cpu = 0; cpu < cpus; cpu++) { +- if (boot_cpu) +- fprintf(fp, "BOOT CPU:\n"); +- else +- fprintf(fp, "%sCPU %d:\n", cpu ? "\n" : "", cpu); + +- dump_struct("cpuinfo_x86", cpu_data, 0); +- fprintf(fp, "\n"); +- dump_struct("x8664_pda", cpu_pda, 0); +- +- cpu_data += SIZE(cpuinfo_x86); +- cpu_pda += SIZE(x8664_pda); +- } +-} ++ /* ++ * Bait and switch an incoming .text.lock address ++ * with the containing function's address. ++ */ ++ if (STRNEQ(sp->name, ".text.lock.") && ++ (locking_func = text_lock_function(sp->name, bt, textaddr))) { ++ if (!(sp = value_search(locking_func, &offset))) { ++ bt->flags |= BT_FRAMESIZE_DISABLE; ++ return 0; ++ } ++ textaddr_save = textaddr; ++ textaddr = locking_func; ++ } else ++ textaddr_save = 0; + +-/* +- * "mach -m" +- */ +-static char *e820type[] = { +- "(invalid type)", +- "E820_RAM", +- "E820_RESERVED", +- "E820_ACPI", +- "E820_NVS", +-}; ++ framesize = 0; ++ max_instructions = textaddr - sp->value; ++ instr = arg = -1; + +-static void +-x86_64_display_memmap(void) +-{ +- ulong e820; +- int nr_map, i; +- char *buf, *e820entry_ptr; +- ulonglong addr, size; +- uint type; ++ open_tmpfile2(); + +- e820 = symbol_value("e820"); +- if (CRASHDEBUG(1)) +- dump_struct("e820map", e820, RADIX(16)); +- buf = (char *)GETBUF(SIZE(e820map)); ++ sprintf(buf, "x/%ldi 0x%lx", ++ max_instructions, sp->value); + +- readmem(e820, KVADDR, &buf[0], SIZE(e820map), +- "e820map", FAULT_ON_ERROR); ++ if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { ++ close_tmpfile2(); ++ bt->flags |= BT_FRAMESIZE_DISABLE; ++ return 0; ++ } + +- nr_map = INT(buf + OFFSET(e820map_nr_map)); ++ rewind(pc->tmpfile2); ++ while (fgets(buf, BUFSIZE, pc->tmpfile2)) { ++ strcpy(buf2, buf); + +- fprintf(fp, " PHYSICAL ADDRESS RANGE TYPE\n"); ++ if (CRASHDEBUG(3)) ++ fprintf(pc->saved_fp, buf2); + +- for (i = 0; i < nr_map; i++) { +- e820entry_ptr = buf + sizeof(int) + (SIZE(e820entry) * i); +- addr = ULONGLONG(e820entry_ptr + OFFSET(e820entry_addr)); +- size = ULONGLONG(e820entry_ptr + OFFSET(e820entry_size)); +- type = UINT(e820entry_ptr + OFFSET(e820entry_type)); +- fprintf(fp, "%016llx - %016llx %s\n", addr, addr+size, +- e820type[type]); +- } +-} ++ c = parse_line(buf, arglist); + ++ if (instr == -1) { ++ /* ++ * Check whether are ++ * in the output string. ++ */ ++ if (LASTCHAR(arglist[0]) == ':') { ++ instr = 1; ++ arg = 2; ++ } else { ++ instr = 2; ++ arg = 3; ++ } ++ } + +-static const char *hook_files[] = { +- "arch/x86_64/kernel/entry.S", +- "arch/x86_64/kernel/head.S", +- "arch/x86_64/kernel/semaphore.c" +-}; ++ if (c < (arg+1)) ++ continue; + +-#define ENTRY_S ((char **)&hook_files[0]) +-#define HEAD_S ((char **)&hook_files[1]) +-#define SEMAPHORE_C ((char **)&hook_files[2]) ++ reterror = 0; ++ current = htol(strip_ending_char(arglist[0], ':'), ++ RETURN_ON_ERROR, &reterror); ++ if (reterror) ++ continue; ++ if (current >= textaddr) ++ break; + +-static struct line_number_hook x86_64_line_number_hooks[] = { +- {"ret_from_fork", ENTRY_S}, +- {"system_call", ENTRY_S}, +- {"int_ret_from_sys_call", ENTRY_S}, +- {"ptregscall_common", ENTRY_S}, +- {"stub_execve", ENTRY_S}, +- {"stub_rt_sigreturn", ENTRY_S}, +- {"common_interrupt", ENTRY_S}, +- {"ret_from_intr", ENTRY_S}, +- {"load_gs_index", ENTRY_S}, +- {"arch_kernel_thread", ENTRY_S}, +- {"execve", ENTRY_S}, +- {"page_fault", ENTRY_S}, +- {"coprocessor_error", ENTRY_S}, +- {"simd_coprocessor_error", ENTRY_S}, +- {"device_not_available", ENTRY_S}, +- {"debug", ENTRY_S}, +- {"nmi", ENTRY_S}, +- {"int3", ENTRY_S}, +- {"overflow", ENTRY_S}, +- {"bounds", ENTRY_S}, +- {"invalid_op", ENTRY_S}, +- {"coprocessor_segment_overrun", ENTRY_S}, +- {"reserved", ENTRY_S}, +- {"double_fault", ENTRY_S}, +- {"invalid_TSS", ENTRY_S}, +- {"segment_not_present", ENTRY_S}, +- {"stack_segment", ENTRY_S}, +- {"general_protection", ENTRY_S}, +- {"alignment_check", ENTRY_S}, +- {"divide_error", ENTRY_S}, +- {"spurious_interrupt_bug", ENTRY_S}, +- {"machine_check", ENTRY_S}, +- {"call_debug", ENTRY_S}, ++ if (STRNEQ(arglist[instr], "push")) { ++ framesize += 8; ++ if (CRASHDEBUG(2) || (bt->flags & BT_FRAMESIZE_DEBUG)) ++ fprintf(pc->saved_fp, "%s\t[framesize: %d]\n", ++ strip_linefeeds(buf2), framesize); ++ } else if (STRNEQ(arglist[instr], "pop")) { ++ framesize -= 8; ++ if (CRASHDEBUG(2) || (bt->flags & BT_FRAMESIZE_DEBUG)) ++ fprintf(pc->saved_fp, "%s\t[framesize: %d]\n", ++ strip_linefeeds(buf2), framesize); ++ } else if (STRNEQ(arglist[instr], "add") && ++ (p1 = strstr(arglist[arg], ",%rsp"))) { ++ *p1 = NULLCHAR; ++ p2 = arglist[arg]; ++ reterror = 0; ++ offset = htol(p2+1, RETURN_ON_ERROR, &reterror); ++ if (reterror) ++ continue; ++ framesize -= offset; ++ if (CRASHDEBUG(2) || (bt->flags & BT_FRAMESIZE_DEBUG)) ++ fprintf(pc->saved_fp, "%s\t[framesize: %d]\n", ++ strip_linefeeds(buf2), framesize); ++ } else if (STRNEQ(arglist[instr], "sub") && ++ (p1 = strstr(arglist[arg], ",%rsp"))) { ++ *p1 = NULLCHAR; ++ p2 = arglist[arg]; ++ reterror = 0; ++ offset = htol(p2+1, RETURN_ON_ERROR, &reterror); ++ if (reterror) ++ continue; ++ framesize += offset; ++ if (CRASHDEBUG(2) || (bt->flags & BT_FRAMESIZE_DEBUG)) ++ fprintf(pc->saved_fp, "%s\t[framesize: %d]\n", ++ strip_linefeeds(buf2), framesize); ++ } else if (STRNEQ(arglist[instr], "retq")) { ++ bt->flags |= BT_FRAMESIZE_DISABLE; ++ framesize = -1; ++ if (CRASHDEBUG(2) || (bt->flags & BT_FRAMESIZE_DEBUG)) ++ fprintf(pc->saved_fp, "%s\t[framesize: DISABLED]\n", ++ strip_linefeeds(buf2)); ++ break; ++ } ++ } ++ close_tmpfile2(); + +- {NULL, NULL} /* list must be NULL-terminated */ +-}; ++ if (textaddr_save) ++ textaddr = textaddr_save; + +-static void +-x86_64_dump_line_number(ulong callpc) +-{ +- error(FATAL, "x86_64_dump_line_number: TBD\n"); ++ return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, textaddr, &framesize)); + } + +-void +-x86_64_compiler_warning_stub(void) ++static void ++x86_64_framesize_debug(struct bt_info *bt) + { +- struct line_number_hook *lhp; +- char **p; ++ int framesize; + +- lhp = &x86_64_line_number_hooks[0]; lhp++; +- p = ENTRY_S; +- x86_64_back_trace(NULL, NULL); +- get_x86_64_frame(NULL, NULL, NULL); +- x86_64_dump_line_number(0); +-} ++ switch (bt->hp->esp) ++ { ++ case 1: /* "dump" */ ++ if (bt->hp->eip) { ++ framesize = 1; ++ x86_64_framesize_cache_func(FRAMESIZE_ENTER, bt->hp->eip, ++ &framesize); ++ } else ++ x86_64_framesize_cache_func(FRAMESIZE_DUMP, 0, NULL); ++ break; ++ ++ case 0: ++ if (bt->hp->eip) { ++ framesize = 0; ++ x86_64_framesize_cache_func(FRAMESIZE_ENTER, bt->hp->eip, ++ &framesize); ++ } else /* "clear" */ ++ BZERO(&x86_64_framesize_cache[0], ++ sizeof(struct framesize_cache)*framesize_cache_entries); ++ break; ++ ++ case -1: ++ if (!bt->hp->eip) ++ error(INFO, "x86_64_framesize_debug: ignoring command\n"); ++ else ++ x86_64_get_framesize(bt, bt->hp->eip); ++ break; + ++ default: ++ if (bt->hp->esp > 1) { ++ framesize = bt->hp->esp; ++ if (bt->hp->eip) ++ x86_64_framesize_cache_func(FRAMESIZE_ENTER, bt->hp->eip, ++ &framesize); ++ } else ++ error(INFO, "x86_64_framesize_debug: ignoring command\n"); ++ break; ++ } ++} + #endif /* X86_64 */ +--- crash/diskdump.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/diskdump.c 2009-01-15 16:17:17.000000000 -0500 +@@ -1,16 +1,16 @@ + /* + * diskdump.c + * +- * NOTE: The Red Hat diskdump module currently creates +- * vmcore dumpfiles that are identical to those made +- * by the Red Hat netdump module, and therefore the +- * dumpfile is recognized as such. But just in case +- * there's ever a divergence, this file is being kept +- * in place, along with the DISKDUMP-related #define's +- * and their usage throughout the crash sources. ++ * The diskdump module optionally creates either ELF vmcore ++ * dumpfiles, or compressed dumpfiles derived from the LKCD format. ++ * In the case of ELF vmcore files, since they are identical to ++ * netdump dumpfiles, the facilities in netdump.c are used. For ++ * compressed dumpfiles, the facilities in this file are used. + * +- * Copyright (C) 2004, 2005 David Anderson +- * Copyright (C) 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004, 2005, 2006, 2007, 2008 David Anderson ++ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2005 FUJITSU LIMITED ++ * Copyright (C) 2005 NEC Corporation + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. +@@ -18,22 +18,295 @@ + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * Author: David Anderson + */ + + #include "defs.h" + #include "diskdump.h" + ++#define BITMAP_SECT_LEN 4096 ++ + struct diskdump_data { + ulong flags; /* DISKDUMP_LOCAL, plus anything else... */ + int dfd; /* dumpfile file descriptor */ + FILE *ofp; /* fprintf(dd->ofp, "xxx"); */ + int machine_type; /* machine type identifier */ ++ ++ /* header */ ++ struct disk_dump_header *header; ++ struct disk_dump_sub_header *sub_header; ++ struct kdump_sub_header *sub_header_kdump; ++ ++ size_t data_offset; ++ int block_size; ++ int block_shift; ++ char *bitmap; ++ int bitmap_len; ++ char *dumpable_bitmap; ++ int byte, bit; ++ char *compressed_page; /* copy of compressed page data */ ++ char *curbufptr; /* ptr to uncompressed page buffer */ ++ ++ /* page cache */ ++ struct page_cache_hdr { /* header for each cached page */ ++ uint32_t pg_flags; ++ uint64_t pg_addr; ++ char *pg_bufptr; ++ ulong pg_hit_count; ++ } page_cache_hdr[DISKDUMP_CACHED_PAGES]; ++ char *page_cache_buf; /* base of cached buffer pages */ ++ int evict_index; /* next page to evict */ ++ ulong evictions; /* total evictions done */ ++ ulong cached_reads; ++ ulong *valid_pages; ++ ulong accesses; + }; + + static struct diskdump_data diskdump_data = { 0 }; + static struct diskdump_data *dd = &diskdump_data; ++static int get_dump_level(void); ++ ++ulong *diskdump_flags = &diskdump_data.flags; ++ ++static inline int get_bit(char *map, int byte, int bit) ++{ ++ return map[byte] & (1<bitmap, nr >> 3, nr & 7); ++} ++ ++static inline int page_is_dumpable(unsigned int nr) ++{ ++ return dd->dumpable_bitmap[nr>>3] & (1 << (nr & 7)); ++} ++ ++static inline int dump_is_partial(const struct disk_dump_header *header) ++{ ++ return header->bitmap_blocks >= ++ divideup(divideup(header->max_mapnr, 8), dd->block_size) * 2; ++} ++ ++static int open_dump_file(char *file) ++{ ++ int fd; ++ ++ fd = open(file, O_RDONLY); ++ if (fd < 0) { ++ error(INFO, "diskdump / compressed kdump: unable to open dump file %s", file); ++ return FALSE; ++ } ++ dd->dfd = fd; ++ return TRUE; ++} ++ ++static int read_dump_header(char *file) ++{ ++ struct disk_dump_header *header = NULL; ++ struct disk_dump_sub_header *sub_header = NULL; ++ struct kdump_sub_header *sub_header_kdump = NULL; ++ int bitmap_len; ++ int block_size = (int)sysconf(_SC_PAGESIZE); ++ off_t offset; ++ const off_t failed = (off_t)-1; ++ ulong pfn; ++ int i, j, max_sect_len; ++ ++ if (block_size < 0) ++ return FALSE; ++ ++restart: ++ if ((header = realloc(header, block_size)) == NULL) ++ error(FATAL, "diskdump / compressed kdump: cannot malloc block_size buffer\n"); ++ ++ if (lseek(dd->dfd, 0, SEEK_SET) == failed) { ++ if (CRASHDEBUG(1)) ++ error(INFO, "diskdump / compressed kdump: cannot lseek dump header\n"); ++ goto err; ++ } ++ ++ if (read(dd->dfd, header, block_size) < block_size) { ++ if (CRASHDEBUG(1)) ++ error(INFO, "diskdump / compressed kdump: cannot read dump header\n"); ++ goto err; ++ } ++ ++ /* validate dump header */ ++ if (!memcmp(header->signature, DISK_DUMP_SIGNATURE, ++ sizeof(header->signature))) { ++ dd->flags |= DISKDUMP_LOCAL; ++ } else if (!memcmp(header->signature, KDUMP_SIGNATURE, ++ sizeof(header->signature))) { ++ dd->flags |= KDUMP_CMPRS_LOCAL; ++ if (header->header_version >= 1) ++ dd->flags |= ERROR_EXCLUDED; ++ } else { ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "diskdump / compressed kdump: dump does not have panic dump header\n"); ++ goto err; ++ } ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "%s: header->utsname.machine: %s\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump", ++ header->utsname.machine); ++ ++ if (STRNEQ(header->utsname.machine, "i686") && ++ machine_type_mismatch(file, "X86", NULL, 0)) ++ goto err; ++ else if (STRNEQ(header->utsname.machine, "x86_64") && ++ machine_type_mismatch(file, "X86_64", NULL, 0)) ++ goto err; ++ else if (STRNEQ(header->utsname.machine, "ia64") && ++ machine_type_mismatch(file, "IA64", NULL, 0)) ++ goto err; ++ else if (STRNEQ(header->utsname.machine, "ppc64") && ++ machine_type_mismatch(file, "PPC64", NULL, 0)) ++ goto err; ++ ++ if (header->block_size != block_size) { ++ block_size = header->block_size; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "retrying with different block/page size: %d\n", ++ header->block_size); ++ goto restart; ++ } ++ dd->block_size = header->block_size; ++ dd->block_shift = ffs(header->block_size) - 1; ++ ++ if (sizeof(*header) + sizeof(void *) * header->nr_cpus > block_size || ++ header->nr_cpus <= 0) { ++ error(INFO, "%s: invalid nr_cpus value: %d\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump", ++ header->nr_cpus); ++ goto err; ++ } ++ ++ /* read sub header */ ++ offset = (off_t)block_size; ++ if (lseek(dd->dfd, offset, SEEK_SET) == failed) { ++ error(INFO, "%s: cannot lseek dump sub header\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); ++ ++ goto err; ++ } ++ ++ if (DISKDUMP_VALID()) { ++ if ((sub_header = malloc(block_size)) == NULL) ++ error(FATAL, "diskdump: cannot malloc sub_header buffer\n"); ++ ++ if (read(dd->dfd, sub_header, block_size) ++ < block_size) { ++ error(INFO, "diskdump: cannot read dump sub header\n"); ++ goto err; ++ } ++ dd->sub_header = sub_header; ++ } else if (KDUMP_CMPRS_VALID()) { ++ if ((sub_header_kdump = malloc(block_size)) == NULL) ++ error(FATAL, "compressed kdump: cannot malloc sub_header_kdump buffer\n"); ++ ++ if (read(dd->dfd, sub_header_kdump, block_size) ++ < block_size) { ++ error(INFO, "compressed kdump: cannot read dump sub header\n"); ++ goto err; ++ } ++ dd->sub_header_kdump = sub_header_kdump; ++ } ++ ++ /* read memory bitmap */ ++ bitmap_len = block_size * header->bitmap_blocks; ++ dd->bitmap_len = bitmap_len; ++ ++ offset = (off_t)block_size * (1 + header->sub_hdr_size); ++ if (lseek(dd->dfd, offset, SEEK_SET) == failed) { ++ error(INFO, "%s: cannot lseek memory bitmap\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); ++ ++ goto err; ++ } ++ ++ if ((dd->bitmap = malloc(bitmap_len)) == NULL) ++ error(FATAL, "%s: cannot malloc bitmap buffer\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); ++ ++ dd->dumpable_bitmap = calloc(bitmap_len, 1); ++ if (read(dd->dfd, dd->bitmap, bitmap_len) < bitmap_len) { ++ error(INFO, "%s: cannot read memory bitmap\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); ++ goto err; ++ } ++ ++ if (dump_is_partial(header)) ++ memcpy(dd->dumpable_bitmap, dd->bitmap + bitmap_len/2, ++ bitmap_len/2); ++ else ++ memcpy(dd->dumpable_bitmap, dd->bitmap, bitmap_len); ++ ++ dd->data_offset ++ = (1 + header->sub_hdr_size + header->bitmap_blocks) ++ * header->block_size; ++ ++ dd->header = header; ++ ++ if (machine_type("X86")) ++ dd->machine_type = EM_386; ++ else if (machine_type("X86_64")) ++ dd->machine_type = EM_X86_64; ++ else if (machine_type("IA64")) ++ dd->machine_type = EM_IA_64; ++ else if (machine_type("PPC64")) ++ dd->machine_type = EM_PPC64; ++ else { ++ error(INFO, "%s: unsupported machine type: %s\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump", ++ MACHINE_TYPE); ++ goto err; ++ } ++ ++ max_sect_len = divideup(header->max_mapnr, BITMAP_SECT_LEN); ++ ++ dd->valid_pages = calloc(sizeof(ulong), max_sect_len + 1); ++ pfn = 0; ++ for (i = 1; i < max_sect_len + 1; i++) { ++ dd->valid_pages[i] = dd->valid_pages[i - 1]; ++ for (j = 0; j < BITMAP_SECT_LEN; j++, pfn++) ++ if (page_is_dumpable(pfn)) ++ dd->valid_pages[i]++; ++ } ++ ++ return TRUE; ++ ++err: ++ free(header); ++ if (sub_header) ++ free(sub_header); ++ if (sub_header_kdump) ++ free(sub_header_kdump); ++ if (dd->bitmap) ++ free(dd->bitmap); ++ if (dd->dumpable_bitmap) ++ free(dd->dumpable_bitmap); ++ dd->flags &= ~(DISKDUMP_LOCAL|KDUMP_CMPRS_LOCAL); ++ return FALSE; ++} ++ ++static int ++pfn_to_pos(ulong pfn) ++{ ++ int desc_pos, j, valid; ++ ++ valid = dd->valid_pages[pfn / BITMAP_SECT_LEN]; ++ ++ for (j = round(pfn, BITMAP_SECT_LEN), desc_pos = valid; j <= pfn; j++) ++ if (page_is_dumpable(j)) ++ desc_pos++; ++ ++ return desc_pos; ++} ++ + + /* + * Determine whether a file is a diskdump creation, and if TRUE, +@@ -43,7 +316,28 @@ + int + is_diskdump(char *file) + { +- return FALSE; ++ int sz, i; ++ ++ if (!open_dump_file(file) || !read_dump_header(file)) ++ return FALSE; ++ ++ sz = dd->block_size * (DISKDUMP_CACHED_PAGES); ++ if ((dd->page_cache_buf = malloc(sz)) == NULL) ++ error(FATAL, "%s: cannot malloc compressed page_cache_buf\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); ++ ++ for (i = 0; i < DISKDUMP_CACHED_PAGES; i++) ++ dd->page_cache_hdr[i].pg_bufptr = ++ &dd->page_cache_buf[i * dd->block_size]; ++ ++ if ((dd->compressed_page = (char *)malloc(dd->block_size)) == NULL) ++ error(FATAL, "%s: cannot malloc compressed page space\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); ++ ++ if (CRASHDEBUG(1)) ++ diskdump_memory_dump(fp); ++ ++ return TRUE; + } + + /* +@@ -53,11 +347,141 @@ + int + diskdump_init(char *unused, FILE *fptr) + { +- if (!DISKDUMP_VALID()) +- return FALSE; ++ if (!DISKDUMP_VALID() && !KDUMP_CMPRS_VALID()) ++ return FALSE; + +- dd->ofp = fptr; +- return TRUE; ++ dd->ofp = fptr; ++ return TRUE; ++} ++ ++/* ++ * Get the relocational offset from the sub header of kdump. ++ */ ++int ++diskdump_phys_base(unsigned long *phys_base) ++{ ++ if (KDUMP_CMPRS_VALID()) { ++ *phys_base = dd->sub_header_kdump->phys_base; ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++/* ++ * Check whether paddr is already cached. ++ */ ++static int ++page_is_cached(physaddr_t paddr) ++{ ++ int i; ++ struct page_cache_hdr *pgc; ++ ++ dd->accesses++; ++ ++ for (i = 0; i < DISKDUMP_CACHED_PAGES; i++) { ++ ++ pgc = &dd->page_cache_hdr[i]; ++ ++ if (!DISKDUMP_VALID_PAGE(pgc->pg_flags)) ++ continue; ++ ++ if (pgc->pg_addr == paddr) { ++ pgc->pg_hit_count++; ++ dd->curbufptr = pgc->pg_bufptr; ++ dd->cached_reads++; ++ return TRUE; ++ } ++ } ++ return FALSE; ++} ++ ++/* ++ * Cache the page's data. ++ * ++ * If an empty page cache location is available, take it. Otherwise, evict ++ * the entry indexed by evict_index, and then bump evict index. The hit_count ++ * is only gathered for dump_diskdump_environment(). ++ * ++ * If the page is compressed, uncompress it into the selected page cache entry. ++ * If the page is raw, just copy it into the selected page cache entry. ++ * If all works OK, update diskdump->curbufptr to point to the page's ++ * uncompressed data. ++ */ ++static int ++cache_page(physaddr_t paddr) ++{ ++ int i, ret; ++ int found; ++ ulong pfn; ++ int desc_pos; ++ off_t seek_offset; ++ page_desc_t pd; ++ const int block_size = dd->block_size; ++ const off_t failed = (off_t)-1; ++ ulong retlen; ++ ++ for (i = found = 0; i < DISKDUMP_CACHED_PAGES; i++) { ++ if (DISKDUMP_VALID_PAGE(dd->page_cache_hdr[i].pg_flags)) ++ continue; ++ found = TRUE; ++ break; ++ } ++ ++ if (!found) { ++ i = dd->evict_index; ++ dd->page_cache_hdr[i].pg_hit_count = 0; ++ dd->evict_index = ++ (dd->evict_index+1) % DISKDUMP_CACHED_PAGES; ++ dd->evictions++; ++ } ++ ++ dd->page_cache_hdr[i].pg_flags = 0; ++ dd->page_cache_hdr[i].pg_addr = paddr; ++ dd->page_cache_hdr[i].pg_hit_count++; ++ ++ /* find page descriptor */ ++ pfn = paddr >> dd->block_shift; ++ desc_pos = pfn_to_pos(pfn); ++ seek_offset = dd->data_offset ++ + (off_t)(desc_pos - 1)*sizeof(page_desc_t); ++ lseek(dd->dfd, seek_offset, SEEK_SET); ++ ++ /* read page descriptor */ ++ if (read(dd->dfd, &pd, sizeof(pd)) != sizeof(pd)) ++ return READ_ERROR; ++ ++ /* sanity check */ ++ if (pd.size > block_size) ++ return READ_ERROR; ++ ++ if (lseek(dd->dfd, pd.offset, SEEK_SET) == failed) ++ return SEEK_ERROR; ++ ++ /* read page data */ ++ if (read(dd->dfd, dd->compressed_page, pd.size) != pd.size) ++ return READ_ERROR; ++ ++ if (pd.flags & DUMP_DH_COMPRESSED) { ++ retlen = block_size; ++ ret = uncompress((unsigned char *)dd->page_cache_hdr[i].pg_bufptr, ++ &retlen, ++ (unsigned char *)dd->compressed_page, ++ pd.size); ++ if ((ret != Z_OK) || (retlen != block_size)) { ++ error(INFO, "%s: uncompress failed: %d\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump", ++ ret); ++ return READ_ERROR; ++ } ++ } else ++ memcpy(dd->page_cache_hdr[i].pg_bufptr, ++ dd->compressed_page, block_size); ++ ++ dd->page_cache_hdr[i].pg_flags |= PAGE_VALID; ++ dd->curbufptr = dd->page_cache_hdr[i].pg_bufptr; ++ ++ return TRUE; + } + + /* +@@ -66,7 +490,31 @@ + int + read_diskdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) + { +- return 0; ++ int ret; ++ physaddr_t curpaddr; ++ ulong pfn, page_offset; ++ ++ pfn = paddr >> dd->block_shift; ++ curpaddr = paddr & ~((physaddr_t)(dd->block_size-1)); ++ page_offset = paddr & ((physaddr_t)(dd->block_size-1)); ++ ++ if ((pfn >= dd->header->max_mapnr) || !page_is_ram(pfn)) ++ return SEEK_ERROR; ++ if (!page_is_dumpable(pfn)) { ++ if ((dd->flags & (ZERO_EXCLUDED|ERROR_EXCLUDED)) == ++ ERROR_EXCLUDED) ++ return PAGE_EXCLUDED; ++ memset(bufptr, 0, cnt); ++ return cnt; ++ } ++ ++ if (!page_is_cached(curpaddr)) ++ if ((ret = cache_page(curpaddr)) < 0) ++ return ret; ++ ++ memcpy(bufptr, dd->curbufptr + page_offset, cnt); ++ ++ return cnt; + } + + /* +@@ -81,7 +529,23 @@ + ulong + get_diskdump_panic_task(void) + { +- return NO_TASK; ++ if ((!DISKDUMP_VALID() && !KDUMP_CMPRS_VALID()) ++ || !get_active_set()) ++ return NO_TASK; ++ ++ return (ulong)dd->header->tasks[dd->header->current_cpu]; ++} ++ ++extern void get_netdump_regs_x86(struct bt_info *, ulong *, ulong *); ++extern void get_netdump_regs_x86_64(struct bt_info *, ulong *, ulong *); ++ ++static void ++get_diskdump_regs_ppc64(struct bt_info *bt, ulong *eip, ulong *esp) ++{ ++ if ((bt->task == tt->panic_task) && DISKDUMP_VALID()) ++ bt->machdep = &dd->sub_header->elf_regs; ++ ++ machdep->get_stack_frame(bt, eip, esp); + } + + /* +@@ -91,12 +555,35 @@ + void + get_diskdump_regs(struct bt_info *bt, ulong *eip, ulong *esp) + { +- switch (dd->machine_type) +- { +- default: +- error(FATAL, +- "diskdump support for this machine type is not available\n"); +- } ++ switch (dd->machine_type) ++ { ++ case EM_386: ++ return get_netdump_regs_x86(bt, eip, esp); ++ break; ++ ++ case EM_IA_64: ++ /* For normal backtraces, this information will be obtained ++ * frome the switch_stack structure, which is pointed to by ++ * the thread.ksp field of the task_struct. But it's still ++ * needed by the "bt -t" option. ++ */ ++ machdep->get_stack_frame(bt, eip, esp); ++ break; ++ ++ case EM_PPC64: ++ return get_diskdump_regs_ppc64(bt, eip, esp); ++ break; ++ ++ case EM_X86_64: ++ return get_netdump_regs_x86_64(bt, eip, esp); ++ break; ++ ++ default: ++ error(FATAL, "%s: unsupported machine type: %s\n", ++ DISKDUMP_VALID() ? "diskdump" : "compressed kdump", ++ MACHINE_TYPE); ++ ++ } + } + + /* +@@ -105,7 +592,10 @@ + uint + diskdump_page_size(void) + { +- return 0; ++ if (!DISKDUMP_VALID() && !KDUMP_CMPRS_VALID()) ++ return 0; ++ ++ return dd->header->block_size; + } + + /* +@@ -131,6 +621,197 @@ + int + diskdump_memory_dump(FILE *fp) + { ++ int i, others, dump_level; ++ struct disk_dump_header *dh; ++ struct disk_dump_sub_header *dsh; ++ struct kdump_sub_header *kdsh; ++ ulong *tasks; ++ ++ fprintf(fp, "diskdump_data: \n"); ++ fprintf(fp, " flags: %lx (", dd->flags); ++ others = 0; ++ if (dd->flags & DISKDUMP_LOCAL) ++ fprintf(fp, "%sDISKDUMP_LOCAL", others++ ? "|" : ""); ++ if (dd->flags & KDUMP_CMPRS_LOCAL) ++ fprintf(fp, "%sKDUMP_CMPRS_LOCAL", others++ ? "|" : ""); ++ if (dd->flags & ERROR_EXCLUDED) ++ fprintf(fp, "%sERROR_EXCLUDED", others++ ? "|" : ""); ++ if (dd->flags & ZERO_EXCLUDED) ++ fprintf(fp, "%sZERO_EXCLUDED", others++ ? "|" : ""); ++ fprintf(fp, ")\n"); ++ fprintf(fp, " dfd: %d\n", dd->dfd); ++ fprintf(fp, " ofp: %lx\n", (ulong)dd->ofp); ++ fprintf(fp, " machine_type: %d ", dd->machine_type); ++ switch (dd->machine_type) ++ { ++ case EM_386: ++ fprintf(fp, "(EM_386)\n"); break; ++ case EM_X86_64: ++ fprintf(fp, "(EM_X86_64)\n"); break; ++ case EM_IA_64: ++ fprintf(fp, "(EM_IA_64)\n"); break; ++ case EM_PPC64: ++ fprintf(fp, "(EM_PPC64)\n"); break; ++ default: ++ fprintf(fp, "(unknown)\n"); break; ++ } ++ ++ fprintf(fp, "\n header: %lx\n", (ulong)dd->header); ++ dh = dd->header; ++ fprintf(fp, " signature: \""); ++ for (i = 0; i < SIG_LEN; i++) ++ if (dh->signature[i]) ++ fprintf(fp, "%c", dh->signature[i]); ++ fprintf(fp, "\"\n"); ++ fprintf(fp, " header_version: %d\n", dh->header_version); ++ fprintf(fp, " utsname:\n"); ++ fprintf(fp, " sysname: %s\n", dh->utsname.sysname); ++ fprintf(fp, " nodename: %s\n", dh->utsname.nodename); ++ fprintf(fp, " release: %s\n", dh->utsname.release); ++ fprintf(fp, " version: %s\n", dh->utsname.version); ++ fprintf(fp, " machine: %s\n", dh->utsname.machine); ++ fprintf(fp, " domainname: %s\n", dh->utsname.domainname); ++ fprintf(fp, " timestamp:\n"); ++ fprintf(fp, " tv_sec: %lx\n", dh->timestamp.tv_sec); ++ fprintf(fp, " tv_usec: %lx\n", dh->timestamp.tv_usec); ++ fprintf(fp, " status: %x (", dh->status); ++ others = 0; ++ if (dh->status & DUMP_HEADER_COMPLETED) ++ fprintf(fp, "%sDUMP_HEADER_COMPLETED", others++ ? "|" : ""); ++ if (dh->status & DUMP_HEADER_INCOMPLETED) ++ fprintf(fp, "%sDUMP_HEADER_INCOMPLETED", others++ ? "|" : ""); ++ if (dh->status & DUMP_HEADER_COMPRESSED) ++ fprintf(fp, "%sDUMP_HEADER_COMPRESSED", others++ ? "|" : ""); ++ fprintf(fp, ")\n"); ++ fprintf(fp, " block_size: %d\n", dh->block_size); ++ fprintf(fp, " sub_hdr_size: %d\n", dh->sub_hdr_size); ++ fprintf(fp, " bitmap_blocks: %u\n", dh->bitmap_blocks); ++ fprintf(fp, " max_mapnr: %u\n", dh->max_mapnr); ++ fprintf(fp, " total_ram_blocks: %u\n", dh->total_ram_blocks); ++ fprintf(fp, " device_blocks: %u\n", dh->device_blocks); ++ fprintf(fp, " written_blocks: %u\n", dh->written_blocks); ++ fprintf(fp, " current_cpu: %u\n", dh->current_cpu); ++ fprintf(fp, " nr_cpus: %d\n", dh->nr_cpus); ++ tasks = (ulong *)&dh->tasks[0]; ++ fprintf(fp, " tasks[nr_cpus]: %lx\n", *tasks); ++ for (tasks++, i = 1; i < dh->nr_cpus; i++) { ++ fprintf(fp, " %lx\n", *tasks); ++ tasks++; ++ } ++ fprintf(fp, "\n"); ++ fprintf(fp, " sub_header: %lx ", (ulong)dd->sub_header); ++ if ((dsh = dd->sub_header)) { ++ fprintf(fp, "\n elf_regs: %lx\n", ++ (ulong)&dsh->elf_regs); ++ fprintf(fp, " dump_level: "); ++ if ((pc->flags & RUNTIME) && ++ ((dump_level = get_dump_level()) >= 0)) { ++ fprintf(fp, "%d (0x%x) %s", dump_level, dump_level, ++ dump_level ? "(" : ""); ++ ++#define DUMP_EXCLUDE_CACHE 0x00000001 /* Exclude LRU & SwapCache pages*/ ++#define DUMP_EXCLUDE_CLEAN 0x00000002 /* Exclude all-zero pages */ ++#define DUMP_EXCLUDE_FREE 0x00000004 /* Exclude free pages */ ++#define DUMP_EXCLUDE_ANON 0x00000008 /* Exclude Anon pages */ ++#define DUMP_SAVE_PRIVATE 0x00000010 /* Save private pages */ ++ ++ others = 0; ++ if (dump_level & DUMP_EXCLUDE_CACHE) ++ fprintf(fp, "%sDUMP_EXCLUDE_CACHE", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_EXCLUDE_CLEAN) ++ fprintf(fp, "%sDUMP_EXCLUDE_CLEAN", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_EXCLUDE_FREE) ++ fprintf(fp, "%sDUMP_EXCLUDE_FREE", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_EXCLUDE_ANON) ++ fprintf(fp, "%sDUMP_EXCLUDE_ANON", ++ others++ ? "|" : ""); ++ if (dump_level & DUMP_SAVE_PRIVATE) ++ fprintf(fp, "%sDUMP_SAVE_PRIVATE", ++ others++ ? "|" : ""); ++ fprintf(fp, "%s\n\n", dump_level ? ")" : ""); ++ } else ++ fprintf(fp, "%s\n\n", pc->flags & RUNTIME ? ++ "(unknown)" : "(undetermined)"); ++ ++ } else ++ fprintf(fp, "(n/a)\n\n"); ++ ++ fprintf(fp, " sub_header_kdump: %lx ", (ulong)dd->sub_header_kdump); ++ if ((kdsh = dd->sub_header_kdump)) { ++ fprintf(fp, "\n phys_base: %lx\n", ++ (ulong)kdsh->phys_base); ++ fprintf(fp, " dump_level: "); ++ if ((dump_level = get_dump_level()) >= 0) { ++ fprintf(fp, "%d (0x%x) %s", dump_level, dump_level, ++ dump_level ? "(" : ""); ++ ++#define DL_EXCLUDE_ZERO (0x001) /* Exclude Pages filled with Zeros */ ++#define DL_EXCLUDE_CACHE (0x002) /* Exclude Cache Pages without Private Pages */ ++#define DL_EXCLUDE_CACHE_PRI (0x004) /* Exclude Cache Pages with Private Pages */ ++#define DL_EXCLUDE_USER_DATA (0x008) /* Exclude UserProcessData Pages */ ++#define DL_EXCLUDE_FREE (0x010) /* Exclude Free Pages */ ++ ++ if (dump_level & DL_EXCLUDE_ZERO) ++ fprintf(fp, "%sDUMP_EXCLUDE_ZERO", ++ others++ ? "|" : ""); ++ if (dump_level & DL_EXCLUDE_CACHE) ++ fprintf(fp, "%sDUMP_EXCLUDE_CACHE", ++ others++ ? "|" : ""); ++ if (dump_level & DL_EXCLUDE_CACHE_PRI) ++ fprintf(fp, "%sDUMP_EXCLUDE_CACHE_PRI", ++ others++ ? "|" : ""); ++ if (dump_level & DL_EXCLUDE_USER_DATA) ++ fprintf(fp, "%sDUMP_EXCLUDE_USER_DATA", ++ others++ ? "|" : ""); ++ if (dump_level & DL_EXCLUDE_FREE) ++ fprintf(fp, "%sDUMP_EXCLUDE_FREE", ++ others++ ? "|" : ""); ++ others = 0; ++ ++ fprintf(fp, "%s\n\n", dump_level ? ")" : ""); ++ } else ++ fprintf(fp, "(unknown)\n\n"); ++ } else ++ fprintf(fp, "(n/a)\n\n"); ++ ++ fprintf(fp, " data_offset: %lx\n", (ulong)dd->data_offset); ++ fprintf(fp, " block_size: %d\n", dd->block_size); ++ fprintf(fp, " block_shift: %d\n", dd->block_shift); ++ fprintf(fp, " bitmap: %lx\n", (ulong)dd->bitmap); ++ fprintf(fp, " bitmap_len: %d\n", dd->bitmap_len); ++ fprintf(fp, " dumpable_bitmap: %lx\n", (ulong)dd->dumpable_bitmap); ++ fprintf(fp, " byte: %d\n", dd->byte); ++ fprintf(fp, " bit: %d\n", dd->bit); ++ fprintf(fp, " compressed_page: %lx\n", (ulong)dd->compressed_page); ++ fprintf(fp, " curbufptr: %lx\n\n", (ulong)dd->curbufptr); ++ ++ for (i = 0; i < DISKDUMP_CACHED_PAGES; i++) { ++ fprintf(fp, "%spage_cache_hdr[%d]:\n", i < 10 ? " " : "", i); ++ fprintf(fp, " pg_flags: %x (", dd->page_cache_hdr[i].pg_flags); ++ others = 0; ++ if (dd->page_cache_hdr[i].pg_flags & PAGE_VALID) ++ fprintf(fp, "%sPAGE_VALID", others++ ? "|" : ""); ++ fprintf(fp, ")\n"); ++ fprintf(fp, " pg_addr: %llx\n", (ulonglong)dd->page_cache_hdr[i].pg_addr); ++ fprintf(fp, " pg_bufptr: %lx\n", (ulong)dd->page_cache_hdr[i].pg_bufptr); ++ fprintf(fp, " pg_hit_count: %ld\n", dd->page_cache_hdr[i].pg_hit_count); ++ } ++ ++ fprintf(fp, "\n page_cache_buf: %lx\n", (ulong)dd->page_cache_buf); ++ fprintf(fp, " evict_index: %d\n", dd->evict_index); ++ fprintf(fp, " evictions: %ld\n", dd->evictions); ++ fprintf(fp, " accesses: %ld\n", dd->accesses); ++ fprintf(fp, " cached_reads: %ld ", dd->cached_reads); ++ if (dd->accesses) ++ fprintf(fp, "(%ld%%)\n", ++ dd->cached_reads * 100 / dd->accesses); ++ else ++ fprintf(fp, "\n"); ++ fprintf(fp, " valid_pages: %lx\n", (ulong)dd->valid_pages); ++ + return 0; + } + +@@ -142,3 +823,36 @@ + { + return 0; + } ++ ++/* ++ * Versions of disk_dump that support it contain the "dump_level" symbol. ++ * Version 1 and later compressed kdump dumpfiles contain the dump level ++ * in an additional field of the sub_header_kdump structure. ++ */ ++static int ++get_dump_level(void) ++{ ++ int dump_level; ++ ++ if (DISKDUMP_VALID()) { ++ if (symbol_exists("dump_level") && ++ readmem(symbol_value("dump_level"), KVADDR, &dump_level, ++ sizeof(dump_level), "dump_level", QUIET|RETURN_ON_ERROR)) ++ return dump_level; ++ } else if (KDUMP_CMPRS_VALID()) { ++ if (dd->header->header_version >= 1) ++ return dd->sub_header_kdump->dump_level; ++ } ++ ++ return -1; ++} ++ ++/* ++ * Used by the "sys" command to display [PARTIAL DUMP] ++ * after the dumpfile name. ++ */ ++int ++is_partial_diskdump(void) ++{ ++ return (get_dump_level() > 0 ? TRUE : FALSE); ++} +--- crash/net.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/net.c 2008-03-17 17:28:54.000000000 -0400 +@@ -1,8 +1,8 @@ + /* net.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 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 +@@ -50,6 +50,7 @@ + #define STRUCT_NET_DEVICE (0x4) + #define SOCK_V1 (0x8) + #define SOCK_V2 (0x10) ++#define NO_INET_SOCK (0x20) + + #define DEV_NAME_MAX 100 + struct devinfo { +@@ -64,6 +65,8 @@ + #define BYTES_IP_TUPLE (BYTES_IP_ADDR + BYTES_PORT_NUM + 1) + + static void show_net_devices(void); ++static void show_net_devices_v2(void); ++static void show_net_devices_v3(void); + static void print_neighbour_q(ulong, int); + static void get_netdev_info(ulong, struct devinfo *); + static void get_device_name(ulong, char *); +@@ -75,6 +78,7 @@ + static void dump_sockets(ulong, struct reference *); + static int sym_socket_dump(ulong, int, int, ulong, struct reference *); + static void dump_hw_addr(unsigned char *, int); ++static char *dump_in6_addr_port(uint16_t *, uint16_t, char *, int *); + + + #define MK_TYPE_T(f,s,m) \ +@@ -109,6 +113,8 @@ + "net_device", "addr_len"); + net->dev_ip_ptr = MEMBER_OFFSET_INIT(net_device_ip_ptr, + "net_device", "ip_ptr"); ++ MEMBER_OFFSET_INIT(net_device_dev_list, "net_device", "dev_list"); ++ MEMBER_OFFSET_INIT(net_dev_base_head, "net", "dev_base_head"); + ARRAY_LENGTH_INIT(net->net_device_name_index, + net_device_name, "net_device.name", NULL, sizeof(char)); + net->flags |= (NETDEV_INIT|STRUCT_NET_DEVICE); +@@ -158,13 +164,6 @@ + "in_ifaddr", "ifa_address"); + + STRUCT_SIZE_INIT(sock, "sock"); +- MEMBER_OFFSET_INIT(sock_daddr, "sock", "daddr"); +- MEMBER_OFFSET_INIT(sock_rcv_saddr, "sock", "rcv_saddr"); +- MEMBER_OFFSET_INIT(sock_dport, "sock", "dport"); +- MEMBER_OFFSET_INIT(sock_sport, "sock", "sport"); +- MEMBER_OFFSET_INIT(sock_num, "sock", "num"); +- MEMBER_OFFSET_INIT(sock_family, "sock", "family"); +- MEMBER_OFFSET_INIT(sock_type, "sock", "type"); + + MEMBER_OFFSET_INIT(sock_family, "sock", "family"); + if (VALID_MEMBER(sock_family)) { +@@ -195,7 +194,23 @@ + */ + STRUCT_SIZE_INIT(inet_sock, "inet_sock"); + STRUCT_SIZE_INIT(socket, "socket"); +- MEMBER_OFFSET_INIT(inet_sock_inet, "inet_sock", "inet"); ++ ++ if (STRUCT_EXISTS("inet_opt")) { ++ MEMBER_OFFSET_INIT(inet_sock_inet, "inet_sock", "inet"); ++ MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_opt", "daddr"); ++ MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_opt", "rcv_saddr"); ++ MEMBER_OFFSET_INIT(inet_opt_dport, "inet_opt", "dport"); ++ MEMBER_OFFSET_INIT(inet_opt_sport, "inet_opt", "sport"); ++ MEMBER_OFFSET_INIT(inet_opt_num, "inet_opt", "num"); ++ } else { /* inet_opt moved to inet_sock */ ++ ASSIGN_OFFSET(inet_sock_inet) = 0; ++ MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_sock", "daddr"); ++ MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_sock", "rcv_saddr"); ++ MEMBER_OFFSET_INIT(inet_opt_dport, "inet_sock", "dport"); ++ MEMBER_OFFSET_INIT(inet_opt_sport, "inet_sock", "sport"); ++ MEMBER_OFFSET_INIT(inet_opt_num, "inet_sock", "num"); ++ } ++ + if (VALID_STRUCT(inet_sock) && + INVALID_MEMBER(inet_sock_inet)) { + /* +@@ -210,15 +225,36 @@ + * to subtract the size of the inet_opt struct + * from the size of the containing inet_sock. + */ ++ net->flags |= NO_INET_SOCK; + ASSIGN_OFFSET(inet_sock_inet) = + SIZE(inet_sock) - STRUCT_SIZE("inet_opt"); + } +- MEMBER_OFFSET_INIT(inet_opt_daddr, "inet_opt", "daddr"); +- MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "inet_opt", +- "rcv_saddr"); +- MEMBER_OFFSET_INIT(inet_opt_dport, "inet_opt", "dport"); +- MEMBER_OFFSET_INIT(inet_opt_sport, "inet_opt", "sport"); +- MEMBER_OFFSET_INIT(inet_opt_num, "inet_opt", "num"); ++ ++ /* ++ * If necessary, set inet_sock size and inet_sock_inet offset, ++ * accounting for the configuration-dependent, intervening, ++ * struct ipv6_pinfo pointer located in between the sock and ++ * inet_opt members of the inet_sock. ++ */ ++ if (!VALID_STRUCT(inet_sock)) ++ { ++ if (symbol_exists("tcpv6_protocol") && ++ symbol_exists("udpv6_protocol")) { ++ ASSIGN_SIZE(inet_sock) = SIZE(sock) + ++ sizeof(void *) + STRUCT_SIZE("inet_opt"); ++ ASSIGN_OFFSET(inet_sock_inet) = SIZE(sock) + ++ sizeof(void *); ++ } else { ++ ASSIGN_SIZE(inet_sock) = SIZE(sock) + ++ STRUCT_SIZE("inet_opt"); ++ ASSIGN_OFFSET(inet_sock_inet) = SIZE(sock); ++ } ++ } ++ ++ MEMBER_OFFSET_INIT(ipv6_pinfo_rcv_saddr, "ipv6_pinfo", "rcv_saddr"); ++ MEMBER_OFFSET_INIT(ipv6_pinfo_daddr, "ipv6_pinfo", "daddr"); ++ STRUCT_SIZE_INIT(in6_addr, "in6_addr"); ++ + net->flags |= SOCK_V2; + } + } +@@ -323,8 +359,16 @@ + long flen; + char buf[BUFSIZE]; + ++ if (symbol_exists("dev_base_head")) { ++ show_net_devices_v2(); ++ return; ++ } else if (symbol_exists("init_net")) { ++ show_net_devices_v3(); ++ return; ++ } ++ + if (!symbol_exists("dev_base")) +- error(FATAL, "dev_base does not exist!\n"); ++ error(FATAL, "dev_base, dev_base_head or init_net do not exist!\n"); + + get_symbol_data("dev_base", sizeof(void *), &next); + +@@ -352,6 +396,114 @@ + } while (next); + } + ++static void ++show_net_devices_v2(void) ++{ ++ struct list_data list_data, *ld; ++ char *net_device_buf; ++ char buf[BUFSIZE]; ++ ulong *ndevlist; ++ int ndevcnt, i; ++ long flen; ++ ++ if (!net->netdevice) /* initialized in net_init() */ ++ return; ++ ++ flen = MAX(VADDR_PRLEN, strlen(net->netdevice)); ++ ++ fprintf(fp, "%s NAME IP ADDRESS(ES)\n", ++ mkstring(upper_case(net->netdevice, buf), ++ flen, CENTER|LJUST, NULL)); ++ ++ net_device_buf = GETBUF(SIZE(net_device)); ++ ++ ld = &list_data; ++ BZERO(ld, sizeof(struct list_data)); ++ get_symbol_data("dev_base_head", sizeof(void *), &ld->start); ++ ld->end = symbol_value("dev_base_head"); ++ ld->list_head_offset = OFFSET(net_device_dev_list); ++ ++ hq_open(); ++ ndevcnt = do_list(ld); ++ ndevlist = (ulong *)GETBUF(ndevcnt * sizeof(ulong)); ++ ndevcnt = retrieve_list(ndevlist, ndevcnt); ++ hq_close(); ++ ++ for (i = 0; i < ndevcnt; ++i) { ++ readmem(ndevlist[i], KVADDR, net_device_buf, ++ SIZE(net_device), "net_device buffer", ++ FAULT_ON_ERROR); ++ ++ fprintf(fp, "%s ", ++ mkstring(buf, flen, CENTER|RJUST|LONG_HEX, ++ MKSTR(ndevlist[i]))); ++ ++ get_device_name(ndevlist[i], buf); ++ fprintf(fp, "%-6s ", buf); ++ ++ get_device_address(ndevlist[i], buf); ++ fprintf(fp, "%s\n", buf); ++ } ++ ++ FREEBUF(ndevlist); ++ FREEBUF(net_device_buf); ++} ++ ++static void ++show_net_devices_v3(void) ++{ ++ struct list_data list_data, *ld; ++ char *net_device_buf; ++ char buf[BUFSIZE]; ++ ulong *ndevlist; ++ int ndevcnt, i; ++ long flen; ++ ++ if (!net->netdevice) /* initialized in net_init() */ ++ return; ++ ++ flen = MAX(VADDR_PRLEN, strlen(net->netdevice)); ++ ++ fprintf(fp, "%s NAME IP ADDRESS(ES)\n", ++ mkstring(upper_case(net->netdevice, buf), ++ flen, CENTER|LJUST, NULL)); ++ ++ net_device_buf = GETBUF(SIZE(net_device)); ++ ++ ld = &list_data; ++ BZERO(ld, sizeof(struct list_data)); ++ ld->start = ld->end = ++ symbol_value("init_net") + OFFSET(net_dev_base_head); ++ ld->list_head_offset = OFFSET(net_device_dev_list); ++ ++ hq_open(); ++ ndevcnt = do_list(ld); ++ ndevlist = (ulong *)GETBUF(ndevcnt * sizeof(ulong)); ++ ndevcnt = retrieve_list(ndevlist, ndevcnt); ++ hq_close(); ++ ++ /* ++ * Skip the first entry (init_net). ++ */ ++ for (i = 1; i < ndevcnt; ++i) { ++ readmem(ndevlist[i], KVADDR, net_device_buf, ++ SIZE(net_device), "net_device buffer", ++ FAULT_ON_ERROR); ++ ++ fprintf(fp, "%s ", ++ mkstring(buf, flen, CENTER|RJUST|LONG_HEX, ++ MKSTR(ndevlist[i]))); ++ ++ get_device_name(ndevlist[i], buf); ++ fprintf(fp, "%-6s ", buf); ++ ++ get_device_address(ndevlist[i], buf); ++ fprintf(fp, "%s\n", buf); ++ } ++ ++ FREEBUF(ndevlist); ++ FREEBUF(net_device_buf); ++} + + /* + * Perform the actual work of dumping the ARP table... +@@ -378,6 +530,24 @@ + nhash_buckets = (i = ARRAY_LENGTH(neigh_table_hash_buckets)) ? + i : get_array_length("neigh_table.hash_buckets", + NULL, sizeof(void *)); ++ ++ /* ++ * NOTE: 2.6.8 -> 2.6.9 neigh_table struct changed from: ++ * ++ * struct neighbour *hash_buckets[32]; ++ * to ++ * struct neighbour **hash_buckets; ++ * ++ * Even after hardwiring and testing with the correct ++ * array size, other changes cause this command to break ++ * down, so it needs to be looked at by someone who cares... ++ */ ++ ++ if (nhash_buckets == 0) { ++ option_not_supported('a'); ++ return; ++ } ++ + hash_bytes = nhash_buckets * sizeof(*hash_buckets); + + hash_buckets = (ulong *)GETBUF(hash_bytes); +@@ -609,8 +779,14 @@ + uint16_t dport, sport; + ushort num, family, type; + char *sockbuf, *inet_sockbuf; ++ ulong ipv6_pinfo, ipv6_rcv_saddr, ipv6_daddr; ++ uint16_t u6_addr16_src[8]; ++ uint16_t u6_addr16_dest[8]; ++ char buf2[BUFSIZE]; ++ int len; + + BZERO(buf, BUFSIZE); ++ BZERO(buf2, BUFSIZE); + sockbuf = inet_sockbuf = NULL; + + switch (net->flags & (SOCK_V1|SOCK_V2)) +@@ -646,6 +822,7 @@ + OFFSET(inet_opt_num)); + family = USHORT(inet_sockbuf + OFFSET(sock_common_skc_family)); + type = USHORT(inet_sockbuf + OFFSET(sock_sk_type)); ++ ipv6_pinfo = ULONG(inet_sockbuf + SIZE(sock)); + break; + } + +@@ -723,27 +900,28 @@ + } + + /* make sure we have room at the end... */ +- sprintf(&buf[strlen(buf)], "%s", space(MINSPACE-1)); ++// sprintf(&buf[strlen(buf)], "%s", space(MINSPACE-1)); ++ sprintf(&buf[strlen(buf)], " "); + + if (family == AF_INET) { + if (BITS32()) { +- sprintf(&buf[strlen(buf)], "%*s:%-*d%s", ++ sprintf(&buf[strlen(buf)], "%*s-%-*d%s", + BYTES_IP_ADDR, + inet_ntoa(*((struct in_addr *)&rcv_saddr)), + BYTES_PORT_NUM, + ntohs(sport), + space(1)); +- sprintf(&buf[strlen(buf)], "%*s:%-*d%s", ++ sprintf(&buf[strlen(buf)], "%*s-%-*d%s", + BYTES_IP_ADDR, + inet_ntoa(*((struct in_addr *)&daddr)), + BYTES_PORT_NUM, + ntohs(dport), + space(1)); + } else { +- sprintf(&buf[strlen(buf)], " %s:%d ", ++ sprintf(&buf[strlen(buf)], " %s-%d ", + inet_ntoa(*((struct in_addr *)&rcv_saddr)), + ntohs(sport)); +- sprintf(&buf[strlen(buf)], "%s:%d", ++ sprintf(&buf[strlen(buf)], "%s-%d", + inet_ntoa(*((struct in_addr *)&daddr)), + ntohs(dport)); + } +@@ -753,6 +931,60 @@ + FREEBUF(sockbuf); + if (inet_sockbuf) + FREEBUF(inet_sockbuf); ++ ++ if (family != AF_INET6) ++ return; ++ ++ switch (net->flags & (SOCK_V1|SOCK_V2)) ++ { ++ case SOCK_V1: ++ break; ++ ++ case SOCK_V2: ++ if (INVALID_MEMBER(ipv6_pinfo_rcv_saddr) || ++ INVALID_MEMBER(ipv6_pinfo_daddr)) ++ break; ++ ++ ipv6_rcv_saddr = ipv6_pinfo + OFFSET(ipv6_pinfo_rcv_saddr); ++ ipv6_daddr = ipv6_pinfo + OFFSET(ipv6_pinfo_daddr); ++ ++ if (!readmem(ipv6_rcv_saddr, KVADDR, u6_addr16_src, SIZE(in6_addr), ++ "ipv6_rcv_saddr buffer", QUIET|RETURN_ON_ERROR)) ++ break; ++ if (!readmem(ipv6_daddr, KVADDR, u6_addr16_dest, SIZE(in6_addr), ++ "ipv6_daddr buffer", QUIET|RETURN_ON_ERROR)) ++ break; ++ ++ sprintf(&buf[strlen(buf)], "%*s ", BITS32() ? 22 : 12, ++ dump_in6_addr_port(u6_addr16_src, sport, buf2, &len)); ++ if (BITS32() && (len > 22)) ++ len = 1; ++ mkstring(dump_in6_addr_port(u6_addr16_dest, dport, buf2, NULL), ++ len, CENTER, NULL); ++ sprintf(&buf[strlen(buf)], "%s", buf2); ++ ++ break; ++ } ++} ++ ++static char * ++dump_in6_addr_port(uint16_t *addr, uint16_t port, char *buf, int *len) ++{ ++ sprintf(buf, "%x:%x:%x:%x:%x:%x:%x:%x-%d", ++ ntohs(addr[0]), ++ ntohs(addr[1]), ++ ntohs(addr[2]), ++ ntohs(addr[3]), ++ ntohs(addr[4]), ++ ntohs(addr[5]), ++ ntohs(addr[6]), ++ ntohs(addr[7]), ++ ntohs(port)); ++ ++ if (len) ++ *len = strlen(buf); ++ ++ return buf; + } + + +@@ -899,6 +1131,8 @@ + fprintf(fp, "%sSTRUCT_DEVICE", others++ ? "|" : ""); + if (net->flags & STRUCT_NET_DEVICE) + fprintf(fp, "%sSTRUCT_NET_DEVICE", others++ ? "|" : ""); ++ if (net->flags & NO_INET_SOCK) ++ fprintf(fp, "%sNO_INET_SOCK", others++ ? "|" : ""); + if (net->flags & SOCK_V1) + fprintf(fp, "%sSOCK_V1", others++ ? "|" : ""); + if (net->flags & SOCK_V2) +@@ -972,7 +1206,7 @@ + void + dump_sockets_workhorse(ulong task, ulong flag, struct reference *ref) + { +- ulong files_struct_addr = 0; ++ ulong files_struct_addr = 0, fdtable_addr = 0; + int max_fdset = 0; + int max_fds = 0; + ulong open_fds_addr = 0; +@@ -1004,32 +1238,54 @@ + sizeof(void *), "task files contents", FAULT_ON_ERROR); + + if (files_struct_addr) { +- readmem(files_struct_addr + OFFSET(files_struct_max_fdset), +- KVADDR, &max_fdset, sizeof(int), +- "files_struct max_fdset", FAULT_ON_ERROR); +- +- readmem(files_struct_addr + OFFSET(files_struct_max_fds), +- KVADDR, &max_fds, sizeof(int), "files_struct max_fds", +- FAULT_ON_ERROR); +- } ++ if (VALID_MEMBER(files_struct_max_fdset)) { ++ readmem(files_struct_addr + OFFSET(files_struct_max_fdset), ++ KVADDR, &max_fdset, sizeof(int), ++ "files_struct max_fdset", FAULT_ON_ERROR); ++ readmem(files_struct_addr + OFFSET(files_struct_max_fds), ++ KVADDR, &max_fds, sizeof(int), "files_struct max_fds", ++ FAULT_ON_ERROR); ++ } ++ else if (VALID_MEMBER(files_struct_fdt)) { ++ readmem(files_struct_addr + OFFSET(files_struct_fdt), KVADDR, ++ &fdtable_addr, sizeof(void *), "fdtable buffer", ++ FAULT_ON_ERROR); ++ if (VALID_MEMBER(fdtable_max_fdset)) ++ readmem(fdtable_addr + OFFSET(fdtable_max_fdset), ++ KVADDR, &max_fdset, sizeof(int), ++ "fdtable_struct max_fdset", FAULT_ON_ERROR); ++ else ++ max_fdset = -1; ++ readmem(fdtable_addr + OFFSET(fdtable_max_fds), ++ KVADDR, &max_fds, sizeof(int), "fdtable_struct max_fds", ++ FAULT_ON_ERROR); ++ } ++ } + +- if (!files_struct_addr || (max_fdset == 0) || (max_fds == 0)) { ++ if ((VALID_MEMBER(files_struct_fdt) && !fdtable_addr) || ++ !files_struct_addr || (max_fdset == 0) || (max_fds == 0)) { + if (!NET_REFERENCE_CHECK(ref)) + fprintf(fp, "No open sockets.\n"); + return; + } + +- readmem(files_struct_addr + OFFSET(files_struct_open_fds), KVADDR, +- &open_fds_addr, sizeof(void *), "files_struct open_fds addr", +- FAULT_ON_ERROR); ++ if (VALID_MEMBER(fdtable_open_fds)){ ++ readmem(fdtable_addr + OFFSET(fdtable_open_fds), KVADDR, ++ &open_fds_addr, sizeof(void *), "files_struct open_fds addr", ++ FAULT_ON_ERROR); ++ readmem(fdtable_addr + OFFSET(fdtable_fd), KVADDR, &fd, ++ sizeof(void *), "files_struct fd addr", FAULT_ON_ERROR); ++ } else { ++ readmem(files_struct_addr + OFFSET(files_struct_open_fds), KVADDR, ++ &open_fds_addr, sizeof(void *), "files_struct open_fds addr", ++ FAULT_ON_ERROR); ++ readmem(files_struct_addr + OFFSET(files_struct_fd), KVADDR, &fd, ++ sizeof(void *), "files_struct fd addr", FAULT_ON_ERROR); ++ } + + if (open_fds_addr) +- readmem(open_fds_addr, KVADDR, &open_fds, sizeof(fd_set), +- "files_struct open_fds", FAULT_ON_ERROR); +- +- readmem(files_struct_addr + OFFSET(files_struct_fd), KVADDR, &fd, +- sizeof(void *), "files_struct fd addr", FAULT_ON_ERROR); +- ++ readmem(open_fds_addr, KVADDR, &open_fds, sizeof(fd_set), ++ "files_struct open_fds", FAULT_ON_ERROR); + if (!open_fds_addr || !fd) { + if (!NET_REFERENCE_CHECK(ref)) + fprintf(fp, "No open sockets.\n"); +@@ -1061,7 +1317,7 @@ + for (;;) { + unsigned long set; + i = j * __NFDBITS; +- if ((i >= max_fdset) || (i >= max_fds)) ++ if (((max_fdset >= 0) && (i >= max_fdset)) || (i >= max_fds)) + break; + set = open_fds.__fds_bits[j++]; + while (set) { +@@ -1096,9 +1352,9 @@ + */ + + static char *socket_hdr_32 = +-"FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT"; ++"FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT"; + static char *socket_hdr_64 = +-"FD SOCKET SOCK FAMILY:TYPE SOURCE:PORT DESTINATION:PORT"; ++"FD SOCKET SOCK FAMILY:TYPE SOURCE-PORT DESTINATION-PORT"; + + static int + sym_socket_dump(ulong file, +@@ -1223,7 +1479,12 @@ + dump_struct("sock", sock, 0); + break; + case SOCK_V2: +- dump_struct("inet_sock", sock, 0); ++ if (STRUCT_EXISTS("inet_sock") && !(net->flags & NO_INET_SOCK)) ++ dump_struct("inet_sock", sock, 0); ++ else if (STRUCT_EXISTS("sock")) ++ dump_struct("sock", sock, 0); ++ else ++ fprintf(fp, "\nunable to display inet_sock structure\n"); + break; + } + break; +--- crash/ppc64.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/ppc64.c 2009-02-04 15:34:33.000000000 -0500 +@@ -1,8 +1,8 @@ + /* ppc64.c -- core analysis suite + * +- * Copyright (C) 2004, 2005 David Anderson +- * Copyright (C) 2004, 2005 Red Hat, Inc. All rights reserved. +- * Copyright (C) 2004 Haren Myneni, IBM Corporation ++ * Copyright (C) 2004, 2005, 2006, 2007, 2008 David Anderson ++ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004, 2006 Haren Myneni, IBM Corporation + * + * 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 +@@ -47,6 +47,9 @@ + static char * ppc64_check_eframe(struct ppc64_pt_regs *); + static void ppc64_print_eframe(char *, struct ppc64_pt_regs *, + struct bt_info *); ++static void parse_cmdline_arg(void); ++static void ppc64_paca_init(void); ++static void ppc64_clear_machdep_cache(void); + + struct machine_specific ppc64_machine_specific = { { 0 }, 0, 0 }; + +@@ -64,26 +67,53 @@ + machdep->verify_symbol = ppc64_verify_symbol; + if (pc->flags & KERNEL_DEBUG_QUERY) + return; +- machdep->pagesize = memory_page_size(); ++ machdep->stacksize = PPC64_STACK_SIZE; ++ machdep->last_pgd_read = 0; ++ machdep->last_pmd_read = 0; ++ machdep->last_ptbl_read = 0; ++ machdep->machspec->last_level4_read = 0; ++ machdep->verify_paddr = generic_verify_paddr; ++ machdep->ptrs_per_pgd = PTRS_PER_PGD; ++ machdep->flags |= MACHDEP_BT_TEXT; ++ if (machdep->cmdline_arg) ++ parse_cmdline_arg(); ++ machdep->clear_machdep_cache = ppc64_clear_machdep_cache; ++ break; ++ ++ case PRE_GDB: ++ /* ++ * Recently there were changes made to kexec tools ++ * to support 64K page size. With those changes ++ * vmcore file obtained from a kernel which supports ++ * 64K page size cannot be analyzed using crash on a ++ * machine running with kernel supporting 4K page size ++ * ++ * The following modifications are required in crash ++ * tool to be in sync with kexec tools. ++ * ++ * Look if the following symbol exists. If yes then ++ * the dump was taken with a kernel supporting 64k ++ * page size. So change the page size accordingly. ++ * ++ * Also moved the following code block from ++ * PRE_SYMTAB case here. ++ */ ++ if (symbol_exists("__hash_page_64K")) ++ machdep->pagesize = PPC64_64K_PAGE_SIZE; ++ else ++ machdep->pagesize = memory_page_size(); + machdep->pageshift = ffs(machdep->pagesize) - 1; + machdep->pageoffset = machdep->pagesize - 1; + machdep->pagemask = ~((ulonglong)machdep->pageoffset); +- machdep->stacksize = 4 * machdep->pagesize; + if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pgd space."); + if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pmd space."); + if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc ptbl space."); +- machdep->last_pgd_read = 0; +- machdep->last_pmd_read = 0; +- machdep->last_ptbl_read = 0; +- machdep->verify_paddr = generic_verify_paddr; +- machdep->ptrs_per_pgd = PTRS_PER_PGD; +- machdep->flags |= MACHDEP_BT_TEXT; +- break; ++ if ((machdep->machspec->level4 = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc level4 space."); + +- case PRE_GDB: + machdep->kvbase = symbol_value("_stext"); + machdep->identity_map_base = machdep->kvbase; + machdep->is_kvaddr = generic_is_kvaddr; +@@ -109,6 +139,57 @@ + break; + + case POST_GDB: ++ if (!(machdep->flags & (VM_ORIG|VM_4_LEVEL))) { ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,14)) { ++ machdep->flags |= VM_4_LEVEL; ++ } else { ++ machdep->flags |= VM_ORIG; ++ } ++ } ++ if (machdep->flags & VM_ORIG) { ++ /* pre-2.6.14 layout */ ++ free(machdep->machspec->level4); ++ machdep->machspec->level4 = NULL; ++ machdep->ptrs_per_pgd = PTRS_PER_PGD; ++ } else { ++ /* 2.6.14 layout */ ++ struct machine_specific *m = machdep->machspec; ++ if (machdep->pagesize == 65536) { ++ /* 64K pagesize */ ++ m->l1_index_size = PTE_INDEX_SIZE_L4_64K; ++ m->l2_index_size = PMD_INDEX_SIZE_L4_64K; ++ m->l3_index_size = PUD_INDEX_SIZE_L4_64K; ++ m->l4_index_size = PGD_INDEX_SIZE_L4_64K; ++ m->pte_shift = symbol_exists("demote_segment_4k") ? ++ PTE_SHIFT_L4_64K_V2 : PTE_SHIFT_L4_64K_V1; ++ m->l2_masked_bits = PMD_MASKED_BITS_64K; ++ } else { ++ /* 4K pagesize */ ++ m->l1_index_size = PTE_INDEX_SIZE_L4_4K; ++ m->l2_index_size = PMD_INDEX_SIZE_L4_4K; ++ m->l3_index_size = PUD_INDEX_SIZE_L4_4K; ++ m->l4_index_size = PGD_INDEX_SIZE_L4_4K; ++ m->pte_shift = PTE_SHIFT_L4_4K; ++ m->l2_masked_bits = PMD_MASKED_BITS_4K; ++ } ++ ++ /* Compute ptrs per each level */ ++ m->l1_shift = machdep->pageshift; ++ m->ptrs_per_l1 = (1 << m->l1_index_size); ++ m->ptrs_per_l2 = (1 << m->l2_index_size); ++ m->ptrs_per_l3 = (1 << m->l3_index_size); ++ ++ machdep->ptrs_per_pgd = m->ptrs_per_l3; ++ ++ /* Compute shifts */ ++ m->l2_shift = m->l1_shift + m->l1_index_size; ++ m->l3_shift = m->l2_shift + m->l2_index_size; ++ m->l4_shift = m->l3_shift + m->l3_index_size; ++ } ++ ++ machdep->section_size_bits = _SECTION_SIZE_BITS; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; ++ ppc64_paca_init(); + machdep->vmalloc_start = ppc64_vmalloc_start; + MEMBER_OFFSET_INIT(thread_struct_pg_tables, + "thread_struct", "pg_tables"); +@@ -178,9 +259,11 @@ + */ + BZERO(&machdep->machspec->hwintrstack, + NR_CPUS*sizeof(ulong)); +- machdep->hz = HZ; +- if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) +- machdep->hz = 1000; ++ if (!machdep->hz) { ++ machdep->hz = HZ; ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) ++ machdep->hz = 1000; ++ } + /* + * IRQ stacks are introduced in 2.6 and also configurable. + */ +@@ -223,16 +306,18 @@ + void + ppc64_dump_machdep_table(ulong arg) + { +- int others; ++ int i, c, others; + + others = 0; + fprintf(fp, " flags: %lx (", machdep->flags); + if (machdep->flags & KSYMS_START) + fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + if (machdep->flags & MACHDEP_BT_TEXT) + fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : ""); ++ if (machdep->flags & VM_ORIG) ++ fprintf(fp, "%sVM_ORIG", others++ ? "|" : ""); ++ if (machdep->flags & VM_4_LEVEL) ++ fprintf(fp, "%sVM_4_LEVEL", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); +@@ -269,15 +354,56 @@ + fprintf(fp, " is_kvaddr: generic_is_kvaddr()\n"); + fprintf(fp, " is_uvaddr: generic_is_uvaddr()\n"); + fprintf(fp, " verify_paddr: generic_verify_paddr()\n"); ++ fprintf(fp, " xendump_p2m_create: NULL\n"); ++ fprintf(fp, "xen_kdump_p2m_create: NULL\n"); + fprintf(fp, " line_number_hooks: ppc64_line_number_hooks\n"); + fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); + fprintf(fp, " last_pmd_read: %lx\n", machdep->last_pmd_read); + fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read); ++ fprintf(fp, "clear_machdep_cache: ppc64_clear_machdep_cache()\n"); + fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); ++ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); ++ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); ++ fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); + fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); ++ fprintf(fp, " hwintrstack[%d]: ", NR_CPUS); ++ for (c = 0; c < NR_CPUS; c++) { ++ for (others = 0, i = c; i < NR_CPUS; i++) { ++ if (machdep->machspec->hwintrstack[i]) ++ others++; ++ } ++ if (!others) { ++ fprintf(fp, "%s%s", ++ c && ((c % 4) == 0) ? "\n " : "", ++ c ? "(remainder unused)" : "(unused)"); ++ break; ++ } ++ ++ fprintf(fp, "%s%016lx ", ++ ((c % 4) == 0) ? "\n " : "", ++ machdep->machspec->hwintrstack[c]); ++ } ++ fprintf(fp, "\n"); ++ fprintf(fp, " hwstackbuf: %lx\n", (ulong)machdep->machspec->hwstackbuf); ++ fprintf(fp, " hwstacksize: %d\n", machdep->machspec->hwstacksize); ++ fprintf(fp, " level4: %lx\n", (ulong)machdep->machspec->level4); ++ fprintf(fp, " last_level4_read: %lx\n", (ulong)machdep->machspec->last_level4_read); ++ fprintf(fp, " l4_index_size: %d\n", machdep->machspec->l4_index_size); ++ fprintf(fp, " l3_index_size: %d\n", machdep->machspec->l3_index_size); ++ fprintf(fp, " l2_index_size: %d\n", machdep->machspec->l2_index_size); ++ fprintf(fp, " l1_index_size: %d\n", machdep->machspec->l1_index_size); ++ fprintf(fp, " ptrs_per_l3: %d\n", machdep->machspec->ptrs_per_l3); ++ fprintf(fp, " ptrs_per_l2: %d\n", machdep->machspec->ptrs_per_l2); ++ fprintf(fp, " ptrs_per_l1: %d\n", machdep->machspec->ptrs_per_l1); ++ fprintf(fp, " l4_shift: %d\n", machdep->machspec->l4_shift); ++ fprintf(fp, " l3_shift: %d\n", machdep->machspec->l3_shift); ++ fprintf(fp, " l2_shift: %d\n", machdep->machspec->l2_shift); ++ fprintf(fp, " l1_shift: %d\n", machdep->machspec->l1_shift); ++ fprintf(fp, " pte_shift: %d\n", machdep->machspec->pte_shift); ++ fprintf(fp, " l2_masked_bits: %x\n", machdep->machspec->l2_masked_bits); + } + + /* +@@ -342,7 +468,7 @@ + if (!(pte & _PAGE_PRESENT)) { + if (pte && verbose) { + fprintf(fp, "\n"); +- ppc64_translate_pte(pte, 0, 0); ++ ppc64_translate_pte(pte, 0, PTE_SHIFT); + } + return FALSE; + } +@@ -354,7 +480,90 @@ + + if (verbose) { + fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); +- ppc64_translate_pte(pte, 0, 0); ++ ppc64_translate_pte(pte, 0, PTE_SHIFT); ++ } ++ ++ return TRUE; ++} ++ ++/* ++ * Virtual to physical memory translation. This function will be called ++ * by both ppc64_kvtop and ppc64_uvtop. ++ */ ++static int ++ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose) ++{ ++ ulong *level4_dir; ++ ulong *page_dir; ++ ulong *page_middle; ++ ulong *page_table; ++ ulong level4_pte, pgd_pte, pmd_pte; ++ ulong pte; ++ ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)level4); ++ ++ level4_dir = (ulong *)((ulong *)level4 + L4_OFFSET(vaddr)); ++ FILL_L4(PAGEBASE(level4), KVADDR, PAGESIZE()); ++ level4_pte = ULONG(machdep->machspec->level4 + PAGEOFFSET(level4_dir)); ++ if (verbose) ++ fprintf(fp, " L4: %lx => %lx\n", (ulong)level4_dir, level4_pte); ++ if (!level4_pte) ++ return FALSE; ++ ++ /* Sometimes we don't have level3 pagetable entries */ ++ if (machdep->machspec->l3_index_size != 0) { ++ page_dir = (ulong *)((ulong *)level4_pte + PGD_OFFSET_L4(vaddr)); ++ FILL_PGD(PAGEBASE(level4_pte), KVADDR, PAGESIZE()); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); ++ if (!pgd_pte) ++ return FALSE; ++ } else { ++ pgd_pte = level4_pte; ++ } ++ ++ page_middle = (ulong *)((ulong *)pgd_pte + PMD_OFFSET_L4(vaddr)); ++ FILL_PMD(PAGEBASE(pgd_pte), KVADDR, PAGESIZE()); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); ++ ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte); ++ ++ if (!(pmd_pte)) ++ return FALSE; ++ ++ page_table = (ulong *)(pmd_pte & ~(machdep->machspec->l2_masked_bits)) ++ + (BTOP(vaddr) & (machdep->machspec->ptrs_per_l1 - 1)); ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx\n",(ulong)page_middle, ++ (ulong)page_table); ++ ++ FILL_PTBL(PAGEBASE(pmd_pte), KVADDR, PAGESIZE()); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); ++ ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); ++ ++ if (!(pte & _PAGE_PRESENT)) { ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift); ++ } ++ return FALSE; ++ } ++ ++ if (!pte) ++ return FALSE; ++ ++ *paddr = PAGEBASE(PTOB(pte >> machdep->machspec->pte_shift)) ++ + PAGEOFFSET(vaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift); + } + + return TRUE; +@@ -411,7 +620,10 @@ + FAULT_ON_ERROR); + } + +- return ppc64_vtop(vaddr, pgd, paddr, verbose); ++ if (machdep->flags & VM_4_LEVEL) ++ return ppc64_vtop_level4(vaddr, pgd, paddr, verbose); ++ else ++ return ppc64_vtop(vaddr, pgd, paddr, verbose); + } + + /* +@@ -436,7 +648,10 @@ + return TRUE; + } + +- return ppc64_vtop(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose); ++ if (machdep->flags & VM_4_LEVEL) ++ return ppc64_vtop_level4(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose); ++ else ++ return ppc64_vtop(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose); + } + + /* +@@ -478,8 +693,9 @@ + ulong res, value, ppc_md, md_setup_res; + ulong we_have_of, prep_setup_res; + ulong node, type, name, properties; +- char str_buf[16]; +- uint len, mhz = 0; ++ char str_buf[32]; ++ uint len; ++ ulong mhz = 0; + + if (machdep->mhz) + return(machdep->mhz); +@@ -549,6 +765,23 @@ + mhz /= 1000000; + break; + } ++ else if(len && (strcasecmp(str_buf, ++ "ibm,extended-clock-frequency") == 0)){ ++ /* found the right cpu property */ ++ ++ readmem(properties+ ++ OFFSET(property_value), ++ KVADDR, &value, sizeof(ulong), ++ "clock freqency pointer", ++ FAULT_ON_ERROR); ++ readmem(value, KVADDR, &mhz, ++ sizeof(ulong), ++ "clock frequency value", ++ FAULT_ON_ERROR); ++ mhz /= 1000000; ++ break; ++ } ++ + /* keep looking */ + + readmem(properties+ +@@ -657,7 +890,7 @@ + * If a physaddr pointer is passed in, don't print anything. + */ + static int +-ppc64_translate_pte(ulong pte, void *physaddr, ulonglong unused) ++ppc64_translate_pte(ulong pte, void *physaddr, ulonglong pte_shift) + { + int c, len1, len2, len3, others, page_present; + char buf[BUFSIZE]; +@@ -668,7 +901,7 @@ + char *arglist[MAXARGS]; + ulong paddr; + +- paddr = PTOB(pte >> PTE_SHIFT); ++ paddr = PTOB(pte >> pte_shift); + page_present = (pte & _PAGE_PRESENT); + + if (physaddr) { +@@ -917,6 +1150,9 @@ + { + int c; + ++ if (!(tt->flags & IRQSTACKS)) ++ return 0; ++ + for (c = 0; c < NR_CPUS; c++) { + if (tt->hardirq_ctx[c]) { + if ((addr >= tt->hardirq_ctx[c]) && +@@ -1034,8 +1270,12 @@ + ms->hwstacksize + STACK_FRAME_OVERHEAD; + bt->stackbuf = ms->hwstackbuf; + alter_stackbuf(bt); +- } else +- error(FATAL, "cannot find the stack info"); ++ } else { ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "cannot find the stack info.\n"); ++ } ++ return; ++ } + } + + +@@ -1270,20 +1510,11 @@ + return NULL; + } + +-/* +- * Print exception frame information for ppc64 +- */ + static void +-ppc64_print_eframe(char *efrm_str, struct ppc64_pt_regs *regs, +- struct bt_info *bt) ++ppc64_print_regs(struct ppc64_pt_regs *regs) + { + int i; + +- if (BT_REFERENCE_CHECK(bt)) +- return; +- +- fprintf(fp, " %s [%lx] exception frame:", efrm_str, regs->trap); +- + /* print out the gprs... */ + for(i=0; i<32; i++) { + if(!(i % 3)) +@@ -1315,9 +1546,78 @@ + fprintf(fp, "DAR: %016lx\n", regs->dar); + fprintf(fp, " DSISR: %016lx ", regs->dsisr); + fprintf(fp, " Syscall Result: %016lx\n", regs->result); ++} ++ ++/* ++ * Print the exception frame information ++ */ ++static void ++ppc64_print_eframe(char *efrm_str, struct ppc64_pt_regs *regs, ++ struct bt_info *bt) ++{ ++ if (BT_REFERENCE_CHECK(bt)) ++ return; ++ ++ fprintf(fp, " %s [%lx] exception frame:", efrm_str, regs->trap); ++ ppc64_print_regs(regs); + fprintf(fp, "\n"); + } + ++/* ++ * get SP and IP from the saved ptregs. ++ */ ++static int ++ppc64_kdump_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) ++{ ++ struct ppc64_pt_regs *pt_regs; ++ unsigned long unip; ++ ++ pt_regs = (struct ppc64_pt_regs *)bt_in->machdep; ++ if (!pt_regs->gpr[1]) { ++ /* ++ * Not collected regs. May be the corresponding CPU not ++ * responded to an IPI. ++ */ ++ fprintf(fp, "%0lx: GPR1 register value (SP) was not saved\n", ++ bt_in->task); ++ return FALSE; ++ } ++ *ksp = pt_regs->gpr[1]; ++ if (IS_KVADDR(*ksp)) { ++ readmem(*ksp+16, KVADDR, &unip, sizeof(ulong), "Regs NIP value", ++ FAULT_ON_ERROR); ++ *nip = unip; ++ } else { ++ if (IN_TASK_VMA(bt_in->task, *ksp)) ++ fprintf(fp, "%0lx: Task is running in user space\n", ++ bt_in->task); ++ else ++ fprintf(fp, "%0lx: Invalid Stack Pointer %0lx\n", ++ bt_in->task, *ksp); ++ *nip = pt_regs->nip; ++ } ++ ++ if (bt_in->flags && ++ ((BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_PRINT|BT_TEXT_SYMBOLS_NOPRINT))) ++ return TRUE; ++ ++ /* ++ * Print the collected regs for the active task ++ */ ++ ppc64_print_regs(pt_regs); ++ if (!IS_KVADDR(*ksp)) ++ return FALSE; ++ ++ fprintf(fp, " NIP [%016lx] %s\n", pt_regs->nip, ++ closest_symbol(pt_regs->nip)); ++ if (unip != pt_regs->link) ++ fprintf(fp, " LR [%016lx] %s\n", pt_regs->link, ++ closest_symbol(pt_regs->link)); ++ ++ fprintf(fp, "\n"); ++ ++ return TRUE; ++} + + /* + * Get the starting point for the active cpus in a diskdump/netdump. +@@ -1335,12 +1635,18 @@ + ulong ur_ksp = 0; + int check_hardirq, check_softirq; + int check_intrstack = TRUE; ++ struct ppc64_pt_regs *pt_regs; ++ ++ /* ++ * For the kdump vmcore, Use SP and IP values that are saved in ptregs. ++ */ ++ if (pc->flags & KDUMP) ++ return ppc64_kdump_stack_frame(bt_in, nip, ksp); + + bt = &bt_local; + BCOPY(bt_in, bt, sizeof(struct bt_info)); + ms = machdep->machspec; + ur_nip = ur_ksp = 0; +- struct ppc64_pt_regs *pt_regs; + + panic_task = tt->panic_task == bt->task ? TRUE : FALSE; + +@@ -1424,6 +1730,7 @@ + if (STREQ(sym, ".netconsole_netdump") || + STREQ(sym, ".netpoll_start_netdump") || + STREQ(sym, ".start_disk_dump") || ++ STREQ(sym, ".crash_kexec") || + STREQ(sym, ".disk_dump")) { + *nip = *up; + *ksp = bt->stackbase + +@@ -1853,7 +2160,7 @@ + fprintf(fp, "(unknown)\n"); + fprintf(fp, " HZ: %d\n", machdep->hz); + fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); +- fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); ++// fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); + fprintf(fp, "KERNEL VIRTUAL BASE: %lx\n", machdep->kvbase); + fprintf(fp, "KERNEL VMALLOC BASE: %lx\n", vt->vmalloc_start); + fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); +@@ -2000,4 +2307,145 @@ + ppc64_dump_line_number(0); + } + ++/* ++ * Force the VM address-range selection via: ++ * ++ * --machdep vm=orig ++ * --machdep vm=2.6.14 ++ */ ++ ++void ++parse_cmdline_arg(void) ++{ ++ int i, c, errflag; ++ char *p; ++ char buf[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ int lines = 0; ++ ++ if (!strstr(machdep->cmdline_arg, "=")) { ++ error(WARNING, "ignoring --machdep option: %s\n\n", ++ machdep->cmdline_arg); ++ return; ++ } ++ ++ strcpy(buf, machdep->cmdline_arg); ++ ++ for (p = buf; *p; p++) { ++ if (*p == ',') ++ *p = ' '; ++ } ++ ++ c = parse_line(buf, arglist); ++ ++ for (i = 0; i < c; i++) { ++ errflag = 0; ++ ++ if (STRNEQ(arglist[i], "vm=")) { ++ p = arglist[i] + strlen("vm="); ++ if (strlen(p)) { ++ if (STREQ(p, "orig")) { ++ machdep->flags |= VM_ORIG; ++ continue; ++ } else if (STREQ(p, "2.6.14")) { ++ machdep->flags |= VM_4_LEVEL; ++ continue; ++ } ++ } ++ } ++ ++ error(WARNING, "ignoring --machdep option: %s\n", arglist[i]); ++ lines++; ++ } ++ ++ switch (machdep->flags & (VM_ORIG|VM_4_LEVEL)) ++ { ++ case VM_ORIG: ++ error(NOTE, "using original PPC64 VM address ranges\n"); ++ lines++; ++ break; ++ ++ case VM_4_LEVEL: ++ error(NOTE, "using 4-level pagetable PPC64 VM address ranges\n"); ++ lines++; ++ break; ++ ++ case (VM_ORIG|VM_4_LEVEL): ++ error(WARNING, "cannot set both vm=orig and vm=2.6.14\n"); ++ lines++; ++ machdep->flags &= ~(VM_ORIG|VM_4_LEVEL); ++ break; ++ } ++ ++ if (lines) ++ fprintf(fp, "\n"); ++} ++ ++/* ++ * Updating any smp-related items that were possibly bypassed ++ * or improperly initialized in kernel_init(). ++ */ ++static void ++ppc64_paca_init(void) ++{ ++ int i, cpus, nr_paca; ++ char *cpu_paca_buf; ++ ulong data_offset; ++ int map; ++ ++ if (!symbol_exists("paca")) ++ error(FATAL, "PPC64: Could not find 'paca' symbol\n"); ++ ++ if (symbol_exists("cpu_present_map")) ++ map = PRESENT; ++ else if (symbol_exists("cpu_online_map")) ++ map = ONLINE; ++ else ++ error(FATAL, ++ "PPC64: cannot find 'cpu_present_map' or 'cpu_online_map' symbols\n"); ++ ++ if (!MEMBER_EXISTS("paca_struct", "data_offset")) ++ return; ++ ++ STRUCT_SIZE_INIT(ppc64_paca, "paca_struct"); ++ data_offset = MEMBER_OFFSET("paca_struct", "data_offset"); ++ ++ cpu_paca_buf = GETBUF(SIZE(ppc64_paca)); ++ ++ if (!(nr_paca = get_array_length("paca", NULL, 0))) ++ nr_paca = NR_CPUS; ++ ++ if (nr_paca > NR_CPUS) { ++ error(WARNING, ++ "PPC64: Number of paca entries (%d) greater than NR_CPUS (%d)\n", ++ nr_paca, NR_CPUS); ++ error(FATAL, "Recompile crash with larger NR_CPUS\n"); ++ } ++ ++ for (i = cpus = 0; i < nr_paca; i++) { ++ /* ++ * CPU present (or online)? ++ */ ++ if (!in_cpu_map(map, i)) ++ continue; ++ ++ readmem(symbol_value("paca") + (i * SIZE(ppc64_paca)), ++ KVADDR, cpu_paca_buf, SIZE(ppc64_paca), ++ "paca entry", FAULT_ON_ERROR); ++ ++ kt->__per_cpu_offset[i] = ULONG(cpu_paca_buf + data_offset); ++ kt->flags |= PER_CPU_OFF; ++ cpus++; ++ } ++ kt->cpus = cpus; ++ if (kt->cpus > 1) ++ kt->flags |= SMP; ++} ++ ++void ++ppc64_clear_machdep_cache(void) ++{ ++ if (machdep->machspec->last_level4_read != vt->kernel_pgd[0]) ++ machdep->machspec->last_level4_read = 0; ++} + #endif /* PPC64 */ +--- crash/memory.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/memory.c 2009-02-12 08:36:21.000000000 -0500 +@@ -1,8 +1,8 @@ + /* memory.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002 Silicon Graphics, Inc. + * + * This program is free software; you can redistribute it and/or modify +@@ -35,34 +35,47 @@ + ulong order; + ulong slabsize; + ulong num_slabs; ++ ulong objects; + ulonglong spec_addr; + ulong flags; + ulong size; ++ ulong objsize; + int memtype; + int free; ++ int slab_offset; + char *reqname; + char *curname; + ulong *addrlist; + int *kmem_bufctl; + ulong *cpudata[NR_CPUS]; ++ ulong *shared_array_cache; ++ int current_cache_index; + ulong found; + ulong retval; + char *ignore; + int errors; + int calls; + int cpu; ++ int cache_count; + ulong get_shared; + ulong get_totalram; + ulong get_buffers; + ulong get_slabs; + char *slab_buf; + char *cache_buf; ++ ulong *cache_list; ++ struct vmlist { ++ ulong addr; ++ ulong size; ++ } *vmlist; ++ ulong container; + }; + + static char *memtype_string(int, int); + static char *error_handle_string(ulong); + static void dump_mem_map(struct meminfo *); +-static void fill_mem_map_cache(ulong, char *); ++static void dump_mem_map_SPARSEMEM(struct meminfo *); ++static void fill_mem_map_cache(ulong, ulong, char *); + static void dump_free_pages(struct meminfo *); + static int dump_zone_page_usage(void); + static void dump_multidimensional_free_pages(struct meminfo *); +@@ -72,19 +85,29 @@ + static void dump_page_hash_table(struct meminfo *); + static void kmem_search(struct meminfo *); + static void kmem_cache_init(void); ++static void kmem_cache_init_slub(void); + static ulong max_cpudata_limit(ulong, ulong *); + static int ignore_cache(struct meminfo *, char *); + static char *is_kmem_cache_addr(ulong, char *); ++static char *is_kmem_cache_addr_slub(ulong, char *); + static void kmem_cache_list(void); + static void dump_kmem_cache(struct meminfo *); + static void dump_kmem_cache_percpu_v1(struct meminfo *); + static void dump_kmem_cache_percpu_v2(struct meminfo *); ++static void dump_kmem_cache_slub(struct meminfo *); + static void dump_kmem_cache_info_v2(struct meminfo *); +-static char *vaddr_to_kmem_cache(ulong, char *); ++static void kmem_cache_list_slub(void); ++static ulong get_cpu_slab_ptr(struct meminfo *, int, ulong *); ++static unsigned int oo_order(ulong); ++static unsigned int oo_objects(ulong); ++static char *vaddr_to_kmem_cache(ulong, char *, int); + static ulong vaddr_to_slab(ulong); + static void do_slab_chain(int, struct meminfo *); + static void do_slab_chain_percpu_v1(long, struct meminfo *); + static void do_slab_chain_percpu_v2(long, struct meminfo *); ++static void do_slab_chain_percpu_v2_nodes(long, struct meminfo *); ++static void do_slab_slub(struct meminfo *, int); ++static void do_kmem_cache_slub(struct meminfo *); + static void save_slab_data(struct meminfo *); + static int slab_data_saved(struct meminfo *); + static void dump_saved_slab_data(void); +@@ -97,7 +120,9 @@ + static void gather_slab_free_list_percpu(struct meminfo *); + static void gather_cpudata_list_v1(struct meminfo *); + static void gather_cpudata_list_v2(struct meminfo *); ++static void gather_cpudata_list_v2_nodes(struct meminfo *, int); + static int check_cpudata_list(struct meminfo *, ulong); ++static int check_shared_list(struct meminfo *, ulong); + static void gather_slab_cached_count(struct meminfo *); + static void dump_slab_objects(struct meminfo *); + static void dump_slab_objects_percpu(struct meminfo *); +@@ -110,6 +135,9 @@ + static void search(ulong, ulong, ulong, int, ulong *, int); + static int next_upage(struct task_context *, ulong, ulong *); + static int next_kpage(ulong, ulong *); ++static ulong last_vmalloc_address(void); ++static ulong next_vmlist_vaddr(ulong); ++static int next_identity_mapping(ulong, ulong *); + static int vm_area_page_dump(ulong, ulong, ulong, ulong, void *, + struct reference *); + static int dump_swap_info(ulong, ulong *, ulong *); +@@ -118,15 +146,45 @@ + static char *vma_file_offset(ulong, ulong, char *); + static ssize_t read_dev_kmem(ulong, char *, long); + static void dump_memory_nodes(int); ++static void dump_zone_stats(void); + #define MEMORY_NODES_DUMP (0) + #define MEMORY_NODES_INITIALIZE (1) + static void node_table_init(void); + static int compare_node_data(const void *, const void *); + static void do_vm_flags(ulong); + static void PG_reserved_flag_init(void); ++static void PG_slab_flag_init(void); + static ulong nr_blockdev_pages(void); +- +- ++void sparse_mem_init(void); ++void dump_mem_sections(void); ++void list_mem_sections(void); ++ulong sparse_decode_mem_map(ulong, ulong); ++char *read_mem_section(ulong); ++ulong nr_to_section(ulong); ++int valid_section(ulong); ++int section_has_mem_map(ulong); ++ulong section_mem_map_addr(ulong); ++ulong valid_section_nr(ulong); ++ulong pfn_to_map(ulong); ++static int get_nodes_online(void); ++static int next_online_node(int); ++static ulong next_online_pgdat(int); ++static int vm_stat_init(void); ++static int vm_event_state_init(void); ++static int dump_vm_stat(char *, long *, ulong); ++static int dump_vm_event_state(void); ++static int dump_page_states(void); ++static int generic_read_dumpfile(ulonglong, void *, long, char *, ulong); ++static int generic_write_dumpfile(ulonglong, void *, long, char *, ulong); ++static int page_to_nid(ulong); ++static int get_kmem_cache_list(ulong **); ++static int get_kmem_cache_slub_data(long, struct meminfo *); ++static ulong compound_head(ulong); ++static long count_partial(ulong); ++static ulong get_freepointer(struct meminfo *, void *); ++static int count_free_objects(struct meminfo *, ulong); ++char *is_slab_page(struct meminfo *, char *); ++static void do_node_lists_slub(struct meminfo *, ulong, int); + + /* + * Memory display modes specific to this file. +@@ -142,6 +200,8 @@ + #define DECIMAL (0x100) + #define UDECIMAL (0x200) + #define ASCII_ENDLINE (0x400) ++#define NO_ASCII (0x800) ++#define SLAB_CACHE (0x1000) + + static ulong DISPLAY_DEFAULT; + +@@ -182,6 +242,10 @@ + MEMBER_OFFSET_INIT(mm_struct_mmap, "mm_struct", "mmap"); + MEMBER_OFFSET_INIT(mm_struct_pgd, "mm_struct", "pgd"); + MEMBER_OFFSET_INIT(mm_struct_rss, "mm_struct", "rss"); ++ if (!VALID_MEMBER(mm_struct_rss)) ++ MEMBER_OFFSET_INIT(mm_struct_rss, "mm_struct", "_rss"); ++ MEMBER_OFFSET_INIT(mm_struct_anon_rss, "mm_struct", "_anon_rss"); ++ MEMBER_OFFSET_INIT(mm_struct_file_rss, "mm_struct", "_file_rss"); + MEMBER_OFFSET_INIT(mm_struct_total_vm, "mm_struct", "total_vm"); + MEMBER_OFFSET_INIT(mm_struct_start_code, "mm_struct", "start_code"); + MEMBER_OFFSET_INIT(vm_area_struct_vm_mm, "vm_area_struct", "vm_mm"); +@@ -222,7 +286,16 @@ + MEMBER_OFFSET_INIT(page_count, "page", "_count"); + MEMBER_OFFSET_INIT(page_flags, "page", "flags"); + MEMBER_OFFSET_INIT(page_mapping, "page", "mapping"); ++ if (INVALID_MEMBER(page_mapping)) ++ ANON_MEMBER_OFFSET_INIT(page_mapping, "page", "mapping"); ++ if (INVALID_MEMBER(page_mapping) && ++ (THIS_KERNEL_VERSION < LINUX(2,6,17)) && ++ MEMBER_EXISTS("page", "_mapcount")) ++ ASSIGN_OFFSET(page_mapping) = MEMBER_OFFSET("page", "_mapcount") + ++ STRUCT_SIZE("atomic_t") + sizeof(ulong); + MEMBER_OFFSET_INIT(page_index, "page", "index"); ++ if (INVALID_MEMBER(page_index)) ++ ANON_MEMBER_OFFSET_INIT(page_index, "page", "index"); + MEMBER_OFFSET_INIT(page_buffers, "page", "buffers"); + MEMBER_OFFSET_INIT(page_lru, "page", "lru"); + MEMBER_OFFSET_INIT(page_pte, "page", "pte"); +@@ -249,6 +322,9 @@ + MEMBER_OFFSET_INIT(block_device_bd_disk, "block_device", "bd_disk"); + MEMBER_OFFSET_INIT(inode_i_mapping, "inode", "i_mapping"); + MEMBER_OFFSET_INIT(address_space_nrpages, "address_space", "nrpages"); ++ if (INVALID_MEMBER(address_space_nrpages)) ++ MEMBER_OFFSET_INIT(address_space_nrpages, "address_space", "__nrpages"); ++ + MEMBER_OFFSET_INIT(gendisk_major, "gendisk", "major"); + MEMBER_OFFSET_INIT(gendisk_fops, "gendisk", "fops"); + MEMBER_OFFSET_INIT(gendisk_disk_name, "gendisk", "disk_name"); +@@ -270,6 +346,7 @@ + STRUCT_SIZE_INIT(kmem_slab_s, "kmem_slab_s"); + STRUCT_SIZE_INIT(slab_s, "slab_s"); + STRUCT_SIZE_INIT(slab, "slab"); ++ STRUCT_SIZE_INIT(kmem_cache_s, "kmem_cache_s"); + STRUCT_SIZE_INIT(pgd_t, "pgd_t"); + + if (!VALID_STRUCT(kmem_slab_s) && VALID_STRUCT(slab_s)) { +@@ -310,17 +387,49 @@ + !VALID_STRUCT(slab_s) && VALID_STRUCT(slab)) { + vt->flags |= PERCPU_KMALLOC_V2; + +- MEMBER_OFFSET_INIT(kmem_cache_s_num, "kmem_cache_s", "num"); +- MEMBER_OFFSET_INIT(kmem_cache_s_next, "kmem_cache_s", "next"); +- MEMBER_OFFSET_INIT(kmem_cache_s_name, "kmem_cache_s", "name"); +- MEMBER_OFFSET_INIT(kmem_cache_s_colour_off, "kmem_cache_s", +- "colour_off"); +- MEMBER_OFFSET_INIT(kmem_cache_s_objsize, "kmem_cache_s", +- "objsize"); +- MEMBER_OFFSET_INIT(kmem_cache_s_flags, "kmem_cache_s", "flags"); +- MEMBER_OFFSET_INIT(kmem_cache_s_gfporder, +- "kmem_cache_s", "gfporder"); +- ++ if (VALID_STRUCT(kmem_cache_s)) { ++ MEMBER_OFFSET_INIT(kmem_cache_s_num, "kmem_cache_s", "num"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_next, "kmem_cache_s", "next"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_name, "kmem_cache_s", "name"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_colour_off, "kmem_cache_s", ++ "colour_off"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_objsize, "kmem_cache_s", ++ "objsize"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_flags, "kmem_cache_s", "flags"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_gfporder, ++ "kmem_cache_s", "gfporder"); ++ ++ MEMBER_OFFSET_INIT(kmem_cache_s_lists, "kmem_cache_s", "lists"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_array, "kmem_cache_s", "array"); ++ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache_s.array", NULL, 0); ++ } else { ++ STRUCT_SIZE_INIT(kmem_cache_s, "kmem_cache"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_num, "kmem_cache", "num"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_next, "kmem_cache", "next"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_name, "kmem_cache", "name"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_colour_off, "kmem_cache", ++ "colour_off"); ++ if (MEMBER_EXISTS("kmem_cache", "objsize")) ++ MEMBER_OFFSET_INIT(kmem_cache_s_objsize, "kmem_cache", ++ "objsize"); ++ else if (MEMBER_EXISTS("kmem_cache", "buffer_size")) ++ MEMBER_OFFSET_INIT(kmem_cache_s_objsize, "kmem_cache", ++ "buffer_size"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_flags, "kmem_cache", "flags"); ++ MEMBER_OFFSET_INIT(kmem_cache_s_gfporder, ++ "kmem_cache", "gfporder"); ++ ++ if (MEMBER_EXISTS("kmem_cache", "lists")) ++ MEMBER_OFFSET_INIT(kmem_cache_s_lists, "kmem_cache", "lists"); ++ else if (MEMBER_EXISTS("kmem_cache", "nodelists")) { ++ vt->flags |= PERCPU_KMALLOC_V2_NODES; ++ MEMBER_OFFSET_INIT(kmem_cache_s_lists, "kmem_cache", "nodelists"); ++ ARRAY_LENGTH_INIT(vt->kmem_cache_len_nodes, NULL, ++ "kmem_cache.nodelists", NULL, 0); ++ } ++ MEMBER_OFFSET_INIT(kmem_cache_s_array, "kmem_cache", "array"); ++ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.array", NULL, 0); ++ } + MEMBER_OFFSET_INIT(slab_list, "slab", "list"); + MEMBER_OFFSET_INIT(slab_s_mem, "slab", "s_mem"); + MEMBER_OFFSET_INIT(slab_inuse, "slab", "inuse"); +@@ -330,10 +439,6 @@ + MEMBER_OFFSET_INIT(array_cache_limit, "array_cache", "limit"); + STRUCT_SIZE_INIT(array_cache, "array_cache"); + +- MEMBER_OFFSET_INIT(kmem_cache_s_lists, "kmem_cache_s", "lists"); +- MEMBER_OFFSET_INIT(kmem_cache_s_array, "kmem_cache_s", "array"); +- ARRAY_LENGTH_INIT(len, NULL, "kmem_cache_s.array", NULL, 0); +- + MEMBER_OFFSET_INIT(kmem_list3_slabs_partial, + "kmem_list3", "slabs_partial"); + MEMBER_OFFSET_INIT(kmem_list3_slabs_full, +@@ -343,6 +448,52 @@ + MEMBER_OFFSET_INIT(kmem_list3_free_objects, + "kmem_list3", "free_objects"); + MEMBER_OFFSET_INIT(kmem_list3_shared, "kmem_list3", "shared"); ++ } else if (MEMBER_EXISTS("kmem_cache", "cpu_slab") && ++ STRUCT_EXISTS("kmem_cache_node")) { ++ vt->flags |= KMALLOC_SLUB; ++ ++ STRUCT_SIZE_INIT(kmem_cache, "kmem_cache"); ++ MEMBER_OFFSET_INIT(kmem_cache_size, "kmem_cache", "size"); ++ MEMBER_OFFSET_INIT(kmem_cache_objsize, "kmem_cache", "objsize"); ++ MEMBER_OFFSET_INIT(kmem_cache_offset, "kmem_cache", "offset"); ++ MEMBER_OFFSET_INIT(kmem_cache_order, "kmem_cache", "order"); ++ MEMBER_OFFSET_INIT(kmem_cache_local_node, "kmem_cache", "local_node"); ++ MEMBER_OFFSET_INIT(kmem_cache_objects, "kmem_cache", "objects"); ++ MEMBER_OFFSET_INIT(kmem_cache_inuse, "kmem_cache", "inuse"); ++ MEMBER_OFFSET_INIT(kmem_cache_align, "kmem_cache", "align"); ++ MEMBER_OFFSET_INIT(kmem_cache_node, "kmem_cache", "node"); ++ MEMBER_OFFSET_INIT(kmem_cache_cpu_slab, "kmem_cache", "cpu_slab"); ++ MEMBER_OFFSET_INIT(kmem_cache_list, "kmem_cache", "list"); ++ MEMBER_OFFSET_INIT(kmem_cache_name, "kmem_cache", "name"); ++ MEMBER_OFFSET_INIT(kmem_cache_flags, "kmem_cache", "flags"); ++ MEMBER_OFFSET_INIT(kmem_cache_cpu_freelist, "kmem_cache_cpu", "freelist"); ++ MEMBER_OFFSET_INIT(kmem_cache_cpu_page, "kmem_cache_cpu", "page"); ++ MEMBER_OFFSET_INIT(kmem_cache_cpu_node, "kmem_cache_cpu", "node"); ++ ANON_MEMBER_OFFSET_INIT(page_inuse, "page", "inuse"); ++ ANON_MEMBER_OFFSET_INIT(page_offset, "page", "offset"); ++ ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab"); ++ ANON_MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); ++ ANON_MEMBER_OFFSET_INIT(page_freelist, "page", "freelist"); ++ if (INVALID_MEMBER(kmem_cache_objects)) { ++ MEMBER_OFFSET_INIT(kmem_cache_oo, "kmem_cache", "oo"); ++ ANON_MEMBER_OFFSET_INIT(page_objects, "page", "objects"); ++ } ++ if (VALID_MEMBER(kmem_cache_node)) { ++ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.node", NULL, 0); ++ vt->flags |= CONFIG_NUMA; ++ } ++ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.cpu_slab", NULL, 0); ++ ++ STRUCT_SIZE_INIT(kmem_cache_node, "kmem_cache_node"); ++ STRUCT_SIZE_INIT(kmem_cache_cpu, "kmem_cache_cpu"); ++ MEMBER_OFFSET_INIT(kmem_cache_node_nr_partial, ++ "kmem_cache_node", "nr_partial"); ++ MEMBER_OFFSET_INIT(kmem_cache_node_nr_slabs, ++ "kmem_cache_node", "nr_slabs"); ++ MEMBER_OFFSET_INIT(kmem_cache_node_partial, ++ "kmem_cache_node", "partial"); ++ MEMBER_OFFSET_INIT(kmem_cache_node_full, ++ "kmem_cache_node", "full"); + } else { + MEMBER_OFFSET_INIT(kmem_cache_s_c_nextp, + "kmem_cache_s", "c_nextp"); +@@ -381,6 +532,22 @@ + "kmem_slab_s", "s_magic"); + } + ++ if (!kt->kernel_NR_CPUS) { ++ if (ARRAY_LENGTH(kmem_cache_s_cpudata)) ++ kt->kernel_NR_CPUS = ARRAY_LENGTH(kmem_cache_s_cpudata); ++ else if (ARRAY_LENGTH(kmem_cache_s_array)) ++ kt->kernel_NR_CPUS = ARRAY_LENGTH(kmem_cache_s_array); ++ else if (ARRAY_LENGTH(kmem_cache_cpu_slab)) ++ kt->kernel_NR_CPUS = ARRAY_LENGTH(kmem_cache_cpu_slab); ++ } ++ ++ if (kt->kernel_NR_CPUS > NR_CPUS) { ++ error(WARNING, ++ "kernel-configured NR_CPUS (%d) greater than compiled-in NR_CPUS (%d)\n", ++ kt->kernel_NR_CPUS, NR_CPUS); ++ error(FATAL, "recompile crash with larger NR_CPUS\n"); ++ } ++ + if (machdep->init_kernel_pgd) + machdep->init_kernel_pgd(); + else if (symbol_exists("swapper_pg_dir")) { +@@ -415,10 +582,17 @@ + error(FATAL, "no swapper_pg_dir or cpu_pgd symbols exist?\n"); + + get_symbol_data("high_memory", sizeof(ulong), &vt->high_memory); +- if (kernel_symbol_exists("mem_map")) ++ ++ if (kernel_symbol_exists("mem_section")) ++ vt->flags |= SPARSEMEM; ++ else if (kernel_symbol_exists("mem_map")) { + get_symbol_data("mem_map", sizeof(char *), &vt->mem_map); +- else ++ vt->flags |= FLATMEM; ++ } else + vt->flags |= DISCONTIGMEM; ++ ++ sparse_mem_init(); ++ + vt->vmalloc_start = machdep->vmalloc_start(); + if (IS_VMALLOC_ADDR(vt->mem_map)) + vt->flags |= V_MEM_MAP; +@@ -470,15 +644,15 @@ + + if (kernel_symbol_exists("mem_map")) + get_symbol_data("max_mapnr", sizeof(ulong), &vt->max_mapnr); +- get_symbol_data("nr_swapfiles", sizeof(unsigned int), +- &vt->nr_swapfiles); ++ if (kernel_symbol_exists("nr_swapfiles")) ++ get_symbol_data("nr_swapfiles", sizeof(unsigned int), ++ &vt->nr_swapfiles); + + STRUCT_SIZE_INIT(page, "page"); + STRUCT_SIZE_INIT(free_area, "free_area"); + STRUCT_SIZE_INIT(free_area_struct, "free_area_struct"); + STRUCT_SIZE_INIT(zone, "zone"); + STRUCT_SIZE_INIT(zone_struct, "zone_struct"); +- STRUCT_SIZE_INIT(kmem_cache_s, "kmem_cache_s"); + STRUCT_SIZE_INIT(kmem_bufctl_t, "kmem_bufctl_t"); + STRUCT_SIZE_INIT(swap_info_struct, "swap_info_struct"); + STRUCT_SIZE_INIT(mm_struct, "mm_struct"); +@@ -488,13 +662,20 @@ + if (VALID_STRUCT(pglist_data)) { + vt->flags |= ZONES; + +- if (symbol_exists("pgdat_list")) ++ if (symbol_exists("pgdat_list") && !IS_SPARSEMEM()) + vt->flags |= NODES; + ++ /* ++ * Determine the number of nodes the best way possible, ++ * starting with a default of 1. ++ */ ++ vt->numnodes = 1; ++ + if (symbol_exists("numnodes")) + get_symbol_data("numnodes", sizeof(int), &vt->numnodes); +- else +- vt->numnodes = 1; ++ ++ if (get_nodes_online()) ++ vt->flags |= NODES_ONLINE; + + MEMBER_OFFSET_INIT(pglist_data_node_zones, + "pglist_data", "node_zones"); +@@ -524,6 +705,7 @@ + ARRAY_LENGTH_INIT(vt->nr_zones, pglist_data_node_zones, + "pglist_data.node_zones", NULL, + SIZE_OPTION(zone_struct, zone)); ++ vt->ZONE_HIGHMEM = vt->nr_zones - 1; + + if (VALID_STRUCT(zone_struct)) { + MEMBER_OFFSET_INIT(zone_struct_free_pages, +@@ -539,6 +721,8 @@ + if (INVALID_MEMBER(zone_struct_size)) + MEMBER_OFFSET_INIT(zone_struct_memsize, + "zone_struct", "memsize"); ++ MEMBER_OFFSET_INIT(zone_struct_zone_start_pfn, ++ "zone_struct", "zone_start_pfn"); + MEMBER_OFFSET_INIT(zone_struct_zone_start_paddr, + "zone_struct", "zone_start_paddr"); + MEMBER_OFFSET_INIT(zone_struct_zone_start_mapnr, +@@ -565,8 +749,17 @@ + vt->dump_free_pages = dump_free_pages_zones_v1; + + } else if (VALID_STRUCT(zone)) { +- MEMBER_OFFSET_INIT(zone_free_pages, +- "zone", "free_pages"); ++ MEMBER_OFFSET_INIT(zone_vm_stat, "zone", "vm_stat"); ++ MEMBER_OFFSET_INIT(zone_free_pages, "zone", "free_pages"); ++ if (INVALID_MEMBER(zone_free_pages) && ++ VALID_MEMBER(zone_vm_stat)) { ++ long nr_free_pages = 0; ++ if (!enumerator_value("NR_FREE_PAGES", &nr_free_pages)) ++ error(WARNING, ++ "cannot determine NR_FREE_PAGES enumerator\n"); ++ ASSIGN_OFFSET(zone_free_pages) = OFFSET(zone_vm_stat) + ++ (nr_free_pages * sizeof(long)); ++ } + MEMBER_OFFSET_INIT(zone_free_area, + "zone", "free_area"); + MEMBER_OFFSET_INIT(zone_zone_pgdat, +@@ -579,12 +772,23 @@ + "zone", "zone_start_pfn"); + MEMBER_OFFSET_INIT(zone_spanned_pages, + "zone", "spanned_pages"); ++ MEMBER_OFFSET_INIT(zone_present_pages, ++ "zone", "present_pages"); + MEMBER_OFFSET_INIT(zone_pages_min, + "zone", "pages_min"); + MEMBER_OFFSET_INIT(zone_pages_low, + "zone", "pages_low"); + MEMBER_OFFSET_INIT(zone_pages_high, + "zone", "pages_high"); ++ MEMBER_OFFSET_INIT(zone_nr_active, ++ "zone", "nr_active"); ++ MEMBER_OFFSET_INIT(zone_nr_inactive, ++ "zone", "nr_inactive"); ++ MEMBER_OFFSET_INIT(zone_all_unreclaimable, ++ "zone", "all_unreclaimable"); ++ MEMBER_OFFSET_INIT(zone_flags, "zone", "flags"); ++ MEMBER_OFFSET_INIT(zone_pages_scanned, "zone", ++ "pages_scanned"); + ARRAY_LENGTH_INIT(vt->nr_free_areas, zone_free_area, + "zone.free_area", NULL, SIZE(free_area)); + vt->dump_free_pages = dump_free_pages_zones_v2; +@@ -603,6 +807,8 @@ + vt->dump_kmem_cache = dump_kmem_cache_percpu_v1; + else if (vt->flags & PERCPU_KMALLOC_V2) + vt->dump_kmem_cache = dump_kmem_cache_percpu_v2; ++ else if (vt->flags & KMALLOC_SLUB) ++ vt->dump_kmem_cache = dump_kmem_cache_slub; + else + vt->dump_kmem_cache = dump_kmem_cache; + +@@ -640,13 +846,7 @@ + kmem_cache_init(); + + PG_reserved_flag_init(); +- +- if (VALID_MEMBER(page_pte)) { +- if (THIS_KERNEL_VERSION < LINUX(2,6,0)) +- vt->PG_slab = 10; +- else if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) +- vt->PG_slab = 7; +- } ++ PG_slab_flag_init(); + } + + /* +@@ -685,7 +885,7 @@ + memtype = KVADDR; + count = -1; + +- while ((c = getopt(argcnt, args, "e:pudDuso:81:3:6:")) != EOF) { ++ while ((c = getopt(argcnt, args, "xme:pfudDusSo:81:3:6:")) != EOF) { + switch(c) + { + case '8': +@@ -731,12 +931,15 @@ + break; + + case 's': +- if (flag & DISPLAY_DEFAULT) ++ case 'S': ++ if (flag & DISPLAY_DEFAULT) { + flag |= SYMBOLIC; +- else { +- error(INFO, +- "-s only allowed with %d-bit display\n", +- DISPLAY_DEFAULT == DISPLAY_64 ? ++ if (c == 'S') ++ flag |= SLAB_CACHE; ++ } else { ++ error(INFO, "-%c option" ++ " is only allowed with %d-bit display\n", ++ c, DISPLAY_DEFAULT == DISPLAY_64 ? + 64 : 32); + argerrs++; + } +@@ -748,12 +951,12 @@ + break; + + case 'p': +- memtype &= ~(UVADDR|KVADDR); ++ memtype &= ~(UVADDR|KVADDR|XENMACHADDR|FILEADDR); + memtype = PHYSADDR; + break; + + case 'u': +- memtype &= ~(KVADDR|PHYSADDR); ++ memtype &= ~(KVADDR|PHYSADDR|XENMACHADDR|FILEADDR); + memtype = UVADDR; + break; + +@@ -767,6 +970,25 @@ + flag |= UDECIMAL; + break; + ++ case 'm': ++ if (!(kt->flags & ARCH_XEN)) ++ error(FATAL, "-m option only applies to xen architecture\n"); ++ memtype &= ~(UVADDR|KVADDR|FILEADDR); ++ memtype = XENMACHADDR; ++ break; ++ ++ case 'f': ++ if (!pc->dumpfile) ++ error(FATAL, ++ "-f option requires a dumpfile\n"); ++ memtype &= ~(KVADDR|UVADDR|PHYSADDR|XENMACHADDR); ++ memtype = FILEADDR; ++ break; ++ ++ case 'x': ++ flag |= NO_ASCII; ++ break; ++ + default: + argerrs++; + break; +@@ -830,7 +1052,7 @@ + error(WARNING, + "ending address ignored when count is specified\n"); + +- if ((flag & HEXADECIMAL) && !(flag & SYMBOLIC)) ++ if ((flag & HEXADECIMAL) && !(flag & SYMBOLIC) && !(flag & NO_ASCII)) + flag |= ASCII_ENDLINE; + + if (memtype == KVADDR) { +@@ -839,7 +1061,6 @@ + } + + display_memory(addr, count, flag, memtype); +- + } + + /* +@@ -866,6 +1087,7 @@ + uint16_t u16; + uint32_t u32; + uint64_t u64; ++ uint64_t limit64; + }; + + static void +@@ -884,6 +1106,7 @@ + char ch; + int linelen; + char buf[BUFSIZE]; ++ char slab[BUFSIZE]; + int ascii_start; + char *hex_64_fmt = BITS32() ? "%.*llx " : "%.*lx "; + char *dec_64_fmt = BITS32() ? "%12lld " : "%15ld "; +@@ -903,6 +1126,12 @@ + case PHYSADDR: + addrtype = "PHYSADDR"; + break; ++ case XENMACHADDR: ++ addrtype = "XENMACHADDR"; ++ break; ++ case FILEADDR: ++ addrtype = "FILEADDR"; ++ break; + } + + if (CRASHDEBUG(4)) +@@ -910,6 +1139,7 @@ + addr, count, flag, addrtype); + + origaddr = addr; ++ BZERO(&mem, sizeof(struct memloc)); + + switch (flag & (DISPLAY_TYPES)) + { +@@ -919,6 +1149,8 @@ + location = &mem.u64; + sprintf(readtype, "64-bit %s", addrtype); + per_line = ENTRIES_64; ++ if (machine_type("IA64")) ++ mem.limit64 = kt->end; + break; + + case DISPLAY_32: +@@ -970,9 +1202,23 @@ + case DISPLAY_64: + if ((flag & (HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) == + (HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) { +- if (in_ksymbol_range(mem.u64)) { +- fprintf(fp, "%-16s ", +- value_to_symstr(mem.u64, buf, 0)); ++ if ((!mem.limit64 || (mem.u64 <= mem.limit64)) && ++ in_ksymbol_range(mem.u64) && ++ strlen(value_to_symstr(mem.u64, buf, 0))) { ++ fprintf(fp, "%-16s ", buf); ++ linelen += strlen(buf)+1; ++ break; ++ } ++ if ((flag & SLAB_CACHE) && ++ vaddr_to_kmem_cache(mem.u64, slab, ++ !VERBOSE)) { ++ if (CRASHDEBUG(1)) ++ sprintf(buf, "[%llx:%s]", ++ (ulonglong)mem.u64, ++ slab); ++ else ++ sprintf(buf, "[%s]", slab); ++ fprintf(fp, "%-16s ", buf); + linelen += strlen(buf)+1; + break; + } +@@ -993,11 +1239,23 @@ + case DISPLAY_32: + if ((flag & (HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) == + (HEXADECIMAL|SYMBOLIC|DISPLAY_DEFAULT)) { +- if (in_ksymbol_range(mem.u32)) { ++ if (in_ksymbol_range(mem.u32) && ++ strlen(value_to_symstr(mem.u32, buf, 0))) { ++ fprintf(fp, INT_PRLEN == 16 ? ++ "%-16s " : "%-8s ", buf); ++ linelen += strlen(buf)+1; ++ break; ++ } ++ if ((flag & SLAB_CACHE) && ++ vaddr_to_kmem_cache(mem.u32, slab, ++ !VERBOSE)) { ++ if (CRASHDEBUG(1)) ++ sprintf(buf, "[%x:%s]", ++ mem.u32, slab); ++ else ++ sprintf(buf, "[%s]", slab); + fprintf(fp, INT_PRLEN == 16 ? +- "%-16s " : "%-8s ", +- value_to_symstr(mem.u32, +- buf, 0)); ++ "%-16s " : "%-8s ", buf); + linelen += strlen(buf)+1; + break; + } +@@ -1138,7 +1396,7 @@ + size = sizeof(void*); + addr_entered = value_entered = FALSE; + +- while ((c = getopt(argcnt, args, "ukp81:3:6:")) != EOF) { ++ while ((c = getopt(argcnt, args, "fukp81:3:6:")) != EOF) { + switch(c) + { + case '8': +@@ -1173,17 +1431,33 @@ + break; + + case 'p': ++ memtype &= ~(UVADDR|KVADDR|FILEADDR); + memtype = PHYSADDR; + break; + + case 'u': ++ memtype &= ~(PHYSADDR|KVADDR|FILEADDR); + memtype = UVADDR; + break; + + case 'k': ++ memtype &= ~(PHYSADDR|UVADDR|FILEADDR); + memtype = KVADDR; + break; + ++ case 'f': ++ /* ++ * Unsupported, but can be forcibly implemented ++ * by removing the DUMPFILE() check above and ++ * recompiling. ++ */ ++ if (!pc->dumpfile) ++ error(FATAL, ++ "-f option requires a dumpfile\n"); ++ memtype &= ~(PHYSADDR|UVADDR|KVADDR); ++ memtype = FILEADDR; ++ break; ++ + default: + argerrs++; + break; +@@ -1262,6 +1536,9 @@ + case PHYSADDR: + break; + ++ case FILEADDR: ++ break; ++ + case AMBIGUOUS: + error(INFO, + "ambiguous address: %llx (requires -p, -u or -k)\n", +@@ -1309,6 +1586,8 @@ + raw_data_dump(ulong addr, long count, int symbolic) + { + long wordcnt; ++ ulonglong address; ++ int memtype; + + switch (sizeof(long)) + { +@@ -1328,9 +1607,20 @@ + break; + } + +- display_memory(addr, wordcnt, ++ if (pc->curcmd_flags & MEMTYPE_FILEADDR) { ++ address = pc->curcmd_private; ++ memtype = FILEADDR; ++ } else if (pc->curcmd_flags & MEMTYPE_UVADDR) { ++ address = (ulonglong)addr; ++ memtype = UVADDR; ++ } else { ++ address = (ulonglong)addr; ++ memtype = KVADDR; ++ } ++ ++ display_memory(address, wordcnt, + HEXADECIMAL|DISPLAY_DEFAULT|(symbolic ? SYMBOLIC : ASCII_ENDLINE), +- KVADDR); ++ memtype); + } + + /* +@@ -1351,7 +1641,7 @@ + * is appropriate: + * + * addr a user, kernel or physical memory address. +- * memtype addr type: UVADDR, KVADDR or PHYSADDR. ++ * memtype addr type: UVADDR, KVADDR, PHYSADDR, XENMACHADDR or FILEADDR + * buffer supplied buffer to read the data into. + * size number of bytes to read. + * type string describing the request -- helpful when the read fails. +@@ -1368,6 +1658,7 @@ + #define SEEK_ERRMSG "seek error: %s address: %llx type: \"%s\"\n" + #define READ_ERRMSG "read error: %s address: %llx type: \"%s\"\n" + #define WRITE_ERRMSG "write error: %s address: %llx type: \"%s\"\n" ++#define PAGE_EXCLUDED_ERRMSG "page excluded: %s address: %llx type: \"%s\"\n" + + int + readmem(ulonglong addr, int memtype, void *buffer, long size, +@@ -1376,6 +1667,7 @@ + int fd; + long cnt; + physaddr_t paddr; ++ ulonglong pseudo; + char *bufptr; + + if (CRASHDEBUG(4)) +@@ -1424,7 +1716,11 @@ + break; + + case PHYSADDR: ++ case XENMACHADDR: + break; ++ ++ case FILEADDR: ++ return generic_read_dumpfile(addr, buffer, size, type, error_handle); + } + + while (size > 0) { +@@ -1449,6 +1745,17 @@ + case PHYSADDR: + paddr = addr; + break; ++ ++ case XENMACHADDR: ++ pseudo = xen_m2p(addr); ++ ++ if (pseudo == XEN_MACHADDR_NOT_FOUND) { ++ pc->curcmd_flags |= XEN_MACHINE_ADDR; ++ paddr = addr; ++ } else ++ paddr = pseudo | PAGEOFFSET(addr); ++ ++ break; + } + + /* +@@ -1460,7 +1767,7 @@ + cnt = size; + + switch (READMEM(fd, bufptr, cnt, +- memtype == PHYSADDR ? 0 : addr, paddr)) ++ (memtype == PHYSADDR) || (memtype == XENMACHADDR) ? 0 : addr, paddr)) + { + case SEEK_ERROR: + if (PRINT_ERROR_MESSAGE) +@@ -1472,6 +1779,11 @@ + error(INFO, READ_ERRMSG, memtype_string(memtype, 0), addr, type); + goto readmem_error; + ++ case PAGE_EXCLUDED: ++ if (PRINT_ERROR_MESSAGE) ++ error(INFO, PAGE_EXCLUDED_ERRMSG, memtype_string(memtype, 0), addr, type); ++ goto readmem_error; ++ + default: + break; + } +@@ -1610,6 +1922,9 @@ + int + read_memory_device(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) + { ++ if (pc->curcmd_flags & XEN_MACHINE_ADDR) ++ return READ_ERROR; ++ + if (!machdep->verify_paddr(paddr)) { + if (CRASHDEBUG(1)) + error(INFO, "verify_paddr(%lx) failed\n", paddr); +@@ -1754,6 +2069,12 @@ + case PHYSADDR: + sprintf(membuf, debug ? "PHYSADDR" : "physical"); + break; ++ case XENMACHADDR: ++ sprintf(membuf, debug ? "XENMACHADDR" : "xen machine"); ++ break; ++ case FILEADDR: ++ sprintf(membuf, debug ? "FILEADDR" : "dumpfile"); ++ break; + default: + if (debug) + sprintf(membuf, "0x%x (?)", memtype); +@@ -1849,6 +2170,10 @@ + + case PHYSADDR: + break; ++ ++ ++ case FILEADDR: ++ return generic_write_dumpfile(addr, buffer, size, type, error_handle); + } + + while (size > 0) { +@@ -1946,6 +2271,77 @@ + } + + /* ++ * Generic dumpfile read/write functions to handle FILEADDR ++ * memtype arguments to readmem() and writemem(). These are ++ * not to be confused with pc->readmem/writemem plug-ins. ++ */ ++static int ++generic_read_dumpfile(ulonglong addr, void *buffer, long size, char *type, ++ ulong error_handle) ++{ ++ int fd; ++ int retval; ++ ++ retval = TRUE; ++ ++ if (!pc->dumpfile) ++ error(FATAL, "command requires a dumpfile\n"); ++ ++ if ((fd = open(pc->dumpfile, O_RDONLY)) < 0) ++ error(FATAL, "%s: %s\n", pc->dumpfile, ++ strerror(errno)); ++ ++ if (lseek(fd, addr, SEEK_SET) == -1) { ++ if (PRINT_ERROR_MESSAGE) ++ error(INFO, SEEK_ERRMSG, ++ memtype_string(FILEADDR, 0), addr, type); ++ retval = FALSE; ++ } else if (read(fd, buffer, size) != size) { ++ if (PRINT_ERROR_MESSAGE) ++ error(INFO, READ_ERRMSG, ++ memtype_string(FILEADDR, 0), addr, type); ++ retval = FALSE; ++ } ++ ++ close(fd); ++ ++ return retval; ++} ++ ++static int ++generic_write_dumpfile(ulonglong addr, void *buffer, long size, char *type, ++ ulong error_handle) ++{ ++ int fd; ++ int retval; ++ ++ retval = TRUE; ++ ++ if (!pc->dumpfile) ++ error(FATAL, "command requires a dumpfile\n"); ++ ++ if ((fd = open(pc->dumpfile, O_WRONLY)) < 0) ++ error(FATAL, "%s: %s\n", pc->dumpfile, ++ strerror(errno)); ++ ++ if (lseek(fd, addr, SEEK_SET) == -1) { ++ if (PRINT_ERROR_MESSAGE) ++ error(INFO, SEEK_ERRMSG, ++ memtype_string(FILEADDR, 0), addr, type); ++ retval = FALSE; ++ } else if (write(fd, buffer, size) != size) { ++ if (PRINT_ERROR_MESSAGE) ++ error(INFO, WRITE_ERRMSG, ++ memtype_string(FILEADDR, 0), addr, type); ++ retval = FALSE; ++ } ++ ++ close(fd); ++ ++ return retval; ++} ++ ++/* + * Translates a kernel virtual address to its physical address. cmd_vtop() + * sets the verbose flag so that the pte translation gets displayed; all + * other callers quietly accept the translation. +@@ -2113,6 +2509,8 @@ + break; + } + ++ paddr = 0; ++ + switch (memtype) { + case UVADDR: + fprintf(fp, "%s %s\n", +@@ -2126,9 +2524,12 @@ + return; + } + if (!uvtop(tc, vaddr, &paddr, 0)) { +- fprintf(fp, "%s (not mapped)\n\n", ++ fprintf(fp, "%s %s\n\n", + mkstring(buf1, UVADDR_PRLEN, LJUST|LONG_HEX, +- MKSTR(vaddr))); ++ MKSTR(vaddr)), ++ (XEN() && (paddr == PADDR_NOT_AVAILABLE)) ? ++ "(page not available)" : "(not mapped)"); ++ + page_exists = FALSE; + } else { + fprintf(fp, "%s %s\n\n", +@@ -2161,9 +2562,13 @@ + } + if (vtop_flags & USE_USER_PGD) { + if (!uvtop(tc, vaddr, &paddr, 0)) { +- fprintf(fp, "%s (not mapped)\n\n", ++ fprintf(fp, "%s %s\n\n", + mkstring(buf1, UVADDR_PRLEN, +- LJUST|LONG_HEX, MKSTR(vaddr))); ++ LJUST|LONG_HEX, MKSTR(vaddr)), ++ (XEN() && ++ (paddr == PADDR_NOT_AVAILABLE)) ? ++ "(page not available)" : ++ "(not mapped)"); + page_exists = FALSE; + } else { + fprintf(fp, "%s %s\n\n", +@@ -2176,9 +2581,13 @@ + uvtop(tc, vaddr, &paddr, VERBOSE); + } else { + if (!kvtop(tc, vaddr, &paddr, 0)) { +- fprintf(fp, "%s (not mapped)\n\n", ++ fprintf(fp, "%s %s\n\n", + mkstring(buf1, VADDR_PRLEN, +- LJUST|LONG_HEX, MKSTR(vaddr))); ++ LJUST|LONG_HEX, MKSTR(vaddr)), ++ (XEN() && ++ (paddr == PADDR_NOT_AVAILABLE)) ? ++ "(page not available)" : ++ "(not mapped)"); + page_exists = FALSE; + } else { + fprintf(fp, "%s %s\n\n", +@@ -2568,7 +2977,7 @@ + #define VM_REF_CHECK_DECVAL(X,V) \ + (DO_REF_SEARCH(X) && ((X)->cmdflags & VM_REF_NUMBER) && ((X)->decval == (V))) + #define VM_REF_CHECK_STRING(X,S) \ +- (DO_REF_SEARCH(X) && (S) && FILENAME_COMPONENT((S),(X)->str)) ++ (DO_REF_SEARCH(X) && (string_exists(S)) && FILENAME_COMPONENT((S),(X)->str)) + #define VM_REF_FOUND(X) ((X) && ((X)->cmdflags & VM_REF_HEADER)) + + ulong +@@ -2839,7 +3248,8 @@ + + if (DO_REF_SEARCH(ref)) { + if (VM_REF_CHECK_DECVAL(ref, +- SWP_OFFSET(paddr))) { ++ THIS_KERNEL_VERSION >= LINUX(2,6,0) ? ++ __swp_offset(paddr) : SWP_OFFSET(paddr))) { + if (DO_REF_DISPLAY(ref)) + display = TRUE; + else { +@@ -2979,7 +3386,20 @@ + if (!task_mm(task, TRUE)) + return; + +- tm->rss = ULONG(tt->mm_struct + OFFSET(mm_struct_rss)); ++ if (VALID_MEMBER(mm_struct_rss)) ++ /* ++ * mm_struct.rss or mm_struct._rss exist. ++ */ ++ tm->rss = ULONG(tt->mm_struct + OFFSET(mm_struct_rss)); ++ else { ++ /* ++ * mm_struct._anon_rss and mm_struct._file_rss should exist. ++ */ ++ if (VALID_MEMBER(mm_struct_anon_rss)) ++ tm->rss += ULONG(tt->mm_struct + OFFSET(mm_struct_anon_rss)); ++ if (VALID_MEMBER(mm_struct_file_rss)) ++ tm->rss += ULONG(tt->mm_struct + OFFSET(mm_struct_file_rss)); ++ } + tm->total_vm = ULONG(tt->mm_struct + OFFSET(mm_struct_total_vm)); + tm->pgd_addr = ULONG(tt->mm_struct + OFFSET(mm_struct_pgd)); + +@@ -3036,6 +3456,12 @@ + #define GET_INACTIVE_DIRTY (ADDRESS_SPECIFIED << 13) /* obsolete */ + #define SLAB_GET_COUNTS (ADDRESS_SPECIFIED << 14) + #define SLAB_WALKTHROUGH (ADDRESS_SPECIFIED << 15) ++#define GET_VMLIST_COUNT (ADDRESS_SPECIFIED << 16) ++#define GET_VMLIST (ADDRESS_SPECIFIED << 17) ++#define SLAB_DATA_NOSAVE (ADDRESS_SPECIFIED << 18) ++#define GET_SLUB_SLABS (ADDRESS_SPECIFIED << 19) ++#define GET_SLUB_OBJECTS (ADDRESS_SPECIFIED << 20) ++#define VMLIST_VERIFY (ADDRESS_SPECIFIED << 21) + + #define GET_ALL \ + (GET_SHARED_PAGES|GET_TOTALRAM_PAGES|GET_BUFFERS_PAGES|GET_SLAB_PAGES) +@@ -3045,8 +3471,8 @@ + { + int i; + int c; +- int sflag, Sflag, pflag, fflag, Fflag, vflag; +- int nflag, cflag, Cflag, iflag, lflag, Lflag, Pflag; ++ int sflag, Sflag, pflag, fflag, Fflag, vflag, zflag; ++ int nflag, cflag, Cflag, iflag, lflag, Lflag, Pflag, Vflag; + struct meminfo meminfo; + ulonglong value[MAXARGS]; + char buf[BUFSIZE]; +@@ -3054,18 +3480,26 @@ + int spec_addr; + + spec_addr = 0; +- sflag = Sflag = pflag = fflag = Fflag = Pflag = 0; +- vflag = Cflag = cflag = iflag = nflag = lflag = Lflag = 0; ++ sflag = Sflag = pflag = fflag = Fflag = Pflag = zflag = 0; ++ vflag = Cflag = cflag = iflag = nflag = lflag = Lflag = Vflag = 0; + BZERO(&meminfo, sizeof(struct meminfo)); + BZERO(&value[0], sizeof(ulonglong)*MAXARGS); + +- while ((c = getopt(argcnt, args, "I:sSFfpvcCinl:L:P")) != EOF) { ++ while ((c = getopt(argcnt, args, "I:sSFfpvczCinl:L:PV")) != EOF) { + switch(c) + { ++ case 'V': ++ Vflag = 1; ++ break; ++ + case 'n': + nflag = 1; + break; + ++ case 'z': ++ zflag = 1; ++ break; ++ + case 'i': + iflag = 1; + break; +@@ -3153,13 +3587,13 @@ + if (argerrs) + cmd_usage(pc->curcmd, SYNOPSIS); + +- if ((sflag + Sflag + pflag + fflag + Fflag + ++ if ((sflag + Sflag + pflag + fflag + Fflag + Vflag + + vflag + Cflag + cflag + iflag + lflag + Lflag) > 1) { + error(INFO, "only one flag allowed!\n"); + cmd_usage(pc->curcmd, SYNOPSIS); + } + +- if (sflag || Sflag) ++ if (sflag || Sflag || !(vt->flags & KMEM_CACHE_INIT)) + kmem_cache_init(); + + while (args[optind]) { +@@ -3198,8 +3632,6 @@ + if (pflag) { + meminfo.spec_addr = value[i]; + meminfo.flags = ADDRESS_SPECIFIED; +- if (meminfo.calls++) +- fprintf(fp, "\n"); + dump_mem_map(&meminfo); + pflag++; + } +@@ -3234,6 +3666,8 @@ + } else { + meminfo.spec_addr = value[i]; + meminfo.flags = ADDRESS_SPECIFIED; ++ if (Sflag && (vt->flags & KMALLOC_SLUB)) ++ meminfo.flags |= VERBOSE; + if (meminfo.calls++) + fprintf(fp, "\n"); + vt->dump_kmem_cache(&meminfo); +@@ -3248,8 +3682,6 @@ + if (vflag) { + meminfo.spec_addr = value[i]; + meminfo.flags = ADDRESS_SPECIFIED; +- if (meminfo.calls++) +- fprintf(fp, "\n"); + dump_vmlist(&meminfo); + vflag++; + } +@@ -3275,7 +3707,7 @@ + /* + * no value arguments allowed! + */ +- if (nflag || iflag || Fflag || Cflag || Lflag) { ++ if (zflag || nflag || iflag || Fflag || Cflag || Lflag || Vflag) { + error(INFO, + "no address arguments allowed with this option\n"); + cmd_usage(pc->curcmd, SYNOPSIS); +@@ -3309,24 +3741,25 @@ + } + + if (sflag == 1) { +- if (vt->flags & KMEM_CACHE_UNAVAIL) +- error(FATAL, +- "kmem cache slab subsystem not available\n"); + if (STREQ(meminfo.reqname, "list")) + kmem_cache_list(); ++ else if (vt->flags & KMEM_CACHE_UNAVAIL) ++ error(FATAL, ++ "kmem cache slab subsystem not available\n"); + else + vt->dump_kmem_cache(&meminfo); + } + + if (Sflag == 1) { +- if (vt->flags & KMEM_CACHE_UNAVAIL) +- error(FATAL, +- "kmem cache slab subsystem not available\n"); +- meminfo.flags = VERBOSE; + if (STREQ(meminfo.reqname, "list")) + kmem_cache_list(); +- else ++ else if (vt->flags & KMEM_CACHE_UNAVAIL) ++ error(FATAL, ++ "kmem cache slab subsystem not available\n"); ++ else { ++ meminfo.flags = VERBOSE; + vt->dump_kmem_cache(&meminfo); ++ } + } + + if (vflag == 1) +@@ -3343,6 +3776,9 @@ + if (nflag == 1) + dump_memory_nodes(MEMORY_NODES_DUMP); + ++ if (zflag == 1) ++ dump_zone_stats(); ++ + if (lflag == 1) { + dump_page_lists(&meminfo); + } +@@ -3352,7 +3788,13 @@ + dump_page_lists(&meminfo); + } + +- if (!(sflag + Sflag + pflag + fflag + Fflag + vflag + ++ if (Vflag == 1) { ++ dump_vm_stat(NULL, NULL, 0); ++ dump_page_states(); ++ dump_vm_event_state(); ++ } ++ ++ if (!(sflag + Sflag + pflag + fflag + Fflag + vflag + Vflag + zflag + + cflag + Cflag + iflag + nflag + lflag + Lflag + meminfo.calls)) + cmd_usage(pc->curcmd, SYNOPSIS); + +@@ -3373,12 +3815,13 @@ + buf = (char *)GETBUF(SIZE(page)); + + if (!readmem(pageptr, KVADDR, buf, SIZE(page), +- "reserved page", RETURN_ON_ERROR|QUIET)) ++ "reserved page", RETURN_ON_ERROR|QUIET)) { ++ FREEBUF(buf); + return; ++ } + + flags = ULONG(buf + OFFSET(page_flags)); + +- + if (count_bits_long(flags) == 1) + vt->PG_reserved = flags; + else +@@ -3386,12 +3829,64 @@ + + if (CRASHDEBUG(2)) + fprintf(fp, +- "PG_reserved bit: vaddr: %lx page: %lx flags: %lx => %lx\n", ++ "PG_reserved: vaddr: %lx page: %lx flags: %lx => %lx\n", + vaddr, pageptr, flags, vt->PG_reserved); + + FREEBUF(buf); + } + ++static void ++PG_slab_flag_init(void) ++{ ++ int bit; ++ ulong pageptr; ++ ulong vaddr, flags; ++ char buf[BUFSIZE]; /* safe for a page struct */ ++ ++ /* ++ * Set the old defaults in case the search below fails. ++ */ ++ if (VALID_MEMBER(page_pte)) { ++ if (THIS_KERNEL_VERSION < LINUX(2,6,0)) ++ vt->PG_slab = 10; ++ else if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) ++ vt->PG_slab = 7; ++ } else if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) ++ vt->PG_slab = 7; ++ ++ if (vt->flags & KMALLOC_SLUB) { ++ /* ++ * PG_slab and the following are hardwired for ++ * now -- at least until I can come up with ++ * better way. (PG_slab test below fails because ++ * slub.c uses lower-bit PG_active and PG_error) ++ */ ++#define PG_compound 14 /* Part of a compound page */ ++#define PG_reclaim 17 /* To be reclaimed asap */ ++ vt->PG_head_tail_mask = ((1L << PG_compound) | (1L << PG_reclaim)); ++ ++ return; ++ } ++ ++ if (try_get_symbol_data("vm_area_cachep", sizeof(void *), &vaddr) && ++ phys_to_page((physaddr_t)VTOP(vaddr), &pageptr) && ++ readmem(pageptr, KVADDR, buf, SIZE(page), ++ "vm_area_cachep page", RETURN_ON_ERROR|QUIET)) { ++ ++ flags = ULONG(buf + OFFSET(page_flags)); ++ ++ if ((bit = ffsl(flags))) { ++ vt->PG_slab = bit - 1; ++ ++ if (CRASHDEBUG(2)) ++ fprintf(fp, ++ "PG_slab bit: vaddr: %lx page: %lx flags: %lx => %ld\n", ++ vaddr, pageptr, flags, vt->PG_slab); ++ ++ } ++ } ++} ++ + /* + * dump_mem_map() displays basic data about each entry in the mem_map[] + * array, or if an address is specified, just the mem_map[] entry for that +@@ -3438,22 +3933,20 @@ + #define PGMM_CACHED (512) + + static void +-dump_mem_map(struct meminfo *mi) ++dump_mem_map_SPARSEMEM(struct meminfo *mi) + { +- long i, n; ++ ulong i; + long total_pages; +- int others, page_not_mapped, phys_not_mapped; ++ int others, page_not_mapped, phys_not_mapped, page_mapping; + ulong pp, ppend; + physaddr_t phys, physend; + ulong tmp, reserved, shared, slabs; + ulong PG_reserved_flag; + long buffers; + ulong inode, offset, flags, mapping, index; +- ulong node_size; + uint count; + int print_hdr, pg_spec, phys_spec, done; + int v22; +- struct node_table *nt; + char hdr[BUFSIZE]; + char buf0[BUFSIZE]; + char buf1[BUFSIZE]; +@@ -3462,6 +3955,7 @@ + char buf4[BUFSIZE]; + char *page_cache; + char *pcache; ++ ulong section, section_nr, nr_mem_sections, section_size; + + v22 = VALID_MEMBER(page_inode); /* page.inode vs. page.mapping */ + +@@ -3549,22 +4043,62 @@ + done = FALSE; + total_pages = 0; + +- for (n = 0; n < vt->numnodes; n++) { ++ nr_mem_sections = NR_MEM_SECTIONS(); ++ ++ /* ++ * Iterate over all possible sections ++ */ ++ for (section_nr = 0; section_nr < nr_mem_sections ; section_nr++) { ++ ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "section_nr = %ld\n", section_nr); ++ ++ /* ++ * If we are looking up a specific address, jump directly ++ * to the section with that page ++ */ ++ if (mi->flags & ADDRESS_SPECIFIED) { ++ ulong pfn; ++ physaddr_t tmp; ++ ++ if (pg_spec) { ++ if (!page_to_phys(mi->spec_addr, &tmp)) ++ return; ++ pfn = tmp >> PAGESHIFT(); ++ } else ++ pfn = mi->spec_addr >> PAGESHIFT(); ++ section_nr = pfn_to_section_nr(pfn); ++ } ++ ++ if (!(section = valid_section_nr(section_nr))) { ++#ifdef NOTDEF ++ break; /* On a real sparsemem system we need to check ++ * every section as gaps may exist. But this ++ * can be slow. If we know we don't have gaps ++ * just stop validating sections when we ++ * get to the end of the valid ones. ++ * In the future find a way to short circuit ++ * this loop. ++ */ ++#endif ++ if (mi->flags & ADDRESS_SPECIFIED) ++ break; ++ continue; ++ } ++ + if (print_hdr) { +- fprintf(fp, "%s%s", n ? "\n" : "", hdr); ++ if (!(pc->curcmd_flags & HEADER_PRINTED)) ++ fprintf(fp, "%s", hdr); + print_hdr = FALSE; ++ pc->curcmd_flags |= HEADER_PRINTED; + } + +- nt = &vt->node_table[n]; +- total_pages += nt->size; +- pp = nt->mem_map; +- phys = nt->start_paddr; +- if ((vt->flags & V_MEM_MAP) && (vt->numnodes == 1)) +- node_size = vt->max_mapnr; +- else +- node_size = nt->size; ++ pp = section_mem_map_addr(section); ++ pp = sparse_decode_mem_map(pp, section_nr); ++ phys = (physaddr_t) section_nr * PAGES_PER_SECTION() * PAGESIZE(); ++ section_size = PAGES_PER_SECTION(); + +- for (i = 0; i < node_size; ++ for (i = 0; i < section_size; + i++, pp += SIZE(page), phys += PAGESIZE()) { + + if ((i % PGMM_CACHED) == 0) { +@@ -3581,7 +4115,7 @@ + continue; + } + +- fill_mem_map_cache(pp, page_cache); ++ fill_mem_map_cache(pp, ppend, page_cache); + } + + pcache = page_cache + ((i%PGMM_CACHED) * SIZE(page)); +@@ -3653,11 +4187,12 @@ + } + continue; + } ++ page_mapping = VALID_MEMBER(page_mapping); + + if (v22) { + inode = ULONG(pcache + OFFSET(page_inode)); + offset = ULONG(pcache + OFFSET(page_offset)); +- } else { ++ } else if (page_mapping) { + mapping = ULONG(pcache + + OFFSET(page_mapping)); + index = ULONG(pcache + OFFSET(page_index)); +@@ -3700,6 +4235,20 @@ + space(MINSPACE), + mkstring(buf4, 8, CENTER|RJUST, " "), + " "); ++ else if (!page_mapping) ++ fprintf(fp, "%s%s%s%s%s%s%s %2d ", ++ mkstring(buf0, VADDR_PRLEN, ++ LJUST|LONG_HEX, MKSTR(pp)), ++ space(MINSPACE), ++ mkstring(buf1, MAX(PADDR_PRLEN, ++ strlen("PHYSICAL")), ++ RJUST|LONGLONG_HEX, MKSTR(&phys)), ++ space(MINSPACE), ++ mkstring(buf3, VADDR_PRLEN, ++ CENTER|RJUST, "-------"), ++ space(MINSPACE), ++ mkstring(buf4, 8, CENTER|RJUST, "-----"), ++ count); + else + fprintf(fp, "%s%s%s%s%s%s%8ld %2d ", + mkstring(buf0, VADDR_PRLEN, +@@ -3862,6379 +4411,9961 @@ + FREEBUF(page_cache); + } + +-/* +- * Stash a chunk of PGMM_CACHED page structures, starting at addr, into the +- * passed-in buffer. The mem_map array is normally guaranteed to be +- * readable except in the case of virtual mem_map usage. When V_MEM_MAP +- * is in place, read all pages consumed by PGMM_CACHED page structures +- * that are currently mapped, leaving the unmapped ones just zeroed out. +- */ + static void +-fill_mem_map_cache(ulong pp, char *page_cache) ++dump_mem_map(struct meminfo *mi) + { +- long size, cnt; +- ulong addr; +- char *bufptr; ++ long i, n; ++ long total_pages; ++ int others, page_not_mapped, phys_not_mapped, page_mapping; ++ ulong pp, ppend; ++ physaddr_t phys, physend; ++ ulong tmp, reserved, shared, slabs; ++ ulong PG_reserved_flag; ++ long buffers; ++ ulong inode, offset, flags, mapping, index; ++ ulong node_size; ++ uint count; ++ int print_hdr, pg_spec, phys_spec, done; ++ int v22; ++ struct node_table *nt; ++ char hdr[BUFSIZE]; ++ char buf0[BUFSIZE]; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; ++ char buf4[BUFSIZE]; ++ char *page_cache; ++ char *pcache; + +- /* +- * Try to read it in one fell swoop. +- */ +- if (readmem(pp, KVADDR, page_cache, SIZE(page) * PGMM_CACHED, +- "page struct cache", RETURN_ON_ERROR|QUIET)) ++ if (IS_SPARSEMEM()) { ++ dump_mem_map_SPARSEMEM(mi); + return; ++ } + +- /* +- * Break it into page-size-or-less requests, warning if it's +- * not a virtual mem_map. +- */ +- size = SIZE(page) * PGMM_CACHED; +- addr = pp; +- bufptr = page_cache; ++ v22 = VALID_MEMBER(page_inode); /* page.inode vs. page.mapping */ + +- while (size > 0) { +- /* +- * Compute bytes till end of page. +- */ +- cnt = PAGESIZE() - PAGEOFFSET(addr); +- +- if (cnt > size) +- cnt = size; ++ if (v22) { ++ sprintf(hdr, "%s%s%s%s%s%s%s%sCNT FLAGS\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), ++ space(MINSPACE), ++ mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")), ++ RJUST, "PHYSICAL"), ++ space(MINSPACE), ++ mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "INODE"), ++ space(MINSPACE), ++ mkstring(buf4, 8, CENTER|LJUST, "OFFSET"), ++ space(MINSPACE-1)); ++ } else { ++ sprintf(hdr, "%s%s%s%s%s%s%sCNT FLAGS\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER, "PAGE"), ++ space(MINSPACE), ++ mkstring(buf2, MAX(PADDR_PRLEN, strlen("PHYSICAL")), ++ RJUST, "PHYSICAL"), ++ space(MINSPACE), ++ mkstring(buf3, VADDR_PRLEN, CENTER|RJUST, "MAPPING"), ++ space(MINSPACE), ++ mkstring(buf4, 8, CENTER|RJUST, "INDEX")); ++ } + +- if (!readmem(addr, KVADDR, bufptr, size, +- "virtual page struct cache", RETURN_ON_ERROR|QUIET)) { +- BZERO(bufptr, size); +- if (!(vt->flags & V_MEM_MAP)) +- error(WARNING, +- "mem_map[] from %lx to %lx not accessible\n", +- addr, addr+size); ++ pg_spec = phys_spec = print_hdr = FALSE; ++ ++ switch (mi->flags) ++ { ++ case ADDRESS_SPECIFIED: ++ switch (mi->memtype) ++ { ++ case KVADDR: ++ if (is_page_ptr(mi->spec_addr, NULL)) ++ pg_spec = TRUE; ++ else { ++ if (kvtop(NULL, mi->spec_addr, &phys, 0)) { ++ mi->spec_addr = phys; ++ phys_spec = TRUE; ++ } ++ else ++ return; ++ } ++ break; ++ case PHYSADDR: ++ phys_spec = TRUE; ++ break; ++ default: ++ error(FATAL, "dump_mem_map: no memtype specified\n"); ++ break; + } ++ print_hdr = TRUE; ++ break; + +- addr += cnt; +- bufptr += cnt; +- size -= cnt; +- } +-} ++ case GET_ALL: ++ shared = 0; ++ reserved = 0; ++ buffers = 0; ++ slabs = 0; ++ break; + ++ case GET_SHARED_PAGES: ++ shared = 0; ++ break; + +-/* +- * dump_page_hash_table() displays the entries in each page_hash_table. +- */ ++ case GET_TOTALRAM_PAGES: ++ reserved = 0; ++ break; + +-#define PGHASH_CACHED (1024) ++ case GET_BUFFERS_PAGES: ++ buffers = 0; ++ break; + +-static void +-dump_page_hash_table(struct meminfo *hi) +-{ +- int i; +- int len, entry_len; +- ulong page_hash_table, head; +- struct list_data list_data, *ld; +- struct gnu_request req; +- long total_cached; +- long page_cache_size; +- ulong this_addr, searchpage; +- int errflag, found, cnt, populated, verbose; +- uint ival; +- ulong buffer_pages; +- char buf[BUFSIZE]; +- char hash_table[BUFSIZE]; +- char *pcache, *pghash_cache; ++ case GET_SLAB_PAGES: ++ slabs = 0; ++ break; + +- if (!vt->page_hash_table) { +- if (hi->flags & VERBOSE) +- error(FATAL, +- "address_space page cache radix tree not supported\n"); +- +- if (symbol_exists("nr_pagecache")) { +- buffer_pages = nr_blockdev_pages(); +- get_symbol_data("nr_pagecache", sizeof(int), &ival); +- page_cache_size = (ulong)ival; +- page_cache_size -= buffer_pages; +- fprintf(fp, "page cache size: %ld\n", page_cache_size); +- if (hi->flags & ADDRESS_SPECIFIED) +- error(INFO, +- "address_space page cache radix tree not supported: %lx: ignored\n", +- hi->spec_addr); +- } else +- error(FATAL, "cannot determine page cache size\n"); +- return; ++ default: ++ print_hdr = TRUE; ++ break; + } + +- ld = &list_data; +- +- if (hi->spec_addr && (hi->flags & ADDRESS_SPECIFIED)) { +- verbose = TRUE; +- searchpage = hi->spec_addr; +- } else if (hi->flags & VERBOSE) { +- verbose = TRUE; +- searchpage = 0; +- } else { +- verbose = FALSE; +- searchpage = 0; +- } ++ page_cache = GETBUF(SIZE(page) * PGMM_CACHED); ++ done = FALSE; ++ total_pages = 0; + +- if (vt->page_hash_table_len == 0) +- error(FATAL, "cannot determine size of page_hash_table\n"); ++ for (n = 0; n < vt->numnodes; n++) { ++ if (print_hdr) { ++ if (!(pc->curcmd_flags & HEADER_PRINTED)) ++ fprintf(fp, "%s%s", n ? "\n" : "", hdr); ++ print_hdr = FALSE; ++ pc->curcmd_flags |= HEADER_PRINTED; ++ } + +- page_hash_table = vt->page_hash_table; +- len = vt->page_hash_table_len; +- entry_len = VALID_STRUCT(page_cache_bucket) ? +- SIZE(page_cache_bucket) : sizeof(void *); ++ nt = &vt->node_table[n]; ++ total_pages += nt->size; ++ pp = nt->mem_map; ++ phys = nt->start_paddr; ++ if ((vt->flags & V_MEM_MAP) && (vt->numnodes == 1)) ++ node_size = vt->max_mapnr; ++ else ++ node_size = nt->size; + +- if (CRASHDEBUG(1)) { +- populated = 0; +- fprintf(fp, "page_hash_table length: %d\n", len); +- } ++ for (i = 0; i < node_size; ++ i++, pp += SIZE(page), phys += PAGESIZE()) { + +- get_symbol_type("page_cache_size", NULL, &req); +- if (req.length == sizeof(int)) { +- get_symbol_data("page_cache_size", sizeof(int), &ival); +- page_cache_size = (long)ival; +- } else +- get_symbol_data("page_cache_size", sizeof(long), +- &page_cache_size); ++ if ((i % PGMM_CACHED) == 0) { ++ ppend = pp + ((PGMM_CACHED-1) * SIZE(page)); ++ physend = phys + ((PGMM_CACHED-1) * PAGESIZE()); + +- pghash_cache = GETBUF(sizeof(void *) * PGHASH_CACHED); ++ if ((pg_spec && (mi->spec_addr > ppend)) || ++ (phys_spec && ++ (PHYSPAGEBASE(mi->spec_addr) > physend))) { ++ i += (PGMM_CACHED-1); ++ pp = ppend; ++ phys = physend; ++ continue; ++ } + +- if (searchpage) +- open_tmpfile(); ++ fill_mem_map_cache(pp, ppend, page_cache); ++ } + +- hq_open(); +- for (i = total_cached = 0; i < len; i++, +- page_hash_table += entry_len) { ++ pcache = page_cache + ((i%PGMM_CACHED) * SIZE(page)); + +- if ((i % PGHASH_CACHED) == 0) { +- readmem(page_hash_table, KVADDR, pghash_cache, +- entry_len * PGHASH_CACHED, +- "page hash cache", FAULT_ON_ERROR); +- } ++ if (received_SIGINT()) ++ restart(0); ++ ++ if ((pg_spec && (pp == mi->spec_addr)) || ++ (phys_spec && (phys == PHYSPAGEBASE(mi->spec_addr)))) ++ done = TRUE; + +- pcache = pghash_cache + ((i%PGHASH_CACHED) * entry_len); +- if (VALID_STRUCT(page_cache_bucket)) +- pcache += OFFSET(page_cache_bucket_chain); ++ if (!done && (pg_spec || phys_spec)) ++ continue; + +- head = ULONG(pcache); ++ flags = ULONG(pcache + OFFSET(page_flags)); ++ count = UINT(pcache + OFFSET(page_count)); + +- if (!head) +- continue; ++ switch (mi->flags) ++ { ++ case GET_ALL: ++ case GET_BUFFERS_PAGES: ++ if (VALID_MEMBER(page_buffers)) { ++ tmp = ULONG(pcache + ++ OFFSET(page_buffers)); ++ if (tmp) ++ buffers++; ++ } else if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) { ++ if ((flags >> v26_PG_private) & 1) ++ buffers++; ++ } else ++ error(FATAL, ++ "cannot determine whether pages have buffers\n"); + +- if (verbose) +- fprintf(fp, "page_hash_table[%d]\n", i); +- +- if (CRASHDEBUG(1)) +- populated++; ++ if (mi->flags != GET_ALL) ++ continue; + +- BZERO(ld, sizeof(struct list_data)); +- ld->flags = verbose; +- ld->start = head; +- ld->searchfor = searchpage; +- ld->member_offset = OFFSET(page_next_hash); +- cnt = do_list(ld); +- total_cached += cnt; ++ /* FALLTHROUGH */ + +- if (ld->searchfor) +- break; ++ case GET_SLAB_PAGES: ++ if (v22) { ++ if ((flags >> v22_PG_Slab) & 1) ++ slabs++; ++ } else if (vt->PG_slab) { ++ if ((flags >> vt->PG_slab) & 1) ++ slabs++; ++ } else { ++ if ((flags >> v24_PG_slab) & 1) ++ slabs++; ++ } ++ if (mi->flags != GET_ALL) ++ continue; + +- if (received_SIGINT()) +- restart(0); ++ /* FALLTHROUGH */ ++ ++ case GET_SHARED_PAGES: ++ case GET_TOTALRAM_PAGES: ++ if (vt->PG_reserved) ++ PG_reserved_flag = vt->PG_reserved; ++ else ++ PG_reserved_flag = v22 ? ++ 1 << v22_PG_reserved : ++ 1 << v24_PG_reserved; ++ ++ if (flags & PG_reserved_flag) { ++ reserved++; ++ } else { ++ if (count > 1) ++ shared++; ++ } ++ continue; ++ } ++ ++ page_mapping = VALID_MEMBER(page_mapping); ++ ++ if (v22) { ++ inode = ULONG(pcache + OFFSET(page_inode)); ++ offset = ULONG(pcache + OFFSET(page_offset)); ++ } else if (page_mapping) { ++ mapping = ULONG(pcache + ++ OFFSET(page_mapping)); ++ index = ULONG(pcache + OFFSET(page_index)); ++ } ++ ++ page_not_mapped = phys_not_mapped = FALSE; ++ ++ if (v22) { ++ fprintf(fp, "%lx%s%s%s%s%s%8lx %2d%s", ++ pp, ++ space(MINSPACE), ++ mkstring(buf1, MAX(PADDR_PRLEN, ++ strlen("PHYSICAL")), ++ RJUST|LONGLONG_HEX, MKSTR(&phys)), ++ space(MINSPACE), ++ mkstring(buf2, VADDR_PRLEN, ++ RJUST|LONG_HEX, MKSTR(inode)), ++ space(MINSPACE), ++ offset, ++ count, ++ space(MINSPACE)); ++ } else { ++ if ((vt->flags & V_MEM_MAP)) { ++ if (!machdep->verify_paddr(phys)) ++ phys_not_mapped = TRUE; ++ if (!kvtop(NULL, pp, NULL, 0)) ++ page_not_mapped = TRUE; ++ } ++ if (page_not_mapped) ++ fprintf(fp, "%s%s%s%s%s%s%s %2s ", ++ mkstring(buf0, VADDR_PRLEN, ++ LJUST|LONG_HEX, MKSTR(pp)), ++ space(MINSPACE), ++ mkstring(buf1, MAX(PADDR_PRLEN, ++ strlen("PHYSICAL")), ++ RJUST|LONGLONG_HEX, MKSTR(&phys)), ++ space(MINSPACE), ++ mkstring(buf3, VADDR_PRLEN, ++ CENTER|RJUST, " "), ++ space(MINSPACE), ++ mkstring(buf4, 8, CENTER|RJUST, " "), ++ " "); ++ else if (!page_mapping) ++ fprintf(fp, "%s%s%s%s%s%s%s %2d ", ++ mkstring(buf0, VADDR_PRLEN, ++ LJUST|LONG_HEX, MKSTR(pp)), ++ space(MINSPACE), ++ mkstring(buf1, MAX(PADDR_PRLEN, ++ strlen("PHYSICAL")), ++ RJUST|LONGLONG_HEX, MKSTR(&phys)), ++ space(MINSPACE), ++ mkstring(buf3, VADDR_PRLEN, ++ CENTER|RJUST, "-------"), ++ space(MINSPACE), ++ mkstring(buf4, 8, CENTER|RJUST, "-----"), ++ count); ++ else ++ fprintf(fp, "%s%s%s%s%s%s%8ld %2d ", ++ mkstring(buf0, VADDR_PRLEN, ++ LJUST|LONG_HEX, MKSTR(pp)), ++ space(MINSPACE), ++ mkstring(buf1, MAX(PADDR_PRLEN, ++ strlen("PHYSICAL")), ++ RJUST|LONGLONG_HEX, MKSTR(&phys)), ++ space(MINSPACE), ++ mkstring(buf2, VADDR_PRLEN, ++ RJUST|LONG_HEX, MKSTR(mapping)), ++ space(MINSPACE), ++ index, ++ count); ++ } ++ ++ others = 0; ++ ++ if (v22) { ++ if ((flags >> v22_PG_DMA) & 1) ++ fprintf(fp, "%sDMA", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_locked) & 1) ++ fprintf(fp, "%slocked", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_error) & 1) ++ fprintf(fp, "%serror", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_referenced) & 1) ++ fprintf(fp, "%sreferenced", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_dirty) & 1) ++ fprintf(fp, "%sdirty", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_uptodate) & 1) ++ fprintf(fp, "%suptodate", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_free_after) & 1) ++ fprintf(fp, "%sfree_after", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_decr_after) & 1) ++ fprintf(fp, "%sdecr_after", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_swap_unlock_after) & 1) ++ fprintf(fp, "%sswap_unlock_after", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_Slab) & 1) ++ fprintf(fp, "%sslab", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_swap_cache) & 1) ++ fprintf(fp, "%sswap_cache", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_skip) & 1) ++ fprintf(fp, "%sskip", ++ others++ ? "," : ""); ++ if ((flags >> v22_PG_reserved) & 1) ++ fprintf(fp, "%sreserved", ++ others++ ? "," : ""); ++ fprintf(fp, "\n"); ++ } else if (THIS_KERNEL_VERSION > LINUX(2,4,9)) { ++ fprintf(fp, "%lx\n", flags); ++ } else { ++ ++ if ((flags >> v24_PG_locked) & 1) ++ fprintf(fp, "%slocked", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_error) & 1) ++ fprintf(fp, "%serror", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_referenced) & 1) ++ fprintf(fp, "%sreferenced", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_uptodate) & 1) ++ fprintf(fp, "%suptodate", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_dirty) & 1) ++ fprintf(fp, "%sdirty", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_decr_after) & 1) ++ fprintf(fp, "%sdecr_after", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_active) & 1) ++ fprintf(fp, "%sactive", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_inactive_dirty) & 1) ++ fprintf(fp, "%sinactive_dirty", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_slab) & 1) ++ fprintf(fp, "%sslab", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_swap_cache) & 1) ++ fprintf(fp, "%sswap_cache", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_skip) & 1) ++ fprintf(fp, "%sskip", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_inactive_clean) & 1) ++ fprintf(fp, "%sinactive_clean", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_highmem) & 1) ++ fprintf(fp, "%shighmem", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_checked) & 1) ++ fprintf(fp, "%schecked", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_bigpage) & 1) ++ fprintf(fp, "%sbigpage", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_arch_1) & 1) ++ fprintf(fp, "%sarch_1", ++ others++ ? "," : ""); ++ if ((flags >> v24_PG_reserved) & 1) ++ fprintf(fp, "%sreserved", ++ others++ ? "," : ""); ++ if (phys_not_mapped) ++ fprintf(fp, "%s[NOT MAPPED]", ++ others++ ? " " : ""); ++ ++ fprintf(fp, "\n"); ++ } ++ ++ if (done) ++ break; ++ } ++ ++ if (done) ++ break; ++ } ++ ++ switch (mi->flags) ++ { ++ case GET_TOTALRAM_PAGES: ++ mi->retval = total_pages - reserved; ++ break; ++ ++ case GET_SHARED_PAGES: ++ mi->retval = shared; ++ break; ++ ++ case GET_BUFFERS_PAGES: ++ mi->retval = buffers; ++ break; ++ ++ case GET_SLAB_PAGES: ++ mi->retval = slabs; ++ break; ++ ++ case GET_ALL: ++ mi->get_totalram = total_pages - reserved; ++ mi->get_shared = shared; ++ mi->get_buffers = buffers; ++ mi->get_slabs = slabs; ++ break; ++ ++ case ADDRESS_SPECIFIED: ++ mi->retval = done; ++ break; ++ } ++ ++ FREEBUF(page_cache); ++} ++ ++/* ++ * Stash a chunk of PGMM_CACHED page structures, starting at addr, into the ++ * passed-in buffer. The mem_map array is normally guaranteed to be ++ * readable except in the case of virtual mem_map usage. When V_MEM_MAP ++ * is in place, read all pages consumed by PGMM_CACHED page structures ++ * that are currently mapped, leaving the unmapped ones just zeroed out. ++ */ ++static void ++fill_mem_map_cache(ulong pp, ulong ppend, char *page_cache) ++{ ++ long size, cnt; ++ ulong addr; ++ char *bufptr; ++ ++ /* ++ * Try to read it in one fell swoop. ++ */ ++ if (readmem(pp, KVADDR, page_cache, SIZE(page) * PGMM_CACHED, ++ "page struct cache", RETURN_ON_ERROR|QUIET)) ++ return; ++ ++ /* ++ * Break it into page-size-or-less requests, warning if it's ++ * not a virtual mem_map. ++ */ ++ size = SIZE(page) * PGMM_CACHED; ++ addr = pp; ++ bufptr = page_cache; ++ ++ while (size > 0) { ++ /* ++ * Compute bytes till end of page. ++ */ ++ cnt = PAGESIZE() - PAGEOFFSET(addr); ++ ++ if (cnt > size) ++ cnt = size; ++ ++ if (!readmem(addr, KVADDR, bufptr, size, ++ "virtual page struct cache", RETURN_ON_ERROR|QUIET)) { ++ BZERO(bufptr, size); ++ if (!(vt->flags & V_MEM_MAP) && ((addr+size) < ppend)) ++ error(WARNING, ++ "mem_map[] from %lx to %lx not accessible\n", ++ addr, addr+size); ++ } ++ ++ addr += cnt; ++ bufptr += cnt; ++ size -= cnt; ++ } ++} ++ ++ ++/* ++ * dump_page_hash_table() displays the entries in each page_hash_table. ++ */ ++ ++#define PGHASH_CACHED (1024) ++ ++static void ++dump_page_hash_table(struct meminfo *hi) ++{ ++ int i; ++ int len, entry_len; ++ ulong page_hash_table, head; ++ struct list_data list_data, *ld; ++ struct gnu_request req; ++ long total_cached; ++ long page_cache_size; ++ ulong this_addr, searchpage; ++ int errflag, found, cnt, populated, verbose; ++ uint ival; ++ ulong buffer_pages; ++ char buf[BUFSIZE]; ++ char hash_table[BUFSIZE]; ++ char *pcache, *pghash_cache; ++ ++ if (!vt->page_hash_table) { ++ if (hi->flags & VERBOSE) ++ error(FATAL, ++ "address_space page cache radix tree not supported\n"); ++ ++ if (symbol_exists("nr_pagecache")) { ++ buffer_pages = nr_blockdev_pages(); ++ get_symbol_data("nr_pagecache", sizeof(int), &ival); ++ page_cache_size = (ulong)ival; ++ page_cache_size -= buffer_pages; ++ fprintf(fp, "page cache size: %ld\n", page_cache_size); ++ if (hi->flags & ADDRESS_SPECIFIED) ++ error(INFO, ++ "address_space page cache radix tree not supported: %lx: ignored\n", ++ hi->spec_addr); ++ } else ++ error(FATAL, "cannot determine page cache size\n"); ++ return; ++ } ++ ++ ld = &list_data; ++ ++ if (hi->spec_addr && (hi->flags & ADDRESS_SPECIFIED)) { ++ verbose = TRUE; ++ searchpage = hi->spec_addr; ++ } else if (hi->flags & VERBOSE) { ++ verbose = TRUE; ++ searchpage = 0; ++ } else { ++ verbose = FALSE; ++ searchpage = 0; ++ } ++ ++ if (vt->page_hash_table_len == 0) ++ error(FATAL, "cannot determine size of page_hash_table\n"); ++ ++ page_hash_table = vt->page_hash_table; ++ len = vt->page_hash_table_len; ++ entry_len = VALID_STRUCT(page_cache_bucket) ? ++ SIZE(page_cache_bucket) : sizeof(void *); ++ ++ if (CRASHDEBUG(1)) { ++ populated = 0; ++ fprintf(fp, "page_hash_table length: %d\n", len); ++ } ++ ++ get_symbol_type("page_cache_size", NULL, &req); ++ if (req.length == sizeof(int)) { ++ get_symbol_data("page_cache_size", sizeof(int), &ival); ++ page_cache_size = (long)ival; ++ } else ++ get_symbol_data("page_cache_size", sizeof(long), ++ &page_cache_size); ++ ++ pghash_cache = GETBUF(sizeof(void *) * PGHASH_CACHED); ++ ++ if (searchpage) ++ open_tmpfile(); ++ ++ hq_open(); ++ for (i = total_cached = 0; i < len; i++, ++ page_hash_table += entry_len) { ++ ++ if ((i % PGHASH_CACHED) == 0) { ++ readmem(page_hash_table, KVADDR, pghash_cache, ++ entry_len * PGHASH_CACHED, ++ "page hash cache", FAULT_ON_ERROR); ++ } ++ ++ pcache = pghash_cache + ((i%PGHASH_CACHED) * entry_len); ++ if (VALID_STRUCT(page_cache_bucket)) ++ pcache += OFFSET(page_cache_bucket_chain); ++ ++ head = ULONG(pcache); ++ ++ if (!head) ++ continue; ++ ++ if (verbose) ++ fprintf(fp, "page_hash_table[%d]\n", i); ++ ++ if (CRASHDEBUG(1)) ++ populated++; ++ ++ BZERO(ld, sizeof(struct list_data)); ++ ld->flags = verbose; ++ ld->start = head; ++ ld->searchfor = searchpage; ++ ld->member_offset = OFFSET(page_next_hash); ++ cnt = do_list(ld); ++ total_cached += cnt; ++ ++ if (ld->searchfor) ++ break; ++ ++ if (received_SIGINT()) ++ restart(0); ++ } ++ hq_close(); ++ ++ fprintf(fp, "%spage_cache_size: %ld ", verbose ? "\n" : "", ++ page_cache_size); ++ if (page_cache_size != total_cached) ++ fprintf(fp, "(found %ld)\n", total_cached); ++ else ++ fprintf(fp, "(verified)\n"); ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "heads containing page(s): %d\n", populated); ++ ++ if (searchpage) { ++ rewind(pc->tmpfile); ++ found = FALSE; ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (CRASHDEBUG(1) && STRNEQ(buf, "retval = TRUE; ++ } ++ } ++} ++ ++/* ++ * dump_free_pages() displays basic data about pages currently resident ++ * in the free_area[] memory lists. If the flags contains the VERBOSE ++ * bit, each page slab base address is dumped. If an address is specified ++ * only the free_area[] data containing that page is displayed, along with ++ * the page slab base address. Specified addresses can either be physical ++ * address or page structure pointers. ++ */ ++char *free_area_hdr1 = \ ++ "AREA SIZE FREE_AREA_STRUCT BLOCKS PAGES\n"; ++char *free_area_hdr2 = \ ++ "AREA SIZE FREE_AREA_STRUCT\n"; ++ ++static void ++dump_free_pages(struct meminfo *fi) ++{ ++ int i; ++ int order; ++ ulong free_area; ++ char *free_area_buf; ++ ulong *pp; ++ int nr_mem_lists; ++ struct list_data list_data, *ld; ++ long cnt, total_free, chunk_size; ++ int nr_free_pages; ++ char buf[BUFSIZE]; ++ char last_free[BUFSIZE]; ++ char last_free_hdr[BUFSIZE]; ++ int verbose, errflag, found; ++ physaddr_t searchphys; ++ ulong this_addr; ++ physaddr_t this_phys; ++ int do_search; ++ ulong kfp, offset; ++ int flen, dimension; ++ ++ if (vt->flags & (NODES|ZONES)) ++ error(FATAL, "dump_free_pages called with (NODES|ZONES)\n"); ++ ++ nr_mem_lists = ARRAY_LENGTH(free_area); ++ dimension = ARRAY_LENGTH(free_area_DIMENSION); ++ ++ if (nr_mem_lists == 0) ++ error(FATAL, "cannot determine size/dimensions of free_area\n"); ++ ++ if (dimension) ++ error(FATAL, ++ "dump_free_pages called with multidimensional free area\n"); ++ ++ ld = &list_data; ++ total_free = 0; ++ searchphys = 0; ++ do_search = FALSE; ++ get_symbol_data("nr_free_pages", sizeof(int), &nr_free_pages); ++ ++ switch (fi->flags) ++ { ++ case GET_FREE_HIGHMEM_PAGES: ++ error(FATAL, "GET_FREE_HIGHMEM_PAGES invalid in this kernel\n"); ++ ++ case GET_FREE_PAGES: ++ fi->retval = (ulong)nr_free_pages; ++ return; ++ ++ case ADDRESS_SPECIFIED: ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ if (!page_to_phys(fi->spec_addr, &searchphys)) { ++ if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) ++ return; ++ } ++ break; ++ case PHYSADDR: ++ searchphys = fi->spec_addr; ++ break; ++ default: ++ error(FATAL, "dump_free_pages: no memtype specified\n"); ++ } ++ do_search = TRUE; ++ break; ++ } ++ ++ verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++ ++ free_area_buf = GETBUF(nr_mem_lists * SIZE(free_area_struct)); ++ kfp = free_area = symbol_value("free_area"); ++ flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT")); ++ readmem(free_area, KVADDR, free_area_buf, ++ SIZE(free_area_struct) * nr_mem_lists, ++ "free_area_struct", FAULT_ON_ERROR); ++ ++ if (do_search) ++ open_tmpfile(); ++ ++ if (!verbose) ++ fprintf(fp, free_area_hdr1); ++ ++ hq_open(); ++ for (i = 0; i < nr_mem_lists; i++) { ++ pp = (ulong *)(free_area_buf + (SIZE(free_area_struct)*i)); ++ ++ chunk_size = power(2, i); ++ ++ if (verbose) ++ fprintf(fp, free_area_hdr2); ++ ++ fprintf(fp, "%3d ", i); ++ sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024); ++ fprintf(fp, "%5s ", buf); ++ ++ fprintf(fp, "%s %s", ++ mkstring(buf, flen, CENTER|LONG_HEX, MKSTR(kfp)), ++ verbose ? "\n" : ""); ++ ++ if (is_page_ptr(*pp, NULL)) { ++ BZERO(ld, sizeof(struct list_data)); ++ ld->flags = verbose; ++ ld->start = *pp; ++ ld->end = free_area; ++ cnt = do_list(ld); ++ total_free += (cnt * chunk_size); ++ } else ++ cnt = 0; ++ ++ if (!verbose) ++ fprintf(fp, "%6ld %6ld\n", cnt, cnt * chunk_size ); ++ ++ free_area += SIZE(free_area_struct); ++ kfp += SIZE(free_area_struct); ++ } ++ hq_close(); ++ ++ fprintf(fp, "\nnr_free_pages: %d ", nr_free_pages); ++ if (total_free != nr_free_pages) ++ fprintf(fp, "(found %ld)\n", total_free); ++ else ++ fprintf(fp, "(verified)\n"); ++ ++ if (!do_search) ++ return; ++ ++ found = FALSE; ++ rewind(pc->tmpfile); ++ order = offset = 0; ++ ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (CRASHDEBUG(1) && STRNEQ(buf, "= this_phys) && ++ (searchphys < (this_phys+chunk_size))) { ++ if (searchphys > this_phys) ++ offset = (searchphys - this_phys)/PAGESIZE(); ++ found = TRUE; ++ break; ++ } ++ } ++ close_tmpfile(); ++ ++ if (found) { ++ order--; ++ ++ fprintf(fp, last_free_hdr); ++ fprintf(fp, last_free); ++ fprintf(fp, "%lx ", this_addr); ++ if (order) { ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); ++ break; ++ case PHYSADDR: ++ fprintf(fp, "(%llx is %s", fi->spec_addr, ++ PAGEOFFSET(fi->spec_addr) ? "in " : ""); ++ break; ++ } ++ fprintf(fp, "%s of %ld pages) ", ++ ordinal(offset+1, buf), power(2, order)); ++ } ++ ++ fi->retval = TRUE; ++ fprintf(fp, "\n"); ++ } ++} ++ ++/* ++ * Dump free pages on kernels with a multi-dimensional free_area array. ++ */ ++char *free_area_hdr5 = \ ++ " AREA SIZE FREE_AREA_STRUCT BLOCKS PAGES\n"; ++char *free_area_hdr6 = \ ++ " AREA SIZE FREE_AREA_STRUCT\n"; ++ ++static void ++dump_multidimensional_free_pages(struct meminfo *fi) ++{ ++ int i, j; ++ struct list_data list_data, *ld; ++ long cnt, total_free; ++ ulong kfp, free_area; ++ physaddr_t searchphys; ++ int flen, errflag, verbose, nr_free_pages; ++ int nr_mem_lists, dimension, order, do_search; ++ ulong sum, found, offset; ++ char *free_area_buf, *p; ++ ulong *pp; ++ long chunk_size; ++ ulong this_addr; ++ physaddr_t this_phys; ++ char buf[BUFSIZE]; ++ char last_area[BUFSIZE]; ++ char last_area_hdr[BUFSIZE]; ++ ++ ++ if (vt->flags & (NODES|ZONES)) ++ error(FATAL, ++ "dump_multidimensional_free_pages called with (NODES|ZONES)\n"); ++ ++ ld = &list_data; ++ if (SIZE(free_area_struct) % sizeof(ulong)) ++ error(FATAL, "free_area_struct not long-word aligned?\n"); ++ ++ total_free = 0; ++ searchphys = 0; ++ do_search = FALSE; ++ get_symbol_data("nr_free_pages", sizeof(int), &nr_free_pages); ++ ++ switch (fi->flags) ++ { ++ case GET_FREE_HIGHMEM_PAGES: ++ error(FATAL, "GET_FREE_HIGHMEM_PAGES invalid in this kernel\n"); ++ ++ case GET_FREE_PAGES: ++ fi->retval = (ulong)nr_free_pages; ++ return; ++ ++ case ADDRESS_SPECIFIED: ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ if (!page_to_phys(fi->spec_addr, &searchphys)) { ++ if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) ++ return; ++ } ++ break; ++ case PHYSADDR: ++ searchphys = fi->spec_addr; ++ break; ++ default: ++ error(FATAL, ++ "dump_multidimensional_free_pages: no memtype specified\n"); ++ } ++ do_search = TRUE; ++ break; ++ } ++ ++ verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++ ++ flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT")); ++ nr_mem_lists = ARRAY_LENGTH(free_area); ++ dimension = ARRAY_LENGTH(free_area_DIMENSION); ++ if (!nr_mem_lists || !dimension) ++ error(FATAL, "cannot determine free_area dimensions\n"); ++ free_area_buf = ++ GETBUF((nr_mem_lists * SIZE(free_area_struct)) * dimension); ++ kfp = free_area = symbol_value("free_area"); ++ readmem(free_area, KVADDR, free_area_buf, ++ (SIZE(free_area_struct) * nr_mem_lists) * dimension, ++ "free_area arrays", FAULT_ON_ERROR); ++ ++ if (do_search) ++ open_tmpfile(); ++ ++ hq_open(); ++ for (i = sum = found = 0; i < dimension; i++) { ++ if (!verbose) ++ fprintf(fp, free_area_hdr5); ++ pp = (ulong *)(free_area_buf + ++ ((SIZE(free_area_struct)*nr_mem_lists)*i)); ++ for (j = 0; j < nr_mem_lists; j++) { ++ if (verbose) ++ fprintf(fp, free_area_hdr6); ++ ++ sprintf(buf, "[%d][%d]", i, j); ++ fprintf(fp, "%7s ", buf); ++ ++ chunk_size = power(2, j); ++ ++ sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024); ++ fprintf(fp, "%5s ", buf); ++ ++ fprintf(fp, "%s %s", ++ mkstring(buf, flen, CENTER|LONG_HEX, MKSTR(kfp)), ++ verbose ? "\n" : ""); ++ ++ if (is_page_ptr(*pp, NULL)) { ++ BZERO(ld, sizeof(struct list_data)); ++ ld->flags = verbose; ++ ld->start = *pp; ++ ld->end = free_area; ++ cnt = do_list(ld); ++ total_free += (cnt * chunk_size); ++ } else ++ cnt = 0; ++ ++ if (!verbose) ++ fprintf(fp, ++ "%6ld %6ld\n", cnt, cnt * chunk_size ); ++ ++ pp += (SIZE(free_area_struct)/sizeof(ulong)); ++ free_area += SIZE(free_area_struct); ++ kfp += SIZE(free_area_struct); ++ } ++ fprintf(fp, "\n"); ++ } ++ hq_close(); ++ ++ fprintf(fp, "nr_free_pages: %d ", nr_free_pages); ++ if (total_free != nr_free_pages) ++ fprintf(fp, "(found %ld)\n", total_free); ++ else ++ fprintf(fp, "(verified)\n"); ++ ++ if (!do_search) ++ return; ++ ++ found = FALSE; ++ rewind(pc->tmpfile); ++ order = offset = 0; ++ ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (CRASHDEBUG(1) && STRNEQ(buf, "tmpfile); ++ strcpy(last_area, strip_linefeeds(buf)); ++ p = strstr(buf, "k"); ++ *p = NULLCHAR; ++ while (*p != ' ') ++ p--; ++ chunk_size = atol(p+1) * 1024; ++ if (chunk_size == PAGESIZE()) ++ order = 0; ++ else ++ order++; ++ continue; ++ } ++ ++ errflag = 0; ++ this_addr = htol(strip_linefeeds(buf), ++ RETURN_ON_ERROR, &errflag); ++ if (errflag) ++ continue; ++ ++ if (!page_to_phys(this_addr, &this_phys)) ++ continue; ++ ++ if ((searchphys >= this_phys) && ++ (searchphys < (this_phys+chunk_size))) { ++ if (searchphys > this_phys) ++ offset = (searchphys - this_phys)/PAGESIZE(); ++ found = TRUE; ++ break; ++ } ++ ++ } ++ close_tmpfile(); ++ ++ if (found) { ++ fprintf(fp, last_area_hdr); ++ fprintf(fp, "%s\n", last_area); ++ fprintf(fp, "%lx ", this_addr); ++ if (order) { ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); ++ break; ++ case PHYSADDR: ++ fprintf(fp, "(%llx is %s", fi->spec_addr, ++ PAGEOFFSET(fi->spec_addr) ? "in " : ""); ++ break; ++ } ++ fprintf(fp, "%s of %ld pages) ", ++ ordinal(offset+1, buf), power(2, order)); ++ } ++ ++ fi->retval = TRUE; ++ fprintf(fp, "\n"); ++ } ++} ++ ++ ++/* ++ * Dump free pages in newer kernels that have zones. This is a work in ++ * progress, because although the framework for memory nodes has been laid ++ * down, complete support has not been put in place. ++ */ ++static char *zone_hdr = "ZONE NAME SIZE FREE"; ++ ++static void ++dump_free_pages_zones_v1(struct meminfo *fi) ++{ ++ int i, n; ++ ulong node_zones; ++ ulong size; ++ long zone_size_offset; ++ long chunk_size; ++ int order, errflag, do_search; ++ ulong offset, verbose, value, sum, found; ++ ulong this_addr; ++ physaddr_t this_phys, searchphys; ++ ulong zone_mem_map; ++ ulong zone_start_paddr; ++ ulong zone_start_mapnr; ++ struct node_table *nt; ++ char buf[BUFSIZE], *p; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; ++ char last_node[BUFSIZE]; ++ char last_zone[BUFSIZE]; ++ char last_area[BUFSIZE]; ++ char last_area_hdr[BUFSIZE]; ++ ++ if (!(vt->flags & (NODES|ZONES))) ++ error(FATAL, ++ "dump_free_pages_zones_v1 called without (NODES|ZONES)\n"); ++ ++ if (fi->flags & ADDRESS_SPECIFIED) { ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ if (!page_to_phys(fi->spec_addr, &searchphys)) { ++ if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) ++ return; ++ } ++ break; ++ case PHYSADDR: ++ searchphys = fi->spec_addr; ++ break; ++ default: ++ error(FATAL, ++ "dump_free_pages_zones_v1: no memtype specified\n"); ++ } ++ do_search = TRUE; ++ } else { ++ searchphys = 0; ++ do_search = FALSE; ++ } ++ verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++ ++ if (VALID_MEMBER(zone_struct_size)) ++ zone_size_offset = OFFSET(zone_struct_size); ++ else if (VALID_MEMBER(zone_struct_memsize)) ++ zone_size_offset = OFFSET(zone_struct_memsize); ++ else ++ error(FATAL, ++ "zone_struct has neither size nor memsize field\n"); ++ ++ if (do_search) ++ open_tmpfile(); ++ ++ hq_open(); ++ ++ for (n = sum = found = 0; n < vt->numnodes; n++) { ++ nt = &vt->node_table[n]; ++ node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); ++ ++ for (i = 0; i < vt->nr_zones; i++) { ++ ++ if (fi->flags == GET_FREE_PAGES) { ++ readmem(node_zones+ ++ OFFSET(zone_struct_free_pages), ++ KVADDR, &value, sizeof(ulong), ++ "node_zones free_pages", ++ FAULT_ON_ERROR); ++ sum += value; ++ node_zones += SIZE(zone_struct); ++ continue; ++ } ++ ++ if (fi->flags == GET_FREE_HIGHMEM_PAGES) { ++ if (i == vt->ZONE_HIGHMEM) { ++ readmem(node_zones+ ++ OFFSET(zone_struct_free_pages), ++ KVADDR, &value, sizeof(ulong), ++ "node_zones free_pages", ++ FAULT_ON_ERROR); ++ sum += value; ++ } ++ node_zones += SIZE(zone_struct); ++ continue; ++ } ++ ++ if (fi->flags == GET_ZONE_SIZES) { ++ readmem(node_zones+zone_size_offset, ++ KVADDR, &size, sizeof(ulong), ++ "node_zones {mem}size", FAULT_ON_ERROR); ++ sum += size; ++ node_zones += SIZE(zone_struct); ++ continue; ++ } ++ ++ if ((i == 0) && (vt->flags & NODES)) { ++ if (n) { ++ fprintf(fp, "\n"); ++ pad_line(fp, ++ VADDR_PRLEN > 8 ? 74 : 66, '-'); ++ fprintf(fp, "\n"); ++ } ++ fprintf(fp, "%sNODE\n %2d\n", ++ n ? "\n" : "", nt->node_id); ++ } ++ ++ fprintf(fp, "%s%s %s START_PADDR START_MAPNR\n", ++ i > 0 ? "\n" : "", ++ zone_hdr, ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, ++ "MEM_MAP")); ++ ++ fprintf(fp, "%3d ", i); ++ ++ readmem(node_zones+OFFSET(zone_struct_name), KVADDR, ++ &value, sizeof(void *), ++ "node_zones name", FAULT_ON_ERROR); ++ if (read_string(value, buf, BUFSIZE-1)) ++ fprintf(fp, "%-9s ", buf); ++ else ++ fprintf(fp, "(unknown) "); ++ ++ readmem(node_zones+zone_size_offset, KVADDR, ++ &size, sizeof(ulong), ++ "node_zones {mem}size", FAULT_ON_ERROR); ++ fprintf(fp, "%6ld ", size); ++ ++ readmem(node_zones+OFFSET(zone_struct_free_pages), ++ KVADDR, &value, sizeof(ulong), ++ "node_zones free_pages", FAULT_ON_ERROR); ++ ++ fprintf(fp, "%6ld ", value); ++ ++ readmem(node_zones+OFFSET(zone_struct_zone_start_paddr), ++ KVADDR, &zone_start_paddr, sizeof(ulong), ++ "node_zones zone_start_paddr", FAULT_ON_ERROR); ++ readmem(node_zones+OFFSET(zone_struct_zone_start_mapnr), ++ KVADDR, &zone_start_mapnr, sizeof(ulong), ++ "node_zones zone_start_mapnr", FAULT_ON_ERROR); ++ readmem(node_zones+OFFSET(zone_struct_zone_mem_map), ++ KVADDR, &zone_mem_map, sizeof(ulong), ++ "node_zones zone_mem_map", FAULT_ON_ERROR); ++ ++ fprintf(fp, "%s %s %s\n", ++ mkstring(buf1, VADDR_PRLEN, ++ CENTER|LONG_HEX,MKSTR(zone_mem_map)), ++ mkstring(buf2, strlen("START_PADDR"), ++ CENTER|LONG_HEX|RJUST, ++ MKSTR(zone_start_paddr)), ++ mkstring(buf3, strlen("START_MAPNR"), ++ CENTER|LONG_DEC|RJUST, ++ MKSTR(zone_start_mapnr))); ++ ++ sum += value; ++ ++ if (value) ++ found += dump_zone_free_area(node_zones+ ++ OFFSET(zone_struct_free_area), ++ vt->nr_free_areas, verbose); ++ ++ node_zones += SIZE(zone_struct); ++ } ++ } ++ ++ hq_close(); ++ ++ if (fi->flags & (GET_FREE_PAGES|GET_ZONE_SIZES|GET_FREE_HIGHMEM_PAGES)) { ++ fi->retval = sum; ++ return; ++ } ++ ++ fprintf(fp, "\nnr_free_pages: %ld ", sum); ++ if (sum == found) ++ fprintf(fp, "(verified)\n"); ++ else ++ fprintf(fp, "(found %ld)\n", found); ++ ++ if (!do_search) ++ return; ++ ++ found = FALSE; ++ rewind(pc->tmpfile); ++ order = offset = 0; ++ last_node[0] = NULLCHAR; ++ last_zone[0] = NULLCHAR; ++ last_area[0] = NULLCHAR; ++ last_area_hdr[0] = NULLCHAR; ++ ++ ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (CRASHDEBUG(1) && STRNEQ(buf, "tmpfile); ++ strcpy(last_node, strip_linefeeds(buf)); ++ continue; ++ } ++ if (STRNEQ(buf, "ZONE")) { ++ fgets(buf, BUFSIZE, pc->tmpfile); ++ strcpy(last_zone, strip_linefeeds(buf)); ++ continue; ++ } ++ if (STRNEQ(buf, "AREA")) { ++ strcpy(last_area_hdr, buf); ++ fgets(buf, BUFSIZE, pc->tmpfile); ++ strcpy(last_area, strip_linefeeds(buf)); ++ p = strstr(buf, "k"); ++ *p = NULLCHAR; ++ while (*p != ' ') ++ p--; ++ chunk_size = atol(p+1) * 1024; ++ if (chunk_size == PAGESIZE()) ++ order = 0; ++ else ++ order++; ++ continue; ++ } ++ ++ if (CRASHDEBUG(0) && ++ !hexadecimal(strip_linefeeds(buf), 0)) ++ continue; ++ ++ errflag = 0; ++ this_addr = htol(strip_linefeeds(buf), ++ RETURN_ON_ERROR, &errflag); ++ if (errflag) ++ continue; ++ ++ if (!page_to_phys(this_addr, &this_phys)) ++ continue; ++ ++ if ((searchphys >= this_phys) && ++ (searchphys < (this_phys+chunk_size))) { ++ if (searchphys > this_phys) ++ offset = (searchphys - this_phys)/PAGESIZE(); ++ found = TRUE; ++ break; ++ } ++ ++ } ++ close_tmpfile(); ++ ++ if (found) { ++ if (strlen(last_node)) ++ fprintf(fp, "NODE\n%s\n", last_node); ++ fprintf(fp, "%s %s START_PADDR START_MAPNR\n", ++ zone_hdr, ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_MAP")); ++ fprintf(fp, "%s\n", last_zone); ++ fprintf(fp, last_area_hdr); ++ fprintf(fp, "%s\n", last_area); ++ fprintf(fp, "%lx ", this_addr); ++ if (order) { ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); ++ break; ++ case PHYSADDR: ++ fprintf(fp, "(%llx is %s", fi->spec_addr, ++ PAGEOFFSET(fi->spec_addr) ? "in " : ""); ++ break; ++ } ++ fprintf(fp, "%s of %ld pages) ", ++ ordinal(offset+1, buf), power(2, order)); ++ } ++ ++ fi->retval = TRUE; ++ fprintf(fp, "\n"); ++ } ++} ++ ++ ++/* ++ * Same as dump_free_pages_zones_v1(), but updated for numerous 2.6 zone ++ * and free_area related data structure changes. ++ */ ++static void ++dump_free_pages_zones_v2(struct meminfo *fi) ++{ ++ int i, n; ++ ulong node_zones; ++ ulong size; ++ long zone_size_offset; ++ long chunk_size; ++ int order, errflag, do_search; ++ ulong offset, verbose, value, sum, found; ++ ulong this_addr; ++ physaddr_t phys, this_phys, searchphys; ++ ulong pp; ++ ulong zone_mem_map; ++ ulong zone_start_paddr; ++ ulong zone_start_pfn; ++ ulong zone_start_mapnr; ++ struct node_table *nt; ++ char buf[BUFSIZE], *p; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; ++ char last_node[BUFSIZE]; ++ char last_zone[BUFSIZE]; ++ char last_area[BUFSIZE]; ++ char last_area_hdr[BUFSIZE]; ++ ++ if (!(vt->flags & (NODES|ZONES))) ++ error(FATAL, ++ "dump_free_pages_zones_v2 called without (NODES|ZONES)\n"); ++ ++ if (fi->flags & ADDRESS_SPECIFIED) { ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ if (!page_to_phys(fi->spec_addr, &searchphys)) { ++ if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) ++ return; ++ } ++ break; ++ case PHYSADDR: ++ searchphys = fi->spec_addr; ++ break; ++ default: ++ error(FATAL, ++ "dump_free_pages_zones_v2: no memtype specified\n"); ++ } ++ do_search = TRUE; ++ } else { ++ searchphys = 0; ++ do_search = FALSE; ++ } ++ ++ verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++ ++ if (VALID_MEMBER(zone_spanned_pages)) ++ zone_size_offset = OFFSET(zone_spanned_pages); ++ else ++ error(FATAL, "zone struct has no spanned_pages field\n"); ++ ++ if (do_search) ++ open_tmpfile(); ++ ++ hq_open(); ++ ++ for (n = sum = found = 0; n < vt->numnodes; n++) { ++ nt = &vt->node_table[n]; ++ node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); ++ ++ for (i = 0; i < vt->nr_zones; i++) { ++ if (fi->flags == GET_FREE_PAGES) { ++ readmem(node_zones+ ++ OFFSET(zone_free_pages), ++ KVADDR, &value, sizeof(ulong), ++ "node_zones free_pages", ++ FAULT_ON_ERROR); ++ sum += value; ++ node_zones += SIZE(zone); ++ continue; ++ } ++ ++ if (fi->flags == GET_FREE_HIGHMEM_PAGES) { ++ if (i == vt->ZONE_HIGHMEM) { ++ readmem(node_zones+ ++ OFFSET(zone_free_pages), ++ KVADDR, &value, sizeof(ulong), ++ "node_zones free_pages", ++ FAULT_ON_ERROR); ++ sum += value; ++ } ++ node_zones += SIZE(zone); ++ continue; ++ } ++ ++ if (fi->flags == GET_ZONE_SIZES) { ++ readmem(node_zones+zone_size_offset, ++ KVADDR, &size, sizeof(ulong), ++ "node_zones size", FAULT_ON_ERROR); ++ sum += size; ++ node_zones += SIZE(zone); ++ continue; ++ } ++ ++ if ((i == 0) && ((vt->flags & NODES) || (vt->numnodes > 1))) { ++ if (n) { ++ fprintf(fp, "\n"); ++ pad_line(fp, ++ VADDR_PRLEN > 8 ? 74 : 66, '-'); ++ fprintf(fp, "\n"); ++ } ++ fprintf(fp, "%sNODE\n %2d\n", ++ n ? "\n" : "", nt->node_id); ++ } ++ ++ fprintf(fp, "%s%s %s START_PADDR START_MAPNR\n", ++ i > 0 ? "\n" : "", ++ zone_hdr, ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, ++ "MEM_MAP")); ++ ++ fprintf(fp, "%3d ", i); ++ ++ readmem(node_zones+OFFSET(zone_name), KVADDR, ++ &value, sizeof(void *), ++ "node_zones name", FAULT_ON_ERROR); ++ if (read_string(value, buf, BUFSIZE-1)) ++ fprintf(fp, "%-9s ", buf); ++ else ++ fprintf(fp, "(unknown) "); ++ ++ readmem(node_zones+zone_size_offset, KVADDR, ++ &size, sizeof(ulong), ++ "node_zones size", FAULT_ON_ERROR); ++ fprintf(fp, "%6ld ", size); ++ ++ readmem(node_zones+OFFSET(zone_free_pages), ++ KVADDR, &value, sizeof(ulong), ++ "node_zones free_pages", FAULT_ON_ERROR); ++ ++ fprintf(fp, "%6ld ", value); ++ ++ if (VALID_MEMBER(zone_zone_mem_map)) { ++ readmem(node_zones+OFFSET(zone_zone_mem_map), ++ KVADDR, &zone_mem_map, sizeof(ulong), ++ "node_zones zone_mem_map", FAULT_ON_ERROR); ++ } ++ ++ readmem(node_zones+ OFFSET(zone_zone_start_pfn), ++ KVADDR, &zone_start_pfn, sizeof(ulong), ++ "node_zones zone_start_pfn", FAULT_ON_ERROR); ++ zone_start_paddr = PTOB(zone_start_pfn); ++ ++ if (!VALID_MEMBER(zone_zone_mem_map)) { ++ if (IS_SPARSEMEM() || IS_DISCONTIGMEM()) { ++ zone_mem_map = 0; ++ if (size) { ++ phys = PTOB(zone_start_pfn); ++ if (phys_to_page(phys, &pp)) ++ zone_mem_map = pp; ++ } ++ } else if (vt->flags & FLATMEM) { ++ zone_mem_map = 0; ++ if (size) ++ zone_mem_map = nt->mem_map + ++ (zone_start_pfn * SIZE(page)); ++ } else ++ error(FATAL, "\ncannot determine zone mem_map: TBD\n"); ++ } ++ ++ if (zone_mem_map) ++ zone_start_mapnr = ++ (zone_mem_map - nt->mem_map) / ++ SIZE(page); ++ else ++ zone_start_mapnr = 0; ++ ++ fprintf(fp, "%s %s %s\n", ++ mkstring(buf1, VADDR_PRLEN, ++ CENTER|LONG_HEX,MKSTR(zone_mem_map)), ++ mkstring(buf2, strlen("START_PADDR"), ++ CENTER|LONG_HEX|RJUST, ++ MKSTR(zone_start_paddr)), ++ mkstring(buf3, strlen("START_MAPNR"), ++ CENTER|LONG_DEC|RJUST, ++ MKSTR(zone_start_mapnr))); ++ ++ sum += value; ++ ++ if (value) ++ found += dump_zone_free_area(node_zones+ ++ OFFSET(zone_free_area), ++ vt->nr_free_areas, verbose); ++ ++ node_zones += SIZE(zone); ++ } ++ } ++ ++ hq_close(); ++ ++ if (fi->flags & (GET_FREE_PAGES|GET_ZONE_SIZES|GET_FREE_HIGHMEM_PAGES)) { ++ fi->retval = sum; ++ return; ++ } ++ ++ fprintf(fp, "\nnr_free_pages: %ld ", sum); ++ if (sum == found) ++ fprintf(fp, "(verified)\n"); ++ else ++ fprintf(fp, "(found %ld)\n", found); ++ ++ if (!do_search) ++ return; ++ ++ found = FALSE; ++ rewind(pc->tmpfile); ++ order = offset = 0; ++ last_node[0] = NULLCHAR; ++ last_zone[0] = NULLCHAR; ++ last_area[0] = NULLCHAR; ++ last_area_hdr[0] = NULLCHAR; ++ ++ ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (CRASHDEBUG(1) && STRNEQ(buf, "tmpfile); ++ strcpy(last_node, strip_linefeeds(buf)); ++ continue; ++ } ++ if (STRNEQ(buf, "ZONE")) { ++ fgets(buf, BUFSIZE, pc->tmpfile); ++ strcpy(last_zone, strip_linefeeds(buf)); ++ continue; ++ } ++ if (STRNEQ(buf, "AREA")) { ++ strcpy(last_area_hdr, buf); ++ fgets(buf, BUFSIZE, pc->tmpfile); ++ strcpy(last_area, strip_linefeeds(buf)); ++ p = strstr(buf, "k"); ++ *p = NULLCHAR; ++ while (*p != ' ') ++ p--; ++ chunk_size = atol(p+1) * 1024; ++ if (chunk_size == PAGESIZE()) ++ order = 0; ++ else ++ order++; ++ continue; ++ } ++ ++ if (CRASHDEBUG(0) && ++ !hexadecimal(strip_linefeeds(buf), 0)) ++ continue; ++ ++ errflag = 0; ++ this_addr = htol(strip_linefeeds(buf), ++ RETURN_ON_ERROR, &errflag); ++ if (errflag) ++ continue; ++ ++ if (!page_to_phys(this_addr, &this_phys)) ++ continue; ++ ++ if ((searchphys >= this_phys) && ++ (searchphys < (this_phys+chunk_size))) { ++ if (searchphys > this_phys) ++ offset = (searchphys - this_phys)/PAGESIZE(); ++ found = TRUE; ++ break; ++ } ++ ++ } ++ close_tmpfile(); ++ ++ if (found) { ++ if (strlen(last_node)) ++ fprintf(fp, "NODE\n%s\n", last_node); ++ fprintf(fp, "%s %s START_PADDR START_MAPNR\n", ++ zone_hdr, ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_MAP")); ++ fprintf(fp, "%s\n", last_zone); ++ fprintf(fp, last_area_hdr); ++ fprintf(fp, "%s\n", last_area); ++ fprintf(fp, "%lx ", this_addr); ++ if (order) { ++ switch (fi->memtype) ++ { ++ case KVADDR: ++ fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); ++ break; ++ case PHYSADDR: ++ fprintf(fp, "(%llx is %s", fi->spec_addr, ++ PAGEOFFSET(fi->spec_addr) ? "in " : ""); ++ break; ++ } ++ fprintf(fp, "%s of %ld pages)", ++ ordinal(offset+1, buf), chunk_size/PAGESIZE()); ++ } ++ ++ fi->retval = TRUE; ++ fprintf(fp, "\n"); ++ } ++} ++ ++ ++static char * ++page_usage_hdr = "ZONE NAME FREE ACTIVE INACTIVE_DIRTY INACTIVE_CLEAN MIN/LOW/HIGH"; ++ ++/* ++ * Display info about the non-free pages in each zone. ++ */ ++static int ++dump_zone_page_usage(void) ++{ ++ int i, n; ++ ulong value, node_zones; ++ struct node_table *nt; ++ ulong inactive_dirty_pages, inactive_clean_pages, active_pages; ++ ulong free_pages, pages_min, pages_low, pages_high; ++ char namebuf[BUFSIZE]; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; ++ ++ if (!VALID_MEMBER(zone_struct_inactive_dirty_pages) || ++ !VALID_MEMBER(zone_struct_inactive_clean_pages) || ++ !VALID_MEMBER(zone_struct_active_pages) || ++ !VALID_MEMBER(zone_struct_pages_min) || ++ !VALID_MEMBER(zone_struct_pages_low) || ++ !VALID_MEMBER(zone_struct_pages_high)) ++ return FALSE; ++ ++ fprintf(fp, "\n"); ++ ++ for (n = 0; n < vt->numnodes; n++) { ++ nt = &vt->node_table[n]; ++ node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); ++ ++ if ((i == 0) && (vt->flags & NODES)) { ++ fprintf(fp, "%sNODE\n %2d\n", ++ n ? "\n" : "", nt->node_id); ++ } ++ fprintf(fp, "%s\n", page_usage_hdr); ++ ++ for (i = 0; i < vt->nr_zones; i++) { ++ readmem(node_zones+OFFSET(zone_struct_free_pages), ++ KVADDR, &free_pages, sizeof(ulong), ++ "node_zones free_pages", FAULT_ON_ERROR); ++ readmem(node_zones+ ++ OFFSET(zone_struct_inactive_dirty_pages), ++ KVADDR, &inactive_dirty_pages, sizeof(ulong), ++ "node_zones inactive_dirty_pages", ++ FAULT_ON_ERROR); ++ readmem(node_zones+ ++ OFFSET(zone_struct_inactive_clean_pages), ++ KVADDR, &inactive_clean_pages, sizeof(ulong), ++ "node_zones inactive_clean_pages", ++ FAULT_ON_ERROR); ++ readmem(node_zones+OFFSET(zone_struct_active_pages), ++ KVADDR, &active_pages, sizeof(ulong), ++ "node_zones active_pages", FAULT_ON_ERROR); ++ readmem(node_zones+OFFSET(zone_struct_pages_min), ++ KVADDR, &pages_min, sizeof(ulong), ++ "node_zones pages_min", FAULT_ON_ERROR); ++ readmem(node_zones+OFFSET(zone_struct_pages_low), ++ KVADDR, &pages_low, sizeof(ulong), ++ "node_zones pages_low", FAULT_ON_ERROR); ++ readmem(node_zones+OFFSET(zone_struct_pages_high), ++ KVADDR, &pages_high, sizeof(ulong), ++ "node_zones pages_high", FAULT_ON_ERROR); ++ ++ readmem(node_zones+OFFSET(zone_struct_name), KVADDR, ++ &value, sizeof(void *), ++ "node_zones name", FAULT_ON_ERROR); ++ if (read_string(value, buf1, BUFSIZE-1)) ++ sprintf(namebuf, "%-8s", buf1); ++ else ++ sprintf(namebuf, "(unknown)"); ++ ++ sprintf(buf2, "%ld/%ld/%ld", ++ pages_min, pages_low, pages_high); ++ fprintf(fp, "%3d %s %7ld %7ld %15ld %15ld %s\n", ++ i, ++ namebuf, ++ free_pages, ++ active_pages, ++ inactive_dirty_pages, ++ inactive_clean_pages, ++ mkstring(buf3, strlen("MIN/LOW/HIGH"), ++ CENTER, buf2)); ++ ++ node_zones += SIZE(zone_struct); ++ } ++ } ++ ++ return TRUE; ++} ++ ++ ++/* ++ * Dump the num "order" contents of the zone_t free_area array. ++ */ ++char *free_area_hdr3 = "AREA SIZE FREE_AREA_STRUCT\n"; ++char *free_area_hdr4 = "AREA SIZE FREE_AREA_STRUCT BLOCKS PAGES\n"; ++ ++static int ++dump_zone_free_area(ulong free_area, int num, ulong verbose) ++{ ++ int i, j; ++ long chunk_size; ++ int flen, total_free, cnt; ++ char buf[BUFSIZE]; ++ ulong free_area_buf[3]; ++ char *free_area_buf2; ++ char *free_list_buf; ++ ulong free_list; ++ struct list_data list_data, *ld; ++ int list_count; ++ ulong *free_ptr; ++ ++ if (VALID_STRUCT(free_area_struct)) { ++ if (SIZE(free_area_struct) != (3 * sizeof(ulong))) ++ error(FATAL, ++ "unrecognized free_area_struct size: %ld\n", ++ SIZE(free_area_struct)); ++ list_count = 1; ++ } else if (VALID_STRUCT(free_area)) { ++ if (SIZE(free_area) == (3 * sizeof(ulong))) ++ list_count = 1; ++ else { ++ list_count = MEMBER_SIZE("free_area", ++ "free_list")/SIZE(list_head); ++ free_area_buf2 = GETBUF(SIZE(free_area)); ++ free_list_buf = GETBUF(SIZE(list_head)); ++ readmem(free_area, KVADDR, free_area_buf2, ++ SIZE(free_area), "free_area struct", ++ FAULT_ON_ERROR); ++ } ++ } else error(FATAL, ++ "neither free_area_struct or free_area structures exist\n"); ++ ++ ld = &list_data; ++ ++ if (!verbose) ++ fprintf(fp, free_area_hdr4); ++ ++ total_free = 0; ++ flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT")); ++ ++ if (list_count > 1) ++ goto multiple_lists; ++ ++ for (i = 0; i < num; i++, ++ free_area += SIZE_OPTION(free_area_struct, free_area)) { ++ if (verbose) ++ fprintf(fp, free_area_hdr3); ++ fprintf(fp, "%3d ", i); ++ chunk_size = power(2, i); ++ sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024); ++ fprintf(fp, " %7s ", buf); ++ ++ readmem(free_area, KVADDR, free_area_buf, ++ sizeof(ulong) * 3, "free_area_struct", FAULT_ON_ERROR); ++ ++ fprintf(fp, "%s ", ++ mkstring(buf, flen, CENTER|LONG_HEX, MKSTR(free_area))); ++ ++ if (free_area_buf[0] == free_area) { ++ if (verbose) ++ fprintf(fp, "\n"); ++ else ++ fprintf(fp, "%6d %6d\n", 0, 0); ++ continue; ++ } ++ ++ if (verbose) ++ fprintf(fp, "\n"); ++ ++ BZERO(ld, sizeof(struct list_data)); ++ ld->flags = verbose | RETURN_ON_DUPLICATE; ++ ld->start = free_area_buf[0]; ++ ld->end = free_area; ++ if (VALID_MEMBER(page_list_next)) ++ ld->list_head_offset = OFFSET(page_list); ++ else if (VALID_MEMBER(page_lru)) ++ ld->list_head_offset = OFFSET(page_lru)+ ++ OFFSET(list_head_next); ++ else error(FATAL, ++ "neither page.list or page.lru exist?\n"); ++ ++ cnt = do_list(ld); ++ if (cnt < 0) ++ error(FATAL, ++ "corrupted free list from free_area_struct: %lx\n", ++ free_area); ++ ++ if (!verbose) ++ fprintf(fp, "%6d %6ld\n", cnt, cnt*chunk_size); ++ ++ total_free += (cnt * chunk_size); ++ } ++ ++ return total_free; ++ ++multiple_lists: ++ ++ for (i = 0; i < num; i++, ++ free_area += SIZE_OPTION(free_area_struct, free_area)) { ++ ++ readmem(free_area, KVADDR, free_area_buf2, ++ SIZE(free_area), "free_area struct", FAULT_ON_ERROR); ++ ++ for (j = 0, free_list = free_area; j < list_count; ++ j++, free_list += SIZE(list_head)) { ++ ++ if (verbose) ++ fprintf(fp, free_area_hdr3); ++ ++ fprintf(fp, "%3d ", i); ++ chunk_size = power(2, i); ++ sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024); ++ fprintf(fp, " %7s ", buf); ++ ++ readmem(free_list, KVADDR, free_list_buf, ++ SIZE(list_head), "free_area free_list", ++ FAULT_ON_ERROR); ++ fprintf(fp, "%s ", ++ mkstring(buf, flen, CENTER|LONG_HEX, MKSTR(free_list))); ++ ++ free_ptr = (ulong *)free_list_buf; ++ ++ if (*free_ptr == free_list) { ++ if (verbose) ++ fprintf(fp, "\n"); ++ else ++ fprintf(fp, "%6d %6d\n", 0, 0); ++ continue; ++ } ++ ++ BZERO(ld, sizeof(struct list_data)); ++ ld->flags = verbose | RETURN_ON_DUPLICATE; ++ ld->start = *free_ptr; ++ ld->end = free_list; ++ ld->list_head_offset = OFFSET(page_lru) + ++ OFFSET(list_head_next); ++ ++ cnt = do_list(ld); ++ if (cnt < 0) ++ error(FATAL, ++ "corrupted free list %d from free_area struct: %lx\n", ++ j, free_area); ++ ++ if (!verbose) ++ fprintf(fp, "%6d %6ld\n", cnt, cnt*chunk_size); ++ ++ total_free += (cnt * chunk_size); ++ } ++ } ++ ++ FREEBUF(free_area_buf2); ++ FREEBUF(free_list_buf); ++ return total_free; ++} ++ ++/* ++ * dump_kmeminfo displays basic memory use information typically shown ++ * by /proc/meminfo, and then some... ++ */ ++ ++char *kmeminfo_hdr = " PAGES TOTAL PERCENTAGE\n"; ++ ++static void ++dump_kmeminfo(void) ++{ ++ ulong totalram_pages; ++ ulong freeram_pages; ++ ulong used_pages; ++ ulong shared_pages; ++ ulong buffer_pages; ++ ulong subtract_buffer_pages; ++ ulong totalswap_pages, totalused_pages; ++ ulong totalhigh_pages; ++ ulong freehighmem_pages; ++ ulong totallowmem_pages; ++ ulong freelowmem_pages; ++ long nr_file_pages, nr_slab; ++ ulong swapper_space_nrpages; ++ ulong pct; ++ ulong value1, value2; ++ uint tmp; ++ struct meminfo meminfo; ++ struct gnu_request req; ++ long page_cache_size; ++ ulong get_totalram; ++ ulong get_buffers; ++ ulong get_slabs; ++ struct syment *sp_array[2]; ++ char buf[BUFSIZE]; ++ ++ ++ BZERO(&meminfo, sizeof(struct meminfo)); ++ meminfo.flags = GET_ALL; ++ dump_mem_map(&meminfo); ++ get_totalram = meminfo.get_totalram; ++ shared_pages = meminfo.get_shared; ++ get_buffers = meminfo.get_buffers; ++ get_slabs = meminfo.get_slabs; ++ ++ /* ++ * If vm_stat array exists, override page search info. ++ */ ++ if (vm_stat_init()) { ++ if (dump_vm_stat("NR_SLAB", &nr_slab, 0)) ++ get_slabs = nr_slab; ++ else if (dump_vm_stat("NR_SLAB_RECLAIMABLE", &nr_slab, 0)) { ++ get_slabs = nr_slab; ++ if (dump_vm_stat("NR_SLAB_UNRECLAIMABLE", &nr_slab, 0)) ++ get_slabs += nr_slab; ++ } ++ } ++ ++ fprintf(fp, kmeminfo_hdr); ++ /* ++ * Get total RAM based upon how the various versions of si_meminfo() ++ * have done it, latest to earliest: ++ * ++ * Prior to 2.3.36, count all mem_map pages minus the reserved ones. ++ * From 2.3.36 onwards, use "totalram_pages" if set. ++ */ ++ if (symbol_exists("totalram_pages")) { ++ totalram_pages = vt->totalram_pages ? ++ vt->totalram_pages : get_totalram; ++ } else ++ totalram_pages = get_totalram; ++ ++ fprintf(fp, "%10s %7ld %11s ----\n", "TOTAL MEM", ++ totalram_pages, pages_to_size(totalram_pages, buf)); ++ ++ /* ++ * Get free pages from dump_free_pages() or its associates. ++ * Used pages are a free-bee... ++ */ ++ meminfo.flags = GET_FREE_PAGES; ++ vt->dump_free_pages(&meminfo); ++ freeram_pages = meminfo.retval; ++ pct = (freeram_pages * 100)/totalram_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "FREE", freeram_pages, pages_to_size(freeram_pages, buf), pct); ++ ++ used_pages = totalram_pages - freeram_pages; ++ pct = (used_pages * 100)/totalram_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "USED", used_pages, pages_to_size(used_pages, buf), pct); ++ ++ /* ++ * Get shared pages from dump_mem_map(). Note that this is done ++ * differently than the kernel -- it just tallies the non-reserved ++ * pages that have a count of greater than 1. ++ */ ++ pct = (shared_pages * 100)/totalram_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "SHARED", shared_pages, pages_to_size(shared_pages, buf), pct); ++ ++ subtract_buffer_pages = 0; ++ if (symbol_exists("buffermem_pages")) { ++ get_symbol_data("buffermem_pages", sizeof(int), &tmp); ++ buffer_pages = (ulong)tmp; ++ } else if (symbol_exists("buffermem")) { ++ get_symbol_data("buffermem", sizeof(int), &tmp); ++ buffer_pages = BTOP(tmp); ++ } else if ((THIS_KERNEL_VERSION >= LINUX(2,6,0)) && ++ symbol_exists("nr_blockdev_pages")) { ++ subtract_buffer_pages = buffer_pages = nr_blockdev_pages(); ++ } else ++ buffer_pages = 0; ++ ++ pct = (buffer_pages * 100)/totalram_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "BUFFERS", buffer_pages, pages_to_size(buffer_pages, buf), pct); ++ ++ if (CRASHDEBUG(1)) ++ error(NOTE, "pages with buffers: %ld\n", get_buffers); ++ ++ /* ++ * page_cache_size has evolved from a long to an atomic_t to ++ * not existing at all. ++ */ ++ ++ if (symbol_exists("page_cache_size")) { ++ get_symbol_type("page_cache_size", NULL, &req); ++ if (req.length == sizeof(int)) { ++ get_symbol_data("page_cache_size", sizeof(int), &tmp); ++ page_cache_size = (long)tmp; ++ } else ++ get_symbol_data("page_cache_size", sizeof(long), ++ &page_cache_size); ++ page_cache_size -= subtract_buffer_pages; ++ } else if (symbol_exists("nr_pagecache")) { ++ get_symbol_data("nr_pagecache", sizeof(int), &tmp); ++ page_cache_size = (long)tmp; ++ page_cache_size -= subtract_buffer_pages; ++ } else if (dump_vm_stat("NR_FILE_PAGES", &nr_file_pages, 0)) { ++ char *swapper_space = GETBUF(SIZE(address_space)); ++ ++ if (!readmem(symbol_value("swapper_space"), KVADDR, swapper_space, ++ SIZE(address_space), "swapper_space", RETURN_ON_ERROR)) ++ swapper_space_nrpages = 0; ++ else ++ swapper_space_nrpages = ULONG(swapper_space + ++ OFFSET(address_space_nrpages)); ++ ++ page_cache_size = nr_file_pages - swapper_space_nrpages - ++ buffer_pages; ++ FREEBUF(swapper_space); ++ } ++ ++ ++ pct = (page_cache_size * 100)/totalram_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "CACHED", page_cache_size, ++ pages_to_size(page_cache_size, buf), pct); ++ ++ /* ++ * Although /proc/meminfo doesn't show it, show how much memory ++ * the slabs take up. ++ */ ++ ++ pct = (get_slabs * 100)/totalram_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "SLAB", get_slabs, pages_to_size(get_slabs, buf), pct); ++ ++ if (symbol_exists("totalhigh_pages")) { ++ switch (get_syment_array("totalhigh_pages", sp_array, 2)) ++ { ++ case 1: ++ get_symbol_data("totalhigh_pages", sizeof(ulong), ++ &totalhigh_pages); ++ break; ++ case 2: ++ if (!(readmem(sp_array[0]->value, KVADDR, ++ &value1, sizeof(ulong), ++ "totalhigh_pages #1", RETURN_ON_ERROR))) ++ break; ++ if (!(readmem(sp_array[1]->value, KVADDR, ++ &value2, sizeof(ulong), ++ "totalhigh_pages #2", RETURN_ON_ERROR))) ++ break; ++ totalhigh_pages = MAX(value1, value2); ++ break; ++ } ++ ++ pct = totalhigh_pages ? ++ (totalhigh_pages * 100)/totalram_pages : 0; ++ fprintf(fp, "\n%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "TOTAL HIGH", totalhigh_pages, ++ pages_to_size(totalhigh_pages, buf), pct); ++ ++ meminfo.flags = GET_FREE_HIGHMEM_PAGES; ++ vt->dump_free_pages(&meminfo); ++ freehighmem_pages = meminfo.retval; ++ pct = freehighmem_pages ? ++ (freehighmem_pages * 100)/totalhigh_pages : 0; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL HIGH\n", ++ "FREE HIGH", freehighmem_pages, ++ pages_to_size(freehighmem_pages, buf), pct); ++ ++ totallowmem_pages = totalram_pages - totalhigh_pages; ++ pct = (totallowmem_pages * 100)/totalram_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", ++ "TOTAL LOW", totallowmem_pages, ++ pages_to_size(totallowmem_pages, buf), pct); ++ ++ freelowmem_pages = freeram_pages - freehighmem_pages; ++ pct = (freelowmem_pages * 100)/totallowmem_pages; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL LOW\n", ++ "FREE LOW", freelowmem_pages, ++ pages_to_size(freelowmem_pages, buf), pct); ++ } ++ ++ /* ++ * get swap data from dump_swap_info(). ++ */ ++ fprintf(fp, "\n"); ++ if (dump_swap_info(RETURN_ON_ERROR, &totalswap_pages, ++ &totalused_pages)) { ++ fprintf(fp, "%10s %7ld %11s ----\n", ++ "TOTAL SWAP", totalswap_pages, ++ pages_to_size(totalswap_pages, buf)); ++ pct = totalswap_pages ? (totalused_pages * 100) / ++ totalswap_pages : 100; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL SWAP\n", ++ "SWAP USED", totalused_pages, ++ pages_to_size(totalused_pages, buf), pct); ++ pct = totalswap_pages ? ((totalswap_pages - totalused_pages) * ++ 100) / totalswap_pages : 0; ++ fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL SWAP\n", ++ "SWAP FREE", ++ totalswap_pages - totalused_pages, ++ pages_to_size(totalswap_pages - totalused_pages, buf), ++ pct); ++ } else ++ error(INFO, "swap_info[%ld].swap_map at %lx is unaccessible\n", ++ totalused_pages, totalswap_pages); ++ ++ dump_zone_page_usage(); ++} ++ ++/* ++ * Emulate 2.6 nr_blockdev_pages() function. ++ */ ++static ulong ++nr_blockdev_pages(void) ++{ ++ struct list_data list_data, *ld; ++ ulong *bdevlist; ++ int i, bdevcnt; ++ ulong inode, address_space; ++ ulong nrpages; ++ char *block_device_buf, *inode_buf, *address_space_buf; ++ ++ ld = &list_data; ++ BZERO(ld, sizeof(struct list_data)); ++ get_symbol_data("all_bdevs", sizeof(void *), &ld->start); ++ if (empty_list(ld->start)) ++ return 0; ++ ld->end = symbol_value("all_bdevs"); ++ ld->list_head_offset = OFFSET(block_device_bd_list); ++ ++ block_device_buf = GETBUF(SIZE(block_device)); ++ inode_buf = GETBUF(SIZE(inode)); ++ address_space_buf = GETBUF(SIZE(address_space)); ++ ++ hq_open(); ++ bdevcnt = do_list(ld); ++ bdevlist = (ulong *)GETBUF(bdevcnt * sizeof(ulong)); ++ bdevcnt = retrieve_list(bdevlist, bdevcnt); ++ hq_close(); ++ ++ /* ++ * go through the block_device list, emulating: ++ * ++ * ret += bdev->bd_inode->i_mapping->nrpages; ++ */ ++ for (i = nrpages = 0; i < bdevcnt; i++) { ++ readmem(bdevlist[i], KVADDR, block_device_buf, ++ SIZE(block_device), "block_device buffer", ++ FAULT_ON_ERROR); ++ inode = ULONG(block_device_buf + OFFSET(block_device_bd_inode)); ++ readmem(inode, KVADDR, inode_buf, SIZE(inode), "inode buffer", ++ FAULT_ON_ERROR); ++ address_space = ULONG(inode_buf + OFFSET(inode_i_mapping)); ++ readmem(address_space, KVADDR, address_space_buf, ++ SIZE(address_space), "address_space buffer", ++ FAULT_ON_ERROR); ++ nrpages += ULONG(address_space_buf + ++ OFFSET(address_space_nrpages)); ++ } ++ ++ FREEBUF(bdevlist); ++ FREEBUF(block_device_buf); ++ FREEBUF(inode_buf); ++ FREEBUF(address_space_buf); ++ ++ return nrpages; ++} ++ ++/* ++ * dump_vmlist() displays information from the vmlist. ++ */ ++ ++static void ++dump_vmlist(struct meminfo *vi) ++{ ++ char buf[BUFSIZE]; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ ulong vmlist; ++ ulong addr, size, next, pcheck, count, verified; ++ physaddr_t paddr; ++ ++ get_symbol_data("vmlist", sizeof(void *), &vmlist); ++ next = vmlist; ++ count = verified = 0; ++ ++ while (next) { ++ if (!(pc->curcmd_flags & HEADER_PRINTED) && (next == vmlist) && ++ !(vi->flags & (GET_HIGHEST|GET_PHYS_TO_VMALLOC| ++ GET_VMLIST_COUNT|GET_VMLIST|VMLIST_VERIFY))) { ++ fprintf(fp, "%s ", ++ mkstring(buf, MAX(strlen("VM_STRUCT"), VADDR_PRLEN), ++ CENTER|LJUST, "VM_STRUCT")); ++ fprintf(fp, "%s SIZE\n", ++ mkstring(buf, (VADDR_PRLEN * 2) + strlen(" - "), ++ CENTER|LJUST, "ADDRESS RANGE")); ++ pc->curcmd_flags |= HEADER_PRINTED; ++ } ++ ++ readmem(next+OFFSET(vm_struct_addr), KVADDR, ++ &addr, sizeof(void *), ++ "vmlist addr", FAULT_ON_ERROR); ++ readmem(next+OFFSET(vm_struct_size), KVADDR, ++ &size, sizeof(ulong), ++ "vmlist size", FAULT_ON_ERROR); ++ ++ if (vi->flags & (GET_VMLIST_COUNT|GET_VMLIST)) { ++ /* ++ * Preceding GET_VMLIST_COUNT set vi->retval. ++ */ ++ if (vi->flags & GET_VMLIST) { ++ if (count < vi->retval) { ++ vi->vmlist[count].addr = addr; ++ vi->vmlist[count].size = size; ++ } ++ } ++ count++; ++ goto next_entry; ++ } ++ ++ if (!(vi->flags & ADDRESS_SPECIFIED) || ++ ((vi->memtype == KVADDR) && ++ ((vi->spec_addr >= addr) && (vi->spec_addr < (addr+size))))) { ++ if (vi->flags & VMLIST_VERIFY) { ++ verified++; ++ break; ++ } ++ fprintf(fp, "%s%s %s - %s %6ld\n", ++ mkstring(buf,VADDR_PRLEN, LONG_HEX|CENTER|LJUST, ++ MKSTR(next)), space(MINSPACE-1), ++ mkstring(buf1, VADDR_PRLEN, LONG_HEX|RJUST, ++ MKSTR(addr)), ++ mkstring(buf2, VADDR_PRLEN, LONG_HEX|LJUST, ++ MKSTR(addr+size)), ++ size); ++ } ++ ++ if ((vi->flags & ADDRESS_SPECIFIED) && ++ (vi->memtype == PHYSADDR)) { ++ for (pcheck = addr; pcheck < (addr+size); ++ pcheck += PAGESIZE()) { ++ if (!kvtop(NULL, pcheck, &paddr, 0)) ++ continue; ++ if ((vi->spec_addr >= paddr) && ++ (vi->spec_addr < (paddr+PAGESIZE()))) { ++ if (vi->flags & GET_PHYS_TO_VMALLOC) { ++ vi->retval = pcheck + ++ PAGEOFFSET(paddr); ++ return; ++ } else ++ fprintf(fp, ++ "%s%s %s - %s %6ld\n", ++ mkstring(buf, VADDR_PRLEN, ++ LONG_HEX|CENTER|LJUST, ++ MKSTR(next)), space(MINSPACE-1), ++ mkstring(buf1, VADDR_PRLEN, ++ LONG_HEX|RJUST, MKSTR(addr)), ++ mkstring(buf2, VADDR_PRLEN, ++ LONG_HEX|LJUST, ++ MKSTR(addr+size)), size); ++ break; ++ } ++ } ++ ++ } ++next_entry: ++ readmem(next+OFFSET(vm_struct_next), ++ KVADDR, &next, sizeof(void *), ++ "vmlist next", FAULT_ON_ERROR); ++ } ++ ++ if (vi->flags & GET_HIGHEST) ++ vi->retval = addr+size; ++ ++ if (vi->flags & GET_VMLIST_COUNT) ++ vi->retval = count; ++ ++ if (vi->flags & VMLIST_VERIFY) ++ vi->retval = verified; ++} ++ ++/* ++ * dump_page_lists() displays information from the active_list, ++ * inactive_dirty_list and inactive_clean_list from each zone. ++ */ ++static int ++dump_page_lists(struct meminfo *mi) ++{ ++ int i, c, n, retval; ++ ulong node_zones, pgdat; ++ struct node_table *nt; ++ struct list_data list_data, *ld; ++ char buf[BUFSIZE]; ++ ulong value; ++ ulong inactive_clean_pages, inactive_clean_list; ++ int nr_active_pages, nr_inactive_pages; ++ int nr_inactive_dirty_pages; ++ ++ ld = &list_data; ++ ++ retval = FALSE; ++ nr_active_pages = nr_inactive_dirty_pages = -1; ++ ++ BZERO(ld, sizeof(struct list_data)); ++ ld->list_head_offset = OFFSET(page_lru); ++ if (mi->flags & ADDRESS_SPECIFIED) ++ ld->searchfor = mi->spec_addr; ++ else if (mi->flags & VERBOSE) ++ ld->flags |= VERBOSE; ++ ++ if (mi->flags & GET_ACTIVE_LIST) { ++ if (!symbol_exists("active_list")) ++ error(FATAL, ++ "active_list does not exist in this kernel\n"); ++ ++ if (symbol_exists("nr_active_pages")) ++ get_symbol_data("nr_active_pages", sizeof(int), ++ &nr_active_pages); ++ else ++ error(FATAL, ++ "nr_active_pages does not exist in this kernel\n"); ++ ++ ld->end = symbol_value("active_list"); ++ readmem(ld->end, KVADDR, &ld->start, sizeof(void *), ++ "LIST_HEAD contents", FAULT_ON_ERROR); ++ ++ if (mi->flags & VERBOSE) ++ fprintf(fp, "active_list:\n"); ++ ++ if (ld->start == ld->end) { ++ c = 0; ++ ld->searchfor = 0; ++ if (mi->flags & VERBOSE) ++ fprintf(fp, "(empty)\n"); ++ } else { ++ hq_open(); ++ c = do_list(ld); ++ hq_close(); ++ } ++ ++ if ((mi->flags & ADDRESS_SPECIFIED) && ld->searchfor) { ++ fprintf(fp, "%lx\n", ld->searchfor); ++ retval = TRUE; ++ } else { ++ fprintf(fp, "%snr_active_pages: %d ", ++ mi->flags & VERBOSE ? "\n" : "", ++ nr_active_pages); ++ if (c != nr_active_pages) ++ fprintf(fp, "(found %d)\n", c); ++ else ++ fprintf(fp, "(verified)\n"); ++ } ++ } ++ ++ if (mi->flags & GET_INACTIVE_LIST) { ++ if (!symbol_exists("inactive_list")) ++ error(FATAL, ++ "inactive_list does not exist in this kernel\n"); ++ ++ if (symbol_exists("nr_inactive_pages")) ++ get_symbol_data("nr_inactive_pages", sizeof(int), ++ &nr_inactive_pages); ++ else ++ error(FATAL, ++ "nr_active_pages does not exist in this kernel\n"); ++ ++ ld->end = symbol_value("inactive_list"); ++ readmem(ld->end, KVADDR, &ld->start, sizeof(void *), ++ "LIST_HEAD contents", FAULT_ON_ERROR); ++ ++ if (mi->flags & VERBOSE) ++ fprintf(fp, "inactive_list:\n"); ++ ++ if (ld->start == ld->end) { ++ c = 0; ++ ld->searchfor = 0; ++ if (mi->flags & VERBOSE) ++ fprintf(fp, "(empty)\n"); ++ } else { ++ hq_open(); ++ c = do_list(ld); ++ hq_close(); ++ } ++ ++ if ((mi->flags & ADDRESS_SPECIFIED) && ld->searchfor) { ++ fprintf(fp, "%lx\n", ld->searchfor); ++ retval = TRUE; ++ } else { ++ fprintf(fp, "%snr_inactive_pages: %d ", ++ mi->flags & VERBOSE ? "\n" : "", ++ nr_inactive_pages); ++ if (c != nr_inactive_pages) ++ fprintf(fp, "(found %d)\n", c); ++ else ++ fprintf(fp, "(verified)\n"); ++ } ++ } ++ ++ if (mi->flags & GET_INACTIVE_DIRTY) { ++ if (!symbol_exists("inactive_dirty_list")) ++ error(FATAL, ++ "inactive_dirty_list does not exist in this kernel\n"); ++ ++ if (symbol_exists("nr_inactive_dirty_pages")) ++ get_symbol_data("nr_inactive_dirty_pages", sizeof(int), ++ &nr_inactive_dirty_pages); ++ else ++ error(FATAL, ++ "nr_inactive_dirty_pages does not exist in this kernel\n"); ++ ++ ld->end = symbol_value("inactive_dirty_list"); ++ readmem(ld->end, KVADDR, &ld->start, sizeof(void *), ++ "LIST_HEAD contents", FAULT_ON_ERROR); ++ ++ if (mi->flags & VERBOSE) ++ fprintf(fp, "%sinactive_dirty_list:\n", ++ mi->flags & GET_ACTIVE_LIST ? "\n" : ""); ++ ++ if (ld->start == ld->end) { ++ c = 0; ++ ld->searchfor = 0; ++ if (mi->flags & VERBOSE) ++ fprintf(fp, "(empty)\n"); ++ } else { ++ hq_open(); ++ c = do_list(ld); ++ hq_close(); ++ } ++ ++ if ((mi->flags & ADDRESS_SPECIFIED) && ld->searchfor) { ++ fprintf(fp, "%lx\n", ld->searchfor); ++ retval = TRUE; ++ } else { ++ fprintf(fp, "%snr_inactive_dirty_pages: %d ", ++ mi->flags & VERBOSE ? "\n" : "", ++ nr_inactive_dirty_pages); ++ if (c != nr_inactive_dirty_pages) ++ fprintf(fp, "(found %d)\n", c); ++ else ++ fprintf(fp, "(verified)\n"); ++ } ++ } ++ ++ if (mi->flags & GET_INACTIVE_CLEAN) { ++ if (INVALID_MEMBER(zone_struct_inactive_clean_list)) ++ error(FATAL, ++ "inactive_clean_list(s) do not exist in this kernel\n"); ++ ++ get_symbol_data("pgdat_list", sizeof(void *), &pgdat); ++ ++ if ((mi->flags & VERBOSE) && ++ (mi->flags & (GET_ACTIVE_LIST|GET_INACTIVE_DIRTY))) ++ fprintf(fp, "\n"); ++ ++ for (n = 0; pgdat; n++) { ++ nt = &vt->node_table[n]; ++ ++ node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); ++ ++ for (i = 0; i < vt->nr_zones; i++) { ++ readmem(node_zones+OFFSET(zone_struct_name), ++ KVADDR, &value, sizeof(void *), ++ "zone_struct name", FAULT_ON_ERROR); ++ if (!read_string(value, buf, BUFSIZE-1)) ++ sprintf(buf, "(unknown) "); ++ ++ if (mi->flags & VERBOSE) { ++ if (vt->numnodes > 1) ++ fprintf(fp, "NODE %d ", n); ++ fprintf(fp, ++ "\"%s\" inactive_clean_list:\n", ++ buf); ++ } ++ ++ readmem(node_zones + ++ OFFSET(zone_struct_inactive_clean_pages), ++ KVADDR, &inactive_clean_pages, ++ sizeof(ulong), "inactive_clean_pages", ++ FAULT_ON_ERROR); ++ ++ readmem(node_zones + ++ OFFSET(zone_struct_inactive_clean_list), ++ KVADDR, &inactive_clean_list, ++ sizeof(ulong), "inactive_clean_list", ++ FAULT_ON_ERROR); ++ ++ ld->start = inactive_clean_list; ++ ld->end = node_zones + ++ OFFSET(zone_struct_inactive_clean_list); ++ if (mi->flags & ADDRESS_SPECIFIED) ++ ld->searchfor = mi->spec_addr; ++ ++ if (ld->start == ld->end) { ++ c = 0; ++ ld->searchfor = 0; ++ if (mi->flags & VERBOSE) ++ fprintf(fp, "(empty)\n"); ++ } else { ++ hq_open(); ++ c = do_list(ld); ++ hq_close(); ++ } ++ ++ if ((mi->flags & ADDRESS_SPECIFIED) && ++ ld->searchfor) { ++ fprintf(fp, "%lx\n", ld->searchfor); ++ retval = TRUE; ++ } else { ++ if (vt->numnodes > 1) ++ fprintf(fp, "NODE %d ", n); ++ fprintf(fp, "\"%s\" ", buf); ++ fprintf(fp, ++ "inactive_clean_pages: %ld ", ++ inactive_clean_pages); ++ if (c != inactive_clean_pages) ++ fprintf(fp, "(found %d)\n", c); ++ else ++ fprintf(fp, "(verified)\n"); ++ } ++ ++ node_zones += SIZE(zone_struct); ++ } ++ ++ readmem(pgdat + OFFSET_OPTION(pglist_data_node_next, ++ pglist_data_pgdat_next), KVADDR, ++ &pgdat, sizeof(void *), "pglist_data node_next", ++ FAULT_ON_ERROR); ++ } ++ } ++ ++ return retval; ++} ++ ++ ++ ++/* ++ * Check whether an address is a kmem_cache_t address, and if so, return ++ * a pointer to the static buffer containing its name string. Otherwise ++ * return NULL on failure. ++ */ ++ ++#define PERCPU_NOT_SUPPORTED "per-cpu slab format not supported yet\n" ++ ++static char * ++is_kmem_cache_addr(ulong vaddr, char *kbuf) ++{ ++ ulong cache, cache_cache, name; ++ long next_offset, name_offset; ++ char *cache_buf; ++ ++ if (vt->flags & KMEM_CACHE_UNAVAIL) { ++ error(INFO, "kmem cache slab subsystem not available\n"); ++ return NULL; ++ } ++ ++ if (vt->flags & KMALLOC_SLUB) ++ return is_kmem_cache_addr_slub(vaddr, kbuf); ++ ++ name_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? ++ OFFSET(kmem_cache_s_name) : OFFSET(kmem_cache_s_c_name); ++ next_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? ++ OFFSET(kmem_cache_s_next) : OFFSET(kmem_cache_s_c_nextp); ++ ++ cache = cache_cache = symbol_value("cache_cache"); ++ ++ cache_buf = GETBUF(SIZE(kmem_cache_s)); ++ ++ do { ++ readmem(cache, KVADDR, cache_buf, SIZE(kmem_cache_s), ++ "kmem_cache_s buffer", FAULT_ON_ERROR); ++ ++ if (cache == vaddr) { ++ if (vt->kmem_cache_namelen) { ++ BCOPY(cache_buf+name_offset, kbuf, ++ vt->kmem_cache_namelen); ++ } else { ++ name = ULONG(cache_buf + name_offset); ++ if (!read_string(name, kbuf, BUFSIZE-1)) { ++ if (vt->flags & ++ (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) ++ error(WARNING, ++ "cannot read kmem_cache_s.name string at %lx\n", ++ name); ++ else ++ error(WARNING, ++ "cannot read kmem_cache_s.c_name string at %lx\n", ++ name); ++ sprintf(kbuf, "(unknown)"); ++ } ++ } ++ FREEBUF(cache_buf); ++ return kbuf; ++ } ++ ++ cache = ULONG(cache_buf + next_offset); ++ ++ if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) ++ cache -= next_offset; ++ ++ } while (cache != cache_cache); ++ ++ FREEBUF(cache_buf); ++ return NULL; ++} ++ ++/* ++ * Note same functionality as above, but instead it just ++ * dumps all slab cache names and their addresses. ++ */ ++static void ++kmem_cache_list(void) ++{ ++ ulong cache, cache_cache, name; ++ long next_offset, name_offset; ++ char *cache_buf; ++ char buf[BUFSIZE]; ++ ++ if (vt->flags & KMEM_CACHE_UNAVAIL) { ++ error(INFO, "kmem cache slab subsystem not available\n"); ++ return; ++ } ++ ++ if (vt->flags & KMALLOC_SLUB) { ++ kmem_cache_list_slub(); ++ return; ++ } ++ ++ name_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? ++ OFFSET(kmem_cache_s_name) : OFFSET(kmem_cache_s_c_name); ++ next_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? ++ OFFSET(kmem_cache_s_next) : OFFSET(kmem_cache_s_c_nextp); ++ ++ cache = cache_cache = symbol_value("cache_cache"); ++ ++ cache_buf = GETBUF(SIZE(kmem_cache_s)); ++ ++ do { ++ readmem(cache, KVADDR, cache_buf, SIZE(kmem_cache_s), ++ "kmem_cache_s buffer", FAULT_ON_ERROR); ++ ++ if (vt->kmem_cache_namelen) { ++ BCOPY(cache_buf+name_offset, buf, ++ vt->kmem_cache_namelen); ++ } else { ++ name = ULONG(cache_buf + name_offset); ++ if (!read_string(name, buf, BUFSIZE-1)) { ++ if (vt->flags & ++ (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) ++ error(WARNING, ++ "cannot read kmem_cache_s.name string at %lx\n", ++ name); ++ else ++ error(WARNING, ++ "cannot read kmem_cache_s.c_name string at %lx\n", ++ name); ++ sprintf(buf, "(unknown)"); ++ } ++ } ++ ++ fprintf(fp, "%lx %s\n", cache, buf); ++ ++ cache = ULONG(cache_buf + next_offset); ++ ++ if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) ++ cache -= next_offset; ++ ++ } while (cache != cache_cache); ++ ++ FREEBUF(cache_buf); ++} ++ ++/* ++ * Translate an address to its physical page number, verify that the ++ * page in fact belongs to the slab subsystem, and if so, return the ++ * name of the cache to which it belongs. ++ */ ++static char * ++vaddr_to_kmem_cache(ulong vaddr, char *buf, int verbose) ++{ ++ physaddr_t paddr; ++ ulong page; ++ ulong cache; ++ ++ if (!kvtop(NULL, vaddr, &paddr, 0)) { ++ if (verbose) ++ error(WARNING, ++ "cannot make virtual-to-physical translation: %lx\n", ++ vaddr); ++ return NULL; + } +- hq_close(); + +- fprintf(fp, "%spage_cache_size: %ld ", verbose ? "\n" : "", +- page_cache_size); +- if (page_cache_size != total_cached) +- fprintf(fp, "(found %ld)\n", total_cached); ++ if (!phys_to_page(paddr, &page)) { ++ if (verbose) ++ error(WARNING, ++ "cannot find mem_map page for address: %lx\n", ++ vaddr); ++ return NULL; ++ } ++ ++ if (vt->flags & KMALLOC_SLUB) { ++ readmem(compound_head(page)+OFFSET(page_slab), ++ KVADDR, &cache, sizeof(void *), ++ "page.slab", FAULT_ON_ERROR); ++ } else if (VALID_MEMBER(page_next)) ++ readmem(page+OFFSET(page_next), ++ KVADDR, &cache, sizeof(void *), ++ "page.next", FAULT_ON_ERROR); ++ else if (VALID_MEMBER(page_list_next)) ++ readmem(page+OFFSET(page_list_next), ++ KVADDR, &cache, sizeof(void *), ++ "page.list.next", FAULT_ON_ERROR); ++ else if (VALID_MEMBER(page_lru)) ++ readmem(page+OFFSET(page_lru)+OFFSET(list_head_next), ++ KVADDR, &cache, sizeof(void *), ++ "page.lru.next", FAULT_ON_ERROR); ++ else ++ error(FATAL, "cannot determine slab cache from page struct\n"); ++ ++ return(is_kmem_cache_addr(cache, buf)); ++} ++ ++/* ++ * Translate an address to its physical page number, verify that the ++ * page in fact belongs to the slab subsystem, and if so, return the ++ * address of the slab to which it belongs. ++ */ ++static ulong ++vaddr_to_slab(ulong vaddr) ++{ ++ physaddr_t paddr; ++ ulong page; ++ ulong slab; ++ ++ if (!kvtop(NULL, vaddr, &paddr, 0)) { ++ error(WARNING, ++ "cannot make virtual-to-physical translation: %lx\n", ++ vaddr); ++ return 0; ++ } ++ ++ if (!phys_to_page(paddr, &page)) { ++ error(WARNING, "cannot find mem_map page for address: %lx\n", ++ vaddr); ++ return 0; ++ } ++ ++ slab = 0; ++ ++ if (vt->flags & KMALLOC_SLUB) ++ slab = compound_head(page); ++ else if (VALID_MEMBER(page_prev)) ++ readmem(page+OFFSET(page_prev), ++ KVADDR, &slab, sizeof(void *), ++ "page.prev", FAULT_ON_ERROR); ++ else if (VALID_MEMBER(page_list_prev)) ++ readmem(page+OFFSET(page_list_prev), ++ KVADDR, &slab, sizeof(void *), ++ "page.list.prev", FAULT_ON_ERROR); ++ else if (VALID_MEMBER(page_lru)) ++ readmem(page+OFFSET(page_lru)+OFFSET(list_head_prev), ++ KVADDR, &slab, sizeof(void *), ++ "page.lru.prev", FAULT_ON_ERROR); + else +- fprintf(fp, "(verified)\n"); ++ error(FATAL, "unknown definition of struct page?\n"); + +- if (CRASHDEBUG(1)) +- fprintf(fp, "heads containing page(s): %d\n", populated); ++ return slab; ++} + +- if (searchpage) { +- rewind(pc->tmpfile); +- found = FALSE; +- while (fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (CRASHDEBUG(1) && STRNEQ(buf, "flags & KMEM_CACHE_UNAVAIL) ++ return; ++ ++ if ((vt->flags & KMEM_CACHE_DELAY) && !(pc->flags & RUNTIME)) ++ return; ++ ++ if (DUMPFILE() && (vt->flags & KMEM_CACHE_INIT)) ++ return; ++ ++ please_wait("gathering kmem slab cache data"); ++ ++ if (!strlen(slab_hdr)) { ++ if (vt->flags & KMALLOC_SLUB) ++ sprintf(slab_hdr, ++ "SLAB%sMEMORY%sNODE TOTAL ALLOCATED FREE\n", ++ space(VADDR_PRLEN > 8 ? 14 : 6), ++ space(VADDR_PRLEN > 8 ? 12 : 4)); ++ else ++ sprintf(slab_hdr, ++ "SLAB%sMEMORY%sTOTAL ALLOCATED FREE\n", ++ space(VADDR_PRLEN > 8 ? 14 : 6), ++ space(VADDR_PRLEN > 8 ? 12 : 4)); ++ } ++ ++ if (!strlen(kmem_cache_hdr)) ++ sprintf(kmem_cache_hdr, ++ "CACHE%sNAME OBJSIZE ALLOCATED TOTAL SLABS SSIZE\n", ++ space(VADDR_PRLEN > 8 ? 12 : 4)); ++ ++ if (!strlen(free_inuse_hdr)) ++ sprintf(free_inuse_hdr, "FREE / [ALLOCATED]\n"); ++ ++ if (vt->flags & KMALLOC_SLUB) { ++ kmem_cache_init_slub(); ++ return; ++ } ++ ++ num_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? ++ OFFSET(kmem_cache_s_num) : OFFSET(kmem_cache_s_c_num); ++ next_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? ++ OFFSET(kmem_cache_s_next) : OFFSET(kmem_cache_s_c_nextp); ++ max_cnum = max_limit = max_cpus = cache_count = 0; ++ ++ /* ++ * Pre-2.6 versions used the "cache_cache" as the head of the ++ * slab chain list. 2.6 uses the "cache_chain" list_head. ++ */ ++ if (vt->flags & PERCPU_KMALLOC_V2) { ++ get_symbol_data("cache_chain", sizeof(ulong), &cache); ++ cache -= next_offset; ++ cache_end = symbol_value("cache_chain"); ++ } else ++ cache = cache_end = symbol_value("cache_cache"); ++ ++ cache_buf = GETBUF(SIZE(kmem_cache_s)); ++ ++ do { ++ cache_count++; ++ ++ if (!readmem(cache, KVADDR, cache_buf, SIZE(kmem_cache_s), ++ "kmem_cache_s buffer", RETURN_ON_ERROR)) { ++ FREEBUF(cache_buf); ++ vt->flags |= KMEM_CACHE_UNAVAIL; ++ error(INFO, ++ "%sunable to initialize kmem slab cache subsystem\n\n", ++ DUMPFILE() ? "\n" : ""); ++ return; ++ } ++ ++ tmp = (ulong)(UINT(cache_buf + num_offset)); ++ ++ if (tmp > max_cnum) ++ max_cnum = tmp; ++ ++ if ((tmp = max_cpudata_limit(cache, &tmp2)) > max_limit) ++ max_limit = tmp; ++ /* ++ * Recognize and bail out on any max_cpudata_limit() failures. ++ */ ++ if (vt->flags & KMEM_CACHE_UNAVAIL) { ++ FREEBUF(cache_buf); ++ return; ++ } ++ ++ if (tmp2 > max_cpus) ++ max_cpus = tmp2; ++ ++ cache = ULONG(cache_buf + next_offset); ++ ++ switch (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) ++ { ++ case PERCPU_KMALLOC_V1: ++ cache -= next_offset; ++ break; ++ case PERCPU_KMALLOC_V2: ++ if (cache != cache_end) ++ cache -= next_offset; ++ break; ++ } ++ ++ } while (cache != cache_end); ++ ++ FREEBUF(cache_buf); ++ ++ vt->kmem_max_c_num = max_cnum; ++ vt->kmem_max_limit = max_limit; ++ vt->kmem_max_cpus = max_cpus; ++ vt->kmem_cache_count = cache_count; ++ ++ if (CRASHDEBUG(2)) { ++ fprintf(fp, "kmem_cache_init:\n"); ++ fprintf(fp, " kmem_max_c_num: %ld\n", vt->kmem_max_c_num); ++ fprintf(fp, " kmem_max_limit: %ld\n", vt->kmem_max_limit); ++ fprintf(fp, " kmem_max_cpus: %ld\n", vt->kmem_max_cpus); ++ fprintf(fp, " kmem_cache_count: %ld\n", vt->kmem_cache_count); ++ } ++ ++ if (!(vt->flags & KMEM_CACHE_INIT)) { ++ if (vt->flags & PERCPU_KMALLOC_V1) ++ ARRAY_LENGTH_INIT(vt->kmem_cache_namelen, ++ kmem_cache_s_name, "kmem_cache_s.name", ++ NULL, sizeof(char)); ++ else if (vt->flags & PERCPU_KMALLOC_V2) ++ vt->kmem_cache_namelen = 0; ++ else ++ ARRAY_LENGTH_INIT(vt->kmem_cache_namelen, ++ kmem_cache_s_c_name, "kmem_cache_s.c_name", ++ NULL, 0); ++ } ++ ++ please_wait_done(); ++ ++ vt->flags |= KMEM_CACHE_INIT; ++} ++ ++/* ++ * Determine the largest cpudata limit for a given cache. ++ */ ++static ulong ++max_cpudata_limit(ulong cache, ulong *cpus) ++{ ++ int i; ++ ulong cpudata[NR_CPUS]; ++ int limit; ++ ulong max_limit; ++ ulong shared; ++ ulong *start_address; ++ ++ if (vt->flags & PERCPU_KMALLOC_V2_NODES) ++ goto kmem_cache_s_array_nodes; ++ ++ if (vt->flags & PERCPU_KMALLOC_V2) ++ goto kmem_cache_s_array; ++ ++ if (INVALID_MEMBER(kmem_cache_s_cpudata)) { ++ *cpus = 0; ++ return 0; ++ } ++ ++ if (!readmem(cache+OFFSET(kmem_cache_s_cpudata), ++ KVADDR, &cpudata[0], ++ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_cpudata), ++ "cpudata array", RETURN_ON_ERROR)) ++ goto bail_out; ++ ++ for (i = max_limit = 0; (i < ARRAY_LENGTH(kmem_cache_s_cpudata)) && ++ cpudata[i]; i++) { ++ if (!readmem(cpudata[i]+OFFSET(cpucache_s_limit), ++ KVADDR, &limit, sizeof(int), ++ "cpucache limit", RETURN_ON_ERROR)) ++ goto bail_out; ++ if (limit > max_limit) ++ max_limit = limit; ++ } ++ ++ *cpus = i; ++ ++ return max_limit; ++ ++kmem_cache_s_array: ++ ++ if (!readmem(cache+OFFSET(kmem_cache_s_array), ++ KVADDR, &cpudata[0], ++ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array), ++ "array cache array", RETURN_ON_ERROR)) ++ goto bail_out; ++ ++ for (i = max_limit = 0; (i < ARRAY_LENGTH(kmem_cache_s_array)) && ++ cpudata[i]; i++) { ++ if (!readmem(cpudata[i]+OFFSET(array_cache_limit), ++ KVADDR, &limit, sizeof(int), ++ "array cache limit", RETURN_ON_ERROR)) ++ goto bail_out; ++ if (limit > max_limit) ++ max_limit = limit; ++ } ++ ++ /* ++ * If the shared list can be accessed, check its size as well. ++ */ ++ if (VALID_MEMBER(kmem_list3_shared) && ++ VALID_MEMBER(kmem_cache_s_lists) && ++ readmem(cache+OFFSET(kmem_cache_s_lists)+OFFSET(kmem_list3_shared), ++ KVADDR, &shared, sizeof(void *), "kmem_list3 shared", ++ RETURN_ON_ERROR|QUIET) && ++ readmem(shared+OFFSET(array_cache_limit), ++ KVADDR, &limit, sizeof(int), "shared array_cache limit", ++ RETURN_ON_ERROR|QUIET)) { ++ if (limit > max_limit) ++ max_limit = limit; ++ } ++ ++ *cpus = i; ++ return max_limit; ++ ++kmem_cache_s_array_nodes: ++ ++ if (!readmem(cache+OFFSET(kmem_cache_s_array), ++ KVADDR, &cpudata[0], ++ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array), ++ "array cache array", RETURN_ON_ERROR)) ++ goto bail_out; + +- if (CRASHDEBUG(1) && +- !hexadecimal(strip_linefeeds(buf), 0)) +- continue; ++ for (i = max_limit = 0; (i < ARRAY_LENGTH(kmem_cache_s_array)) && ++ cpudata[i]; i++) { ++ if (!readmem(cpudata[i]+OFFSET(array_cache_limit), ++ KVADDR, &limit, sizeof(int), ++ "array cache limit", RETURN_ON_ERROR)) ++ goto bail_out; ++ if (limit > max_limit) ++ max_limit = limit; ++ } + +- this_addr = htol(strip_linefeeds(buf), +- RETURN_ON_ERROR, &errflag); ++ *cpus = i; + +- if (this_addr == searchpage) { +- found = TRUE; ++ /* ++ * Check the shared list of all the nodes. ++ */ ++ start_address = (ulong *)GETBUF(sizeof(ulong) * vt->kmem_cache_len_nodes); ++ ++ if (VALID_MEMBER(kmem_list3_shared) && VALID_MEMBER(kmem_cache_s_lists) && ++ readmem(cache+OFFSET(kmem_cache_s_lists), KVADDR, &start_address[0], ++ sizeof(ulong) * vt->kmem_cache_len_nodes, "array nodelist array", ++ RETURN_ON_ERROR)) { ++ for (i = 0; i < vt->kmem_cache_len_nodes && start_address[i]; i++) { ++ if (readmem(start_address[i] + OFFSET(kmem_list3_shared), ++ KVADDR, &shared, sizeof(void *), ++ "kmem_list3 shared", RETURN_ON_ERROR|QUIET)) { ++ if (!shared) ++ break; ++ } ++ if (readmem(shared + OFFSET(array_cache_limit), ++ KVADDR, &limit, sizeof(int), "shared array_cache limit", ++ RETURN_ON_ERROR|QUIET)) { ++ if (limit > max_limit) ++ max_limit = limit; + break; + } + } +- close_tmpfile(); ++ } ++ FREEBUF(start_address); ++ return max_limit; + +- if (found) { +- fprintf(fp, hash_table); +- fprintf(fp, "%lx\n", searchpage); +- hi->retval = TRUE; +- } ++bail_out: ++ vt->flags |= KMEM_CACHE_UNAVAIL; ++ error(INFO, "unable to initialize kmem slab cache subsystem\n\n"); ++ *cpus = 0; ++ return 0; ++} ++ ++/* ++ * Determine whether the current slab cache is contained in ++ * the comma-separated list from a "kmem -I list1,list2 ..." ++ * command entry. ++ */ ++static int ++ignore_cache(struct meminfo *si, char *name) ++{ ++ int i, argc; ++ char *p1; ++ char *arglist[MAXARGS]; ++ char buf[BUFSIZE]; ++ ++ if (!si->ignore) ++ return FALSE; ++ ++ strcpy(buf, si->ignore); ++ ++ p1 = buf; ++ while (*p1) { ++ if (*p1 == ',') ++ *p1 = ' '; ++ p1++; ++ } ++ ++ argc = parse_line(buf, arglist); ++ ++ for (i = 0; i < argc; i++) { ++ if (STREQ(name, arglist[i])) ++ return TRUE; + } ++ ++ return FALSE; + } + ++ + /* +- * dump_free_pages() displays basic data about pages currently resident +- * in the free_area[] memory lists. If the flags contains the VERBOSE +- * bit, each page slab base address is dumped. If an address is specified +- * only the free_area[] data containing that page is displayed, along with +- * the page slab base address. Specified addresses can either be physical +- * address or page structure pointers. ++ * dump_kmem_cache() displays basic information about kmalloc() slabs. ++ * At this point, only kmem_cache_s structure data for each slab is dumped. ++ * ++ * TBD: Given a specified physical address, and determine which slab it came ++ * from, and whether it's in use or not. + */ +-char *free_area_hdr1 = \ +- "AREA SIZE FREE_AREA_STRUCT BLOCKS PAGES\n"; +-char *free_area_hdr2 = \ +- "AREA SIZE FREE_AREA_STRUCT\n"; ++ ++#define SLAB_C_MAGIC 0x4F17A36DUL ++#define SLAB_MAGIC_ALLOC 0xA5C32F2BUL /* slab is alive */ ++#define SLAB_MAGIC_DESTROYED 0xB2F23C5AUL /* slab has been destroyed */ ++ ++#define SLAB_CFLGS_BUFCTL 0x020000UL /* bufctls in own cache */ ++ ++#define KMEM_SLAB_ADDR (1) ++#define KMEM_BUFCTL_ADDR (2) ++#define KMEM_OBJECT_ADDR_FREE (3) ++#define KMEM_OBJECT_ADDR_INUSE (4) ++#define KMEM_OBJECT_ADDR_CACHED (5) ++#define KMEM_ON_SLAB (6) ++#define KMEM_OBJECT_ADDR_SHARED (7) ++ ++#define DUMP_KMEM_CACHE_INFO_V1() \ ++ { \ ++ char b1[BUFSIZE]; \ ++ fprintf(fp, "%s %-18s %8ld ", \ ++ mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->cache)), \ ++ buf, si->size); \ ++ fprintf(fp, "%9ld %8ld %5ld %3ldk\n", \ ++ vt->flags & PERCPU_KMALLOC_V1 ? \ ++ si->inuse - si->cpucached_cache : \ ++ si->inuse, si->num_slabs * si->c_num, \ ++ si->num_slabs, si->slabsize/1024); \ ++ } ++ ++#define DUMP_KMEM_CACHE_INFO_V2() dump_kmem_cache_info_v2(si) + + static void +-dump_free_pages(struct meminfo *fi) ++dump_kmem_cache_info_v2(struct meminfo *si) + { +- int i; +- int order; +- ulong free_area; +- char *free_area_buf; +- ulong *pp; +- int nr_mem_lists; +- struct list_data list_data, *ld; +- long cnt, total_free, chunk_size; +- int nr_free_pages; +- char buf[BUFSIZE]; +- char last_free[BUFSIZE]; +- char last_free_hdr[BUFSIZE]; +- int verbose, errflag, found; +- physaddr_t searchphys; +- ulong this_addr; +- physaddr_t this_phys; +- int do_search; +- ulong kfp, offset; +- int flen, dimension; ++ char b1[BUFSIZE]; ++ char b2[BUFSIZE]; ++ int namelen, sizelen, spacelen; + +- if (vt->flags & (NODES|ZONES)) +- error(FATAL, "dump_free_pages called with (NODES|ZONES)\n"); ++ fprintf(fp, "%s ", ++ mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->cache))); + +- nr_mem_lists = ARRAY_LENGTH(free_area); +- dimension = ARRAY_LENGTH(free_area_DIMENSION); ++ namelen = strlen(si->curname); ++ sprintf(b2, "%ld", si->size); ++ sizelen = strlen(b2); ++ spacelen = 0; + +- if (nr_mem_lists == 0) +- error(FATAL, "cannot determine size/dimensions of free_area\n"); ++ if (namelen++ > 18) { ++ spacelen = 29 - namelen - sizelen; ++ fprintf(fp, "%s%s%ld ", si->curname, ++ space(spacelen <= 0 ? 1 : spacelen), si->size); ++ if (spacelen > 0) ++ spacelen = 1; ++ sprintf(b1, "%c%dld ", '%', 9 + spacelen - 1); ++ } else { ++ fprintf(fp, "%-18s %8ld ", si->curname, si->size); ++ sprintf(b1, "%c%dld ", '%', 9); ++ } + +- if (dimension) ++ fprintf(fp, b1, vt->flags & (PERCPU_KMALLOC_V2) ? ++ si->inuse - si->cpucached_cache : si->inuse); ++ ++ fprintf(fp, "%8ld %5ld %3ldk\n", ++ si->num_slabs * si->c_num, ++ si->num_slabs, si->slabsize/1024); ++} ++ ++#define DUMP_SLAB_INFO() \ ++ { \ ++ char b1[BUFSIZE], b2[BUFSIZE]; \ ++ ulong allocated, freeobjs; \ ++ if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) { \ ++ allocated = si->s_inuse - si->cpucached_slab; \ ++ freeobjs = si->c_num - allocated - si->cpucached_slab; \ ++ } else { \ ++ allocated = si->s_inuse; \ ++ freeobjs = si->c_num - si->s_inuse; \ ++ } \ ++ fprintf(fp, "%s %s %5ld %9ld %4ld\n", \ ++ mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->slab)), \ ++ mkstring(b2, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->s_mem)), \ ++ si->c_num, allocated, \ ++ vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? \ ++ freeobjs + si->cpucached_slab : freeobjs); \ ++ } ++ ++static void ++dump_kmem_cache(struct meminfo *si) ++{ ++ char buf[BUFSIZE]; ++ char kbuf[BUFSIZE]; ++ char *reqname; ++ ulong cache_cache; ++ ulong name, magic; ++ int cnt; ++ char *p1; ++ ++ if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) + error(FATAL, +- "dump_free_pages called with multidimensional free area\n"); ++ "dump_kmem_cache called with PERCPU_KMALLOC_V[12] set\n"); + +- ld = &list_data; +- total_free = 0; +- searchphys = 0; +- do_search = FALSE; +- get_symbol_data("nr_free_pages", sizeof(int), &nr_free_pages); +- +- switch (fi->flags) +- { +- case GET_FREE_HIGHMEM_PAGES: +- error(FATAL, "GET_FREE_HIGHMEM_PAGES invalid in this kernel\n"); ++ si->found = si->retval = 0; ++ reqname = NULL; + +- case GET_FREE_PAGES: +- fi->retval = (ulong)nr_free_pages; +- return; ++ if ((!(si->flags & VERBOSE) || si->reqname) && ++ !(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) ++ fprintf(fp, kmem_cache_hdr); + +- case ADDRESS_SPECIFIED: +- switch (fi->memtype) +- { +- case KVADDR: +- if (!page_to_phys(fi->spec_addr, &searchphys)) { +- if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) +- return; +- } +- break; +- case PHYSADDR: +- searchphys = fi->spec_addr; +- break; +- default: +- error(FATAL, "dump_free_pages: no memtype specified\n"); ++ si->addrlist = (ulong *)GETBUF((vt->kmem_max_c_num+1) * sizeof(ulong)); ++ cnt = 0; ++ si->cache = cache_cache = symbol_value("cache_cache"); ++ ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (!(p1 = vaddr_to_kmem_cache(si->spec_addr, kbuf, VERBOSE))) { ++ error(INFO, ++ "address is not allocated in slab subsystem: %lx\n", ++ si->spec_addr); ++ return; + } +- do_search = TRUE; +- break; +- } ++ ++ if (si->reqname && (si->reqname != p1)) ++ error(INFO, ++ "ignoring pre-selected %s cache for address: %lx\n", ++ si->reqname, si->spec_addr, si->reqname); + +- verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++ reqname = p1; ++ } else ++ reqname = si->reqname; + +- free_area_buf = GETBUF(nr_mem_lists * SIZE(free_area_struct)); +- kfp = free_area = symbol_value("free_area"); +- flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT")); +- readmem(free_area, KVADDR, free_area_buf, +- SIZE(free_area_struct) * nr_mem_lists, +- "free_area_struct", FAULT_ON_ERROR); ++ si->cache_buf = GETBUF(SIZE(kmem_cache_s)); + +- if (do_search) +- open_tmpfile(); ++ do { ++ if ((si->flags & VERBOSE) && !si->reqname && ++ !(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, "%s%s", cnt++ ? "\n" : "", kmem_cache_hdr); + +- if (!verbose) +- fprintf(fp, free_area_hdr1); ++ readmem(si->cache, KVADDR, si->cache_buf, SIZE(kmem_cache_s), ++ "kmem_cache_s buffer", FAULT_ON_ERROR); + +- hq_open(); +- for (i = 0; i < nr_mem_lists; i++) { +- pp = (ulong *)(free_area_buf + (SIZE(free_area_struct)*i)); ++ if (vt->kmem_cache_namelen) { ++ BCOPY(si->cache_buf + OFFSET(kmem_cache_s_c_name), ++ buf, vt->kmem_cache_namelen); ++ } else { ++ name = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_s_c_name)); ++ if (!read_string(name, buf, BUFSIZE-1)) { ++ error(WARNING, ++ "cannot read kmem_cache_s.c_name string at %lx\n", ++ name); ++ sprintf(buf, "(unknown)"); ++ } ++ } + +- chunk_size = power(2, i); ++ if (reqname && !STREQ(reqname, buf)) ++ goto next_cache; + +- if (verbose) +- fprintf(fp, free_area_hdr2); ++ if (ignore_cache(si, buf)) { ++ fprintf(fp, "%lx %-18s [IGNORED]\n", si->cache, buf); ++ goto next_cache; ++ } + +- fprintf(fp, "%3d ", i); +- sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024); +- fprintf(fp, "%5s ", buf); ++ si->curname = buf; + +- fprintf(fp, "%s %s", +- mkstring(buf, flen, CENTER|LONG_HEX, MKSTR(kfp)), +- verbose ? "\n" : ""); ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "cache: %lx %s\n", si->cache, si->curname); ++ console("cache: %lx %s\n", si->cache, si->curname); + +- if (is_page_ptr(*pp, NULL)) { +- BZERO(ld, sizeof(struct list_data)); +- ld->flags = verbose; +- ld->start = *pp; +- ld->end = free_area; +- cnt = do_list(ld); +- total_free += (cnt * chunk_size); +- } else +- cnt = 0; ++ magic = ULONG(si->cache_buf + OFFSET(kmem_cache_s_c_magic)); + +- if (!verbose) +- fprintf(fp, "%6ld %6ld\n", cnt, cnt * chunk_size ); ++ if (magic == SLAB_C_MAGIC) { + +- free_area += SIZE(free_area_struct); +- kfp += SIZE(free_area_struct); +- } +- hq_close(); ++ si->size = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_s_c_org_size)); ++ if (!si->size) { ++ if (STREQ(si->curname, "kmem_cache")) ++ si->size = SIZE(kmem_cache_s); ++ else { ++ error(INFO, ++ "\"%s\" cache: c_org_size: %ld\n", ++ si->curname, si->size); ++ si->errors++; ++ } ++ } ++ si->c_flags = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_s_c_flags)); ++ si->c_offset = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_s_c_offset)); ++ si->order = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_s_c_gfporder)); ++ si->c_num = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_s_c_num)); + +- fprintf(fp, "\nnr_free_pages: %d ", nr_free_pages); +- if (total_free != nr_free_pages) +- fprintf(fp, "(found %ld)\n", total_free); +- else +- fprintf(fp, "(verified)\n"); ++ do_slab_chain(SLAB_GET_COUNTS, si); + +- if (!do_search) +- return; ++ if (!(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) ++ DUMP_KMEM_CACHE_INFO_V1(); + +- found = FALSE; +- rewind(pc->tmpfile); +- order = offset = 0; ++ if (si->flags == GET_SLAB_PAGES) ++ si->retval += (si->num_slabs * ++ (si->slabsize/PAGESIZE())); + +- while (fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (CRASHDEBUG(1) && STRNEQ(buf, "flags & (VERBOSE|ADDRESS_SPECIFIED)) { ++ si->slab = (si->flags & ADDRESS_SPECIFIED) ? ++ vaddr_to_slab(si->spec_addr) : 0; ++ ++ do_slab_chain(SLAB_WALKTHROUGH, si); + +- if (strstr(buf, "nr_free_pages") || +- STREQ(buf, "\n")) +- continue; ++ if (si->found) { ++ fprintf(fp, kmem_cache_hdr); ++ DUMP_KMEM_CACHE_INFO_V1(); ++ fprintf(fp, slab_hdr); ++ DUMP_SLAB_INFO(); + +- if (strstr(buf, "AREA")) { +- strcpy(last_free_hdr, buf); +- continue; +- } ++ switch (si->found) ++ { ++ case KMEM_BUFCTL_ADDR: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp, ++ "(ON-SLAB kmem_bufctl_t)\n"); ++ break; + +- if (strstr(buf, "k")) { +- strcpy(last_free, buf); +- chunk_size = power(2, order) * PAGESIZE(); +- order++; +- continue; +- } ++ case KMEM_SLAB_ADDR: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp, ++ "(ON-SLAB kmem_slab_t)\n"); ++ break; + +- if (CRASHDEBUG(1) && !hexadecimal(strip_linefeeds(buf), 0)) +- continue; ++ case KMEM_ON_SLAB: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp, ++ "(unused part of slab)\n"); ++ break; ++ ++ case KMEM_OBJECT_ADDR_FREE: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, " %lx\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr); ++ break; + +- errflag = 0; +- this_addr = htol(strip_linefeeds(buf), +- RETURN_ON_ERROR, &errflag); +- if (errflag) +- continue; ++ case KMEM_OBJECT_ADDR_INUSE: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, " [%lx]\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr); ++ break; ++ } + +- if (!page_to_phys(this_addr, &this_phys)) +- continue; ++ break; ++ } ++ } + +- if ((searchphys >= this_phys) && +- (searchphys < (this_phys+chunk_size))) { +- if (searchphys > this_phys) +- offset = (searchphys - this_phys)/PAGESIZE(); +- found = TRUE; +- break; ++ } else { ++ error(INFO, "\"%s\" cache: invalid c_magic: %lx\n", ++ si->curname, magic); ++ si->errors++; + } +- } +- close_tmpfile(); + +- if (found) { +- order--; ++next_cache: ++ si->cache = ULONG(si->cache_buf + OFFSET(kmem_cache_s_c_nextp)); + +- fprintf(fp, last_free_hdr); +- fprintf(fp, last_free); +- fprintf(fp, "%lx ", this_addr); +- if (order) { +- switch (fi->memtype) +- { +- case KVADDR: +- fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); +- break; +- case PHYSADDR: +- fprintf(fp, "(%llx is %s", fi->spec_addr, +- PAGEOFFSET(fi->spec_addr) ? "in " : ""); +- break; +- } +- fprintf(fp, "%s of %ld pages) ", +- ordinal(offset+1, buf), power(2, order)); +- } ++ } while (si->cache != cache_cache); + +- fi->retval = TRUE; +- fprintf(fp, "\n"); +- } ++ FREEBUF(si->cache_buf); ++ ++ if ((si->flags & ADDRESS_SPECIFIED) && !si->found) ++ error(INFO, "%s: address not found in cache: %lx\n", ++ reqname, si->spec_addr); ++ ++ if (si->errors) ++ error(INFO, "%ld error%s encountered\n", ++ si->errors, si->errors > 1 ? "s" : ""); ++ ++ FREEBUF(si->addrlist); + } + + /* +- * Dump free pages on kernels with a multi-dimensional free_area array. ++ * dump_kmem_cache() adapted for newer percpu slab format. + */ +-char *free_area_hdr5 = \ +- " AREA SIZE FREE_AREA_STRUCT BLOCKS PAGES\n"; +-char *free_area_hdr6 = \ +- " AREA SIZE FREE_AREA_STRUCT\n"; + + static void +-dump_multidimensional_free_pages(struct meminfo *fi) ++dump_kmem_cache_percpu_v1(struct meminfo *si) + { +- int i, j; +- struct list_data list_data, *ld; +- long cnt, total_free; +- ulong kfp, free_area; +- physaddr_t searchphys; +- int flen, errflag, verbose, nr_free_pages; +- int nr_mem_lists, dimension, order, do_search; +- ulong sum, found, offset; +- char *free_area_buf, *p; +- ulong *pp; +- long chunk_size; +- ulong this_addr; +- physaddr_t this_phys; ++ int i; + char buf[BUFSIZE]; +- char last_area[BUFSIZE]; +- char last_area_hdr[BUFSIZE]; ++ char kbuf[BUFSIZE]; ++ char *reqname; ++ ulong cache_cache; ++ ulong name; ++ int cnt; ++ uint tmp_val; /* Used as temporary variable to read sizeof(int) and ++ assigned to ulong variable. We are doing this to mask ++ the endian issue */ ++ char *p1; ++ ++ if (!(vt->flags & PERCPU_KMALLOC_V1)) ++ error(FATAL, ++ "dump_kmem_cache_percpu called without PERCPU_KMALLOC_V1\n"); ++ ++ si->found = si->retval = 0; ++ reqname = NULL; ++ ++ if ((!(si->flags & VERBOSE) || si->reqname) && ++ !(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) ++ fprintf(fp, kmem_cache_hdr); + ++ si->addrlist = (ulong *)GETBUF((vt->kmem_max_c_num+1) * sizeof(ulong)); ++ si->kmem_bufctl = (int *)GETBUF((vt->kmem_max_c_num+1) * sizeof(int)); ++ for (i = 0; i < vt->kmem_max_cpus; i++) ++ si->cpudata[i] = (ulong *) ++ GETBUF(vt->kmem_max_limit * sizeof(ulong)); + +- if (vt->flags & (NODES|ZONES)) +- error(FATAL, +- "dump_multidimensional_free_pages called with (NODES|ZONES)\n"); ++ cnt = 0; ++ si->cache = cache_cache = symbol_value("cache_cache"); + +- ld = &list_data; +- if (SIZE(free_area_struct) % sizeof(ulong)) +- error(FATAL, "free_area_struct not long-word aligned?\n"); ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (!(p1 = vaddr_to_kmem_cache(si->spec_addr, kbuf, VERBOSE))) { ++ error(INFO, ++ "address is not allocated in slab subsystem: %lx\n", ++ si->spec_addr); ++ return; ++ } ++ ++ if (si->reqname && (si->reqname != p1)) ++ error(INFO, ++ "ignoring pre-selected %s cache for address: %lx\n", ++ si->reqname, si->spec_addr, si->reqname); ++ reqname = p1; ++ } else ++ reqname = si->reqname; + +- total_free = 0; +- searchphys = 0; +- do_search = FALSE; +- get_symbol_data("nr_free_pages", sizeof(int), &nr_free_pages); ++ do { ++ if ((si->flags & VERBOSE) && !si->reqname && ++ !(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, "%s%s", cnt++ ? "\n" : "", kmem_cache_hdr); + +- switch (fi->flags) +- { +- case GET_FREE_HIGHMEM_PAGES: +- error(FATAL, "GET_FREE_HIGHMEM_PAGES invalid in this kernel\n"); ++ if (vt->kmem_cache_namelen) { ++ readmem(si->cache+OFFSET(kmem_cache_s_name), ++ KVADDR, buf, vt->kmem_cache_namelen, ++ "name array", FAULT_ON_ERROR); ++ } else { ++ readmem(si->cache+OFFSET(kmem_cache_s_name), ++ KVADDR, &name, sizeof(ulong), ++ "name", FAULT_ON_ERROR); ++ if (!read_string(name, buf, BUFSIZE-1)) { ++ error(WARNING, ++ "cannot read kmem_cache_s.name string at %lx\n", ++ name); ++ sprintf(buf, "(unknown)"); ++ } ++ } + +- case GET_FREE_PAGES: +- fi->retval = (ulong)nr_free_pages; +- return; ++ if (reqname && !STREQ(reqname, buf)) ++ goto next_cache; + +- case ADDRESS_SPECIFIED: +- switch (fi->memtype) +- { +- case KVADDR: +- if (!page_to_phys(fi->spec_addr, &searchphys)) { +- if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) +- return; +- } +- break; +- case PHYSADDR: +- searchphys = fi->spec_addr; +- break; +- default: +- error(FATAL, +- "dump_multidimensional_free_pages: no memtype specified\n"); ++ if (ignore_cache(si, buf)) { ++ fprintf(fp, "%lx %-18s [IGNORED]\n", si->cache, buf); ++ goto next_cache; + } +- do_search = TRUE; +- break; +- } + +- verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++ si->curname = buf; + +- flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT")); +- nr_mem_lists = ARRAY_LENGTH(free_area); +- dimension = ARRAY_LENGTH(free_area_DIMENSION); +- if (!nr_mem_lists || !dimension) +- error(FATAL, "cannot determine free_area dimensions\n"); +- free_area_buf = +- GETBUF((nr_mem_lists * SIZE(free_area_struct)) * dimension); +- kfp = free_area = symbol_value("free_area"); +- readmem(free_area, KVADDR, free_area_buf, +- (SIZE(free_area_struct) * nr_mem_lists) * dimension, +- "free_area arrays", FAULT_ON_ERROR); ++ readmem(si->cache+OFFSET(kmem_cache_s_objsize), ++ KVADDR, &tmp_val, sizeof(uint), ++ "objsize", FAULT_ON_ERROR); ++ si->size = (ulong)tmp_val; + +- if (do_search) +- open_tmpfile(); ++ if (!si->size) { ++ if (STREQ(si->curname, "kmem_cache")) ++ si->size = SIZE(kmem_cache_s); ++ else { ++ error(INFO, "\"%s\" cache: objsize: %ld\n", ++ si->curname, si->size); ++ si->errors++; ++ } ++ } + +- hq_open(); +- for (i = sum = found = 0; i < dimension; i++) { +- if (!verbose) +- fprintf(fp, free_area_hdr5); +- pp = (ulong *)(free_area_buf + +- ((SIZE(free_area_struct)*nr_mem_lists)*i)); +- for (j = 0; j < nr_mem_lists; j++) { +- if (verbose) +- fprintf(fp, free_area_hdr6); ++ readmem(si->cache+OFFSET(kmem_cache_s_flags), ++ KVADDR, &tmp_val, sizeof(uint), ++ "kmem_cache_s flags", FAULT_ON_ERROR); ++ si->c_flags = (ulong)tmp_val; + +- sprintf(buf, "[%d][%d]", i, j); +- fprintf(fp, "%7s ", buf); ++ readmem(si->cache+OFFSET(kmem_cache_s_gfporder), ++ KVADDR, &tmp_val, sizeof(uint), ++ "gfporder", FAULT_ON_ERROR); ++ si->order = (ulong)tmp_val; + +- chunk_size = power(2, j); ++ readmem(si->cache+OFFSET(kmem_cache_s_num), ++ KVADDR, &tmp_val, sizeof(uint), ++ "kmem_cache_s num", FAULT_ON_ERROR); ++ si->c_num = (ulong)tmp_val; + +- sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024); +- fprintf(fp, "%5s ", buf); ++ do_slab_chain_percpu_v1(SLAB_GET_COUNTS, si); + +- fprintf(fp, "%s %s", +- mkstring(buf, flen, CENTER|LONG_HEX, MKSTR(kfp)), +- verbose ? "\n" : ""); ++ if (!(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) { ++ DUMP_KMEM_CACHE_INFO_V1(); ++ if (CRASHDEBUG(3)) ++ dump_struct("kmem_cache_s", si->cache, 0); ++ } + +- if (is_page_ptr(*pp, NULL)) { +- BZERO(ld, sizeof(struct list_data)); +- ld->flags = verbose; +- ld->start = *pp; +- ld->end = free_area; +- cnt = do_list(ld); +- total_free += (cnt * chunk_size); +- } else +- cnt = 0; ++ if (si->flags == GET_SLAB_PAGES) ++ si->retval += (si->num_slabs * ++ (si->slabsize/PAGESIZE())); + +- if (!verbose) +- fprintf(fp, +- "%6ld %6ld\n", cnt, cnt * chunk_size ); ++ if (si->flags & (VERBOSE|ADDRESS_SPECIFIED)) { + +- pp += (SIZE(free_area_struct)/sizeof(ulong)); +- free_area += SIZE(free_area_struct); +- kfp += SIZE(free_area_struct); +- } +- fprintf(fp, "\n"); +- } +- hq_close(); ++ gather_cpudata_list_v1(si); + +- fprintf(fp, "nr_free_pages: %d ", nr_free_pages); +- if (total_free != nr_free_pages) +- fprintf(fp, "(found %ld)\n", total_free); +- else +- fprintf(fp, "(verified)\n"); ++ si->slab = (si->flags & ADDRESS_SPECIFIED) ? ++ vaddr_to_slab(si->spec_addr) : 0; + +- if (!do_search) +- return; ++ do_slab_chain_percpu_v1(SLAB_WALKTHROUGH, si); + +- found = FALSE; +- rewind(pc->tmpfile); +- order = offset = 0; ++ if (si->found) { ++ fprintf(fp, kmem_cache_hdr); ++ DUMP_KMEM_CACHE_INFO_V1(); ++ fprintf(fp, slab_hdr); ++ gather_slab_cached_count(si); ++ DUMP_SLAB_INFO(); + +- while (fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (CRASHDEBUG(1) && STRNEQ(buf, "found) ++ { ++ case KMEM_BUFCTL_ADDR: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp,"(kmem_bufctl_t)\n"); ++ break; + +- if (STRNEQ(buf, "nr_free_pages:")) +- continue; ++ case KMEM_SLAB_ADDR: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp, "(slab_s)\n"); ++ break; + +- if (strstr(buf, "AREA")) { +- strcpy(last_area_hdr, buf); +- fgets(buf, BUFSIZE, pc->tmpfile); +- strcpy(last_area, strip_linefeeds(buf)); +- p = strstr(buf, "k"); +- *p = NULLCHAR; +- while (*p != ' ') +- p--; +- chunk_size = atol(p+1) * 1024; +- if (chunk_size == PAGESIZE()) +- order = 0; +- else +- order++; +- continue; +- } ++ case KMEM_ON_SLAB: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp, "(unused part of slab)\n"); ++ break; ++ ++ case KMEM_OBJECT_ADDR_FREE: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, " %lx\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr); ++ break; + +- errflag = 0; +- this_addr = htol(strip_linefeeds(buf), +- RETURN_ON_ERROR, &errflag); +- if (errflag) +- continue; ++ case KMEM_OBJECT_ADDR_INUSE: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, " [%lx]\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr); ++ break; + +- if (!page_to_phys(this_addr, &this_phys)) +- continue; ++ case KMEM_OBJECT_ADDR_CACHED: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, ++ " %lx (cpu %d cache)\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr, si->cpu); ++ break; ++ } ++ ++ break; ++ } ++ } ++ ++next_cache: ++ readmem(si->cache+OFFSET(kmem_cache_s_next), ++ KVADDR, &si->cache, sizeof(ulong), ++ "kmem_cache_s next", FAULT_ON_ERROR); + +- if ((searchphys >= this_phys) && +- (searchphys < (this_phys+chunk_size))) { +- if (searchphys > this_phys) +- offset = (searchphys - this_phys)/PAGESIZE(); +- found = TRUE; +- break; +- } ++ si->cache -= OFFSET(kmem_cache_s_next); + +- } +- close_tmpfile(); ++ } while (si->cache != cache_cache); + +- if (found) { +- fprintf(fp, last_area_hdr); +- fprintf(fp, "%s\n", last_area); +- fprintf(fp, "%lx ", this_addr); +- if (order) { +- switch (fi->memtype) +- { +- case KVADDR: +- fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); +- break; +- case PHYSADDR: +- fprintf(fp, "(%llx is %s", fi->spec_addr, +- PAGEOFFSET(fi->spec_addr) ? "in " : ""); +- break; +- } +- fprintf(fp, "%s of %ld pages) ", +- ordinal(offset+1, buf), power(2, order)); +- } ++ if ((si->flags & ADDRESS_SPECIFIED) && !si->found) ++ error(INFO, "%s: address not found in cache: %lx\n", ++ reqname, si->spec_addr); ++ ++ if (si->errors) ++ error(INFO, "%ld error%s encountered\n", ++ si->errors, si->errors > 1 ? "s" : ""); + +- fi->retval = TRUE; +- fprintf(fp, "\n"); +- } +-} ++ FREEBUF(si->addrlist); ++ FREEBUF(si->kmem_bufctl); ++ for (i = 0; i < vt->kmem_max_cpus; i++) ++ FREEBUF(si->cpudata[i]); + ++} + +-/* +- * Dump free pages in newer kernels that have zones. This is a work in +- * progress, because although the framework for memory nodes has been laid +- * down, complete support has not been put in place. +- */ +-static char *zone_hdr = "ZONE NAME SIZE FREE"; + + /* +- * From linux/mmzone.h ++ * Updated for 2.6 slab substructure. + */ +-#define ZONE_DMA 0 +-#define ZONE_NORMAL 1 +-#define ZONE_HIGHMEM 2 +- + static void +-dump_free_pages_zones_v1(struct meminfo *fi) ++dump_kmem_cache_percpu_v2(struct meminfo *si) + { +- int i, n; +- ulong node_zones; +- ulong size; +- long zone_size_offset; +- long chunk_size; +- int order, errflag, do_search; +- ulong offset, verbose, value, sum, found; +- ulong this_addr; +- physaddr_t this_phys, searchphys; +- ulong zone_mem_map; +- ulong zone_start_paddr; +- ulong zone_start_mapnr; +- struct node_table *nt; +- char buf[BUFSIZE], *p; +- char buf1[BUFSIZE]; +- char buf2[BUFSIZE]; +- char buf3[BUFSIZE]; +- char last_node[BUFSIZE]; +- char last_zone[BUFSIZE]; +- char last_area[BUFSIZE]; +- char last_area_hdr[BUFSIZE]; +- +- if (!(vt->flags & (NODES|ZONES))) +- error(FATAL, +- "dump_free_pages_zones_v1 called without (NODES|ZONES)\n"); +- +- if (fi->flags & ADDRESS_SPECIFIED) { +- switch (fi->memtype) +- { +- case KVADDR: +- if (!page_to_phys(fi->spec_addr, &searchphys)) { +- if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) +- return; +- } +- break; +- case PHYSADDR: +- searchphys = fi->spec_addr; +- break; +- default: +- error(FATAL, +- "dump_free_pages_zones_v1: no memtype specified\n"); +- } +- do_search = TRUE; +- } else { +- searchphys = 0; +- do_search = FALSE; +- } +- verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++ int i; ++ char buf[BUFSIZE]; ++ char kbuf[BUFSIZE]; ++ char *reqname; ++ ulong cache_end; ++ ulong name; ++ int cnt; ++ uint tmp_val; /* Used as temporary variable to read sizeof(int) and ++ assigned to ulong variable. We are doing this to mask ++ the endian issue */ ++ char *p1; + +- if (VALID_MEMBER(zone_struct_size)) +- zone_size_offset = OFFSET(zone_struct_size); +- else if (VALID_MEMBER(zone_struct_memsize)) +- zone_size_offset = OFFSET(zone_struct_memsize); +- else +- error(FATAL, +- "zone_struct has neither size nor memsize field\n"); ++ if (!(vt->flags & PERCPU_KMALLOC_V2)) ++ error(FATAL, ++ "dump_kmem_cache_percpu called without PERCPU_KMALLOC_V2\n"); + +- if (do_search) +- open_tmpfile(); ++ si->found = si->retval = 0; ++ reqname = NULL; + +- hq_open(); ++ if ((!(si->flags & VERBOSE) || si->reqname) && ++ !(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) ++ fprintf(fp, kmem_cache_hdr); + +- for (n = sum = found = 0; n < vt->numnodes; n++) { +- nt = &vt->node_table[n]; +- node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); ++ si->addrlist = (ulong *)GETBUF((vt->kmem_max_c_num+1) * sizeof(ulong)); ++ si->kmem_bufctl = (int *)GETBUF((vt->kmem_max_c_num+1) * sizeof(int)); ++ for (i = 0; i < vt->kmem_max_cpus; i++) ++ si->cpudata[i] = (ulong *) ++ GETBUF(vt->kmem_max_limit * sizeof(ulong)); ++ if(vt->flags & PERCPU_KMALLOC_V2_NODES) ++ si->shared_array_cache = (ulong *) ++ GETBUF(vt->kmem_cache_len_nodes * ++ (vt->kmem_max_limit+1) * sizeof(ulong)); ++ else ++ si->shared_array_cache = (ulong *) ++ GETBUF((vt->kmem_max_limit+1) * sizeof(ulong)); + +- for (i = 0; i < vt->nr_zones; i++) { +- +- if (fi->flags == GET_FREE_PAGES) { +- readmem(node_zones+ +- OFFSET(zone_struct_free_pages), +- KVADDR, &value, sizeof(ulong), +- "node_zones free_pages", +- FAULT_ON_ERROR); +- sum += value; +- node_zones += SIZE(zone_struct); +- continue; +- } +- +- if (fi->flags == GET_FREE_HIGHMEM_PAGES) { +- if (i == ZONE_HIGHMEM) { +- readmem(node_zones+ +- OFFSET(zone_struct_free_pages), +- KVADDR, &value, sizeof(ulong), +- "node_zones free_pages", +- FAULT_ON_ERROR); +- sum += value; +- } +- node_zones += SIZE(zone_struct); +- continue; +- } +- +- if (fi->flags == GET_ZONE_SIZES) { +- readmem(node_zones+zone_size_offset, +- KVADDR, &size, sizeof(ulong), +- "node_zones {mem}size", FAULT_ON_ERROR); +- sum += size; +- node_zones += SIZE(zone_struct); +- continue; +- } ++ cnt = 0; + +- if ((i == 0) && (vt->flags & NODES)) { +- if (n) { +- fprintf(fp, "\n"); +- pad_line(fp, +- VADDR_PRLEN > 8 ? 74 : 66, '-'); +- fprintf(fp, "\n"); +- } +- fprintf(fp, "%sNODE\n %2d\n", +- n ? "\n" : "", nt->node_id); +- } ++ get_symbol_data("cache_chain", sizeof(ulong), &si->cache); ++ si->cache -= OFFSET(kmem_cache_s_next); ++ cache_end = symbol_value("cache_chain"); + +- fprintf(fp, "%s%s %s START_PADDR START_MAPNR\n", +- i > 0 ? "\n" : "", +- zone_hdr, +- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, +- "MEM_MAP")); +- +- fprintf(fp, "%3d ", i); +- +- readmem(node_zones+OFFSET(zone_struct_name), KVADDR, +- &value, sizeof(void *), +- "node_zones name", FAULT_ON_ERROR); +- if (read_string(value, buf, BUFSIZE-1)) +- fprintf(fp, "%-9s ", buf); +- else +- fprintf(fp, "(unknown) "); +- +- readmem(node_zones+zone_size_offset, KVADDR, +- &size, sizeof(ulong), +- "node_zones {mem}size", FAULT_ON_ERROR); +- fprintf(fp, "%6ld ", size); +- +- readmem(node_zones+OFFSET(zone_struct_free_pages), +- KVADDR, &value, sizeof(ulong), +- "node_zones free_pages", FAULT_ON_ERROR); +- +- fprintf(fp, "%6ld ", value); +- +- readmem(node_zones+OFFSET(zone_struct_zone_start_paddr), +- KVADDR, &zone_start_paddr, sizeof(ulong), +- "node_zones zone_start_paddr", FAULT_ON_ERROR); +- readmem(node_zones+OFFSET(zone_struct_zone_start_mapnr), +- KVADDR, &zone_start_mapnr, sizeof(ulong), +- "node_zones zone_start_mapnr", FAULT_ON_ERROR); +- readmem(node_zones+OFFSET(zone_struct_zone_mem_map), +- KVADDR, &zone_mem_map, sizeof(ulong), +- "node_zones zone_mem_map", FAULT_ON_ERROR); +- +- fprintf(fp, "%s %s %s\n", +- mkstring(buf1, VADDR_PRLEN, +- CENTER|LONG_HEX,MKSTR(zone_mem_map)), +- mkstring(buf2, strlen("START_PADDR"), +- CENTER|LONG_HEX|RJUST, +- MKSTR(zone_start_paddr)), +- mkstring(buf3, strlen("START_MAPNR"), +- CENTER|LONG_DEC|RJUST, +- MKSTR(zone_start_mapnr))); +- +- sum += value; ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (!(p1 = vaddr_to_kmem_cache(si->spec_addr, kbuf, VERBOSE))) { ++ error(INFO, ++ "address is not allocated in slab subsystem: %lx\n", ++ si->spec_addr); ++ return; ++ } ++ ++ if (si->reqname && (si->reqname != p1)) ++ error(INFO, ++ "ignoring pre-selected %s cache for address: %lx\n", ++ si->reqname, si->spec_addr, si->reqname); ++ reqname = p1; ++ } else ++ reqname = si->reqname; + +- if (value) +- found += dump_zone_free_area(node_zones+ +- OFFSET(zone_struct_free_area), +- vt->nr_free_areas, verbose); ++ do { ++ if ((si->flags & VERBOSE) && !si->reqname && ++ !(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, "%s%s", cnt++ ? "\n" : "", kmem_cache_hdr); + +- node_zones += SIZE(zone_struct); ++ if (vt->kmem_cache_namelen) { ++ readmem(si->cache+OFFSET(kmem_cache_s_name), ++ KVADDR, buf, vt->kmem_cache_namelen, ++ "name array", FAULT_ON_ERROR); ++ } else { ++ readmem(si->cache+OFFSET(kmem_cache_s_name), ++ KVADDR, &name, sizeof(ulong), ++ "name", FAULT_ON_ERROR); ++ if (!read_string(name, buf, BUFSIZE-1)) { ++ error(WARNING, ++ "cannot read kmem_cache_s.name string at %lx\n", ++ name); ++ sprintf(buf, "(unknown)"); ++ } + } +- } + +- hq_close(); ++ if (reqname && !STREQ(reqname, buf)) ++ goto next_cache; + +- if (fi->flags & (GET_FREE_PAGES|GET_ZONE_SIZES|GET_FREE_HIGHMEM_PAGES)){ +- fi->retval = sum; +- return; +- } ++ if (ignore_cache(si, buf)) { ++ fprintf(fp, "%lx %-18s [IGNORED]\n", si->cache, buf); ++ goto next_cache; ++ } + +- fprintf(fp, "\nnr_free_pages: %ld ", sum); +- if (sum == found) +- fprintf(fp, "(verified)\n"); +- else +- fprintf(fp, "(found %ld)\n", found); ++ si->curname = buf; + +- if (!do_search) +- return; ++ readmem(si->cache+OFFSET(kmem_cache_s_objsize), ++ KVADDR, &tmp_val, sizeof(uint), ++ "objsize", FAULT_ON_ERROR); ++ si->size = (ulong)tmp_val; + +- found = FALSE; +- rewind(pc->tmpfile); +- order = offset = 0; +- last_node[0] = NULLCHAR; +- last_zone[0] = NULLCHAR; +- last_area[0] = NULLCHAR; +- last_area_hdr[0] = NULLCHAR; ++ if (!si->size) { ++ if (STREQ(si->curname, "kmem_cache")) ++ si->size = SIZE(kmem_cache_s); ++ else { ++ error(INFO, "\"%s\" cache: objsize: %ld\n", ++ si->curname, si->size); ++ si->errors++; ++ } ++ } + ++ readmem(si->cache+OFFSET(kmem_cache_s_flags), ++ KVADDR, &tmp_val, sizeof(uint), ++ "kmem_cache_s flags", FAULT_ON_ERROR); ++ si->c_flags = (ulong)tmp_val; + +- while (fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (CRASHDEBUG(1) && STRNEQ(buf, "cache+OFFSET(kmem_cache_s_gfporder), ++ KVADDR, &tmp_val, sizeof(uint), ++ "gfporder", FAULT_ON_ERROR); ++ si->order = (ulong)tmp_val; + +- if (STRNEQ(buf, "nr_free_pages:")) +- continue; ++ readmem(si->cache+OFFSET(kmem_cache_s_num), ++ KVADDR, &tmp_val, sizeof(uint), ++ "kmem_cache_s num", FAULT_ON_ERROR); ++ si->c_num = (ulong)tmp_val; + +- if (STRNEQ(buf, "NODE")) { +- fgets(buf, BUFSIZE, pc->tmpfile); +- strcpy(last_node, strip_linefeeds(buf)); +- continue; +- } +- if (STRNEQ(buf, "ZONE")) { +- fgets(buf, BUFSIZE, pc->tmpfile); +- strcpy(last_zone, strip_linefeeds(buf)); +- continue; ++ if( vt->flags & PERCPU_KMALLOC_V2_NODES ) ++ do_slab_chain_percpu_v2_nodes(SLAB_GET_COUNTS, si); ++ else ++ do_slab_chain_percpu_v2(SLAB_GET_COUNTS, si); ++ ++ if (!(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) { ++ DUMP_KMEM_CACHE_INFO_V2(); ++ if (CRASHDEBUG(3)) ++ dump_struct("kmem_cache_s", si->cache, 0); + } +- if (STRNEQ(buf, "AREA")) { +- strcpy(last_area_hdr, buf); +- fgets(buf, BUFSIZE, pc->tmpfile); +- strcpy(last_area, strip_linefeeds(buf)); +- p = strstr(buf, "k"); +- *p = NULLCHAR; +- while (*p != ' ') +- p--; +- chunk_size = atol(p+1) * 1024; +- if (chunk_size == PAGESIZE()) +- order = 0; +- else +- order++; +- continue; +- } + +- if (CRASHDEBUG(0) && +- !hexadecimal(strip_linefeeds(buf), 0)) +- continue; ++ if (si->flags == GET_SLAB_PAGES) ++ si->retval += (si->num_slabs * ++ (si->slabsize/PAGESIZE())); + +- errflag = 0; +- this_addr = htol(strip_linefeeds(buf), +- RETURN_ON_ERROR, &errflag); +- if (errflag) +- continue; ++ if (si->flags & (VERBOSE|ADDRESS_SPECIFIED)) { + +- if (!page_to_phys(this_addr, &this_phys)) +- continue; ++ if (!(vt->flags & PERCPU_KMALLOC_V2_NODES)) ++ gather_cpudata_list_v2(si); + +- if ((searchphys >= this_phys) && +- (searchphys < (this_phys+chunk_size))) { +- if (searchphys > this_phys) +- offset = (searchphys - this_phys)/PAGESIZE(); +- found = TRUE; +- break; +- } ++ si->slab = (si->flags & ADDRESS_SPECIFIED) ? ++ vaddr_to_slab(si->spec_addr) : 0; + +- } +- close_tmpfile(); ++ if (vt->flags & PERCPU_KMALLOC_V2_NODES) ++ do_slab_chain_percpu_v2_nodes(SLAB_WALKTHROUGH, si); ++ else ++ do_slab_chain_percpu_v2(SLAB_WALKTHROUGH, si); + +- if (found) { +- if (strlen(last_node)) +- fprintf(fp, "NODE\n%s\n", last_node); +- fprintf(fp, "%s %s START_PADDR START_MAPNR\n", +- zone_hdr, +- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_MAP")); +- fprintf(fp, "%s\n", last_zone); +- fprintf(fp, last_area_hdr); +- fprintf(fp, "%s\n", last_area); +- fprintf(fp, "%lx ", this_addr); +- if (order) { +- switch (fi->memtype) +- { +- case KVADDR: +- fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); +- break; +- case PHYSADDR: +- fprintf(fp, "(%llx is %s", fi->spec_addr, +- PAGEOFFSET(fi->spec_addr) ? "in " : ""); +- break; +- } +- fprintf(fp, "%s of %ld pages) ", +- ordinal(offset+1, buf), power(2, order)); +- } ++ if (si->found) { ++ fprintf(fp, kmem_cache_hdr); ++ DUMP_KMEM_CACHE_INFO_V2(); ++ fprintf(fp, slab_hdr); ++ gather_slab_cached_count(si); ++ DUMP_SLAB_INFO(); + +- fi->retval = TRUE; +- fprintf(fp, "\n"); +- } +-} ++ switch (si->found) ++ { ++ case KMEM_BUFCTL_ADDR: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp,"(kmem_bufctl_t)\n"); ++ break; + ++ case KMEM_SLAB_ADDR: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp, "(slab)\n"); ++ break; + +-/* +- * Same as dump_free_pages_zones_v1(), but updated for numerous 2.6 zone +- * and free_area related data structure changes. +- */ +-static void +-dump_free_pages_zones_v2(struct meminfo *fi) +-{ +- int i, n; +- ulong node_zones; +- ulong size; +- long zone_size_offset; +- long chunk_size; +- int order, errflag, do_search; +- ulong offset, verbose, value, sum, found; +- ulong this_addr; +- physaddr_t this_phys, searchphys; +- ulong zone_mem_map; +- ulong zone_start_paddr; +- ulong zone_start_pfn; +- ulong zone_start_mapnr; +- struct node_table *nt; +- char buf[BUFSIZE], *p; +- char buf1[BUFSIZE]; +- char buf2[BUFSIZE]; +- char buf3[BUFSIZE]; +- char last_node[BUFSIZE]; +- char last_zone[BUFSIZE]; +- char last_area[BUFSIZE]; +- char last_area_hdr[BUFSIZE]; ++ case KMEM_ON_SLAB: ++ fprintf(fp, " %lx ", ++ (ulong)si->spec_addr); ++ fprintf(fp, "(unused part of slab)\n"); ++ break; ++ ++ case KMEM_OBJECT_ADDR_FREE: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, " %lx\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr); ++ break; + +- if (!(vt->flags & (NODES|ZONES))) +- error(FATAL, +- "dump_free_pages_zones_v2 called without (NODES|ZONES)\n"); ++ case KMEM_OBJECT_ADDR_INUSE: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, " [%lx]\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr); ++ break; + +- if (fi->flags & ADDRESS_SPECIFIED) { +- switch (fi->memtype) +- { +- case KVADDR: +- if (!page_to_phys(fi->spec_addr, &searchphys)) { +- if (!kvtop(NULL, fi->spec_addr, &searchphys, 0)) +- return; +- } +- break; +- case PHYSADDR: +- searchphys = fi->spec_addr; +- break; +- default: +- error(FATAL, +- "dump_free_pages_zones_v2: no memtype specified\n"); +- } +- do_search = TRUE; +- } else { +- searchphys = 0; +- do_search = FALSE; +- } ++ case KMEM_OBJECT_ADDR_CACHED: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, ++ " %lx (cpu %d cache)\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr, si->cpu); ++ break; ++ ++ case KMEM_OBJECT_ADDR_SHARED: ++ fprintf(fp, free_inuse_hdr); ++ fprintf(fp, ++ " %lx (shared cache)\n", ++ si->container ? si->container : ++ (ulong)si->spec_addr); ++ break; ++ } ++ ++ break; ++ } ++ } + +- verbose = (do_search || (fi->flags & VERBOSE)) ? TRUE : FALSE; ++next_cache: ++ readmem(si->cache+OFFSET(kmem_cache_s_next), ++ KVADDR, &si->cache, sizeof(ulong), ++ "kmem_cache_s next", FAULT_ON_ERROR); + +- if (VALID_MEMBER(zone_spanned_pages)) +- zone_size_offset = OFFSET(zone_spanned_pages); +- else +- error(FATAL, "zone struct has no spanned_pages field\n"); ++ if (si->cache != cache_end) ++ si->cache -= OFFSET(kmem_cache_s_next); + +- if (do_search) +- open_tmpfile(); ++ } while (si->cache != cache_end); + +- hq_open(); ++ if ((si->flags & ADDRESS_SPECIFIED) && !si->found) ++ error(INFO, "%s: address not found in cache: %lx\n", ++ reqname, si->spec_addr); ++ ++ if (si->errors) ++ error(INFO, "%ld error%s encountered\n", ++ si->errors, si->errors > 1 ? "s" : ""); + +- for (n = sum = found = 0; n < vt->numnodes; n++) { +- nt = &vt->node_table[n]; +- node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); ++ FREEBUF(si->addrlist); ++ FREEBUF(si->kmem_bufctl); ++ for (i = 0; i < vt->kmem_max_cpus; i++) ++ FREEBUF(si->cpudata[i]); ++ FREEBUF(si->shared_array_cache); + +- for (i = 0; i < vt->nr_zones; i++) { +- +- if (fi->flags == GET_FREE_PAGES) { +- readmem(node_zones+ +- OFFSET(zone_free_pages), +- KVADDR, &value, sizeof(ulong), +- "node_zones free_pages", +- FAULT_ON_ERROR); +- sum += value; +- node_zones += SIZE(zone); +- continue; +- } +- +- if (fi->flags == GET_FREE_HIGHMEM_PAGES) { +- if (i == ZONE_HIGHMEM) { +- readmem(node_zones+ +- OFFSET(zone_free_pages), +- KVADDR, &value, sizeof(ulong), +- "node_zones free_pages", +- FAULT_ON_ERROR); +- sum += value; +- } +- node_zones += SIZE(zone); +- continue; +- } +- +- if (fi->flags == GET_ZONE_SIZES) { +- readmem(node_zones+zone_size_offset, +- KVADDR, &size, sizeof(ulong), +- "node_zones size", FAULT_ON_ERROR); +- sum += size; +- node_zones += SIZE(zone); +- continue; +- } ++} + +- if ((i == 0) && (vt->flags & NODES)) { +- if (n) { +- fprintf(fp, "\n"); +- pad_line(fp, +- VADDR_PRLEN > 8 ? 74 : 66, '-'); +- fprintf(fp, "\n"); +- } +- fprintf(fp, "%sNODE\n %2d\n", +- n ? "\n" : "", nt->node_id); +- } + +- fprintf(fp, "%s%s %s START_PADDR START_MAPNR\n", +- i > 0 ? "\n" : "", +- zone_hdr, +- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, +- "MEM_MAP")); +- +- fprintf(fp, "%3d ", i); +- +- readmem(node_zones+OFFSET(zone_name), KVADDR, +- &value, sizeof(void *), +- "node_zones name", FAULT_ON_ERROR); +- if (read_string(value, buf, BUFSIZE-1)) +- fprintf(fp, "%-9s ", buf); +- else +- fprintf(fp, "(unknown) "); +- +- readmem(node_zones+zone_size_offset, KVADDR, +- &size, sizeof(ulong), +- "node_zones size", FAULT_ON_ERROR); +- fprintf(fp, "%6ld ", size); +- +- readmem(node_zones+OFFSET(zone_free_pages), +- KVADDR, &value, sizeof(ulong), +- "node_zones free_pages", FAULT_ON_ERROR); +- +- fprintf(fp, "%6ld ", value); +- +- readmem(node_zones+OFFSET(zone_zone_mem_map), +- KVADDR, &zone_mem_map, sizeof(ulong), +- "node_zones zone_mem_map", FAULT_ON_ERROR); ++/* ++ * Walk through the slab chain hanging off a kmem_cache_s structure, ++ * gathering basic statistics. ++ * ++ * TBD: Given a specified physical address, determine whether it's in this ++ * slab chain, and whether it's in use or not. ++ */ + +- readmem(node_zones+ OFFSET(zone_zone_start_pfn), +- KVADDR, &zone_start_pfn, sizeof(ulong), +- "node_zones zone_start_pfn", FAULT_ON_ERROR); +- zone_start_paddr = PTOB(zone_start_pfn); ++#define INSLAB(obj, si) \ ++ ((ulong)((ulong)(obj) & ~(si->slabsize-1)) == si->s_mem) + +- if (zone_mem_map) +- zone_start_mapnr = +- (zone_mem_map - nt->mem_map) / +- SIZE(page); +- else +- zone_start_mapnr = 0; +- +- fprintf(fp, "%s %s %s\n", +- mkstring(buf1, VADDR_PRLEN, +- CENTER|LONG_HEX,MKSTR(zone_mem_map)), +- mkstring(buf2, strlen("START_PADDR"), +- CENTER|LONG_HEX|RJUST, +- MKSTR(zone_start_paddr)), +- mkstring(buf3, strlen("START_MAPNR"), +- CENTER|LONG_DEC|RJUST, +- MKSTR(zone_start_mapnr))); +- +- sum += value; ++static void ++do_slab_chain(int cmd, struct meminfo *si) ++{ ++ ulong tmp, magic; ++ ulong kmem_slab_end; ++ char *kmem_slab_s_buf; + +- if (value) +- found += dump_zone_free_area(node_zones+ +- OFFSET(zone_free_area), +- vt->nr_free_areas, verbose); ++ si->slabsize = (power(2, si->order) * PAGESIZE()); + +- node_zones += SIZE(zone); +- } +- } ++ kmem_slab_end = si->cache + OFFSET(kmem_cache_s_c_offset); + +- hq_close(); ++ switch (cmd) ++ { ++ case SLAB_GET_COUNTS: ++ si->slab = ULONG(si->cache_buf + OFFSET(kmem_cache_s_c_firstp)); + +- if (fi->flags & (GET_FREE_PAGES|GET_ZONE_SIZES|GET_FREE_HIGHMEM_PAGES)){ +- fi->retval = sum; +- return; +- } ++ if (slab_data_saved(si)) ++ return; + +- fprintf(fp, "\nnr_free_pages: %ld ", sum); +- if (sum == found) +- fprintf(fp, "(verified)\n"); +- else +- fprintf(fp, "(found %ld)\n", found); ++ si->num_slabs = si->inuse = 0; + +- if (!do_search) +- return; ++ if (si->slab == kmem_slab_end) ++ return; + +- found = FALSE; +- rewind(pc->tmpfile); +- order = offset = 0; +- last_node[0] = NULLCHAR; +- last_zone[0] = NULLCHAR; +- last_area[0] = NULLCHAR; +- last_area_hdr[0] = NULLCHAR; ++ kmem_slab_s_buf = GETBUF(SIZE(kmem_slab_s)); + ++ do { ++ if (received_SIGINT()) { ++ FREEBUF(kmem_slab_s_buf); ++ restart(0); ++ } + +- while (fgets(buf, BUFSIZE, pc->tmpfile)) { +- if (CRASHDEBUG(1) && STRNEQ(buf, "slab, KVADDR, kmem_slab_s_buf, ++ SIZE(kmem_slab_s), "kmem_slab_s buffer", ++ FAULT_ON_ERROR); + +- if (STRNEQ(buf, "nr_free_pages:")) +- continue; ++ magic = ULONG(kmem_slab_s_buf + ++ OFFSET(kmem_slab_s_s_magic)); + +- if (STRNEQ(buf, "NODE")) { +- fgets(buf, BUFSIZE, pc->tmpfile); +- strcpy(last_node, strip_linefeeds(buf)); +- continue; +- } +- if (STRNEQ(buf, "ZONE")) { +- fgets(buf, BUFSIZE, pc->tmpfile); +- strcpy(last_zone, strip_linefeeds(buf)); +- continue; +- } +- if (STRNEQ(buf, "AREA")) { +- strcpy(last_area_hdr, buf); +- fgets(buf, BUFSIZE, pc->tmpfile); +- strcpy(last_area, strip_linefeeds(buf)); +- p = strstr(buf, "k"); +- *p = NULLCHAR; +- while (*p != ' ') +- p--; +- chunk_size = atol(p+1) * 1024; +- if (chunk_size == PAGESIZE()) +- order = 0; +- else +- order++; +- continue; +- } ++ if (magic == SLAB_MAGIC_ALLOC) { ++ ++ tmp = ULONG(kmem_slab_s_buf + ++ OFFSET(kmem_slab_s_s_inuse)); ++ ++ si->inuse += tmp; ++ si->num_slabs++; ++ } else { ++ fprintf(fp, ++ "\"%s\" cache: invalid s_magic: %lx\n", ++ si->curname, magic); ++ si->errors++; ++ FREEBUF(kmem_slab_s_buf); ++ return; ++ } ++ ++ si->slab = ULONG(kmem_slab_s_buf + ++ OFFSET(kmem_slab_s_s_nextp)); ++ ++ } while (si->slab != kmem_slab_end); ++ ++ FREEBUF(kmem_slab_s_buf); ++ save_slab_data(si); ++ break; + +- if (CRASHDEBUG(0) && +- !hexadecimal(strip_linefeeds(buf), 0)) +- continue; ++ case SLAB_WALKTHROUGH: ++ if (!si->slab) ++ si->slab = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_s_c_firstp)); + +- errflag = 0; +- this_addr = htol(strip_linefeeds(buf), +- RETURN_ON_ERROR, &errflag); +- if (errflag) +- continue; ++ if (si->slab == kmem_slab_end) ++ return; ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "search cache: [%s] ", si->curname); ++ if (si->flags & ADDRESS_SPECIFIED) ++ fprintf(fp, "for %llx", si->spec_addr); ++ fprintf(fp, "\n"); ++ } + +- if (!page_to_phys(this_addr, &this_phys)) +- continue; ++ si->slab_buf = kmem_slab_s_buf = GETBUF(SIZE(kmem_slab_s)); + +- if ((searchphys >= this_phys) && +- (searchphys < (this_phys+chunk_size))) { +- if (searchphys > this_phys) +- offset = (searchphys - this_phys)/PAGESIZE(); +- found = TRUE; +- break; +- } ++ do { ++ if (received_SIGINT()) { ++ FREEBUF(kmem_slab_s_buf); ++ restart(0); ++ } + +- } +- close_tmpfile(); ++ readmem(si->slab, KVADDR, kmem_slab_s_buf, ++ SIZE(kmem_slab_s), "kmem_slab_s buffer", ++ FAULT_ON_ERROR); + +- if (found) { +- if (strlen(last_node)) +- fprintf(fp, "NODE\n%s\n", last_node); +- fprintf(fp, "%s %s START_PADDR START_MAPNR\n", +- zone_hdr, +- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "MEM_MAP")); +- fprintf(fp, "%s\n", last_zone); +- fprintf(fp, last_area_hdr); +- fprintf(fp, "%s\n", last_area); +- fprintf(fp, "%lx ", this_addr); +- if (order) { +- switch (fi->memtype) +- { +- case KVADDR: +- fprintf(fp, "(%lx is ", (ulong)fi->spec_addr); +- break; +- case PHYSADDR: +- fprintf(fp, "(%llx is %s", fi->spec_addr, +- PAGEOFFSET(fi->spec_addr) ? "in " : ""); +- break; ++ dump_slab(si); ++ ++ if (si->found) { ++ FREEBUF(kmem_slab_s_buf); ++ return; + } +- fprintf(fp, "%s of %ld pages) ", +- ordinal(offset+1, buf), power(2, order)); +- } ++ ++ si->slab = ULONG(kmem_slab_s_buf + ++ OFFSET(kmem_slab_s_s_nextp)); ++ ++ } while (si->slab != kmem_slab_end); + +- fi->retval = TRUE; +- fprintf(fp, "\n"); ++ FREEBUF(kmem_slab_s_buf); ++ break; + } + } + + +-static char * +-page_usage_hdr = "ZONE NAME FREE ACTIVE INACTIVE_DIRTY INACTIVE_CLEAN MIN/LOW/HIGH"; +- + /* +- * Display info about the non-free pages in each zone. ++ * do_slab_chain() adapted for newer percpu slab format. + */ +-static int +-dump_zone_page_usage(void) +-{ +- int i, n; +- ulong value, node_zones; +- struct node_table *nt; +- ulong inactive_dirty_pages, inactive_clean_pages, active_pages; +- ulong free_pages, pages_min, pages_low, pages_high; +- char namebuf[BUFSIZE]; +- char buf1[BUFSIZE]; +- char buf2[BUFSIZE]; +- char buf3[BUFSIZE]; + +- if (!VALID_MEMBER(zone_struct_inactive_dirty_pages) || +- !VALID_MEMBER(zone_struct_inactive_clean_pages) || +- !VALID_MEMBER(zone_struct_active_pages) || +- !VALID_MEMBER(zone_struct_pages_min) || +- !VALID_MEMBER(zone_struct_pages_low) || +- !VALID_MEMBER(zone_struct_pages_high)) +- return FALSE; ++#define SLAB_BASE(X) (PTOB(BTOP(X))) + +- fprintf(fp, "\n"); ++#define INSLAB_PERCPU(obj, si) \ ++ ((ulong)((ulong)(obj) & ~(si->slabsize-1)) == SLAB_BASE(si->s_mem)) + +- for (n = 0; n < vt->numnodes; n++) { +- nt = &vt->node_table[n]; +- node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); +- +- if ((i == 0) && (vt->flags & NODES)) { +- fprintf(fp, "%sNODE\n %2d\n", +- n ? "\n" : "", nt->node_id); +- } +- fprintf(fp, "%s\n", page_usage_hdr); ++#define SLAB_CHAINS (3) + +- for (i = 0; i < vt->nr_zones; i++) { +- readmem(node_zones+OFFSET(zone_struct_free_pages), +- KVADDR, &free_pages, sizeof(ulong), +- "node_zones free_pages", FAULT_ON_ERROR); +- readmem(node_zones+ +- OFFSET(zone_struct_inactive_dirty_pages), +- KVADDR, &inactive_dirty_pages, sizeof(ulong), +- "node_zones inactive_dirty_pages", +- FAULT_ON_ERROR); +- readmem(node_zones+ +- OFFSET(zone_struct_inactive_clean_pages), +- KVADDR, &inactive_clean_pages, sizeof(ulong), +- "node_zones inactive_clean_pages", +- FAULT_ON_ERROR); +- readmem(node_zones+OFFSET(zone_struct_active_pages), +- KVADDR, &active_pages, sizeof(ulong), +- "node_zones active_pages", FAULT_ON_ERROR); +- readmem(node_zones+OFFSET(zone_struct_pages_min), +- KVADDR, &pages_min, sizeof(ulong), +- "node_zones pages_min", FAULT_ON_ERROR); +- readmem(node_zones+OFFSET(zone_struct_pages_low), +- KVADDR, &pages_low, sizeof(ulong), +- "node_zones pages_low", FAULT_ON_ERROR); +- readmem(node_zones+OFFSET(zone_struct_pages_high), +- KVADDR, &pages_high, sizeof(ulong), +- "node_zones pages_high", FAULT_ON_ERROR); ++static char *slab_chain_name_v1[] = {"full", "partial", "free"}; + +- readmem(node_zones+OFFSET(zone_struct_name), KVADDR, +- &value, sizeof(void *), +- "node_zones name", FAULT_ON_ERROR); +- if (read_string(value, buf1, BUFSIZE-1)) +- sprintf(namebuf, "%-8s", buf1); +- else +- sprintf(namebuf, "(unknown)"); ++static void ++do_slab_chain_percpu_v1(long cmd, struct meminfo *si) ++{ ++ int i, tmp, s; ++ int list_borked; ++ char *slab_s_buf; ++ ulong specified_slab; ++ ulong last; ++ ulong slab_chains[SLAB_CHAINS]; + +- sprintf(buf2, "%ld/%ld/%ld", +- pages_min, pages_low, pages_high); +- fprintf(fp, "%3d %s %7ld %7ld %15ld %15ld %s\n", +- i, +- namebuf, +- free_pages, +- active_pages, +- inactive_dirty_pages, +- inactive_clean_pages, +- mkstring(buf3, strlen("MIN/LOW/HIGH"), +- CENTER, buf2)); ++ list_borked = 0; ++ si->slabsize = (power(2, si->order) * PAGESIZE()); ++ si->cpucached_slab = 0; + +- node_zones += SIZE(zone_struct); +- } ++ if (VALID_MEMBER(kmem_cache_s_slabs)) { ++ slab_chains[0] = si->cache + OFFSET(kmem_cache_s_slabs); ++ slab_chains[1] = 0; ++ slab_chains[2] = 0; ++ } else { ++ slab_chains[0] = si->cache + OFFSET(kmem_cache_s_slabs_full); ++ slab_chains[1] = si->cache + OFFSET(kmem_cache_s_slabs_partial); ++ slab_chains[2] = si->cache + OFFSET(kmem_cache_s_slabs_free); + } + +- return TRUE; +-} ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "[ %s: %lx ", si->curname, si->cache); ++ fprintf(fp, "full: %lx partial: %lx free: %lx ]\n", ++ slab_chains[0], slab_chains[1], slab_chains[2]); ++ } + ++ switch (cmd) ++ { ++ case SLAB_GET_COUNTS: ++ si->flags |= SLAB_GET_COUNTS; ++ si->flags &= ~SLAB_WALKTHROUGH; ++ si->cpucached_cache = 0; ++ si->num_slabs = si->inuse = 0; ++ gather_cpudata_list_v1(si); + +-/* +- * Dump the num "order" contents of the zone_t free_area array. +- */ +-char *free_area_hdr3 = "AREA SIZE FREE_AREA_STRUCT\n"; +-char *free_area_hdr4 = "AREA SIZE FREE_AREA_STRUCT BLOCKS PAGES\n"; ++ slab_s_buf = GETBUF(SIZE(slab_s)); + +-static int +-dump_zone_free_area(ulong free_area, int num, ulong verbose) +-{ +- int i; +- long chunk_size; +- int flen, total_free, cnt; +- char buf[BUFSIZE]; +- ulong free_area_buf[3]; +- struct list_data list_data, *ld; ++ for (s = 0; s < SLAB_CHAINS; s++) { + +- if (VALID_STRUCT(free_area_struct)) { +- if (SIZE(free_area_struct) != (3 * sizeof(ulong))) +- error(FATAL, +- "unrecognized free_area_struct size: %ld\n", +- SIZE(free_area_struct)); +- } else if (VALID_STRUCT(free_area)) { +- if (SIZE(free_area) != (3 * sizeof(ulong))) +- error(FATAL, +- "unrecognized free_area struct size: %ld\n", +- SIZE(free_area)); +- } else error(FATAL, +- "neither free_area_struct or free_area structures exist\n"); ++ if (!slab_chains[s]) ++ continue; + +- ld = &list_data; ++ if (!readmem(slab_chains[s], ++ KVADDR, &si->slab, sizeof(ulong), ++ "first slab", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, ++ "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, slab_chain_name_v1[s], ++ slab_chains[s]); ++ list_borked = 1; ++ continue; ++ } ++ ++ if (slab_data_saved(si)) { ++ FREEBUF(slab_s_buf); ++ return; ++ } ++ ++ if (si->slab == slab_chains[s]) ++ continue; ++ ++ last = slab_chains[s]; ++ ++ do { ++ if (received_SIGINT()) { ++ FREEBUF(slab_s_buf); ++ restart(0); ++ } ++ ++ if (!verify_slab_v1(si, last, s)) { ++ list_borked = 1; ++ continue; ++ } ++ last = si->slab - OFFSET(slab_s_list); ++ ++ readmem(si->slab, KVADDR, slab_s_buf, ++ SIZE(slab_s), "slab_s buffer", ++ FAULT_ON_ERROR); ++ ++ tmp = INT(slab_s_buf + OFFSET(slab_s_inuse)); ++ si->inuse += tmp; ++ ++ if (ACTIVE()) ++ gather_cpudata_list_v1(si); ++ ++ si->s_mem = ULONG(slab_s_buf + ++ OFFSET(slab_s_s_mem)); ++ gather_slab_cached_count(si); ++ ++ si->num_slabs++; ++ ++ si->slab = ULONG(slab_s_buf + ++ OFFSET(slab_s_list)); ++ si->slab -= OFFSET(slab_s_list); + +- if (!verbose) +- fprintf(fp, free_area_hdr4); ++ /* ++ * Check for slab transition. (Tony Dziedzic) ++ */ ++ for (i = 0; i < SLAB_CHAINS; i++) { ++ if ((i != s) && ++ (si->slab == slab_chains[i])) { ++ error(NOTE, ++ "%s: slab chain inconsistency: %s list\n", ++ si->curname, ++ slab_chain_name_v1[s]); ++ list_borked = 1; ++ } ++ } ++ ++ } while (si->slab != slab_chains[s] && !list_borked); ++ } + +- total_free = 0; +- flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT")); ++ FREEBUF(slab_s_buf); ++ if (!list_borked) ++ save_slab_data(si); ++ break; + +- for (i = 0; i < num; i++, +- free_area += SIZE_OPTION(free_area_struct, free_area)) { +- if (verbose) +- fprintf(fp, free_area_hdr3); +- fprintf(fp, "%3d ", i); +- chunk_size = power(2, i); +- sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024); +- fprintf(fp, " %7s ", buf); ++ case SLAB_WALKTHROUGH: ++ specified_slab = si->slab; ++ si->flags |= SLAB_WALKTHROUGH; ++ si->flags &= ~SLAB_GET_COUNTS; + +- readmem(free_area, KVADDR, free_area_buf, +- sizeof(ulong) * 3, "free_area_struct", FAULT_ON_ERROR); ++ for (s = 0; s < SLAB_CHAINS; s++) { ++ if (!slab_chains[s]) ++ continue; + +- fprintf(fp, "%s ", +- mkstring(buf, flen, CENTER|LONG_HEX, MKSTR(free_area))); ++ if (!specified_slab) { ++ if (!readmem(slab_chains[s], ++ KVADDR, &si->slab, sizeof(ulong), ++ "slabs", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, ++ "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, ++ slab_chain_name_v1[s], ++ slab_chains[s]); ++ list_borked = 1; ++ continue; ++ } ++ last = slab_chains[s]; ++ } else ++ last = 0; ++ ++ if (si->slab == slab_chains[s]) ++ continue; + +- if (free_area_buf[0] == free_area) { +- if (verbose) ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "search cache: [%s] ", si->curname); ++ if (si->flags & ADDRESS_SPECIFIED) ++ fprintf(fp, "for %llx", si->spec_addr); + fprintf(fp, "\n"); +- else +- fprintf(fp, "%6d %6d\n", 0, 0); +- continue; +- } ++ } + +- if (verbose) +- fprintf(fp, "\n"); +- +- BZERO(ld, sizeof(struct list_data)); +- ld->flags = verbose | RETURN_ON_DUPLICATE; +- ld->start = free_area_buf[0]; +- ld->end = free_area; +- if (VALID_MEMBER(page_list_next)) +- ld->list_head_offset = OFFSET(page_list); +- else if (VALID_MEMBER(page_lru)) +- ld->list_head_offset = OFFSET(page_lru)+ +- OFFSET(list_head_next); +- else error(FATAL, +- "neither page.list or page.lru exist?\n"); +- +- cnt = do_list(ld); +- if (cnt < 0) +- error(FATAL, +- "corrupted free list from free_area_struct: %lx\n", +- free_area); ++ do { ++ if (received_SIGINT()) ++ restart(0); + +- if (!verbose) +- fprintf(fp, "%6d %6ld\n", cnt, cnt*chunk_size); ++ if (!verify_slab_v1(si, last, s)) { ++ list_borked = 1; ++ continue; ++ } ++ last = si->slab - OFFSET(slab_s_list); ++ ++ dump_slab_percpu_v1(si); ++ ++ if (si->found) { ++ return; ++ } ++ ++ readmem(si->slab+OFFSET(slab_s_list), ++ KVADDR, &si->slab, sizeof(ulong), ++ "slab list", FAULT_ON_ERROR); ++ ++ si->slab -= OFFSET(slab_s_list); ++ ++ } while (si->slab != slab_chains[s] && !list_borked); ++ } + +- total_free += (cnt * chunk_size); ++ break; + } +- +- return total_free; + } + + /* +- * dump_kmeminfo displays basic memory use information typically shown +- * by /proc/meminfo, and then some... ++ * Try to preclude any attempt to translate a bogus slab structure. + */ + +-char *kmeminfo_hdr = " PAGES TOTAL PERCENTAGE\n"; +- +-static void +-dump_kmeminfo(void) ++static int ++verify_slab_v1(struct meminfo *si, ulong last, int s) + { +- ulong totalram_pages; +- ulong freeram_pages; +- ulong used_pages; +- ulong shared_pages; +- ulong buffer_pages; +- ulong subtract_buffer_pages; +- ulong totalswap_pages, totalused_pages; +- ulong totalhigh_pages; +- ulong freehighmem_pages; +- ulong totallowmem_pages; +- ulong freelowmem_pages; +- ulong pct; +- ulong value1, value2; +- uint tmp; +- struct meminfo meminfo; +- struct gnu_request req; +- long page_cache_size; +- ulong get_totalram; +- ulong get_buffers; +- ulong get_slabs; +- struct syment *sp_array[2]; +- char buf[BUFSIZE]; +- +- +- BZERO(&meminfo, sizeof(struct meminfo)); +- meminfo.flags = GET_ALL; +- dump_mem_map(&meminfo); +- get_totalram = meminfo.get_totalram; +- shared_pages = meminfo.get_shared; +- get_buffers = meminfo.get_buffers; +- get_slabs = meminfo.get_slabs; +- +- fprintf(fp, kmeminfo_hdr); +- /* +- * Get total RAM based upon how the various versions of si_meminfo() +- * have done it, latest to earliest: +- * +- * Prior to 2.3.36, count all mem_map pages minus the reserved ones. +- * From 2.3.36 onwards, use "totalram_pages" if set. +- */ +- if (symbol_exists("totalram_pages")) { +- totalram_pages = vt->totalram_pages ? +- vt->totalram_pages : get_totalram; +- } else +- totalram_pages = get_totalram; +- +- fprintf(fp, "%10s %7ld %11s ----\n", "TOTAL MEM", +- totalram_pages, pages_to_size(totalram_pages, buf)); +- +- /* +- * Get free pages from dump_free_pages() or its associates. +- * Used pages are a free-bee... +- */ +- meminfo.flags = GET_FREE_PAGES; +- vt->dump_free_pages(&meminfo); +- freeram_pages = meminfo.retval; +- pct = (freeram_pages * 100)/totalram_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "FREE", freeram_pages, pages_to_size(freeram_pages, buf), pct); +- +- used_pages = totalram_pages - freeram_pages; +- pct = (used_pages * 100)/totalram_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "USED", used_pages, pages_to_size(used_pages, buf), pct); +- +- /* +- * Get shared pages from dump_mem_map(). Note that this is done +- * differently than the kernel -- it just tallies the non-reserved +- * pages that have a count of greater than 1. +- */ +- pct = (shared_pages * 100)/totalram_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "SHARED", shared_pages, pages_to_size(shared_pages, buf), pct); +- +- subtract_buffer_pages = 0; +- if (symbol_exists("buffermem_pages")) { +- get_symbol_data("buffermem_pages", sizeof(int), &tmp); +- buffer_pages = (ulong)tmp; +- } else if (symbol_exists("buffermem")) { +- get_symbol_data("buffermem", sizeof(int), &tmp); +- buffer_pages = BTOP(tmp); +- } else if ((THIS_KERNEL_VERSION >= LINUX(2,6,0)) && +- symbol_exists("nr_blockdev_pages")) { +- subtract_buffer_pages = buffer_pages = nr_blockdev_pages(); +- } else +- buffer_pages = 0; +- +- pct = (buffer_pages * 100)/totalram_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "BUFFERS", buffer_pages, pages_to_size(buffer_pages, buf), pct); ++ char slab_s_buf[BUFSIZE]; ++ struct kernel_list_head *list_head; ++ unsigned int inuse; ++ ulong s_mem; ++ char *list; ++ int errcnt; + +- if (CRASHDEBUG(1)) +- error(NOTE, "pages with buffers: %ld\n", get_buffers); ++ list = slab_chain_name_v1[s]; + +- /* +- * page_cache_size has evolved from a long to an atomic_t to +- * not existing at all. +- */ +- +- if (symbol_exists("page_cache_size")) { +- get_symbol_type("page_cache_size", NULL, &req); +- if (req.length == sizeof(int)) { +- get_symbol_data("page_cache_size", sizeof(int), &tmp); +- page_cache_size = (long)tmp; +- } else +- get_symbol_data("page_cache_size", sizeof(long), +- &page_cache_size); +- } else if (symbol_exists("nr_pagecache")) { +- get_symbol_data("nr_pagecache", sizeof(int), &tmp); +- page_cache_size = (long)tmp; +- } ++ errcnt = 0; + +- page_cache_size -= subtract_buffer_pages; ++ if (!readmem(si->slab, KVADDR, slab_s_buf, ++ SIZE(slab_s), "slab_s buffer", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, list, si->slab); ++ return FALSE; ++ } + +- pct = (page_cache_size * 100)/totalram_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "CACHED", page_cache_size, +- pages_to_size(page_cache_size, buf), pct); ++ list_head = (struct kernel_list_head *) ++ (slab_s_buf + OFFSET(slab_s_list)); + +- /* +- * Although /proc/meminfo doesn't show it, show how much memory +- * the slabs take up. +- */ ++ if (!IS_KVADDR((ulong)list_head->next) || ++ !accessible((ulong)list_head->next)) { ++ error(INFO, "%s: %s list: slab: %lx bad next pointer: %lx\n", ++ si->curname, list, si->slab, ++ (ulong)list_head->next); ++ errcnt++; ++ } + +- pct = (get_slabs * 100)/totalram_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "SLAB", get_slabs, pages_to_size(get_slabs, buf), pct); ++ if (last && (last != (ulong)list_head->prev)) { ++ error(INFO, "%s: %s list: slab: %lx bad prev pointer: %lx\n", ++ si->curname, list, si->slab, ++ (ulong)list_head->prev); ++ errcnt++; ++ } + +- if (symbol_exists("totalhigh_pages")) { +- switch (get_syment_array("totalhigh_pages", sp_array, 2)) +- { +- case 1: +- get_symbol_data("totalhigh_pages", sizeof(ulong), +- &totalhigh_pages); +- break; +- case 2: +- if (!(readmem(sp_array[0]->value, KVADDR, +- &value1, sizeof(ulong), +- "totalhigh_pages #1", RETURN_ON_ERROR))) +- break; +- if (!(readmem(sp_array[1]->value, KVADDR, +- &value2, sizeof(ulong), +- "totalhigh_pages #2", RETURN_ON_ERROR))) +- break; +- totalhigh_pages = MAX(value1, value2); +- break; +- } ++ inuse = UINT(slab_s_buf + OFFSET(slab_s_inuse)); ++ if (inuse > si->c_num) { ++ error(INFO, "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; ++ } + +- pct = totalhigh_pages ? +- (totalhigh_pages * 100)/totalram_pages : 0; +- fprintf(fp, "\n%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "TOTAL HIGH", totalhigh_pages, +- pages_to_size(totalhigh_pages, buf), pct); ++ if (!last) ++ goto no_inuse_check_v1; + +- meminfo.flags = GET_FREE_HIGHMEM_PAGES; +- vt->dump_free_pages(&meminfo); +- freehighmem_pages = meminfo.retval; +- pct = freehighmem_pages ? +- (freehighmem_pages * 100)/totalhigh_pages : 0; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL HIGH\n", +- "FREE HIGH", freehighmem_pages, +- pages_to_size(freehighmem_pages, buf), pct); ++ switch (s) ++ { ++ case 0: /* full -- but can be one singular list */ ++ if (VALID_MEMBER(kmem_cache_s_slabs_full) && ++ (inuse != si->c_num)) { ++ error(INFO, ++ "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; ++ } ++ break; + +- totallowmem_pages = totalram_pages - totalhigh_pages; +- pct = (totallowmem_pages * 100)/totalram_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL MEM\n", +- "TOTAL LOW", totallowmem_pages, +- pages_to_size(totallowmem_pages, buf), pct); ++ case 1: /* partial */ ++ if ((inuse == 0) || (inuse == si->c_num)) { ++ error(INFO, ++ "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; ++ } ++ break; + +- freelowmem_pages = freeram_pages - freehighmem_pages; +- pct = (freelowmem_pages * 100)/totallowmem_pages; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL LOW\n", +- "FREE LOW", freelowmem_pages, +- pages_to_size(freelowmem_pages, buf), pct); +- } ++ case 2: /* free */ ++ if (inuse > 0) { ++ error(INFO, ++ "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; ++ } ++ break; ++ } + +- /* +- * get swap data from dump_swap_info(). +- */ +- fprintf(fp, "\n"); +- if (dump_swap_info(RETURN_ON_ERROR, &totalswap_pages, +- &totalused_pages)) { +- fprintf(fp, "%10s %7ld %11s ----\n", +- "TOTAL SWAP", totalswap_pages, +- pages_to_size(totalswap_pages, buf)); +- pct = totalswap_pages ? (totalused_pages * 100) / +- totalswap_pages : 100; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL SWAP\n", +- "SWAP USED", totalused_pages, +- pages_to_size(totalused_pages, buf), pct); +- pct = totalswap_pages ? ((totalswap_pages - totalused_pages) * +- 100) / totalswap_pages : 0; +- fprintf(fp, "%10s %7ld %11s %3ld%% of TOTAL SWAP\n", +- "SWAP FREE", +- totalswap_pages - totalused_pages, +- pages_to_size(totalswap_pages - totalused_pages, buf), +- pct); +- } else +- error(INFO, "swap_info[%ld].swap_map at %lx is unaccessible\n", +- totalused_pages, totalswap_pages); ++no_inuse_check_v1: ++ s_mem = ULONG(slab_s_buf + OFFSET(slab_s_s_mem)); ++ if (!IS_KVADDR(s_mem) || !accessible(s_mem)) { ++ error(INFO, "%s: %s list: slab: %lx bad s_mem pointer: %lx\n", ++ si->curname, list, si->slab, s_mem); ++ errcnt++; ++ } + +- dump_zone_page_usage(); ++ return(errcnt ? FALSE : TRUE); + } + + /* +- * Emulate 2.6 nr_blockdev_pages() function. ++ * Updated for 2.6 slab substructure. + */ +-static ulong +-nr_blockdev_pages(void) +-{ +- struct list_data list_data, *ld; +- ulong *bdevlist; +- int i, bdevcnt; +- ulong inode, address_space; +- ulong nrpages; +- char *block_device_buf, *inode_buf, *address_space_buf; + +- block_device_buf = GETBUF(SIZE(block_device)); +- inode_buf = GETBUF(SIZE(inode)); +- address_space_buf = GETBUF(SIZE(address_space)); ++static char *slab_chain_name_v2[] = {"partial", "full", "free"}; + +- ld = &list_data; +- BZERO(ld, sizeof(struct list_data)); ++static void ++do_slab_chain_percpu_v2(long cmd, struct meminfo *si) ++{ ++ int i, tmp, s; ++ int list_borked; ++ char *slab_buf; ++ ulong specified_slab; ++ ulong last; ++ ulong slab_chains[SLAB_CHAINS]; + +- get_symbol_data("all_bdevs", sizeof(void *), &ld->start); +- ld->end = symbol_value("all_bdevs"); +- ld->list_head_offset = OFFSET(block_device_bd_list); ++ list_borked = 0; ++ si->slabsize = (power(2, si->order) * PAGESIZE()); ++ si->cpucached_slab = 0; + +- hq_open(); +- bdevcnt = do_list(ld); +- bdevlist = (ulong *)GETBUF(bdevcnt * sizeof(ulong)); +- bdevcnt = retrieve_list(bdevlist, bdevcnt); +- hq_close(); ++ slab_chains[0] = si->cache + OFFSET(kmem_cache_s_lists) + ++ OFFSET(kmem_list3_slabs_partial); ++ slab_chains[1] = si->cache + OFFSET(kmem_cache_s_lists) + ++ OFFSET(kmem_list3_slabs_full); ++ slab_chains[2] = si->cache + OFFSET(kmem_cache_s_lists) + ++ OFFSET(kmem_list3_slabs_free); + +- /* +- * go through the block_device list, emulating: +- * +- * ret += bdev->bd_inode->i_mapping->nrpages; +- */ +- for (i = nrpages = 0; i < bdevcnt; i++) { +- readmem(bdevlist[i], KVADDR, block_device_buf, +- SIZE(block_device), "block_device buffer", +- FAULT_ON_ERROR); +- inode = ULONG(block_device_buf + OFFSET(block_device_bd_inode)); +- readmem(inode, KVADDR, inode_buf, SIZE(inode), "inode buffer", +- FAULT_ON_ERROR); +- address_space = ULONG(inode_buf + OFFSET(inode_i_mapping)); +- readmem(address_space, KVADDR, address_space_buf, +- SIZE(address_space), "address_space buffer", +- FAULT_ON_ERROR); +- nrpages += ULONG(address_space_buf + +- OFFSET(address_space_nrpages)); +- } ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "[ %s: %lx ", si->curname, si->cache); ++ fprintf(fp, "partial: %lx full: %lx free: %lx ]\n", ++ slab_chains[0], slab_chains[1], slab_chains[2]); ++ } + +- FREEBUF(bdevlist); +- FREEBUF(block_device_buf); +- FREEBUF(inode_buf); +- FREEBUF(address_space_buf); ++ switch (cmd) ++ { ++ case SLAB_GET_COUNTS: ++ si->flags |= SLAB_GET_COUNTS; ++ si->flags &= ~SLAB_WALKTHROUGH; ++ si->cpucached_cache = 0; ++ si->num_slabs = si->inuse = 0; ++ gather_cpudata_list_v2(si); + +- return nrpages; +-} ++ slab_buf = GETBUF(SIZE(slab)); + +-/* +- * dump_vmlist() displays information from the vmlist. +- */ ++ for (s = 0; s < SLAB_CHAINS; s++) { ++ if (!slab_chains[s]) ++ continue; + +-static void +-dump_vmlist(struct meminfo *vi) +-{ +- char buf[BUFSIZE]; +- char buf1[BUFSIZE]; +- char buf2[BUFSIZE]; +- ulong vmlist; +- ulong addr, size, next, pcheck; +- physaddr_t paddr; ++ if (!readmem(slab_chains[s], ++ KVADDR, &si->slab, sizeof(ulong), ++ "first slab", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, ++ "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, ++ slab_chain_name_v2[s], ++ slab_chains[s]); ++ list_borked = 1; ++ continue; ++ } ++ ++ if (slab_data_saved(si)) { ++ FREEBUF(slab_buf); ++ return; ++ } ++ ++ if (si->slab == slab_chains[s]) ++ continue; ++ ++ last = slab_chains[s]; ++ ++ do { ++ if (received_SIGINT()) { ++ FREEBUF(slab_buf); ++ restart(0); ++ } + +- get_symbol_data("vmlist", sizeof(void *), &vmlist); +- next = vmlist; ++ if (!verify_slab_v2(si, last, s)) { ++ list_borked = 1; ++ continue; ++ } ++ last = si->slab - OFFSET(slab_list); ++ ++ readmem(si->slab, KVADDR, slab_buf, ++ SIZE(slab), "slab buffer", ++ FAULT_ON_ERROR); ++ ++ tmp = INT(slab_buf + OFFSET(slab_inuse)); ++ si->inuse += tmp; ++ ++ if (ACTIVE()) ++ gather_cpudata_list_v2(si); + +- while (next) { +- if ((next == vmlist) && +- !(vi->flags & (GET_HIGHEST|GET_PHYS_TO_VMALLOC))) { +- fprintf(fp, "%s ", +- mkstring(buf, MAX(strlen("VM_STRUCT"), VADDR_PRLEN), +- CENTER|LJUST, "VM_STRUCT")); +- fprintf(fp, "%s SIZE\n", +- mkstring(buf, (VADDR_PRLEN * 2) + strlen(" - "), +- CENTER|LJUST, "ADDRESS RANGE")); ++ si->s_mem = ULONG(slab_buf + ++ OFFSET(slab_s_mem)); ++ gather_slab_cached_count(si); ++ ++ si->num_slabs++; ++ ++ si->slab = ULONG(slab_buf + ++ OFFSET(slab_list)); ++ si->slab -= OFFSET(slab_list); ++ ++ /* ++ * Check for slab transition. (Tony Dziedzic) ++ */ ++ for (i = 0; i < SLAB_CHAINS; i++) { ++ if ((i != s) && ++ (si->slab == slab_chains[i])) { ++ error(NOTE, ++ "%s: slab chain inconsistency: %s list\n", ++ si->curname, ++ slab_chain_name_v2[s]); ++ list_borked = 1; ++ } ++ } ++ ++ } while (si->slab != slab_chains[s] && !list_borked); + } + +- readmem(next+OFFSET(vm_struct_addr), KVADDR, +- &addr, sizeof(void *), +- "vmlist addr", FAULT_ON_ERROR); +- readmem(next+OFFSET(vm_struct_size), KVADDR, +- &size, sizeof(ulong), +- "vmlist size", FAULT_ON_ERROR); ++ FREEBUF(slab_buf); ++ if (!list_borked) ++ save_slab_data(si); ++ break; + +- if (!(vi->flags & ADDRESS_SPECIFIED) || +- ((vi->memtype == KVADDR) && +- ((vi->spec_addr >= addr) && (vi->spec_addr < (addr+size))))) +- fprintf(fp, "%s%s %s - %s %6ld\n", +- mkstring(buf,VADDR_PRLEN, LONG_HEX|CENTER|LJUST, +- MKSTR(next)), space(MINSPACE-1), +- mkstring(buf1, VADDR_PRLEN, LONG_HEX|RJUST, +- MKSTR(addr)), +- mkstring(buf2, VADDR_PRLEN, LONG_HEX|LJUST, +- MKSTR(addr+size)), +- size); ++ case SLAB_WALKTHROUGH: ++ specified_slab = si->slab; ++ si->flags |= SLAB_WALKTHROUGH; ++ si->flags &= ~SLAB_GET_COUNTS; + +- if ((vi->flags & ADDRESS_SPECIFIED) && +- (vi->memtype == PHYSADDR)) { +- for (pcheck = addr; pcheck < (addr+size); +- pcheck += PAGESIZE()) { +- if (!kvtop(NULL, pcheck, &paddr, 0)) ++ for (s = 0; s < SLAB_CHAINS; s++) { ++ if (!slab_chains[s]) ++ continue; ++ ++ if (!specified_slab) { ++ if (!readmem(slab_chains[s], ++ KVADDR, &si->slab, sizeof(ulong), ++ "slabs", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, ++ "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, ++ slab_chain_name_v2[s], ++ slab_chains[s]); ++ list_borked = 1; + continue; +- if ((vi->spec_addr >= paddr) && +- (vi->spec_addr < (paddr+PAGESIZE()))) { +- if (vi->flags & GET_PHYS_TO_VMALLOC) { +- vi->retval = pcheck + +- PAGEOFFSET(paddr); +- return; +- } else +- fprintf(fp, +- "%s%s %s - %s %6ld\n", +- mkstring(buf, VADDR_PRLEN, +- LONG_HEX|CENTER|LJUST, +- MKSTR(next)), space(MINSPACE-1), +- mkstring(buf1, VADDR_PRLEN, +- LONG_HEX|RJUST, MKSTR(addr)), +- mkstring(buf2, VADDR_PRLEN, +- LONG_HEX|LJUST, +- MKSTR(addr+size)), size); +- break; + } ++ last = slab_chains[s]; ++ } else ++ last = 0; ++ ++ if (si->slab == slab_chains[s]) ++ continue; ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "search cache: [%s] ", si->curname); ++ if (si->flags & ADDRESS_SPECIFIED) ++ fprintf(fp, "for %llx", si->spec_addr); ++ fprintf(fp, "\n"); + } ++ ++ do { ++ if (received_SIGINT()) ++ restart(0); ++ ++ if (!verify_slab_v2(si, last, s)) { ++ list_borked = 1; ++ continue; ++ } ++ last = si->slab - OFFSET(slab_list); + ++ dump_slab_percpu_v2(si); ++ ++ if (si->found) { ++ return; ++ } ++ ++ readmem(si->slab+OFFSET(slab_list), ++ KVADDR, &si->slab, sizeof(ulong), ++ "slab list", FAULT_ON_ERROR); ++ ++ si->slab -= OFFSET(slab_list); ++ ++ } while (si->slab != slab_chains[s] && !list_borked); + } + +- readmem(next+OFFSET(vm_struct_next), +- KVADDR, &next, sizeof(void *), +- "vmlist next", FAULT_ON_ERROR); ++ break; + } +- +- if (vi->flags & GET_HIGHEST) +- vi->retval = addr+size; + } + +-/* +- * dump_page_lists() displays information from the active_list, +- * inactive_dirty_list and inactive_clean_list from each zone. +- */ +-static int +-dump_page_lists(struct meminfo *mi) +-{ +- int i, c, n, retval; +- ulong node_zones, pgdat; +- struct node_table *nt; +- struct list_data list_data, *ld; +- char buf[BUFSIZE]; +- ulong value; +- ulong inactive_clean_pages, inactive_clean_list; +- int nr_active_pages, nr_inactive_pages; +- int nr_inactive_dirty_pages; +- +- ld = &list_data; + +- retval = FALSE; +- nr_active_pages = nr_inactive_dirty_pages = -1; ++/* ++* Added To Traverse the Nodelists ++*/ + +- BZERO(ld, sizeof(struct list_data)); +- ld->list_head_offset = OFFSET(page_lru); +- if (mi->flags & ADDRESS_SPECIFIED) +- ld->searchfor = mi->spec_addr; +- else if (mi->flags & VERBOSE) +- ld->flags |= VERBOSE; +- +- if (mi->flags & GET_ACTIVE_LIST) { +- if (!symbol_exists("active_list")) +- error(FATAL, +- "active_list does not exist in this kernel\n"); ++static void ++do_slab_chain_percpu_v2_nodes(long cmd, struct meminfo *si) ++{ ++ int i, tmp, s, node; ++ int list_borked; ++ char *slab_buf; ++ ulong specified_slab; ++ ulong last; ++ ulong slab_chains[SLAB_CHAINS]; ++ ulong *start_address; ++ int index; + +- if (symbol_exists("nr_active_pages")) +- get_symbol_data("nr_active_pages", sizeof(int), +- &nr_active_pages); +- else +- error(FATAL, +- "nr_active_pages does not exist in this kernel\n"); ++ list_borked = 0; ++ si->slabsize = (power(2, si->order) * PAGESIZE()); ++ si->cpucached_slab = 0; ++ start_address = (ulong *)GETBUF(sizeof(ulong) * vt->kmem_cache_len_nodes); + +- ld->end = symbol_value("active_list"); +- readmem(ld->end, KVADDR, &ld->start, sizeof(void *), +- "LIST_HEAD contents", FAULT_ON_ERROR); +- +- if (mi->flags & VERBOSE) +- fprintf(fp, "active_list:\n"); ++ if (!readmem(si->cache+OFFSET(kmem_cache_s_lists), KVADDR, ++ &start_address[0], sizeof(ulong) * vt->kmem_cache_len_nodes, ++ "array nodelist array", RETURN_ON_ERROR)) ++ error(INFO, "cannot read kmem_cache nodelists array"); + +- if (ld->start == ld->end) { +- c = 0; +- ld->searchfor = 0; +- if (mi->flags & VERBOSE) +- fprintf(fp, "(empty)\n"); +- } else { +- hq_open(); +- c = do_list(ld); +- hq_close(); +- } ++ switch (cmd) ++ { ++ case SLAB_GET_COUNTS: ++ si->flags |= SLAB_GET_COUNTS; ++ si->flags &= ~SLAB_WALKTHROUGH; ++ si->cpucached_cache = 0; ++ si->num_slabs = si->inuse = 0; ++ slab_buf = GETBUF(SIZE(slab)); ++ for (index=0; (index < vt->kmem_cache_len_nodes) && start_address[index]; index++) ++ { ++ if (vt->flags & NODES_ONLINE) { ++ node = next_online_node(index); ++ if (node < 0) ++ break; ++ if (node != index) ++ continue; ++ } + +- if ((mi->flags & ADDRESS_SPECIFIED) && ld->searchfor) { +- fprintf(fp, "%lx\n", ld->searchfor); +- retval = TRUE; +- } else { +- fprintf(fp, "%snr_active_pages: %d ", +- mi->flags & VERBOSE ? "\n" : "", +- nr_active_pages); +- if (c != nr_active_pages) +- fprintf(fp, "(found %d)\n", c); +- else +- fprintf(fp, "(verified)\n"); +- } +- } ++ slab_chains[0] = start_address[index] + OFFSET(kmem_list3_slabs_partial); ++ slab_chains[1] = start_address[index] + OFFSET(kmem_list3_slabs_full); ++ slab_chains[2] = start_address[index] + OFFSET(kmem_list3_slabs_free); ++ ++ gather_cpudata_list_v2_nodes(si, index); ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "[ %s: %lx ", si->curname, si->cache); ++ fprintf(fp, "partial: %lx full: %lx free: %lx ]\n", ++ slab_chains[0], slab_chains[1], slab_chains[2]); ++ } + +- if (mi->flags & GET_INACTIVE_LIST) { +- if (!symbol_exists("inactive_list")) +- error(FATAL, +- "inactive_list does not exist in this kernel\n"); ++ for (s = 0; s < SLAB_CHAINS; s++) { ++ if (!slab_chains[s]) ++ continue; ++ ++ if (!readmem(slab_chains[s], ++ KVADDR, &si->slab, sizeof(ulong), ++ "first slab", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, ++ "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, ++ slab_chain_name_v2[s], ++ slab_chains[s]); ++ list_borked = 1; ++ continue; ++ } ++ ++ if (slab_data_saved(si)) { ++ FREEBUF(slab_buf); ++ FREEBUF(start_address); ++ return; ++ } ++ ++ if (si->slab == slab_chains[s]) ++ continue; ++ ++ last = slab_chains[s]; + +- if (symbol_exists("nr_inactive_pages")) +- get_symbol_data("nr_inactive_pages", sizeof(int), +- &nr_inactive_pages); +- else +- error(FATAL, +- "nr_active_pages does not exist in this kernel\n"); ++ do { ++ if (received_SIGINT()) { ++ FREEBUF(slab_buf); ++ FREEBUF(start_address); ++ restart(0); ++ } + +- ld->end = symbol_value("inactive_list"); +- readmem(ld->end, KVADDR, &ld->start, sizeof(void *), +- "LIST_HEAD contents", FAULT_ON_ERROR); ++ if (!verify_slab_v2(si, last, s)) { ++ list_borked = 1; ++ continue; ++ } ++ last = si->slab - OFFSET(slab_list); + +- if (mi->flags & VERBOSE) +- fprintf(fp, "inactive_list:\n"); ++ readmem(si->slab, KVADDR, slab_buf, ++ SIZE(slab), "slab buffer", ++ FAULT_ON_ERROR); ++ ++ tmp = INT(slab_buf + OFFSET(slab_inuse)); ++ si->inuse += tmp; ++ ++ if (ACTIVE()) ++ gather_cpudata_list_v2_nodes(si, index); + +- if (ld->start == ld->end) { +- c = 0; +- ld->searchfor = 0; +- if (mi->flags & VERBOSE) +- fprintf(fp, "(empty)\n"); +- } else { +- hq_open(); +- c = do_list(ld); +- hq_close(); +- } ++ si->s_mem = ULONG(slab_buf + ++ OFFSET(slab_s_mem)); ++ gather_slab_cached_count(si); ++ ++ si->num_slabs++; ++ ++ si->slab = ULONG(slab_buf + ++ OFFSET(slab_list)); ++ si->slab -= OFFSET(slab_list); + +- if ((mi->flags & ADDRESS_SPECIFIED) && ld->searchfor) { +- fprintf(fp, "%lx\n", ld->searchfor); +- retval = TRUE; +- } else { +- fprintf(fp, "%snr_inactive_pages: %d ", +- mi->flags & VERBOSE ? "\n" : "", +- nr_inactive_pages); +- if (c != nr_inactive_pages) +- fprintf(fp, "(found %d)\n", c); +- else +- fprintf(fp, "(verified)\n"); ++ /* ++ * Check for slab transition. (Tony Dziedzic) ++ */ ++ for (i = 0; i < SLAB_CHAINS; i++) { ++ if ((i != s) && ++ (si->slab == slab_chains[i])) { ++ error(NOTE, ++ "%s: slab chain inconsistency: %s list\n", ++ si->curname, ++ slab_chain_name_v2[s]); ++ list_borked = 1; ++ } ++ } ++ ++ } while (si->slab != slab_chains[s] && !list_borked); ++ } + } +- } + +- if (mi->flags & GET_INACTIVE_DIRTY) { +- if (!symbol_exists("inactive_dirty_list")) +- error(FATAL, +- "inactive_dirty_list does not exist in this kernel\n"); ++ if (!list_borked) ++ save_slab_data(si); ++ break; + +- if (symbol_exists("nr_inactive_dirty_pages")) +- get_symbol_data("nr_inactive_dirty_pages", sizeof(int), +- &nr_inactive_dirty_pages); +- else +- error(FATAL, +- "nr_inactive_dirty_pages does not exist in this kernel\n"); ++ case SLAB_WALKTHROUGH: ++ specified_slab = si->slab; ++ si->flags |= SLAB_WALKTHROUGH; ++ si->flags &= ~SLAB_GET_COUNTS; ++ slab_buf = GETBUF(SIZE(slab)); ++ for (index=0; (index < vt->kmem_cache_len_nodes) && start_address[index]; index++) ++ { ++ if (vt->flags & NODES_ONLINE) { ++ node = next_online_node(index); ++ if (node < 0) ++ break; ++ if (node != index) ++ continue; ++ } + +- ld->end = symbol_value("inactive_dirty_list"); +- readmem(ld->end, KVADDR, &ld->start, sizeof(void *), +- "LIST_HEAD contents", FAULT_ON_ERROR); ++ slab_chains[0] = start_address[index] + OFFSET(kmem_list3_slabs_partial); ++ slab_chains[1] = start_address[index] + OFFSET(kmem_list3_slabs_full); ++ slab_chains[2] = start_address[index] + OFFSET(kmem_list3_slabs_free); ++ ++ gather_cpudata_list_v2_nodes(si, index); ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "[ %s: %lx ", si->curname, si->cache); ++ fprintf(fp, "partial: %lx full: %lx free: %lx ]\n", ++ slab_chains[0], slab_chains[1], slab_chains[2]); ++ } + +- if (mi->flags & VERBOSE) +- fprintf(fp, "%sinactive_dirty_list:\n", +- mi->flags & GET_ACTIVE_LIST ? "\n" : ""); ++ for (s = 0; s < SLAB_CHAINS; s++) { ++ if (!slab_chains[s]) ++ continue; + +- if (ld->start == ld->end) { +- c = 0; +- ld->searchfor = 0; +- if (mi->flags & VERBOSE) +- fprintf(fp, "(empty)\n"); +- } else { +- hq_open(); +- c = do_list(ld); +- hq_close(); +- } ++ if (!specified_slab) { ++ if (!readmem(slab_chains[s], ++ KVADDR, &si->slab, sizeof(ulong), ++ "slabs", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, ++ "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, ++ slab_chain_name_v2[s], ++ slab_chains[s]); ++ list_borked = 1; ++ continue; ++ } ++ last = slab_chains[s]; ++ } else ++ last = 0; ++ ++ if (si->slab == slab_chains[s]) ++ continue; ++ ++ readmem(si->slab, KVADDR, slab_buf, ++ SIZE(slab), "slab buffer", ++ FAULT_ON_ERROR); ++ ++ si->s_mem = ULONG(slab_buf + ++ OFFSET(slab_s_mem)); + +- if ((mi->flags & ADDRESS_SPECIFIED) && ld->searchfor) { +- fprintf(fp, "%lx\n", ld->searchfor); +- retval = TRUE; +- } else { +- fprintf(fp, "%snr_inactive_dirty_pages: %d ", +- mi->flags & VERBOSE ? "\n" : "", +- nr_inactive_dirty_pages); +- if (c != nr_inactive_dirty_pages) +- fprintf(fp, "(found %d)\n", c); +- else +- fprintf(fp, "(verified)\n"); ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "search cache: [%s] ", si->curname); ++ if (si->flags & ADDRESS_SPECIFIED) ++ fprintf(fp, "for %llx", si->spec_addr); ++ fprintf(fp, "\n"); ++ } ++ ++ do { ++ if (received_SIGINT()) ++ { ++ FREEBUF(start_address); ++ FREEBUF(slab_buf); ++ restart(0); ++ } ++ ++ if (!verify_slab_v2(si, last, s)) { ++ list_borked = 1; ++ continue; ++ } ++ last = si->slab - OFFSET(slab_list); ++ ++ dump_slab_percpu_v2(si); ++ ++ if (si->found) { ++ FREEBUF(start_address); ++ FREEBUF(slab_buf); ++ return; ++ } ++ ++ readmem(si->slab+OFFSET(slab_list), ++ KVADDR, &si->slab, sizeof(ulong), ++ "slab list", FAULT_ON_ERROR); ++ ++ si->slab -= OFFSET(slab_list); ++ ++ } while (si->slab != slab_chains[s] && !list_borked); ++ } + } +- } + +- if (mi->flags & GET_INACTIVE_CLEAN) { +- if (INVALID_MEMBER(zone_struct_inactive_clean_list)) +- error(FATAL, +- "inactive_clean_list(s) do not exist in this kernel\n"); +- +- get_symbol_data("pgdat_list", sizeof(void *), &pgdat); +- +- if ((mi->flags & VERBOSE) && +- (mi->flags & (GET_ACTIVE_LIST|GET_INACTIVE_DIRTY))) +- fprintf(fp, "\n"); +- +- for (n = 0; pgdat; n++) { +- nt = &vt->node_table[n]; +- +- node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); ++ break; ++ } ++ FREEBUF(slab_buf); ++ FREEBUF(start_address); ++} + +- for (i = 0; i < vt->nr_zones; i++) { +- readmem(node_zones+OFFSET(zone_struct_name), +- KVADDR, &value, sizeof(void *), +- "zone_struct name", FAULT_ON_ERROR); +- if (!read_string(value, buf, BUFSIZE-1)) +- sprintf(buf, "(unknown) "); ++/* ++ * Try to preclude any attempt to translate a bogus slab structure. ++ */ ++static int ++verify_slab_v2(struct meminfo *si, ulong last, int s) ++{ ++ char slab_buf[BUFSIZE]; ++ struct kernel_list_head *list_head; ++ unsigned int inuse; ++ ulong s_mem; ++ char *list; ++ int errcnt; + +- if (mi->flags & VERBOSE) { +- if (vt->numnodes > 1) +- fprintf(fp, "NODE %d ", n); +- fprintf(fp, +- "\"%s\" inactive_clean_list:\n", +- buf); +- } ++ list = slab_chain_name_v2[s]; + +- readmem(node_zones + +- OFFSET(zone_struct_inactive_clean_pages), +- KVADDR, &inactive_clean_pages, +- sizeof(ulong), "inactive_clean_pages", +- FAULT_ON_ERROR); ++ errcnt = 0; + +- readmem(node_zones + +- OFFSET(zone_struct_inactive_clean_list), +- KVADDR, &inactive_clean_list, +- sizeof(ulong), "inactive_clean_list", +- FAULT_ON_ERROR); ++ if (!readmem(si->slab, KVADDR, slab_buf, ++ SIZE(slab), "slab buffer", QUIET|RETURN_ON_ERROR)) { ++ error(INFO, "%s: %s list: bad slab pointer: %lx\n", ++ si->curname, list, si->slab); ++ return FALSE; ++ } + +- ld->start = inactive_clean_list; +- ld->end = node_zones + +- OFFSET(zone_struct_inactive_clean_list); +- if (mi->flags & ADDRESS_SPECIFIED) +- ld->searchfor = mi->spec_addr; ++ list_head = (struct kernel_list_head *)(slab_buf + OFFSET(slab_list)); ++ if (!IS_KVADDR((ulong)list_head->next) || ++ !accessible((ulong)list_head->next)) { ++ error(INFO, "%s: %s list: slab: %lx bad next pointer: %lx\n", ++ si->curname, list, si->slab, ++ (ulong)list_head->next); ++ errcnt++; ++ } + +- if (ld->start == ld->end) { +- c = 0; +- ld->searchfor = 0; +- if (mi->flags & VERBOSE) +- fprintf(fp, "(empty)\n"); +- } else { +- hq_open(); +- c = do_list(ld); +- hq_close(); +- } ++ if (last && (last != (ulong)list_head->prev)) { ++ error(INFO, "%s: %s list: slab: %lx bad prev pointer: %lx\n", ++ si->curname, list, si->slab, ++ (ulong)list_head->prev); ++ errcnt++; ++ } + +- if ((mi->flags & ADDRESS_SPECIFIED) && +- ld->searchfor) { +- fprintf(fp, "%lx\n", ld->searchfor); +- retval = TRUE; +- } else { +- if (vt->numnodes > 1) +- fprintf(fp, "NODE %d ", n); +- fprintf(fp, "\"%s\" ", buf); +- fprintf(fp, +- "inactive_clean_pages: %ld ", +- inactive_clean_pages); +- if (c != inactive_clean_pages) +- fprintf(fp, "(found %d)\n", c); +- else +- fprintf(fp, "(verified)\n"); +- } ++ inuse = UINT(slab_buf + OFFSET(slab_inuse)); ++ if (inuse > si->c_num) { ++ error(INFO, "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; ++ } + +- node_zones += SIZE(zone_struct); +- } ++ if (!last) ++ goto no_inuse_check_v2; + +- readmem(pgdat + OFFSET_OPTION(pglist_data_node_next, +- pglist_data_pgdat_next), KVADDR, +- &pgdat, sizeof(void *), "pglist_data node_next", +- FAULT_ON_ERROR); ++ switch (s) ++ { ++ case 0: /* partial */ ++ if ((inuse == 0) || (inuse == si->c_num)) { ++ error(INFO, ++ "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; + } +- } ++ break; + +- return retval; +-} ++ case 1: /* full */ ++ if (inuse != si->c_num) { ++ error(INFO, ++ "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; ++ } ++ break; ++ ++ case 2: /* free */ ++ if (inuse > 0) { ++ error(INFO, ++ "%s: %s list: slab: %lx bad inuse counter: %ld\n", ++ si->curname, list, si->slab, inuse); ++ errcnt++; ++ } ++ break; ++ } + ++no_inuse_check_v2: ++ s_mem = ULONG(slab_buf + OFFSET(slab_s_mem)); ++ if (!IS_KVADDR(s_mem) || !accessible(s_mem)) { ++ error(INFO, "%s: %s list: slab: %lx bad s_mem pointer: %lx\n", ++ si->curname, list, si->slab, s_mem); ++ errcnt++; ++ } + ++ return(errcnt ? FALSE : TRUE); ++} + + /* +- * Check whether an address is a kmem_cache_t address, and if so, return +- * a pointer to the static buffer containing its name string. Otherwise +- * return NULL on failure. ++ * If it's a dumpfile, save the essential slab data to avoid re-reading ++ * the whole slab chain more than once. This may seem like overkill, but ++ * if the problem is a memory leak, or just the over-use of the buffer_head ++ * cache, it's painful to wait each time subsequent kmem -s or -i commands ++ * simply need the basic slab counts. + */ ++struct slab_data { ++ ulong cache_addr; ++ int num_slabs; ++ int inuse; ++ ulong cpucached_cache; ++}; + +-#define PERCPU_NOT_SUPPORTED "per-cpu slab format not supported yet\n" ++#define NO_SLAB_DATA ((void *)(-1)) + +-static char * +-is_kmem_cache_addr(ulong vaddr, char *kbuf) ++static void ++save_slab_data(struct meminfo *si) + { +- ulong cache, cache_cache, name; +- long next_offset, name_offset; +- char *cache_buf; ++ int i; + +- if (vt->flags & KMEM_CACHE_UNAVAIL) { +- error(INFO, "kmem cache slab subsystem not available\n"); +- return NULL; ++ if (si->flags & SLAB_DATA_NOSAVE) { ++ si->flags &= ~SLAB_DATA_NOSAVE; ++ return; + } + +- name_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? +- OFFSET(kmem_cache_s_name) : OFFSET(kmem_cache_s_c_name); +- next_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? +- OFFSET(kmem_cache_s_next) : OFFSET(kmem_cache_s_c_nextp); ++ if (ACTIVE()) ++ return; + +- cache = cache_cache = symbol_value("cache_cache"); ++ if (vt->slab_data == NO_SLAB_DATA) ++ return; + +- cache_buf = GETBUF(SIZE(kmem_cache_s)); ++ if (!vt->slab_data) { ++ if (!(vt->slab_data = (struct slab_data *) ++ malloc(sizeof(struct slab_data) * vt->kmem_cache_count))) { ++ error(INFO, "cannot malloc slab_data table"); ++ vt->slab_data = NO_SLAB_DATA; ++ return; ++ } ++ for (i = 0; i < vt->kmem_cache_count; i++) { ++ vt->slab_data[i].cache_addr = (ulong)NO_SLAB_DATA; ++ vt->slab_data[i].num_slabs = 0; ++ vt->slab_data[i].inuse = 0; ++ vt->slab_data[i].cpucached_cache = 0; ++ } ++ } + +- do { +- readmem(cache, KVADDR, cache_buf, SIZE(kmem_cache_s), +- "kmem_cache_s buffer", FAULT_ON_ERROR); ++ for (i = 0; i < vt->kmem_cache_count; i++) { ++ if (vt->slab_data[i].cache_addr == si->cache) ++ break; + +- if (cache == vaddr) { +- if (vt->kmem_cache_namelen) { +- BCOPY(cache_buf+name_offset, kbuf, +- vt->kmem_cache_namelen); +- } else { +- name = ULONG(cache_buf + name_offset); +- if (!read_string(name, kbuf, BUFSIZE-1)) { +- if (vt->flags & +- (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) +- error(FATAL, +- "cannot read kmem_cache_s.name string at %lx\n", +- name); +- else +- error(FATAL, +- "cannot read kmem_cache_s.c_name string at %lx\n", +- name); +- } +- } +- FREEBUF(cache_buf); +- return kbuf; ++ if (vt->slab_data[i].cache_addr == (ulong)NO_SLAB_DATA) { ++ vt->slab_data[i].cache_addr = si->cache; ++ vt->slab_data[i].num_slabs = si->num_slabs; ++ vt->slab_data[i].inuse = si->inuse; ++ vt->slab_data[i].cpucached_cache = si->cpucached_cache; ++ break; + } ++ } ++} + +- cache = ULONG(cache_buf + next_offset); ++static int ++slab_data_saved(struct meminfo *si) ++{ ++ int i; + +- if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) +- cache -= next_offset; ++ if (ACTIVE() || !vt->slab_data || (vt->slab_data == NO_SLAB_DATA)) ++ return FALSE; + +- } while (cache != cache_cache); ++ for (i = 0; i < vt->kmem_cache_count; i++) { ++ if (vt->slab_data[i].cache_addr == si->cache) { ++ si->inuse = vt->slab_data[i].inuse; ++ si->num_slabs = vt->slab_data[i].num_slabs; ++ si->cpucached_cache = vt->slab_data[i].cpucached_cache; ++ return TRUE; ++ } ++ } + +- FREEBUF(cache_buf); +- return NULL; ++ return FALSE; + } + +-/* +- * Note same functionality as above, but instead it just +- * dumps all slab cache names and their addresses. +- */ + static void +-kmem_cache_list(void) ++dump_saved_slab_data(void) + { +- ulong cache, cache_cache, name; +- long next_offset, name_offset; +- char *cache_buf; +- char buf[BUFSIZE]; ++ int i; + +- if (vt->flags & KMEM_CACHE_UNAVAIL) { +- error(INFO, "kmem cache slab subsystem not available\n"); ++ if (!vt->slab_data || (vt->slab_data == NO_SLAB_DATA)) + return; +- } +- +- name_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? +- OFFSET(kmem_cache_s_name) : OFFSET(kmem_cache_s_c_name); +- next_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? +- OFFSET(kmem_cache_s_next) : OFFSET(kmem_cache_s_c_nextp); + +- cache = cache_cache = symbol_value("cache_cache"); ++ for (i = 0; i < vt->kmem_cache_count; i++) { ++ if (vt->slab_data[i].cache_addr == (ulong)NO_SLAB_DATA) ++ break; + +- cache_buf = GETBUF(SIZE(kmem_cache_s)); ++ fprintf(fp, ++ " cache: %lx inuse: %5d num_slabs: %3d cpucached_cache: %ld\n", ++ vt->slab_data[i].cache_addr, ++ vt->slab_data[i].inuse, ++ vt->slab_data[i].num_slabs, ++ vt->slab_data[i].cpucached_cache); ++ } ++} + +- do { +- readmem(cache, KVADDR, cache_buf, SIZE(kmem_cache_s), +- "kmem_cache_s buffer", FAULT_ON_ERROR); ++/* ++ * Dump the contents of a kmem slab. ++ */ + +- if (vt->kmem_cache_namelen) { +- BCOPY(cache_buf+name_offset, buf, +- vt->kmem_cache_namelen); +- } else { +- name = ULONG(cache_buf + name_offset); +- if (!read_string(name, buf, BUFSIZE-1)) { +- if (vt->flags & +- (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) +- error(FATAL, +- "cannot read kmem_cache_s.name string at %lx\n", +- name); +- else +- error(FATAL, +- "cannot read kmem_cache_s.c_name string at %lx\n", +- name); +- } +- } ++static void ++dump_slab(struct meminfo *si) ++{ ++ uint16_t s_offset; + +- fprintf(fp, "%lx %s\n", cache, buf); ++ si->s_mem = ULONG(si->slab_buf + OFFSET(kmem_slab_s_s_mem)); ++ si->s_mem = PTOB(BTOP(si->s_mem)); + +- cache = ULONG(cache_buf + next_offset); ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INSLAB(si->slab, si) && (si->spec_addr >= si->slab) && ++ (si->spec_addr < (si->slab+SIZE(kmem_slab_s)))) { ++ si->found = KMEM_SLAB_ADDR; ++ return; ++ } ++ if (INSLAB(si->spec_addr, si)) ++ si->found = KMEM_ON_SLAB; /* But don't return yet... */ ++ else ++ return; ++ } + +- if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) +- cache -= next_offset; ++ si->s_freep = VOID_PTR(si->slab_buf + OFFSET(kmem_slab_s_s_freep)); ++ si->s_inuse = ULONG(si->slab_buf + OFFSET(kmem_slab_s_s_inuse)); ++ si->s_index = ULONG_PTR(si->slab_buf + OFFSET(kmem_slab_s_s_index)); ++ s_offset = USHORT(si->slab_buf + OFFSET(kmem_slab_s_s_offset)); + +- } while (cache != cache_cache); ++ if (!(si->flags & ADDRESS_SPECIFIED)) { ++ fprintf(fp, slab_hdr); ++ DUMP_SLAB_INFO(); ++ } + +- FREEBUF(cache_buf); ++ dump_slab_objects(si); + } + + /* +- * Translate an address to its physical page number, verify that the +- * page in fact belongs to the slab subsystem, and if so, return the +- * name of the cache to which it belongs. ++ * dump_slab() adapted for newer percpu slab format. + */ +-static char * +-vaddr_to_kmem_cache(ulong vaddr, char *buf) ++ ++static void ++dump_slab_percpu_v1(struct meminfo *si) + { +- physaddr_t paddr; +- ulong page; +- ulong cache; ++ int tmp; ++ ++ readmem(si->slab+OFFSET(slab_s_s_mem), ++ KVADDR, &si->s_mem, sizeof(ulong), ++ "s_mem", FAULT_ON_ERROR); ++ ++ /* ++ * Include the array of kmem_bufctl_t's appended to slab. ++ */ ++ tmp = SIZE(slab_s) + (SIZE(kmem_bufctl_t) * si->c_num); ++ ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INSLAB_PERCPU(si->slab, si) && ++ (si->spec_addr >= si->slab) && ++ (si->spec_addr < (si->slab+tmp))) { ++ if (si->spec_addr >= (si->slab + SIZE(slab_s))) ++ si->found = KMEM_BUFCTL_ADDR; ++ else ++ si->found = KMEM_SLAB_ADDR; ++ } else if (INSLAB_PERCPU(si->spec_addr, si)) ++ si->found = KMEM_ON_SLAB; /* But don't return yet... */ ++ else ++ return; ++ } + +- if (!kvtop(NULL, vaddr, &paddr, 0)) { +- error(WARNING, +- "cannot make virtual-to-physical translation: %lx\n", +- vaddr); +- return NULL; +- } ++ readmem(si->slab+OFFSET(slab_s_inuse), ++ KVADDR, &tmp, sizeof(int), ++ "inuse", FAULT_ON_ERROR); ++ si->s_inuse = tmp; + +- if (!phys_to_page(paddr, &page)) { +- error(WARNING, "cannot find mem_map page for address: %lx\n", +- vaddr); +- return NULL; +- } ++ readmem(si->slab+OFFSET(slab_s_free), ++ KVADDR, &si->free, SIZE(kmem_bufctl_t), ++ "kmem_bufctl_t", FAULT_ON_ERROR); + +- if (VALID_MEMBER(page_next)) +- readmem(page+OFFSET(page_next), +- KVADDR, &cache, sizeof(void *), +- "page.next", FAULT_ON_ERROR); +- else if (VALID_MEMBER(page_list_next)) +- readmem(page+OFFSET(page_list_next), +- KVADDR, &cache, sizeof(void *), +- "page.list.next", FAULT_ON_ERROR); +- else if (VALID_MEMBER(page_lru)) +- readmem(page+OFFSET(page_lru)+OFFSET(list_head_next), +- KVADDR, &cache, sizeof(void *), +- "page.lru.next", FAULT_ON_ERROR); +- else +- error(FATAL, "cannot determine slab cache from page struct\n"); ++ gather_slab_free_list_percpu(si); ++ gather_slab_cached_count(si); + +- return(is_kmem_cache_addr(cache, buf)); ++ if (!(si->flags & ADDRESS_SPECIFIED)) { ++ fprintf(fp, slab_hdr); ++ DUMP_SLAB_INFO(); ++ } ++ ++ dump_slab_objects_percpu(si); + } + ++ + /* +- * Translate an address to its physical page number, verify that the +- * page in fact belongs to the slab subsystem, and if so, return the +- * address of the slab to which it belongs. ++ * Updated for 2.6 slab substructure. + */ +-static ulong +-vaddr_to_slab(ulong vaddr) ++static void ++dump_slab_percpu_v2(struct meminfo *si) + { +- physaddr_t paddr; +- ulong page; +- ulong slab; ++ int tmp; + +- if (!kvtop(NULL, vaddr, &paddr, 0)) { +- error(WARNING, +- "cannot make virtual-to-physical translation: %lx\n", +- vaddr); +- return 0; +- } ++ readmem(si->slab+OFFSET(slab_s_mem), ++ KVADDR, &si->s_mem, sizeof(ulong), ++ "s_mem", FAULT_ON_ERROR); + +- if (!phys_to_page(paddr, &page)) { +- error(WARNING, "cannot find mem_map page for address: %lx\n", +- vaddr); +- return 0; ++ /* ++ * Include the array of kmem_bufctl_t's appended to slab. ++ */ ++ tmp = SIZE(slab) + (SIZE(kmem_bufctl_t) * si->c_num); ++ ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INSLAB_PERCPU(si->slab, si) && ++ (si->spec_addr >= si->slab) && ++ (si->spec_addr < (si->slab+tmp))) { ++ if (si->spec_addr >= (si->slab + SIZE(slab))) ++ si->found = KMEM_BUFCTL_ADDR; ++ else ++ si->found = KMEM_SLAB_ADDR; ++ } else if (INSLAB_PERCPU(si->spec_addr, si)) ++ si->found = KMEM_ON_SLAB; /* But don't return yet... */ ++ else ++ return; + } + +- slab = 0; ++ readmem(si->slab+OFFSET(slab_inuse), ++ KVADDR, &tmp, sizeof(int), ++ "inuse", FAULT_ON_ERROR); ++ si->s_inuse = tmp; + +- if (VALID_MEMBER(page_prev)) +- readmem(page+OFFSET(page_prev), +- KVADDR, &slab, sizeof(void *), +- "page.prev", FAULT_ON_ERROR); +- else if (VALID_MEMBER(page_list_prev)) +- readmem(page+OFFSET(page_list_prev), +- KVADDR, &slab, sizeof(void *), +- "page.list.prev", FAULT_ON_ERROR); +- else if (VALID_MEMBER(page_lru)) +- readmem(page+OFFSET(page_lru)+OFFSET(list_head_prev), +- KVADDR, &slab, sizeof(void *), +- "page.lru.prev", FAULT_ON_ERROR); +- else +- error(FATAL, "unknown definition of struct page?\n"); ++ readmem(si->slab+OFFSET(slab_free), ++ KVADDR, &si->free, SIZE(kmem_bufctl_t), ++ "kmem_bufctl_t", FAULT_ON_ERROR); + +- return slab; ++ gather_slab_free_list_percpu(si); ++ gather_slab_cached_count(si); ++ ++ if (!(si->flags & ADDRESS_SPECIFIED)) { ++ fprintf(fp, slab_hdr); ++ DUMP_SLAB_INFO(); ++ } ++ ++ dump_slab_objects_percpu(si); + } + + ++ + /* +- * Initialize any data required for scouring the kmalloc subsystem more +- * efficiently. ++ * Gather the free objects in a slab into the si->addrlist, checking for ++ * specified addresses that are in-slab kmem_bufctls, and making error checks ++ * along the way. Object address checks are deferred to dump_slab_objects(). + */ +-char slab_hdr[BUFSIZE] = { 0 }; +-char kmem_cache_hdr[BUFSIZE] = { 0 }; +-char free_inuse_hdr[BUFSIZE] = { 0 }; ++ ++#define INOBJECT(addr, obj) ((addr >= obj) && (addr < (obj+si->size))) + + static void +-kmem_cache_init(void) ++gather_slab_free_list(struct meminfo *si) + { +- ulong cache, cache_end, max_cnum, max_limit, max_cpus, tmp, tmp2; +- long cache_count, num_offset, next_offset; +- char *cache_buf; +- +- if (vt->flags & KMEM_CACHE_UNAVAIL) +- return; +- +- if (DUMPFILE() && (vt->flags & KMEM_CACHE_INIT)) +- return; +- +- if (!strlen(slab_hdr)) +- sprintf(slab_hdr, +- "SLAB%sMEMORY%sTOTAL ALLOCATED FREE\n", +- space(VADDR_PRLEN > 8 ? 14 : 6), +- space(VADDR_PRLEN > 8 ? 12 : 4)); +- +- if (!strlen(kmem_cache_hdr)) +- sprintf(kmem_cache_hdr, +- "CACHE%sNAME OBJSIZE ALLOCATED TOTAL SLABS SSIZE\n", +- space(VADDR_PRLEN > 8 ? 12 : 4)); +- +- if (!strlen(free_inuse_hdr)) +- sprintf(free_inuse_hdr, "FREE / [ALLOCATED]\n"); ++ ulong *next, obj; ++ ulong expected, cnt; + +- num_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? +- OFFSET(kmem_cache_s_num) : OFFSET(kmem_cache_s_c_num); +- next_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? +- OFFSET(kmem_cache_s_next) : OFFSET(kmem_cache_s_c_nextp); +- max_cnum = max_limit = max_cpus = cache_count = 0; ++ BNEG(si->addrlist, sizeof(ulong) * (si->c_num+1)); + +- /* +- * Pre-2.6 versions used the "cache_cache" as the head of the +- * slab chain list. 2.6 uses the "cache_chain" list_head. +- */ +- if (vt->flags & PERCPU_KMALLOC_V2) { +- get_symbol_data("cache_chain", sizeof(ulong), &cache); +- cache -= next_offset; +- cache_end = symbol_value("cache_chain"); +- } else +- cache = cache_end = symbol_value("cache_cache"); ++ if (!si->s_freep) ++ return; + +- cache_buf = GETBUF(SIZE(kmem_cache_s)); ++ cnt = 0; ++ expected = si->c_num - si->s_inuse; + +- do { +- cache_count++; ++ next = si->s_freep; ++ do { + +- if (!readmem(cache, KVADDR, cache_buf, SIZE(kmem_cache_s), +- "kmem_cache_s buffer", RETURN_ON_ERROR)) { +- vt->flags |= KMEM_CACHE_UNAVAIL; ++ if (cnt == si->c_num) { + error(INFO, +- "unable to initialize kmem slab cache subsystem\n\n"); ++ "\"%s\" cache: too many objects found in slab free list\n", ++ si->curname); ++ si->errors++; + return; + } + +- tmp = (ulong)(UINT(cache_buf + num_offset)); +- +- if (tmp > max_cnum) +- max_cnum = tmp; +- +- if ((tmp = max_cpudata_limit(cache, &tmp2)) > max_limit) +- max_limit = tmp; ++ /* ++ * Off-slab kmem_bufctls are contained in arrays of object ++ * pointers that point to: ++ * 1. next kmem_bufctl (or NULL) if the object is free. ++ * 2. to the object if it the object is in use. ++ * ++ * On-slab kmem_bufctls resides just after the object itself, ++ * and point to: ++ * 1. next kmem_bufctl (or NULL) if object is free. ++ * 2. the containing slab if the object is in use. ++ */ + +- if (tmp2 > max_cpus) +- max_cpus = tmp2; ++ if (si->c_flags & SLAB_CFLGS_BUFCTL) ++ obj = si->s_mem + ((next - si->s_index) * si->c_offset); ++ else ++ obj = (ulong)next - si->c_offset; + +- cache = ULONG(cache_buf + next_offset); ++ si->addrlist[cnt] = obj; + +- switch (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) +- { +- case PERCPU_KMALLOC_V1: +- cache -= next_offset; +- break; +- case PERCPU_KMALLOC_V2: +- if (cache != cache_end) +- cache -= next_offset; +- break; ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INSLAB(next, si) && ++ (si->spec_addr >= (ulong)next) && ++ (si->spec_addr < (ulong)(next + 1))) { ++ si->found = KMEM_BUFCTL_ADDR; ++ return; ++ } + } + +- } while (cache != cache_end); +- +- FREEBUF(cache_buf); ++ cnt++; + +- vt->kmem_max_c_num = max_cnum; +- vt->kmem_max_limit = max_limit; +- vt->kmem_max_cpus = max_cpus; +- vt->kmem_cache_count = cache_count; ++ if (!INSLAB(obj, si)) { ++ error(INFO, ++ "\"%s\" cache: address not contained within slab: %lx\n", ++ si->curname, obj); ++ si->errors++; ++ } + +- if (CRASHDEBUG(2)) { +- fprintf(fp, "kmem_cache_init:\n"); +- fprintf(fp, " kmem_max_c_num: %ld\n", vt->kmem_max_c_num); +- fprintf(fp, " kmem_max_limit: %ld\n", vt->kmem_max_limit); +- fprintf(fp, " kmem_max_cpus: %ld\n", vt->kmem_max_cpus); +- fprintf(fp, " kmem_cache_count: %ld\n", vt->kmem_cache_count); +- } ++ readmem((ulong)next, KVADDR, &next, sizeof(void *), ++ "s_freep chain entry", FAULT_ON_ERROR); ++ } while (next); + +- if (!(vt->flags & KMEM_CACHE_INIT)) { +- if (vt->flags & PERCPU_KMALLOC_V1) +- ARRAY_LENGTH_INIT(vt->kmem_cache_namelen, +- kmem_cache_s_name, "kmem_cache_s.name", +- NULL, sizeof(char)); +- else if (vt->flags & PERCPU_KMALLOC_V2) +- vt->kmem_cache_namelen = 0; +- else +- ARRAY_LENGTH_INIT(vt->kmem_cache_namelen, +- kmem_cache_s_c_name, "kmem_cache_s.c_name", +- NULL, 0); ++ if (cnt != expected) { ++ error(INFO, ++ "\"%s\" cache: free object mismatch: expected: %ld found: %ld\n", ++ si->curname, expected, cnt); ++ si->errors++; + } +- +- vt->flags |= KMEM_CACHE_INIT; + } + ++ + /* +- * Determine the largest cpudata limit for a given cache. ++ * gather_slab_free_list() adapted for newer percpu slab format. + */ +-static ulong +-max_cpudata_limit(ulong cache, ulong *cpus) ++ ++#define BUFCTL_END 0xffffFFFF ++ ++static void ++gather_slab_free_list_percpu(struct meminfo *si) + { + int i; +- ulong cpudata[NR_CPUS]; +- int limit; +- ulong max_limit; +- +- if (vt->flags & PERCPU_KMALLOC_V2) +- goto kmem_cache_s_array; ++ ulong obj; ++ ulong expected, cnt; ++ int free_index; ++ ulong kmembp; ++ short *kbp; + +- if (INVALID_MEMBER(kmem_cache_s_cpudata)) { +- *cpus = 0; +- return 0; +- } ++ BNEG(si->addrlist, sizeof(ulong) * (si->c_num+1)); + +- readmem(cache+OFFSET(kmem_cache_s_cpudata), +- KVADDR, &cpudata[0], +- sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_cpudata), +- "cpudata array", FAULT_ON_ERROR); ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "slab: %lx si->s_inuse: %ld si->c_num: %ld\n", ++ si->slab, si->s_inuse, si->c_num); + +- for (i = max_limit = 0; (i < ARRAY_LENGTH(kmem_cache_s_cpudata)) && +- cpudata[i]; i++) { +- readmem(cpudata[i]+OFFSET(cpucache_s_limit), +- KVADDR, &limit, sizeof(int), +- "cpucache limit", FAULT_ON_ERROR); +- if (limit > max_limit) +- max_limit = limit; +- } ++ if (si->s_inuse == si->c_num ) ++ return; + +- *cpus = i; ++ kmembp = si->slab + SIZE_OPTION(slab_s, slab); ++ readmem((ulong)kmembp, KVADDR, si->kmem_bufctl, ++ SIZE(kmem_bufctl_t) * si->c_num, ++ "kmem_bufctl array", FAULT_ON_ERROR); + +- return max_limit; ++ if (CRASHDEBUG(1)) { ++ for (i = 0; (SIZE(kmem_bufctl_t) == sizeof(int)) && ++ (i < si->c_num); i++) ++ fprintf(fp, "%d ", si->kmem_bufctl[i]); + +-kmem_cache_s_array: ++ for (kbp = (short *)&si->kmem_bufctl[0], i = 0; ++ (SIZE(kmem_bufctl_t) == sizeof(short)) && (i < si->c_num); ++ i++) ++ fprintf(fp, "%d ", *(kbp + i)); + +- readmem(cache+OFFSET(kmem_cache_s_array), +- KVADDR, &cpudata[0], +- sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array), +- "array cache array", FAULT_ON_ERROR); ++ fprintf(fp, "\n"); ++ } + +- for (i = max_limit = 0; (i < ARRAY_LENGTH(kmem_cache_s_array)) && +- cpudata[i]; i++) { +- readmem(cpudata[i]+OFFSET(array_cache_limit), +- KVADDR, &limit, sizeof(int), +- "array cache limit", FAULT_ON_ERROR); +- if (limit > max_limit) +- max_limit = limit; +- } ++ cnt = 0; ++ expected = si->c_num - si->s_inuse; + +- *cpus = i; +- return max_limit; +-} ++ if (SIZE(kmem_bufctl_t) == sizeof(int)) { ++ for (free_index = si->free; free_index != BUFCTL_END; ++ free_index = si->kmem_bufctl[free_index]) { ++ ++ if (cnt == si->c_num) { ++ error(INFO, ++ "\"%s\" cache: too many objects found in slab free list\n", ++ si->curname); ++ si->errors++; ++ return; ++ } ++ ++ obj = si->s_mem + (free_index*si->size); ++ si->addrlist[cnt] = obj; ++ cnt++; ++ } ++ } else if (SIZE(kmem_bufctl_t) == sizeof(short)) { ++ kbp = (short *)&si->kmem_bufctl[0]; + +-/* +- * Determine whether the current slab cache is contained in +- * the comma-separated list from a "kmem -I list1,list2 ..." +- * command entry. +- */ +-static int +-ignore_cache(struct meminfo *si, char *name) +-{ +- int i, argc; +- char *p1; +- char *arglist[MAXARGS]; +- char buf[BUFSIZE]; ++ for (free_index = si->free; free_index != BUFCTL_END; ++ free_index = (int)*(kbp + free_index)) { + +- if (!si->ignore) +- return FALSE; ++ if (cnt == si->c_num) { ++ error(INFO, ++ "\"%s\" cache: too many objects found in slab free list\n", si->curname); ++ si->errors++; ++ return; ++ } + +- strcpy(buf, si->ignore); ++ obj = si->s_mem + (free_index*si->size); ++ si->addrlist[cnt] = obj; ++ cnt++; ++ } ++ } else ++ error(FATAL, ++ "size of kmem_bufctl_t (%d) not sizeof(int) or sizeof(short)\n", ++ SIZE(kmem_bufctl_t)); + +- p1 = buf; +- while (*p1) { +- if (*p1 == ',') +- *p1 = ' '; +- p1++; ++ if (cnt != expected) { ++ error(INFO, ++ "\"%s\" cache: free object mismatch: expected: %ld found: %ld\n", ++ si->curname, expected, cnt); ++ si->errors++; + } ++} + +- argc = parse_line(buf, arglist); + +- for (i = 0; i < argc; i++) { +- if (STREQ(name, arglist[i])) +- return TRUE; +- } + +- return FALSE; +-} ++/* ++ * Dump the FREE, [ALLOCATED] and objects of a slab. ++ */ + ++#define DUMP_SLAB_OBJECT() \ ++ for (j = on_free_list = 0; j < si->c_num; j++) { \ ++ if (obj == si->addrlist[j]) { \ ++ on_free_list = TRUE; \ ++ break; \ ++ } \ ++ } \ ++ \ ++ if (on_free_list) { \ ++ if (!(si->flags & ADDRESS_SPECIFIED)) \ ++ fprintf(fp, " %lx\n", obj); \ ++ if (si->flags & ADDRESS_SPECIFIED) { \ ++ if (INOBJECT(si->spec_addr, obj)) { \ ++ si->found = \ ++ KMEM_OBJECT_ADDR_FREE; \ ++ si->container = obj; \ ++ return; \ ++ } \ ++ } \ ++ } else { \ ++ if (!(si->flags & ADDRESS_SPECIFIED)) \ ++ fprintf(fp, " [%lx]\n", obj); \ ++ cnt++; \ ++ if (si->flags & ADDRESS_SPECIFIED) { \ ++ if (INOBJECT(si->spec_addr, obj)) { \ ++ si->found = \ ++ KMEM_OBJECT_ADDR_INUSE; \ ++ si->container = obj; \ ++ return; \ ++ } \ ++ } \ ++ } + +-/* +- * dump_kmem_cache() displays basic information about kmalloc() slabs. +- * At this point, only kmem_cache_s structure data for each slab is dumped. +- * +- * TBD: Given a specified physical address, and determine which slab it came +- * from, and whether it's in use or not. +- */ ++static void ++dump_slab_objects(struct meminfo *si) ++{ ++ int i, j; ++ ulong *next; ++ int on_free_list; ++ ulong cnt, expected; ++ ulong bufctl, obj; + +-#define SLAB_C_MAGIC 0x4F17A36DUL +-#define SLAB_MAGIC_ALLOC 0xA5C32F2BUL /* slab is alive */ +-#define SLAB_MAGIC_DESTROYED 0xB2F23C5AUL /* slab has been destroyed */ ++ gather_slab_free_list(si); + +-#define SLAB_CFLGS_BUFCTL 0x020000UL /* bufctls in own cache */ ++ if ((si->flags & ADDRESS_SPECIFIED) && (si->found & ~KMEM_ON_SLAB)) ++ return; + +-#define KMEM_SLAB_ADDR (1) +-#define KMEM_BUFCTL_ADDR (2) +-#define KMEM_OBJECT_ADDR_FREE (3) +-#define KMEM_OBJECT_ADDR_INUSE (4) +-#define KMEM_OBJECT_ADDR_CACHED (5) +-#define KMEM_ON_SLAB (6) ++ cnt = 0; ++ expected = si->s_inuse; ++ si->container = 0; + +-#define DUMP_KMEM_CACHE_INFO_V1() \ +- { \ +- char b1[BUFSIZE]; \ +- fprintf(fp, "%s %-18s %8ld ", \ +- mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->cache)), \ +- buf, si->size); \ +- fprintf(fp, "%9ld %8ld %5ld %3ldk\n", \ +- vt->flags & PERCPU_KMALLOC_V1 ? \ +- si->inuse - si->cpucached_cache : \ +- si->inuse, si->num_slabs * si->c_num, \ +- si->num_slabs, si->slabsize/1024); \ +- } ++ if (CRASHDEBUG(1)) ++ for (i = 0; i < si->c_num; i++) { ++ fprintf(fp, "si->addrlist[%d]: %lx\n", ++ i, si->addrlist[i]); ++ } + +-#define DUMP_KMEM_CACHE_INFO_V2() dump_kmem_cache_info_v2(si) ++ if (!(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, free_inuse_hdr); + +-static void +-dump_kmem_cache_info_v2(struct meminfo *si) +-{ +- char b1[BUFSIZE]; +- char b2[BUFSIZE]; +- int namelen, sizelen, spacelen; ++ /* For on-slab bufctls, c_offset is the distance between the start of ++ * an obj and its related bufctl. For off-slab bufctls, c_offset is ++ * the distance between objs in the slab. ++ */ + +- fprintf(fp, "%s ", +- mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->cache))); ++ if (si->c_flags & SLAB_CFLGS_BUFCTL) { ++ for (i = 0, next = si->s_index; i < si->c_num; i++, next++) { ++ obj = si->s_mem + ++ ((next - si->s_index) * si->c_offset); ++ DUMP_SLAB_OBJECT(); ++ } ++ } else { ++ /* ++ * Get the "real" s_mem, i.e., without the offset stripped off. ++ * It contains the address of the first object. ++ */ ++ readmem(si->slab+OFFSET(kmem_slab_s_s_mem), ++ KVADDR, &obj, sizeof(ulong), ++ "s_mem", FAULT_ON_ERROR); + +- namelen = strlen(si->curname); +- sprintf(b2, "%ld", si->size); +- sizelen = strlen(b2); +- spacelen = 0; ++ for (i = 0; i < si->c_num; i++) { ++ DUMP_SLAB_OBJECT(); ++ ++ if (si->flags & ADDRESS_SPECIFIED) { ++ bufctl = obj + si->c_offset; ++ ++ if ((si->spec_addr >= bufctl) && ++ (si->spec_addr < ++ (bufctl + SIZE(kmem_bufctl_t)))) { ++ si->found = KMEM_BUFCTL_ADDR; ++ return; ++ } ++ } + +- if (namelen++ > 18) { +- spacelen = 29 - namelen - sizelen; +- fprintf(fp, "%s%s%ld ", si->curname, +- space(spacelen <= 0 ? 1 : spacelen), si->size); +- if (spacelen > 0) +- spacelen = 1; +- sprintf(b1, "%c%dld ", '%', 9 + spacelen - 1); +- } else { +- fprintf(fp, "%-18s %8ld ", si->curname, si->size); +- sprintf(b1, "%c%dld ", '%', 9); ++ obj += (si->c_offset + SIZE(kmem_bufctl_t)); ++ } + } + +- fprintf(fp, b1, vt->flags & (PERCPU_KMALLOC_V2) ? +- si->inuse - si->cpucached_cache : si->inuse); ++ if (cnt != expected) { ++ error(INFO, ++ "\"%s\" cache: inuse object mismatch: expected: %ld found: %ld\n", ++ si->curname, expected, cnt); ++ si->errors++; ++ } + +- fprintf(fp, "%8ld %5ld %3ldk\n", +- si->num_slabs * si->c_num, +- si->num_slabs, si->slabsize/1024); + } + +-#define DUMP_SLAB_INFO() \ +- { \ +- char b1[BUFSIZE], b2[BUFSIZE]; \ +- ulong allocated, freeobjs; \ +- if (vt->flags & PERCPU_KMALLOC_V1) { \ +- allocated = si->s_inuse - si->cpucached_slab; \ +- freeobjs = si->c_num - allocated - si->cpucached_slab; \ +- } else { \ +- allocated = si->s_inuse; \ +- freeobjs = si->c_num - si->s_inuse; \ +- } \ +- fprintf(fp, "%s %s %5ld %9ld %4ld\n", \ +- mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->slab)), \ +- mkstring(b2, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->s_mem)), \ +- si->c_num, allocated, \ +- vt->flags & PERCPU_KMALLOC_V1 ? freeobjs + si->cpucached_slab :\ +- freeobjs); \ +- } ++ ++/* ++ * dump_slab_objects() adapted for newer percpu slab format. ++ */ + + static void +-dump_kmem_cache(struct meminfo *si) ++dump_slab_objects_percpu(struct meminfo *si) + { +- char buf[BUFSIZE]; +- char kbuf[BUFSIZE]; +- char *reqname; +- ulong cache_cache; +- ulong name, magic; +- int cnt; +- char *p1; +- +- if (vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2)) +- error(FATAL, +- "dump_kmem_cache called with PERCPU_KMALLOC_V[12] set\n"); +- +- si->found = si->retval = 0; +- reqname = NULL; ++ int i, j; ++ int on_free_list, on_cpudata_list, on_shared_list; ++ ulong cnt, expected; ++ ulong obj; + +- if ((!(si->flags & VERBOSE) || si->reqname) && +- !(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) +- fprintf(fp, kmem_cache_hdr); ++ if ((si->flags & ADDRESS_SPECIFIED) && (si->found & ~KMEM_ON_SLAB)) ++ return; + +- si->addrlist = (ulong *)GETBUF((vt->kmem_max_c_num+1) * sizeof(ulong)); +- cnt = 0; +- si->cache = cache_cache = symbol_value("cache_cache"); ++ cnt = 0; ++ expected = si->s_inuse; ++ si->container = 0; + +- if (si->flags & ADDRESS_SPECIFIED) { +- if (!(p1 = vaddr_to_kmem_cache(si->spec_addr, kbuf))) { +- error(INFO, +- "address is not allocated in slab subsystem: %lx\n", +- si->spec_addr); +- return; +- } +- +- if (si->reqname && (si->reqname != p1)) +- error(INFO, +- "ignoring pre-selected %s cache for address: %lx\n", +- si->reqname, si->spec_addr, si->reqname); ++ if (CRASHDEBUG(1)) ++ for (i = 0; i < si->c_num; i++) { ++ fprintf(fp, "si->addrlist[%d]: %lx\n", ++ i, si->addrlist[i]); ++ } + +- reqname = p1; +- } else +- reqname = si->reqname; ++ if (!(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, free_inuse_hdr); + +- si->cache_buf = GETBUF(SIZE(kmem_cache_s)); ++ for (i = 0, obj = si->s_mem; i < si->c_num; i++, obj += si->size) { ++ on_free_list = FALSE; ++ on_cpudata_list = FALSE; ++ on_shared_list = FALSE; + +- do { +- if ((si->flags & VERBOSE) && !si->reqname && +- !(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, "%s%s", cnt++ ? "\n" : "", kmem_cache_hdr); ++ for (j = 0; j < si->c_num; j++) { ++ if (obj == si->addrlist[j]) { ++ on_free_list = TRUE; ++ break; ++ } ++ } + +- readmem(si->cache, KVADDR, si->cache_buf, SIZE(kmem_cache_s), +- "kmem_cache_s buffer", FAULT_ON_ERROR); ++ on_cpudata_list = check_cpudata_list(si, obj); ++ on_shared_list = check_shared_list(si, obj); + +- if (vt->kmem_cache_namelen) { +- BCOPY(si->cache_buf + OFFSET(kmem_cache_s_c_name), +- buf, vt->kmem_cache_namelen); +- } else { +- name = ULONG(si->cache_buf + +- OFFSET(kmem_cache_s_c_name)); +- if (!read_string(name, buf, BUFSIZE-1)) +- error(FATAL, +- "cannot read kmem_cache_s.c_name string at %lx\n", +- name); ++ if (on_free_list && on_cpudata_list) { ++ error(INFO, ++ "\"%s\" cache: object %lx on both free and cpu %d lists\n", ++ si->curname, obj, si->cpu); ++ si->errors++; + } +- +- if (reqname && !STREQ(reqname, buf)) +- goto next_cache; +- +- if (ignore_cache(si, buf)) { +- fprintf(fp, "%lx %-18s [IGNORED]\n", si->cache, buf); +- goto next_cache; ++ if (on_free_list && on_shared_list) { ++ error(INFO, ++ "\"%s\" cache: object %lx on both free and shared lists\n", ++ si->curname, obj); ++ si->errors++; ++ } ++ if (on_cpudata_list && on_shared_list) { ++ error(INFO, ++ "\"%s\" cache: object %lx on both cpu %d and shared lists\n", ++ si->curname, obj, si->cpu); ++ si->errors++; + } ++ ++ if (on_free_list) { ++ if (!(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, " %lx\n", obj); ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INOBJECT(si->spec_addr, obj)) { ++ si->found = ++ KMEM_OBJECT_ADDR_FREE; ++ si->container = obj; ++ return; ++ } ++ } ++ } else if (on_cpudata_list) { ++ if (!(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, " %lx (cpu %d cache)\n", obj, ++ si->cpu); ++ cnt++; ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INOBJECT(si->spec_addr, obj)) { ++ si->found = ++ KMEM_OBJECT_ADDR_CACHED; ++ si->container = obj; ++ return; ++ } ++ } ++ } else if (on_shared_list) { ++ if (!(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, " %lx (shared cache)\n", obj); ++ cnt++; ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INOBJECT(si->spec_addr, obj)) { ++ si->found = ++ KMEM_OBJECT_ADDR_SHARED; ++ si->container = obj; ++ return; ++ } ++ } ++ } else { ++ if (!(si->flags & ADDRESS_SPECIFIED)) ++ fprintf(fp, " [%lx]\n", obj); ++ cnt++; ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (INOBJECT(si->spec_addr, obj)) { ++ si->found = ++ KMEM_OBJECT_ADDR_INUSE; ++ si->container = obj; ++ return; ++ } ++ } ++ } ++ } + +- si->curname = buf; ++ if (cnt != expected) { ++ error(INFO, ++ "\"%s\" cache: inuse object mismatch: expected: %ld found: %ld\n", ++ si->curname, expected, cnt); ++ si->errors++; ++ } ++} + +- if (CRASHDEBUG(1)) +- fprintf(fp, "cache: %lx %s\n", si->cache, si->curname); +- console("cache: %lx %s\n", si->cache, si->curname); ++/* ++ * Determine how many of the "inuse" slab objects are actually cached ++ * in the kmem_cache_s header. Set the per-slab count and update the ++ * cumulative per-cache count. With the addition of the shared list ++ * check, the terms "cpucached_cache" and "cpucached_slab" are somewhat ++ * misleading. But they both are types of objects that are cached ++ * in the kmem_cache_s header, just not necessarily per-cpu. ++ */ + +- magic = ULONG(si->cache_buf + OFFSET(kmem_cache_s_c_magic)); ++static void ++gather_slab_cached_count(struct meminfo *si) ++{ ++ int i; ++ ulong obj; ++ int in_cpudata, in_shared; + +- if (magic == SLAB_C_MAGIC) { ++ si->cpucached_slab = 0; + +- si->size = ULONG(si->cache_buf + +- OFFSET(kmem_cache_s_c_org_size)); +- if (!si->size) { +- if (STREQ(si->curname, "kmem_cache")) +- si->size = SIZE(kmem_cache_s); +- else { +- error(INFO, +- "\"%s\" cache: c_org_size: %ld\n", +- si->curname, si->size); +- si->errors++; +- } ++ for (i = 0, obj = si->s_mem; i < si->c_num; i++, obj += si->size) { ++ in_cpudata = in_shared = 0; ++ if (check_cpudata_list(si, obj)) { ++ in_cpudata = TRUE; ++ si->cpucached_slab++; ++ if (si->flags & SLAB_GET_COUNTS) { ++ si->cpucached_cache++; + } +- si->c_flags = ULONG(si->cache_buf + +- OFFSET(kmem_cache_s_c_flags)); +- si->c_offset = ULONG(si->cache_buf + +- OFFSET(kmem_cache_s_c_offset)); +- si->order = ULONG(si->cache_buf + +- OFFSET(kmem_cache_s_c_gfporder)); +- si->c_num = ULONG(si->cache_buf + +- OFFSET(kmem_cache_s_c_num)); +- +- do_slab_chain(SLAB_GET_COUNTS, si); +- +- if (!(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) +- DUMP_KMEM_CACHE_INFO_V1(); +- +- if (si->flags == GET_SLAB_PAGES) +- si->retval += (si->num_slabs * +- (si->slabsize/PAGESIZE())); ++ } ++ if (check_shared_list(si, obj)) { ++ in_shared = TRUE; ++ if (!in_cpudata) { ++ si->cpucached_slab++; ++ if (si->flags & SLAB_GET_COUNTS) { ++ si->cpucached_cache++; ++ } ++ } ++ } ++ if (in_cpudata && in_shared) { ++ si->flags |= SLAB_DATA_NOSAVE; ++ if (!(si->flags & VERBOSE)) ++ error(INFO, ++ "\"%s\" cache: object %lx on both cpu %d and shared lists\n", ++ si->curname, obj, si->cpu); ++ } ++ } ++} + +- if (si->flags & (VERBOSE|ADDRESS_SPECIFIED)) { +- si->slab = (si->flags & ADDRESS_SPECIFIED) ? +- vaddr_to_slab(si->spec_addr) : 0; +- +- do_slab_chain(SLAB_WALKTHROUGH, si); ++/* ++ * Populate the percpu object list for a given slab. ++ */ + +- if (si->found) { +- fprintf(fp, kmem_cache_hdr); +- DUMP_KMEM_CACHE_INFO_V1(); +- fprintf(fp, slab_hdr); +- DUMP_SLAB_INFO(); ++static void ++gather_cpudata_list_v1(struct meminfo *si) ++{ ++ int i, j; ++ int avail; ++ ulong cpudata[NR_CPUS]; + +- switch (si->found) +- { +- case KMEM_BUFCTL_ADDR: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp, +- "(ON-SLAB kmem_bufctl_t)\n"); +- break; ++ if (INVALID_MEMBER(kmem_cache_s_cpudata)) ++ return; + +- case KMEM_SLAB_ADDR: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp, +- "(ON-SLAB kmem_slab_t)\n"); +- break; ++ readmem(si->cache+OFFSET(kmem_cache_s_cpudata), ++ KVADDR, &cpudata[0], ++ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_cpudata), ++ "cpudata array", FAULT_ON_ERROR); + +- case KMEM_ON_SLAB: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp, +- "(unused part of slab)\n"); +- break; +- +- case KMEM_OBJECT_ADDR_FREE: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, " %lx\n", +- (ulong)si->spec_addr); +- break; ++ for (i = 0; (i < ARRAY_LENGTH(kmem_cache_s_cpudata)) && ++ cpudata[i]; i++) { ++ BZERO(si->cpudata[i], sizeof(ulong) * vt->kmem_max_limit); + +- case KMEM_OBJECT_ADDR_INUSE: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, " [%lx]\n", +- (ulong)si->spec_addr); +- break; +- } ++ readmem(cpudata[i]+OFFSET(cpucache_s_avail), ++ KVADDR, &avail, sizeof(int), ++ "cpucache avail", FAULT_ON_ERROR); + +- break; +- } +- } ++ if (!avail) ++ continue; + +- } else { +- error(INFO, "\"%s\" cache: invalid c_magic: %lx\n", +- si->curname, magic); ++ if (avail > vt->kmem_max_limit) { ++ error(INFO, ++ "\"%s\" cache: cpucache_s.avail %d greater than limit %ld\n", ++ si->curname, avail, vt->kmem_max_limit); + si->errors++; + } + +-next_cache: +- si->cache = ULONG(si->cache_buf + OFFSET(kmem_cache_s_c_nextp)); +- +- } while (si->cache != cache_cache); +- +- FREEBUF(si->cache_buf); ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%s: cpu[%d] avail: %d\n", ++ si->curname, i, avail); + +- if ((si->flags & ADDRESS_SPECIFIED) && !si->found) +- error(INFO, "%s: address not found in cache: %lx\n", +- reqname, si->spec_addr); +- +- if (si->errors) +- error(INFO, "%ld error%s encountered\n", +- si->errors, si->errors > 1 ? "s" : ""); ++ readmem(cpudata[i]+SIZE(cpucache_s), ++ KVADDR, si->cpudata[i], ++ sizeof(void *) * avail, ++ "cpucache avail", FAULT_ON_ERROR); + +- FREEBUF(si->addrlist); ++ if (CRASHDEBUG(2)) ++ for (j = 0; j < avail; j++) ++ fprintf(fp, " %lx\n", si->cpudata[i][j]); ++ } + } + + /* +- * dump_kmem_cache() adapted for newer percpu slab format. ++ * Updated for 2.6 slab percpu data structure, this also gathers ++ * the shared array_cache list as well. + */ +- + static void +-dump_kmem_cache_percpu_v1(struct meminfo *si) ++gather_cpudata_list_v2(struct meminfo *si) + { +- int i; +- char buf[BUFSIZE]; +- char kbuf[BUFSIZE]; +- char *reqname; +- ulong cache_cache; +- ulong name; +- int cnt; +- uint tmp_val; /* Used as temporary variable to read sizeof(int) and +- assigned to ulong variable. We are doing this to mask +- the endian issue */ +- char *p1; +- +- if (!(vt->flags & PERCPU_KMALLOC_V1)) +- error(FATAL, +- "dump_kmem_cache_percpu called without PERCPU_KMALLOC_V1\n"); ++ int i, j; ++ int avail; ++ ulong cpudata[NR_CPUS]; ++ ulong shared; + +- si->found = si->retval = 0; +- reqname = NULL; ++ readmem(si->cache+OFFSET(kmem_cache_s_array), ++ KVADDR, &cpudata[0], ++ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array), ++ "array_cache array", FAULT_ON_ERROR); + +- if ((!(si->flags & VERBOSE) || si->reqname) && +- !(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) +- fprintf(fp, kmem_cache_hdr); ++ for (i = 0; (i < ARRAY_LENGTH(kmem_cache_s_array)) && ++ cpudata[i]; i++) { ++ BZERO(si->cpudata[i], sizeof(ulong) * vt->kmem_max_limit); + +- si->addrlist = (ulong *)GETBUF((vt->kmem_max_c_num+1) * sizeof(ulong)); +- si->kmem_bufctl = (int *)GETBUF((vt->kmem_max_c_num+1) * sizeof(int)); +- for (i = 0; i < vt->kmem_max_cpus; i++) +- si->cpudata[i] = (ulong *) +- GETBUF(vt->kmem_max_limit * sizeof(ulong)); ++ readmem(cpudata[i]+OFFSET(array_cache_avail), ++ KVADDR, &avail, sizeof(int), ++ "array cache avail", FAULT_ON_ERROR); + +- cnt = 0; +- si->cache = cache_cache = symbol_value("cache_cache"); ++ if (!avail) ++ continue; + +- if (si->flags & ADDRESS_SPECIFIED) { +- if (!(p1 = vaddr_to_kmem_cache(si->spec_addr, kbuf))) { ++ if (avail > vt->kmem_max_limit) { + error(INFO, +- "address is not allocated in slab subsystem: %lx\n", +- si->spec_addr); +- return; ++ "\"%s\" cache: array_cache.avail %d greater than limit %ld\n", ++ si->curname, avail, vt->kmem_max_limit); ++ si->errors++; + } +- +- if (si->reqname && (si->reqname != p1)) +- error(INFO, +- "ignoring pre-selected %s cache for address: %lx\n", +- si->reqname, si->spec_addr, si->reqname); +- reqname = p1; +- } else +- reqname = si->reqname; + +- do { +- if ((si->flags & VERBOSE) && !si->reqname && +- !(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, "%s%s", cnt++ ? "\n" : "", kmem_cache_hdr); ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%s: cpu[%d] avail: %d\n", ++ si->curname, i, avail); + +- if (vt->kmem_cache_namelen) { +- readmem(si->cache+OFFSET(kmem_cache_s_name), +- KVADDR, buf, vt->kmem_cache_namelen, +- "name array", FAULT_ON_ERROR); +- } else { +- readmem(si->cache+OFFSET(kmem_cache_s_name), +- KVADDR, &name, sizeof(ulong), +- "name", FAULT_ON_ERROR); +- if (!read_string(name, buf, BUFSIZE-1)) +- error(FATAL, +- "cannot read kmem_cache_s.name string at %lx\n", +- name); +- } ++ readmem(cpudata[i]+SIZE(array_cache), ++ KVADDR, si->cpudata[i], ++ sizeof(void *) * avail, ++ "array_cache avail", FAULT_ON_ERROR); + +- if (reqname && !STREQ(reqname, buf)) +- goto next_cache; ++ if (CRASHDEBUG(2)) ++ for (j = 0; j < avail; j++) ++ fprintf(fp, " %lx (cpu %d)\n", si->cpudata[i][j], i); ++ } + +- if (ignore_cache(si, buf)) { +- fprintf(fp, "%lx %-18s [IGNORED]\n", si->cache, buf); +- goto next_cache; +- } ++ /* ++ * If the shared list contains anything, gather them as well. ++ */ ++ BZERO(si->shared_array_cache, sizeof(ulong) * vt->kmem_max_limit); + +- si->curname = buf; ++ if (!VALID_MEMBER(kmem_list3_shared) || ++ !VALID_MEMBER(kmem_cache_s_lists) || ++ !readmem(si->cache+OFFSET(kmem_cache_s_lists)+ ++ OFFSET(kmem_list3_shared), KVADDR, &shared, sizeof(void *), ++ "kmem_list3 shared", RETURN_ON_ERROR|QUIET) || ++ !readmem(shared+OFFSET(array_cache_avail), ++ KVADDR, &avail, sizeof(int), "shared array_cache avail", ++ RETURN_ON_ERROR|QUIET) || !avail) ++ return; + +- readmem(si->cache+OFFSET(kmem_cache_s_objsize), +- KVADDR, &tmp_val, sizeof(uint), +- "objsize", FAULT_ON_ERROR); +- si->size = (ulong)tmp_val; ++ if (avail > vt->kmem_max_limit) { ++ error(INFO, ++ "\"%s\" cache: shared array_cache.avail %d greater than limit %ld\n", ++ si->curname, avail, vt->kmem_max_limit); ++ si->errors++; ++ return; ++ } + +- if (!si->size) { +- if (STREQ(si->curname, "kmem_cache")) +- si->size = SIZE(kmem_cache_s); +- else { +- error(INFO, "\"%s\" cache: objsize: %ld\n", +- si->curname, si->size); +- si->errors++; +- } +- } ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%s: shared avail: %d\n", ++ si->curname, avail); + +- readmem(si->cache+OFFSET(kmem_cache_s_flags), +- KVADDR, &tmp_val, sizeof(uint), +- "kmem_cache_s flags", FAULT_ON_ERROR); +- si->c_flags = (ulong)tmp_val; ++ readmem(shared+SIZE(array_cache), KVADDR, si->shared_array_cache, ++ sizeof(void *) * avail, "shared array_cache avail", ++ FAULT_ON_ERROR); + +- readmem(si->cache+OFFSET(kmem_cache_s_gfporder), +- KVADDR, &tmp_val, sizeof(uint), +- "gfporder", FAULT_ON_ERROR); +- si->order = (ulong)tmp_val; ++ if (CRASHDEBUG(2)) ++ for (j = 0; j < avail; j++) ++ fprintf(fp, " %lx (shared list)\n", si->shared_array_cache[j]); ++} + +- readmem(si->cache+OFFSET(kmem_cache_s_num), +- KVADDR, &tmp_val, sizeof(uint), +- "kmem_cache_s num", FAULT_ON_ERROR); +- si->c_num = (ulong)tmp_val; + +- do_slab_chain_percpu_v1(SLAB_GET_COUNTS, si); + +- if (!(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) { +- DUMP_KMEM_CACHE_INFO_V1(); +- if (CRASHDEBUG(3)) +- dump_struct("kmem_cache_s", si->cache, 0); ++/* ++ * Updated gather_cpudata_list_v2 for per-node kmem_list3's in kmem_cache ++ */ ++static void ++gather_cpudata_list_v2_nodes(struct meminfo *si, int index) ++{ ++ int i, j; ++ int avail; ++ ulong cpudata[NR_CPUS]; ++ ulong shared; ++ ulong *start_address; ++ ++ start_address = (ulong *) GETBUF(sizeof(ulong) * vt->kmem_cache_len_nodes); ++ readmem(si->cache+OFFSET(kmem_cache_s_array), ++ KVADDR, &cpudata[0], ++ sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array), ++ "array_cache array", FAULT_ON_ERROR); ++ ++ for (i = 0; (i < ARRAY_LENGTH(kmem_cache_s_array)) && ++ (cpudata[i]) && !(index); i++) { ++ BZERO(si->cpudata[i], sizeof(ulong) * vt->kmem_max_limit); ++ ++ readmem(cpudata[i]+OFFSET(array_cache_avail), ++ KVADDR, &avail, sizeof(int), ++ "array cache avail", FAULT_ON_ERROR); ++ ++ if (!avail) ++ continue; ++ ++ if (avail > vt->kmem_max_limit) { ++ error(INFO, ++ "\"%s\" cache: array_cache.avail %d greater than limit %ld\n", ++ si->curname, avail, vt->kmem_max_limit); ++ si->errors++; + } + +- if (si->flags == GET_SLAB_PAGES) +- si->retval += (si->num_slabs * +- (si->slabsize/PAGESIZE())); ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%s: cpu[%d] avail: %d\n", ++ si->curname, i, avail); ++ ++ readmem(cpudata[i]+SIZE(array_cache), ++ KVADDR, si->cpudata[i], ++ sizeof(void *) * avail, ++ "array_cache avail", FAULT_ON_ERROR); + +- if (si->flags & (VERBOSE|ADDRESS_SPECIFIED)) { ++ if (CRASHDEBUG(2)) ++ for (j = 0; j < avail; j++) ++ fprintf(fp, " %lx (cpu %d)\n", si->cpudata[i][j], i); ++ } + +- gather_cpudata_list_v1(si); ++ /* ++ * If the shared list contains anything, gather them as well. ++ */ ++ if (!index) { ++ BZERO(si->shared_array_cache, sizeof(ulong) * ++ vt->kmem_max_limit * vt->kmem_cache_len_nodes); ++ si->current_cache_index = 0; ++ } + +- si->slab = (si->flags & ADDRESS_SPECIFIED) ? +- vaddr_to_slab(si->spec_addr) : 0; ++ if (!readmem(si->cache+OFFSET(kmem_cache_s_lists), KVADDR, &start_address[0], ++ sizeof(ulong) * vt->kmem_cache_len_nodes , "array nodelist array", ++ RETURN_ON_ERROR) || ++ !readmem(start_address[index] + OFFSET(kmem_list3_shared), KVADDR, &shared, ++ sizeof(void *), "kmem_list3 shared", RETURN_ON_ERROR|QUIET) || !shared || ++ !readmem(shared + OFFSET(array_cache_avail), KVADDR, &avail, sizeof(int), ++ "shared array_cache avail", RETURN_ON_ERROR|QUIET) || !avail) { ++ FREEBUF(start_address); ++ return; ++ } + +- do_slab_chain_percpu_v1(SLAB_WALKTHROUGH, si); ++ if (avail > vt->kmem_max_limit) { ++ error(INFO, ++ "\"%s\" cache: shared array_cache.avail %d greater than limit %ld\n", ++ si->curname, avail, vt->kmem_max_limit); ++ si->errors++; ++ FREEBUF(start_address); ++ return; ++ } + +- if (si->found) { +- fprintf(fp, kmem_cache_hdr); +- DUMP_KMEM_CACHE_INFO_V1(); +- fprintf(fp, slab_hdr); +- gather_slab_cached_count(si); +- DUMP_SLAB_INFO(); ++ if (CRASHDEBUG(2)) ++ fprintf(fp, "%s: shared avail: %d\n", ++ si->curname, avail); + +- switch (si->found) +- { +- case KMEM_BUFCTL_ADDR: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp,"(kmem_bufctl_t)\n"); +- break; ++ readmem(shared+SIZE(array_cache), KVADDR, si->shared_array_cache + si->current_cache_index, ++ sizeof(void *) * avail, "shared array_cache avail", ++ FAULT_ON_ERROR); + +- case KMEM_SLAB_ADDR: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp, "(slab_s)\n"); +- break; ++ if ((si->current_cache_index + avail) > ++ (vt->kmem_max_limit * vt->kmem_cache_len_nodes)) { ++ error(INFO, ++ "\"%s\" cache: total shared array_cache.avail %d greater than total limit %ld\n", ++ si->curname, ++ si->current_cache_index + avail, ++ vt->kmem_max_limit * vt->kmem_cache_len_nodes); ++ si->errors++; ++ FREEBUF(start_address); ++ return; ++ } + +- case KMEM_ON_SLAB: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp, "(unused part of slab)\n"); +- break; +- +- case KMEM_OBJECT_ADDR_FREE: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, " %lx\n", +- (ulong)si->spec_addr); +- break; ++ if (CRASHDEBUG(2)) ++ for (j = si->current_cache_index; j < (si->current_cache_index + avail); j++) ++ fprintf(fp, " %lx (shared list)\n", si->shared_array_cache[j]); ++ ++ si->current_cache_index += avail; ++ FREEBUF(start_address); ++} + +- case KMEM_OBJECT_ADDR_INUSE: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, " [%lx]\n", +- (ulong)si->spec_addr); +- break; ++/* ++ * Check whether a given address is contained in the previously-gathered ++ * percpu object cache. ++ */ + +- case KMEM_OBJECT_ADDR_CACHED: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, +- " %lx (cpu %d cache)\n", +- (ulong)si->spec_addr, si->cpu); +- break; +- } ++static int ++check_cpudata_list(struct meminfo *si, ulong obj) ++{ ++ int i, j; + +- break; ++ for (i = 0; i < vt->kmem_max_cpus; i++) { ++ for (j = 0; si->cpudata[i][j]; j++) ++ if (si->cpudata[i][j] == obj) { ++ si->cpu = i; ++ return TRUE; + } +- } ++ } + +-next_cache: +- readmem(si->cache+OFFSET(kmem_cache_s_next), +- KVADDR, &si->cache, sizeof(ulong), +- "kmem_cache_s next", FAULT_ON_ERROR); ++ return FALSE; ++} + +- si->cache -= OFFSET(kmem_cache_s_next); ++/* ++ * Check whether a given address is contained in the previously-gathered ++ * shared object cache. ++ */ + +- } while (si->cache != cache_cache); ++static int ++check_shared_list(struct meminfo *si, ulong obj) ++{ ++ int i; + +- if ((si->flags & ADDRESS_SPECIFIED) && !si->found) +- error(INFO, "%s: address not found in cache: %lx\n", +- reqname, si->spec_addr); +- +- if (si->errors) +- error(INFO, "%ld error%s encountered\n", +- si->errors, si->errors > 1 ? "s" : ""); ++ if (INVALID_MEMBER(kmem_list3_shared) || ++ !si->shared_array_cache) ++ return FALSE; + +- FREEBUF(si->addrlist); +- FREEBUF(si->kmem_bufctl); +- for (i = 0; i < vt->kmem_max_cpus; i++) +- FREEBUF(si->cpudata[i]); ++ for (i = 0; si->shared_array_cache[i]; i++) { ++ if (si->shared_array_cache[i] == obj) ++ return TRUE; ++ } + ++ return FALSE; + } + +- + /* +- * Updated for 2.6 slab substructure. ++ * Search the various memory subsystems for instances of this address. ++ * Start with the most specific areas, ending up with at least the ++ * mem_map page data. + */ + static void +-dump_kmem_cache_percpu_v2(struct meminfo *si) ++kmem_search(struct meminfo *mi) + { +- int i; ++ struct syment *sp; ++ struct meminfo tmp_meminfo; + char buf[BUFSIZE]; +- char kbuf[BUFSIZE]; +- char *reqname; +- ulong cache_end; +- ulong name; +- int cnt; +- uint tmp_val; /* Used as temporary variable to read sizeof(int) and +- assigned to ulong variable. We are doing this to mask +- the endian issue */ +- char *p1; +- +- if (!(vt->flags & PERCPU_KMALLOC_V2)) +- error(FATAL, +- "dump_kmem_cache_percpu called without PERCPU_KMALLOC_V2\n"); ++ ulong vaddr, orig_flags; ++ physaddr_t paddr; ++ ulong offset; ++ ulong task; ++ struct task_context *tc; + +- si->found = si->retval = 0; +- reqname = NULL; ++ pc->curcmd_flags &= ~HEADER_PRINTED; + +- if ((!(si->flags & VERBOSE) || si->reqname) && +- !(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) +- fprintf(fp, kmem_cache_hdr); ++ switch (mi->memtype) ++ { ++ case KVADDR: ++ vaddr = mi->spec_addr; ++ break; + +- si->addrlist = (ulong *)GETBUF((vt->kmem_max_c_num+1) * sizeof(ulong)); +- si->kmem_bufctl = (int *)GETBUF((vt->kmem_max_c_num+1) * sizeof(int)); +- for (i = 0; i < vt->kmem_max_cpus; i++) +- si->cpudata[i] = (ulong *) +- GETBUF(vt->kmem_max_limit * sizeof(ulong)); ++ case PHYSADDR: ++ vaddr = mi->spec_addr < VTOP(vt->high_memory) ? ++ PTOV(mi->spec_addr) : BADADDR; ++ break; ++ } + +- cnt = 0; ++ orig_flags = mi->flags; ++ mi->retval = 0; + +- get_symbol_data("cache_chain", sizeof(ulong), &si->cache); +- si->cache -= OFFSET(kmem_cache_s_next); +- cache_end = symbol_value("cache_chain"); ++ /* ++ * Check first for a possible symbolic display of the virtual ++ * address associated with mi->spec_addr or PTOV(mi->spec_addr). ++ */ ++ if (((vaddr >= kt->stext) && (vaddr <= kt->end)) || ++ IS_MODULE_VADDR(mi->spec_addr)) { ++ if ((sp = value_search(vaddr, &offset))) { ++ show_symbol(sp, offset, SHOW_LINENUM | SHOW_RADIX()); ++ fprintf(fp, "\n"); ++ } ++ } + +- if (si->flags & ADDRESS_SPECIFIED) { +- if (!(p1 = vaddr_to_kmem_cache(si->spec_addr, kbuf))) { +- error(INFO, +- "address is not allocated in slab subsystem: %lx\n", +- si->spec_addr); +- return; ++ /* ++ * Check for a valid mapped address. ++ */ ++ if ((mi->memtype == KVADDR) && IS_VMALLOC_ADDR(mi->spec_addr)) { ++ if (kvtop(NULL, mi->spec_addr, &paddr, 0)) { ++ mi->flags = orig_flags | VMLIST_VERIFY; ++ dump_vmlist(mi); ++ if (mi->retval) { ++ mi->flags = orig_flags; ++ dump_vmlist(mi); ++ fprintf(fp, "\n"); ++ mi->spec_addr = paddr; ++ mi->memtype = PHYSADDR; ++ goto mem_map; ++ } + } +- +- if (si->reqname && (si->reqname != p1)) +- error(INFO, +- "ignoring pre-selected %s cache for address: %lx\n", +- si->reqname, si->spec_addr, si->reqname); +- reqname = p1; +- } else +- reqname = si->reqname; ++ } + +- do { +- if ((si->flags & VERBOSE) && !si->reqname && +- !(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, "%s%s", cnt++ ? "\n" : "", kmem_cache_hdr); ++ /* ++ * If the address is physical, check whether it's in vmalloc space. ++ */ ++ if (mi->memtype == PHYSADDR) { ++ mi->flags = orig_flags; ++ mi->flags |= GET_PHYS_TO_VMALLOC; ++ mi->retval = 0; ++ dump_vmlist(mi); ++ mi->flags &= ~GET_PHYS_TO_VMALLOC; + +- if (vt->kmem_cache_namelen) { +- readmem(si->cache+OFFSET(kmem_cache_s_name), +- KVADDR, buf, vt->kmem_cache_namelen, +- "name array", FAULT_ON_ERROR); +- } else { +- readmem(si->cache+OFFSET(kmem_cache_s_name), +- KVADDR, &name, sizeof(ulong), +- "name", FAULT_ON_ERROR); +- if (!read_string(name, buf, BUFSIZE-1)) +- error(FATAL, +- "cannot read kmem_cache_s.name string at %lx\n", +- name); ++ if (mi->retval) { ++ if ((sp = value_search(mi->retval, &offset))) { ++ show_symbol(sp, offset, ++ SHOW_LINENUM | SHOW_RADIX()); ++ fprintf(fp, "\n"); ++ } ++ dump_vmlist(mi); ++ fprintf(fp, "\n"); ++ goto mem_map; + } ++ } ++ ++ /* ++ * Check whether the containing page belongs to the slab subsystem. ++ */ ++ mi->flags = orig_flags; ++ mi->retval = 0; ++ if ((vaddr != BADADDR) && vaddr_to_kmem_cache(vaddr, buf, VERBOSE)) { ++ BZERO(&tmp_meminfo, sizeof(struct meminfo)); ++ tmp_meminfo.spec_addr = vaddr; ++ tmp_meminfo.memtype = KVADDR; ++ tmp_meminfo.flags = mi->flags; ++ vt->dump_kmem_cache(&tmp_meminfo); ++ fprintf(fp, "\n"); ++ } ++ if ((vaddr != BADADDR) && is_slab_page(mi, buf)) { ++ BZERO(&tmp_meminfo, sizeof(struct meminfo)); ++ tmp_meminfo.spec_addr = vaddr; ++ tmp_meminfo.memtype = KVADDR; ++ tmp_meminfo.flags = mi->flags; ++ vt->dump_kmem_cache(&tmp_meminfo); ++ fprintf(fp, "\n"); ++ } ++ ++ /* ++ * Check free list. ++ */ ++ mi->flags = orig_flags; ++ mi->retval = 0; ++ vt->dump_free_pages(mi); ++ if (mi->retval) ++ fprintf(fp, "\n"); + +- if (reqname && !STREQ(reqname, buf)) +- goto next_cache; ++ if (vt->page_hash_table) { ++ /* ++ * Check the page cache. ++ */ ++ mi->flags = orig_flags; ++ mi->retval = 0; ++ dump_page_hash_table(mi); ++ if (mi->retval) ++ fprintf(fp, "\n"); ++ } + +- if (ignore_cache(si, buf)) { +- fprintf(fp, "%lx %-18s [IGNORED]\n", si->cache, buf); +- goto next_cache; +- } ++ /* ++ * Check whether it's a current task or stack address. ++ */ ++ if ((mi->memtype == KVADDR) && (task = vaddr_in_task_struct(vaddr)) && ++ (tc = task_to_context(task))) { ++ show_context(tc); ++ fprintf(fp, "\n"); ++ } else if ((mi->memtype == KVADDR) && (task = stkptr_to_task(vaddr)) && ++ (tc = task_to_context(task))) { ++ show_context(tc); ++ fprintf(fp, "\n"); ++ } + +- si->curname = buf; ++mem_map: ++ mi->flags = orig_flags; ++ pc->curcmd_flags &= ~HEADER_PRINTED; ++ if (vaddr != BADADDR) ++ dump_mem_map(mi); ++ else ++ mi->retval = FALSE; + +- readmem(si->cache+OFFSET(kmem_cache_s_objsize), +- KVADDR, &tmp_val, sizeof(uint), +- "objsize", FAULT_ON_ERROR); +- si->size = (ulong)tmp_val; ++ if (!mi->retval) ++ fprintf(fp, "%llx: %s address not found in mem map\n", ++ mi->spec_addr, memtype_string(mi->memtype, 0)); ++} + +- if (!si->size) { +- if (STREQ(si->curname, "kmem_cache")) +- si->size = SIZE(kmem_cache_s); +- else { +- error(INFO, "\"%s\" cache: objsize: %ld\n", +- si->curname, si->size); +- si->errors++; +- } +- } ++/* ++ * Determine whether an address is a page pointer from the mem_map[] array. ++ * If the caller requests it, return the associated physical address. ++ */ ++int ++is_page_ptr(ulong addr, physaddr_t *phys) ++{ ++ int n; ++ ulong ppstart, ppend; ++ struct node_table *nt; ++ ulong pgnum, node_size; ++ ulong nr, sec_addr; ++ ulong nr_mem_sections; ++ ulong coded_mem_map, mem_map, end_mem_map; ++ physaddr_t section_paddr; ++ ++ if (IS_SPARSEMEM()) { ++ nr_mem_sections = NR_MEM_SECTIONS(); ++ for (nr = 0; nr <= nr_mem_sections ; nr++) { ++ if ((sec_addr = valid_section_nr(nr))) { ++ coded_mem_map = section_mem_map_addr(sec_addr); ++ mem_map = sparse_decode_mem_map(coded_mem_map, nr); ++ end_mem_map = mem_map + (PAGES_PER_SECTION() * SIZE(page)); ++ ++ if ((addr >= mem_map) && (addr < end_mem_map)) { ++ if ((addr - mem_map) % SIZE(page)) ++ return FALSE; ++ if (phys) { ++ section_paddr = PTOB(section_nr_to_pfn(nr)); ++ pgnum = (addr - mem_map) / SIZE(page); ++ *phys = section_paddr + ((physaddr_t)pgnum * PAGESIZE()); ++ } ++ return TRUE; ++ } ++ } ++ } ++ return FALSE; ++ } + +- readmem(si->cache+OFFSET(kmem_cache_s_flags), +- KVADDR, &tmp_val, sizeof(uint), +- "kmem_cache_s flags", FAULT_ON_ERROR); +- si->c_flags = (ulong)tmp_val; ++ for (n = 0; n < vt->numnodes; n++) { ++ nt = &vt->node_table[n]; ++ if ((vt->flags & V_MEM_MAP) && (vt->numnodes == 1)) ++ node_size = vt->max_mapnr; ++ else ++ node_size = nt->size; + +- readmem(si->cache+OFFSET(kmem_cache_s_gfporder), +- KVADDR, &tmp_val, sizeof(uint), +- "gfporder", FAULT_ON_ERROR); +- si->order = (ulong)tmp_val; ++ ppstart = nt->mem_map; ++ ppend = ppstart + (node_size * SIZE(page)); + +- readmem(si->cache+OFFSET(kmem_cache_s_num), +- KVADDR, &tmp_val, sizeof(uint), +- "kmem_cache_s num", FAULT_ON_ERROR); +- si->c_num = (ulong)tmp_val; ++ if ((addr < ppstart) || (addr >= ppend)) ++ continue; + +- do_slab_chain_percpu_v2(SLAB_GET_COUNTS, si); ++ /* ++ * We're in the mem_map range -- but it is a page pointer? ++ */ ++ if ((addr - ppstart) % SIZE(page)) ++ return FALSE; + +- if (!(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) { +- DUMP_KMEM_CACHE_INFO_V2(); +- if (CRASHDEBUG(3)) +- dump_struct("kmem_cache_s", si->cache, 0); ++ if (phys) { ++ pgnum = (addr - nt->mem_map) / SIZE(page); ++ *phys = ((physaddr_t)pgnum * PAGESIZE()) + nt->start_paddr; + } + +- if (si->flags == GET_SLAB_PAGES) +- si->retval += (si->num_slabs * +- (si->slabsize/PAGESIZE())); +- +- if (si->flags & (VERBOSE|ADDRESS_SPECIFIED)) { +- +- gather_cpudata_list_v2(si); ++ return TRUE; ++ } + +- si->slab = (si->flags & ADDRESS_SPECIFIED) ? +- vaddr_to_slab(si->spec_addr) : 0; ++ return FALSE; + +- do_slab_chain_percpu_v2(SLAB_WALKTHROUGH, si); ++#ifdef PRE_NODES ++ ppstart = vt->mem_map; ++ ppend = ppstart + (vt->total_pages * vt->page_struct_len); + +- if (si->found) { +- fprintf(fp, kmem_cache_hdr); +- DUMP_KMEM_CACHE_INFO_V2(); +- fprintf(fp, slab_hdr); +- gather_slab_cached_count(si); +- DUMP_SLAB_INFO(); ++ if ((addr < ppstart) || (addr >= ppend)) ++ return FALSE; + +- switch (si->found) +- { +- case KMEM_BUFCTL_ADDR: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp,"(kmem_bufctl_t)\n"); +- break; ++ if ((addr - ppstart) % vt->page_struct_len) ++ return FALSE; + +- case KMEM_SLAB_ADDR: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp, "(slab)\n"); +- break; ++ return TRUE; ++#endif ++} + +- case KMEM_ON_SLAB: +- fprintf(fp, " %lx ", +- (ulong)si->spec_addr); +- fprintf(fp, "(unused part of slab)\n"); +- break; +- +- case KMEM_OBJECT_ADDR_FREE: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, " %lx\n", +- (ulong)si->spec_addr); +- break; ++/* ++ * Return the physical address associated with this page pointer. ++ */ ++static int ++page_to_phys(ulong pp, physaddr_t *phys) ++{ ++ return(is_page_ptr(pp, phys)); ++} + +- case KMEM_OBJECT_ADDR_INUSE: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, " [%lx]\n", +- (ulong)si->spec_addr); +- break; + +- case KMEM_OBJECT_ADDR_CACHED: +- fprintf(fp, free_inuse_hdr); +- fprintf(fp, +- " %lx (cpu %d cache)\n", +- (ulong)si->spec_addr, si->cpu); +- break; +- } ++/* ++ * Return the page pointer associated with this physical address. ++ */ ++static int ++phys_to_page(physaddr_t phys, ulong *pp) ++{ ++ int n; ++ ulong pgnum; ++ struct node_table *nt; ++ physaddr_t pstart, pend; ++ ulong node_size; + +- break; +- } ++ if (IS_SPARSEMEM()) { ++ ulong map; ++ map = pfn_to_map(phys >> PAGESHIFT()); ++ if (map) { ++ *pp = map; ++ return TRUE; + } ++ return FALSE; ++ } + +-next_cache: +- readmem(si->cache+OFFSET(kmem_cache_s_next), +- KVADDR, &si->cache, sizeof(ulong), +- "kmem_cache_s next", FAULT_ON_ERROR); ++ for (n = 0; n < vt->numnodes; n++) { ++ nt = &vt->node_table[n]; ++ if ((vt->flags & V_MEM_MAP) && (vt->numnodes == 1)) ++ node_size = vt->max_mapnr; ++ else ++ node_size = nt->size; + +- if (si->cache != cache_end) +- si->cache -= OFFSET(kmem_cache_s_next); ++ pstart = nt->start_paddr; ++ pend = pstart + ((ulonglong)node_size * PAGESIZE()); + +- } while (si->cache != cache_end); ++ if ((phys < pstart) || (phys >= pend)) ++ continue; ++ /* ++ * We're in the physical range -- calculate the page. ++ */ ++ pgnum = BTOP(phys - pstart); ++ *pp = nt->mem_map + (pgnum * SIZE(page)); + +- if ((si->flags & ADDRESS_SPECIFIED) && !si->found) +- error(INFO, "%s: address not found in cache: %lx\n", +- reqname, si->spec_addr); +- +- if (si->errors) +- error(INFO, "%ld error%s encountered\n", +- si->errors, si->errors > 1 ? "s" : ""); ++ return TRUE; ++ } + +- FREEBUF(si->addrlist); +- FREEBUF(si->kmem_bufctl); +- for (i = 0; i < vt->kmem_max_cpus; i++) +- FREEBUF(si->cpudata[i]); ++ return FALSE; + ++#ifdef PRE_NODES ++ if (phys >= (vt->total_pages * PAGESIZE())) ++ return FALSE; ++ ++ pgnum = PTOB(BTOP(phys)) / PAGESIZE(); ++ *pp = vt->mem_map + (pgnum * vt->page_struct_len); ++ ++ return TRUE; ++#endif + } + + + /* +- * Walk through the slab chain hanging off a kmem_cache_s structure, +- * gathering basic statistics. +- * +- * TBD: Given a specified physical address, determine whether it's in this +- * slab chain, and whether it's in use or not. ++ * Try to read a string of non-NULL characters from a memory location, ++ * returning the number of characters read. + */ ++int ++read_string(ulong kvaddr, char *buf, int maxlen) ++{ ++ char strbuf[MIN_PAGE_SIZE]; ++ ulong kp; ++ char *bufptr; ++ long cnt, size; + +-#define INSLAB(obj, si) \ +- ((ulong)((ulong)(obj) & ~(si->slabsize-1)) == si->s_mem) ++ BZERO(buf, maxlen); ++ BZERO(strbuf, MIN_PAGE_SIZE); + +-static void +-do_slab_chain(int cmd, struct meminfo *si) +-{ +- ulong tmp, magic; +- ulong kmem_slab_end; +- char *kmem_slab_s_buf; ++ kp = kvaddr; ++ bufptr = strbuf; ++ size = maxlen; + +- si->slabsize = (power(2, si->order) * PAGESIZE()); ++ while (size > 0) { ++ cnt = MIN_PAGE_SIZE - (kp & (MIN_PAGE_SIZE-1)); ++ ++ if (cnt > size) ++ cnt = size; + +- kmem_slab_end = si->cache + OFFSET(kmem_cache_s_c_offset); ++ if (!readmem(kp, KVADDR, bufptr, cnt, ++ "readstring characters", QUIET|RETURN_ON_ERROR)) ++ break; + +- switch (cmd) +- { +- case SLAB_GET_COUNTS: +- si->slab = ULONG(si->cache_buf + OFFSET(kmem_cache_s_c_firstp)); ++ if (count_buffer_chars(bufptr, NULLCHAR, cnt)) ++ break; + +- if (slab_data_saved(si)) +- return; ++ kp += cnt; ++ bufptr += cnt; ++ size -= cnt; ++ } + +- si->num_slabs = si->inuse = 0; ++ strcpy(buf, strbuf); ++ return (strlen(buf)); ++} + +- if (si->slab == kmem_slab_end) +- return; ++/* ++ * "help -v" output ++ */ ++void ++dump_vm_table(int verbose) ++{ ++ int i; ++ struct node_table *nt; ++ int others; ++ ulong *up; + +- kmem_slab_s_buf = GETBUF(SIZE(kmem_slab_s)); ++ others = 0; ++ fprintf(fp, " flags: %lx %s(", ++ vt->flags, count_bits_long(vt->flags) > 4 ? "\n " : ""); ++ if (vt->flags & NODES) ++ fprintf(fp, "%sNODES", others++ ? "|" : ""); ++ if (vt->flags & NODES_ONLINE) ++ fprintf(fp, "%sNODES_ONLINE", others++ ? "|" : ""); ++ if (vt->flags & ZONES) ++ fprintf(fp, "%sZONES", others++ ? "|" : ""); ++ if (vt->flags & PERCPU_KMALLOC_V1) ++ fprintf(fp, "%sPERCPU_KMALLOC_V1", others++ ? "|" : ""); ++ if (vt->flags & PERCPU_KMALLOC_V2) ++ fprintf(fp, "%sPERCPU_KMALLOC_V2", others++ ? "|" : ""); ++ if (vt->flags & COMMON_VADDR) ++ fprintf(fp, "%sCOMMON_VADDR", others++ ? "|" : ""); ++ if (vt->flags & KMEM_CACHE_INIT) ++ fprintf(fp, "%sKMEM_CACHE_INIT", others++ ? "|" : ""); ++ if (vt->flags & V_MEM_MAP) ++ fprintf(fp, "%sV_MEM_MAP", others++ ? "|" : ""); ++ if (vt->flags & KMEM_CACHE_UNAVAIL) ++ fprintf(fp, "%sKMEM_CACHE_UNAVAIL", others++ ? "|" : ""); ++ if (vt->flags & DISCONTIGMEM) ++ fprintf(fp, "%sDISCONTIGMEM", others++ ? "|" : ""); ++ if (vt->flags & FLATMEM) ++ fprintf(fp, "%sFLATMEM", others++ ? "|" : ""); ++ if (vt->flags & SPARSEMEM) ++ fprintf(fp, "%sSPARSEMEM", others++ ? "|" : "");\ ++ if (vt->flags & SPARSEMEM_EX) ++ fprintf(fp, "%sSPARSEMEM_EX", others++ ? "|" : "");\ ++ if (vt->flags & KMEM_CACHE_DELAY) ++ fprintf(fp, "%sKMEM_CACHE_DELAY", others++ ? "|" : "");\ ++ if (vt->flags & PERCPU_KMALLOC_V2_NODES) ++ fprintf(fp, "%sPERCPU_KMALLOC_V2_NODES", others++ ? "|" : "");\ ++ if (vt->flags & VM_STAT) ++ fprintf(fp, "%sVM_STAT", others++ ? "|" : "");\ ++ if (vt->flags & KMALLOC_SLUB) ++ fprintf(fp, "%sKMALLOC_SLUB", others++ ? "|" : "");\ ++ if (vt->flags & CONFIG_NUMA) ++ fprintf(fp, "%sCONFIG_NUMA", others++ ? "|" : "");\ ++ if (vt->flags & VM_EVENT) ++ fprintf(fp, "%sVM_EVENT", others++ ? "|" : "");\ + +- do { +- if (received_SIGINT()) { +- FREEBUF(kmem_slab_s_buf); +- restart(0); +- } ++ fprintf(fp, ")\n"); ++ if (vt->kernel_pgd[0] == vt->kernel_pgd[1]) ++ fprintf(fp, " kernel_pgd[NR_CPUS]: %lx ...\n", ++ vt->kernel_pgd[0]); ++ else { ++ fprintf(fp, " kernel_pgd[NR_CPUS]: "); ++ for (i = 0; i < NR_CPUS; i++) { ++ if ((i % 4) == 0) ++ fprintf(fp, "\n "); ++ fprintf(fp, "%lx ", vt->kernel_pgd[i]); ++ } ++ fprintf(fp, "\n"); ++ } ++ fprintf(fp, " high_memory: %lx\n", vt->high_memory); ++ fprintf(fp, " vmalloc_start: %lx\n", vt->vmalloc_start); ++ fprintf(fp, " mem_map: %lx\n", vt->mem_map); ++ fprintf(fp, " total_pages: %ld\n", vt->total_pages); ++ fprintf(fp, " max_mapnr: %ld\n", vt->max_mapnr); ++ fprintf(fp, " totalram_pages: %ld\n", vt->totalram_pages); ++ fprintf(fp, " totalhigh_pages: %ld\n", vt->totalhigh_pages); ++ fprintf(fp, " num_physpages: %ld\n", vt->num_physpages); ++ fprintf(fp, " page_hash_table: %lx\n", vt->page_hash_table); ++ fprintf(fp, "page_hash_table_len: %d\n", vt->page_hash_table_len); ++ fprintf(fp, " kmem_max_c_num: %ld\n", vt->kmem_max_c_num); ++ fprintf(fp, " kmem_max_limit: %ld\n", vt->kmem_max_limit); ++ fprintf(fp, " kmem_max_cpus: %ld\n", vt->kmem_max_cpus); ++ fprintf(fp, " kmem_cache_count: %ld\n", vt->kmem_cache_count); ++ fprintf(fp, " kmem_cache_namelen: %d\n", vt->kmem_cache_namelen); ++ fprintf(fp, "kmem_cache_nodelist_len: %ld\n", vt->kmem_cache_len_nodes); ++ fprintf(fp, " PG_reserved: %lx\n", vt->PG_reserved); ++ fprintf(fp, " PG_slab: %ld (%lx)\n", vt->PG_slab, ++ (ulong)1 << vt->PG_slab); ++ fprintf(fp, " PG_head_tail_mask: %lx\n", vt->PG_head_tail_mask); ++ fprintf(fp, " paddr_prlen: %d\n", vt->paddr_prlen); ++ fprintf(fp, " numnodes: %d\n", vt->numnodes); ++ fprintf(fp, " nr_zones: %d\n", vt->nr_zones); ++ fprintf(fp, " nr_free_areas: %d\n", vt->nr_free_areas); ++ for (i = 0; i < vt->numnodes; i++) { ++ nt = &vt->node_table[i]; ++ fprintf(fp, " node_table[%d]: \n", i); ++ fprintf(fp, " id: %d\n", nt->node_id); ++ fprintf(fp, " pgdat: %lx\n", nt->pgdat); ++ fprintf(fp, " size: %ld\n", nt->size); ++ fprintf(fp, " present: %ld\n", nt->present); ++ fprintf(fp, " mem_map: %lx\n", nt->mem_map); ++ fprintf(fp, " start_paddr: %llx\n", nt->start_paddr); ++ fprintf(fp, " start_mapnr: %ld\n", nt->start_mapnr); ++ } + +- readmem(si->slab, KVADDR, kmem_slab_s_buf, +- SIZE(kmem_slab_s), "kmem_slab_s buffer", +- FAULT_ON_ERROR); ++ fprintf(fp, " dump_free_pages: "); ++ if (vt->dump_free_pages == dump_free_pages) ++ fprintf(fp, "dump_free_pages()\n"); ++ else if (vt->dump_free_pages == dump_free_pages_zones_v1) ++ fprintf(fp, "dump_free_pages_zones_v1()\n"); ++ else if (vt->dump_free_pages == dump_free_pages_zones_v2) ++ fprintf(fp, "dump_free_pages_zones_v2()\n"); ++ else if (vt->dump_free_pages == dump_multidimensional_free_pages) ++ fprintf(fp, "dump_multidimensional_free_pages()\n"); ++ else ++ fprintf(fp, "%lx (unknown)\n", (ulong)vt->dump_free_pages); + +- magic = ULONG(kmem_slab_s_buf + +- OFFSET(kmem_slab_s_s_magic)); ++ fprintf(fp, " dump_kmem_cache: "); ++ if (vt->dump_kmem_cache == dump_kmem_cache) ++ fprintf(fp, "dump_kmem_cache()\n"); ++ else if (vt->dump_kmem_cache == dump_kmem_cache_percpu_v1) ++ fprintf(fp, "dump_kmem_cache_percpu_v1()\n"); ++ else if (vt->dump_kmem_cache == dump_kmem_cache_percpu_v2) ++ fprintf(fp, "dump_kmem_cache_percpu_v2()\n"); ++ else if (vt->dump_kmem_cache == dump_kmem_cache_slub) ++ fprintf(fp, "dump_kmem_cache_slub()\n"); ++ else ++ fprintf(fp, "%lx (unknown)\n", (ulong)vt->dump_kmem_cache); ++ fprintf(fp, " slab_data: %lx\n", (ulong)vt->slab_data); ++ if (verbose) ++ dump_saved_slab_data(); ++ fprintf(fp, " cpu_slab_type: %d\n", vt->cpu_slab_type); ++ fprintf(fp, " nr_swapfiles: %d\n", vt->nr_swapfiles); ++ fprintf(fp, " last_swap_read: %lx\n", vt->last_swap_read); ++ fprintf(fp, " swap_info_struct: %lx\n", (ulong)vt->swap_info_struct); ++ fprintf(fp, " mem_sec: %lx\n", (ulong)vt->mem_sec); ++ fprintf(fp, " mem_section: %lx\n", (ulong)vt->mem_section); ++ fprintf(fp, " ZONE_HIGHMEM: %d\n", vt->ZONE_HIGHMEM); ++ fprintf(fp, "node_online_map_len: %d\n", vt->node_online_map_len); ++ if (vt->node_online_map_len) { ++ fprintf(fp, " node_online_map: "); ++ up = (ulong *)vt->node_online_map; ++ for (i = 0; i < vt->node_online_map_len; i++) { ++ fprintf(fp, "%s%lx", i ? ", " : "[", *up); ++ up++; ++ } ++ fprintf(fp, "]\n"); ++ } else { ++ fprintf(fp, " node_online_map: (unused)\n"); ++ } ++ fprintf(fp, " nr_vm_stat_items: %d\n", vt->nr_vm_stat_items); ++ fprintf(fp, " vm_stat_items: %s", (vt->flags & VM_STAT) ? ++ "\n" : "(not used)\n"); ++ for (i = 0; i < vt->nr_vm_stat_items; i++) ++ fprintf(fp, " [%d] %s\n", i, vt->vm_stat_items[i]); ++ ++ fprintf(fp, " nr_vm_event_items: %d\n", vt->nr_vm_event_items); ++ fprintf(fp, " vm_event_items: %s", (vt->flags & VM_EVENT) ? ++ "\n" : "(not used)\n"); ++ for (i = 0; i < vt->nr_vm_event_items; i++) ++ fprintf(fp, " [%d] %s\n", i, vt->vm_event_items[i]); + +- if (magic == SLAB_MAGIC_ALLOC) { +- +- tmp = ULONG(kmem_slab_s_buf + +- OFFSET(kmem_slab_s_s_inuse)); +- +- si->inuse += tmp; +- si->num_slabs++; +- } else { +- fprintf(fp, +- "\"%s\" cache: invalid s_magic: %lx\n", +- si->curname, magic); +- si->errors++; +- FREEBUF(kmem_slab_s_buf); +- return; +- } +- +- si->slab = ULONG(kmem_slab_s_buf + +- OFFSET(kmem_slab_s_s_nextp)); +- +- } while (si->slab != kmem_slab_end); +- +- FREEBUF(kmem_slab_s_buf); +- save_slab_data(si); +- break; ++ dump_vma_cache(VERBOSE); ++} + +- case SLAB_WALKTHROUGH: +- if (!si->slab) +- si->slab = ULONG(si->cache_buf + +- OFFSET(kmem_cache_s_c_firstp)); ++/* ++ * Calculate the amount of memory referenced in the kernel-specific "nodes". ++ */ ++uint64_t ++total_node_memory() ++{ ++ int i; ++ struct node_table *nt; ++ uint64_t total; + +- if (si->slab == kmem_slab_end) +- return; ++ for (i = total = 0; i < vt->numnodes; i++) { ++ nt = &vt->node_table[i]; + + if (CRASHDEBUG(1)) { +- fprintf(fp, "search cache: [%s] ", si->curname); +- if (si->flags & ADDRESS_SPECIFIED) +- fprintf(fp, "for %llx", si->spec_addr); +- fprintf(fp, "\n"); ++ console("node_table[%d]: \n", i); ++ console(" id: %d\n", nt->node_id); ++ console(" pgdat: %lx\n", nt->pgdat); ++ console(" size: %ld\n", nt->size); ++ console(" present: %ld\n", nt->present); ++ console(" mem_map: %lx\n", nt->mem_map); ++ console(" start_paddr: %lx\n", nt->start_paddr); ++ console(" start_mapnr: %ld\n", nt->start_mapnr); + } + +- si->slab_buf = kmem_slab_s_buf = GETBUF(SIZE(kmem_slab_s)); +- +- do { +- if (received_SIGINT()) { +- FREEBUF(kmem_slab_s_buf); +- restart(0); +- } +- +- readmem(si->slab, KVADDR, kmem_slab_s_buf, +- SIZE(kmem_slab_s), "kmem_slab_s buffer", +- FAULT_ON_ERROR); +- +- dump_slab(si); +- +- if (si->found) { +- FREEBUF(kmem_slab_s_buf); +- return; +- } +- +- si->slab = ULONG(kmem_slab_s_buf + +- OFFSET(kmem_slab_s_s_nextp)); +- +- } while (si->slab != kmem_slab_end); ++ if (nt->present) ++ total += (uint64_t)((uint64_t)nt->present * (uint64_t)PAGESIZE()); ++ else ++ total += (uint64_t)((uint64_t)nt->size * (uint64_t)PAGESIZE()); ++ } + +- FREEBUF(kmem_slab_s_buf); +- break; +- } ++ return total; + } + +- + /* +- * do_slab_chain() adapted for newer percpu slab format. ++ * Dump just the vm_area_struct cache table data so that it can be ++ * called from above or for debug purposes. + */ ++void ++dump_vma_cache(ulong verbose) ++{ ++ int i; ++ ulong vhits; + +-#define SLAB_BASE(X) (PTOB(BTOP(X))) ++ if (!verbose) ++ goto show_hits; + +-#define INSLAB_PERCPU(obj, si) \ +- ((ulong)((ulong)(obj) & ~(si->slabsize-1)) == SLAB_BASE(si->s_mem)) ++ for (i = 0; i < VMA_CACHE; i++) ++ fprintf(fp, " cached_vma[%2d]: %lx (%ld)\n", ++ i, vt->cached_vma[i], ++ vt->cached_vma_hits[i]); ++ fprintf(fp, " vma_cache: %lx\n", (ulong)vt->vma_cache); ++ fprintf(fp, " vma_cache_index: %d\n", vt->vma_cache_index); ++ fprintf(fp, " vma_cache_fills: %ld\n", vt->vma_cache_fills); ++ fflush(fp); + +-#define SLAB_CHAINS (3) ++show_hits: ++ if (vt->vma_cache_fills) { ++ for (i = vhits = 0; i < VMA_CACHE; i++) ++ vhits += vt->cached_vma_hits[i]; + +-static char *slab_chain_name_v1[] = {"full", "partial", "free"}; ++ fprintf(stderr, "%s vma hit rate: %2ld%% (%ld of %ld)\n", ++ verbose ? "" : " ", ++ (vhits * 100)/vt->vma_cache_fills, ++ vhits, vt->vma_cache_fills); ++ } ++} + +-static void +-do_slab_chain_percpu_v1(long cmd, struct meminfo *si) ++/* ++ * Guess at the "real" amount of physical memory installed, formatting ++ * it in a MB or GB based string. ++ */ ++char * ++get_memory_size(char *buf) + { +- int i, tmp, s; +- int list_borked; +- char *slab_s_buf; +- ulong specified_slab; +- ulong last; +- ulong slab_chains[SLAB_CHAINS]; ++ uint64_t total; ++ ulong next_gig; ++#ifdef OLDWAY ++ ulong mbs, gbs; ++#endif + +- list_borked = 0; +- si->slabsize = (power(2, si->order) * PAGESIZE()); +- si->cpucached_slab = 0; ++ total = machdep->memory_size(); + +- if (VALID_MEMBER(kmem_cache_s_slabs)) { +- slab_chains[0] = si->cache + OFFSET(kmem_cache_s_slabs); +- slab_chains[1] = 0; +- slab_chains[2] = 0; +- } else { +- slab_chains[0] = si->cache + OFFSET(kmem_cache_s_slabs_full); +- slab_chains[1] = si->cache + OFFSET(kmem_cache_s_slabs_partial); +- slab_chains[2] = si->cache + OFFSET(kmem_cache_s_slabs_free); ++ if ((next_gig = roundup(total, GIGABYTES(1)))) { ++ if ((next_gig - total) <= MEGABYTES(64)) ++ total = next_gig; + } + +- if (CRASHDEBUG(1)) { +- fprintf(fp, "[ %s: %lx ", si->curname, si->cache); +- fprintf(fp, "full: %lx partial: %lx free: %lx ]\n", +- slab_chains[0], slab_chains[1], slab_chains[2]); +- } ++ return (pages_to_size((ulong)(total/PAGESIZE()), buf)); + +- switch (cmd) +- { +- case SLAB_GET_COUNTS: +- si->flags |= SLAB_GET_COUNTS; +- si->flags &= ~SLAB_WALKTHROUGH; +- si->cpucached_cache = 0; +- si->num_slabs = si->inuse = 0; +- gather_cpudata_list_v1(si); ++#ifdef OLDWAY ++ gbs = (ulong)(total/GIGABYTES(1)); ++ mbs = (ulong)(total/MEGABYTES(1)); ++ if (gbs) ++ mbs = (total % GIGABYTES(1))/MEGABYTES(1); + +- slab_s_buf = GETBUF(SIZE(slab_s)); ++ if (total%MEGABYTES(1)) ++ mbs++; + +- for (s = 0; s < SLAB_CHAINS; s++) { ++ if (gbs) ++ sprintf(buf, mbs ? "%ld GB %ld MB" : "%ld GB", gbs, mbs); ++ else ++ sprintf(buf, "%ld MB", mbs); + +- if (!slab_chains[s]) +- continue; ++ return buf; ++#endif ++} + +- if (!readmem(slab_chains[s], +- KVADDR, &si->slab, sizeof(ulong), +- "first slab", QUIET|RETURN_ON_ERROR)) { +- error(INFO, +- "%s: %s list: bad slab pointer: %lx\n", +- si->curname, slab_chain_name_v1[s], +- slab_chains[s]); +- list_borked = 1; +- continue; +- } +- +- if (slab_data_saved(si)) { +- FREEBUF(slab_s_buf); +- return; +- } +- +- if (si->slab == slab_chains[s]) +- continue; +- +- last = slab_chains[s]; ++/* ++ * For use by architectures not having machine-specific manners for ++ * best determining physical memory size. ++ */ ++uint64_t ++generic_memory_size(void) ++{ ++ if (machdep->memsize) ++ return machdep->memsize; + +- do { +- if (received_SIGINT()) { +- FREEBUF(slab_s_buf); +- restart(0); +- } ++ return (machdep->memsize = total_node_memory()); ++} + +- if (!verify_slab_v1(si, last, s)) { +- list_borked = 1; +- continue; +- } +- last = si->slab - OFFSET(slab_s_list); +- +- readmem(si->slab, KVADDR, slab_s_buf, +- SIZE(slab_s), "slab_s buffer", +- FAULT_ON_ERROR); +- +- tmp = INT(slab_s_buf + OFFSET(slab_s_inuse)); +- si->inuse += tmp; +- +- if (ACTIVE()) +- gather_cpudata_list_v1(si); ++/* ++ * Determine whether a virtual address is user or kernel or ambiguous. ++ */ ++int ++vaddr_type(ulong vaddr, struct task_context *tc) ++{ ++ int memtype, found; + +- si->s_mem = ULONG(slab_s_buf + +- OFFSET(slab_s_s_mem)); +- gather_slab_cached_count(si); +- +- si->num_slabs++; +- +- si->slab = ULONG(slab_s_buf + +- OFFSET(slab_s_list)); +- si->slab -= OFFSET(slab_s_list); ++ if (!tc) ++ tc = CURRENT_CONTEXT(); ++ memtype = found = 0; + +- /* +- * Check for slab transition. (Tony Dziedzic) +- */ +- for (i = 0; i < SLAB_CHAINS; i++) { +- if ((i != s) && +- (si->slab == slab_chains[i])) { +- error(NOTE, +- "%s: slab chain inconsistency: %s list\n", +- si->curname, +- slab_chain_name_v1[s]); +- list_borked = 1; +- } +- } +- +- } while (si->slab != slab_chains[s] && !list_borked); +- } ++ if (machdep->is_uvaddr(vaddr, tc)) { ++ memtype |= UVADDR; ++ found++; ++ } + +- FREEBUF(slab_s_buf); +- if (!list_borked) +- save_slab_data(si); +- break; ++ if (machdep->is_kvaddr(vaddr)) { ++ memtype |= KVADDR; ++ found++; ++ } + +- case SLAB_WALKTHROUGH: +- specified_slab = si->slab; +- si->flags |= SLAB_WALKTHROUGH; +- si->flags &= ~SLAB_GET_COUNTS; ++ if (found == 1) ++ return memtype; ++ else ++ return AMBIGUOUS; ++} + +- for (s = 0; s < SLAB_CHAINS; s++) { +- if (!slab_chains[s]) +- continue; ++/* ++ * Determine the first valid user space address ++ */ ++static int ++address_space_start(struct task_context *tc, ulong *addr) ++{ ++ ulong vma; ++ char *vma_buf; + +- if (!specified_slab) { +- if (!readmem(slab_chains[s], +- KVADDR, &si->slab, sizeof(ulong), +- "slabs", QUIET|RETURN_ON_ERROR)) { +- error(INFO, +- "%s: %s list: bad slab pointer: %lx\n", +- si->curname, +- slab_chain_name_v1[s], +- slab_chains[s]); +- list_borked = 1; +- continue; +- } +- last = slab_chains[s]; +- } else +- last = 0; +- +- if (si->slab == slab_chains[s]) +- continue; ++ if (!tc->mm_struct) ++ return FALSE; + +- if (CRASHDEBUG(1)) { +- fprintf(fp, "search cache: [%s] ", si->curname); +- if (si->flags & ADDRESS_SPECIFIED) +- fprintf(fp, "for %llx", si->spec_addr); +- fprintf(fp, "\n"); +- } ++ fill_mm_struct(tc->mm_struct); ++ vma = ULONG(tt->mm_struct + OFFSET(mm_struct_mmap)); ++ if (!vma) ++ return FALSE; ++ vma_buf = fill_vma_cache(vma); ++ *addr = ULONG(vma_buf + OFFSET(vm_area_struct_vm_start)); + +- do { +- if (received_SIGINT()) +- restart(0); ++ return TRUE; ++} + +- if (!verify_slab_v1(si, last, s)) { +- list_borked = 1; +- continue; +- } +- last = si->slab - OFFSET(slab_s_list); +- +- dump_slab_percpu_v1(si); +- +- if (si->found) { +- return; +- } +- +- readmem(si->slab+OFFSET(slab_s_list), +- KVADDR, &si->slab, sizeof(ulong), +- "slab list", FAULT_ON_ERROR); +- +- si->slab -= OFFSET(slab_s_list); +- +- } while (si->slab != slab_chains[s] && !list_borked); +- } ++/* ++ * Search for a given value between a starting and ending address range, ++ * applying an optional mask for "don't care" bits. As an alternative ++ * to entering the starting address value, -k means "start of kernel address ++ * space". For processors with ambiguous user/kernel address spaces, ++ * -u or -k must be used (with or without -s) as a differentiator. ++ */ ++void ++cmd_search(void) ++{ ++ int c; ++ ulong start, end, mask, memtype, len; ++ ulong uvaddr_end; ++ int sflag; ++ struct meminfo meminfo; ++ ulong value_array[MAXARGS]; ++ struct syment *sp; ++ ++ start = end = mask = sflag = memtype = len = 0; ++ uvaddr_end = COMMON_VADDR_SPACE() ? (ulong)(-1) : machdep->kvbase; ++ BZERO(value_array, sizeof(ulong) * MAXARGS); ++ ++ while ((c = getopt(argcnt, args, "l:uks:e:v:m:")) != EOF) { ++ switch(c) ++ { ++ case 'u': ++ if (XEN_HYPER_MODE()) ++ error(FATAL, ++ "-u option is not applicable to the " ++ "Xen hypervisor\n"); + +- break; +- } +-} ++ if (!sflag) { ++ address_space_start(CURRENT_CONTEXT(),&start); ++ sflag++; ++ } ++ memtype = UVADDR; ++ sflag++; ++ break; + +-/* +- * Try to preclude any attempt to translate a bogus slab structure. +- */ ++ case 'k': ++ if (XEN_HYPER_MODE()) ++ error(FATAL, ++ "-k option is not applicable to the " ++ "Xen hypervisor\n"); + +-static int +-verify_slab_v1(struct meminfo *si, ulong last, int s) +-{ +- char slab_s_buf[BUFSIZE]; +- struct kernel_list_head *list_head; +- unsigned int inuse; +- ulong s_mem; +- char *list; +- int errcnt; ++ if (!sflag) { ++ start = machdep->kvbase; ++ if (machine_type("IA64") && ++ (start < machdep->identity_map_base) && ++ (kt->stext > start)) ++ start = kt->stext; ++ sflag++; ++ } ++ memtype = KVADDR; ++ sflag++; ++ break; + +- list = slab_chain_name_v1[s]; ++ case 's': ++ if ((sp = symbol_search(optarg))) ++ start = sp->value; ++ else ++ start = htol(optarg, FAULT_ON_ERROR, NULL); ++ sflag++; ++ break; + +- errcnt = 0; ++ case 'e': ++ if ((sp = symbol_search(optarg))) ++ end = sp->value; ++ else ++ end = htol(optarg, FAULT_ON_ERROR, NULL); ++ break; + +- if (!readmem(si->slab, KVADDR, slab_s_buf, +- SIZE(slab_s), "slab_s buffer", QUIET|RETURN_ON_ERROR)) { +- error(INFO, "%s: %s list: bad slab pointer: %lx\n", +- si->curname, list, si->slab); +- return FALSE; +- } ++ case 'l': ++ len = stol(optarg, FAULT_ON_ERROR, NULL); ++ break; + +- list_head = (struct kernel_list_head *) +- (slab_s_buf + OFFSET(slab_s_list)); ++ case 'm': ++ mask = htol(optarg, FAULT_ON_ERROR, NULL); ++ break; + +- if (!IS_KVADDR((ulong)list_head->next) || +- !accessible((ulong)list_head->next)) { +- error(INFO, "%s: %s list: slab: %lx bad next pointer: %lx\n", +- si->curname, list, si->slab, +- (ulong)list_head->next); +- errcnt++; +- } ++ default: ++ argerrs++; ++ break; ++ } ++ } + +- if (last && (last != (ulong)list_head->prev)) { +- error(INFO, "%s: %s list: slab: %lx bad prev pointer: %lx\n", +- si->curname, list, si->slab, +- (ulong)list_head->prev); +- errcnt++; ++ if (XEN_HYPER_MODE()) { ++ memtype = KVADDR; ++ if (!sflag) ++ error(FATAL, ++ "the \"-s start\" option is required for" ++ " the Xen hypervisor\n"); + } + +- inuse = UINT(slab_s_buf + OFFSET(slab_s_inuse)); +- if (inuse > si->c_num) { +- error(INFO, "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; +- } ++ if (argerrs || !sflag || !args[optind] || (len && end)) ++ cmd_usage(pc->curcmd, SYNOPSIS); + +- if (!last) +- goto no_inuse_check_v1; ++ if (!memtype) ++ memtype = vaddr_type(start, CURRENT_CONTEXT()); + +- switch (s) ++ switch (memtype) + { +- case 0: /* full -- but can be one singular list */ +- if (VALID_MEMBER(kmem_cache_s_slabs_full) && +- (inuse != si->c_num)) { +- error(INFO, +- "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; +- } ++ case UVADDR: ++ if (!IS_UVADDR(start, CURRENT_CONTEXT())) { ++ error(INFO, "invalid user virtual address: %lx\n", ++ start); ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ } + break; + +- case 1: /* partial */ +- if ((inuse == 0) || (inuse == si->c_num)) { +- error(INFO, +- "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; ++ case KVADDR: ++ if (!IS_KVADDR(start)) { ++ error(INFO, "invalid kernel virtual address: %lx\n", ++ start); ++ cmd_usage(pc->curcmd, SYNOPSIS); + } + break; + +- case 2: /* free */ +- if (inuse > 0) { +- error(INFO, +- "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; ++ case AMBIGUOUS: ++ error(INFO, ++ "ambiguous virtual address: %lx (requires -u or -k)\n", ++ start); ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ } ++ ++ if (!end && !len) { ++ switch (memtype) ++ { ++ case UVADDR: ++ end = uvaddr_end; ++ break; ++ ++ case KVADDR: ++ if (XEN_HYPER_MODE()) ++ end = (ulong)(-1); ++ else if (vt->vmalloc_start < machdep->identity_map_base) ++ end = (ulong)(-1); ++ else { ++ meminfo.memtype = KVADDR; ++ meminfo.spec_addr = 0; ++ meminfo.flags = (ADDRESS_SPECIFIED|GET_HIGHEST); ++ dump_vmlist(&meminfo); ++ end = meminfo.retval; ++ if (end < start) ++ end = (ulong)(-1); ++ } ++ break; ++ } ++ } else if (len) ++ end = start + len; ++ ++ switch (memtype) ++ { ++ case UVADDR: ++ if (is_kernel_thread(CURRENT_TASK()) || !task_mm(CURRENT_TASK(), TRUE)) ++ error(FATAL, "current context has no user address space\n"); ++ if (end > uvaddr_end) { ++ error(INFO, ++ "address range starts in user space and ends kernel space\n"); ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ } ++ /* FALLTHROUGH */ ++ case KVADDR: ++ if (end < start) { ++ error(INFO, ++ "ending address %lx is below starting address %lx\n", ++ end, start); ++ cmd_usage(pc->curcmd, SYNOPSIS); + } + break; + } + +-no_inuse_check_v1: +- s_mem = ULONG(slab_s_buf + OFFSET(slab_s_s_mem)); +- if (!IS_KVADDR(s_mem) || !accessible(s_mem)) { +- error(INFO, "%s: %s list: slab: %lx bad s_mem pointer: %lx\n", +- si->curname, list, si->slab, s_mem); +- errcnt++; ++ c = 0; ++ while (args[optind]) { ++ value_array[c] = htol(args[optind], FAULT_ON_ERROR, NULL); ++ c++; ++ optind++; + } + +- return(errcnt ? FALSE : TRUE); ++ search(start, end, mask, memtype, value_array, c); + } + + /* +- * Updated for 2.6 slab substructure. ++ * Do the work for cmd_search(). + */ + +-static char *slab_chain_name_v2[] = {"partial", "full", "free"}; ++#define SEARCHMASK(X) ((X) | mask) + + static void +-do_slab_chain_percpu_v2(long cmd, struct meminfo *si) ++search(ulong start, ulong end, ulong mask, int memtype, ulong *value, int vcnt) + { +- int i, tmp, s; +- int list_borked; +- char *slab_buf; +- ulong specified_slab; +- ulong last; +- ulong slab_chains[SLAB_CHAINS]; +- +- list_borked = 0; +- si->slabsize = (power(2, si->order) * PAGESIZE()); +- si->cpucached_slab = 0; +- +- slab_chains[0] = si->cache + OFFSET(kmem_cache_s_lists) + +- OFFSET(kmem_list3_slabs_partial); +- slab_chains[1] = si->cache + OFFSET(kmem_cache_s_lists) + +- OFFSET(kmem_list3_slabs_full); +- slab_chains[2] = si->cache + OFFSET(kmem_cache_s_lists) + +- OFFSET(kmem_list3_slabs_free); +- +- if (CRASHDEBUG(1)) { +- fprintf(fp, "[ %s: %lx ", si->curname, si->cache); +- fprintf(fp, "partial: %lx full: %lx free: %lx ]\n", +- slab_chains[0], slab_chains[1], slab_chains[2]); +- } +- +- switch (cmd) +- { +- case SLAB_GET_COUNTS: +- si->flags |= SLAB_GET_COUNTS; +- si->flags &= ~SLAB_WALKTHROUGH; +- si->cpucached_cache = 0; +- si->num_slabs = si->inuse = 0; +- gather_cpudata_list_v2(si); +- +- slab_buf = GETBUF(SIZE(slab)); +- +- for (s = 0; s < SLAB_CHAINS; s++) { +- if (!slab_chains[s]) +- continue; +- +- if (!readmem(slab_chains[s], +- KVADDR, &si->slab, sizeof(ulong), +- "first slab", QUIET|RETURN_ON_ERROR)) { +- error(INFO, +- "%s: %s list: bad slab pointer: %lx\n", +- si->curname, +- slab_chain_name_v2[s], +- slab_chains[s]); +- list_borked = 1; +- continue; +- } +- +- if (slab_data_saved(si)) { +- FREEBUF(slab_buf); +- return; +- } +- +- if (si->slab == slab_chains[s]) +- continue; +- +- last = slab_chains[s]; ++ int i, j; ++ ulong pp, next, *ubp; ++ int wordcnt, lastpage; ++ ulong page; ++ physaddr_t paddr; ++ char *pagebuf; + +- do { +- if (received_SIGINT()) { +- FREEBUF(slab_buf); +- restart(0); +- } ++ if (start & (sizeof(long)-1)) { ++ start &= ~(sizeof(long)-1); ++ error(INFO, "rounding down start address to: %lx\n", start); ++ } + +- if (!verify_slab_v2(si, last, s)) { +- list_borked = 1; +- continue; +- } +- last = si->slab - OFFSET(slab_list); +- +- readmem(si->slab, KVADDR, slab_buf, +- SIZE(slab), "slab buffer", +- FAULT_ON_ERROR); +- +- tmp = INT(slab_buf + OFFSET(slab_inuse)); +- si->inuse += tmp; +- +- if (ACTIVE()) +- gather_cpudata_list_v2(si); ++ pagebuf = GETBUF(PAGESIZE()); ++ next = start; + +- si->s_mem = ULONG(slab_buf + +- OFFSET(slab_s_mem)); +- gather_slab_cached_count(si); +- +- si->num_slabs++; +- +- si->slab = ULONG(slab_buf + +- OFFSET(slab_list)); +- si->slab -= OFFSET(slab_list); ++ for (pp = VIRTPAGEBASE(start); next < end; next = pp) { ++ lastpage = (VIRTPAGEBASE(next) == VIRTPAGEBASE(end)); ++ if (LKCD_DUMPFILE()) ++ set_lkcd_nohash(); + +- /* +- * Check for slab transition. (Tony Dziedzic) +- */ +- for (i = 0; i < SLAB_CHAINS; i++) { +- if ((i != s) && +- (si->slab == slab_chains[i])) { +- error(NOTE, +- "%s: slab chain inconsistency: %s list\n", +- si->curname, +- slab_chain_name_v2[s]); +- list_borked = 1; +- } +- } +- +- } while (si->slab != slab_chains[s] && !list_borked); ++ /* ++ * Keep it virtual for Xen hypervisor. ++ */ ++ if (XEN_HYPER_MODE()) { ++ if (!readmem(pp, KVADDR, pagebuf, PAGESIZE(), ++ "search page", RETURN_ON_ERROR|QUIET)) { ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "search suspended at: %lx\n", pp); ++ return; ++ } ++ goto virtual; + } + +- FREEBUF(slab_buf); +- if (!list_borked) +- save_slab_data(si); +- break; ++ switch (memtype) ++ { ++ case UVADDR: ++ if (!uvtop(CURRENT_CONTEXT(), pp, &paddr, 0) || ++ !phys_to_page(paddr, &page)) { ++ if (!next_upage(CURRENT_CONTEXT(), pp, &pp)) ++ return; ++ continue; ++ } ++ break; + +- case SLAB_WALKTHROUGH: +- specified_slab = si->slab; +- si->flags |= SLAB_WALKTHROUGH; +- si->flags &= ~SLAB_GET_COUNTS; ++ case KVADDR: ++ if (!kvtop(CURRENT_CONTEXT(), pp, &paddr, 0) || ++ !phys_to_page(paddr, &page)) { ++ if (!next_kpage(pp, &pp)) ++ return; ++ continue; ++ } ++ break; ++ } + +- for (s = 0; s < SLAB_CHAINS; s++) { +- if (!slab_chains[s]) +- continue; ++ if (!readmem(paddr, PHYSADDR, pagebuf, PAGESIZE(), ++ "search page", RETURN_ON_ERROR|QUIET)) { ++ pp += PAGESIZE(); ++ continue; ++ } ++virtual: ++ ubp = (ulong *)&pagebuf[next - pp]; ++ if (lastpage) { ++ if (end == (ulong)(-1)) ++ wordcnt = PAGESIZE()/sizeof(long); ++ else ++ wordcnt = (end - next)/sizeof(long); ++ } else ++ wordcnt = (PAGESIZE() - (next - pp))/sizeof(long); + +- if (!specified_slab) { +- if (!readmem(slab_chains[s], +- KVADDR, &si->slab, sizeof(ulong), +- "slabs", QUIET|RETURN_ON_ERROR)) { +- error(INFO, +- "%s: %s list: bad slab pointer: %lx\n", +- si->curname, +- slab_chain_name_v2[s], +- slab_chains[s]); +- list_borked = 1; +- continue; +- } +- last = slab_chains[s]; +- } else +- last = 0; +- +- if (si->slab == slab_chains[s]) +- continue; +- +- if (CRASHDEBUG(1)) { +- fprintf(fp, "search cache: [%s] ", si->curname); +- if (si->flags & ADDRESS_SPECIFIED) +- fprintf(fp, "for %llx", si->spec_addr); +- fprintf(fp, "\n"); ++ for (i = 0; i < wordcnt; i++, ubp++, next += sizeof(long)) { ++ for (j = 0; j < vcnt; j++) { ++ if (SEARCHMASK(*ubp) == SEARCHMASK(value[j])) ++ fprintf(fp, "%lx: %lx\n", next, *ubp); + } +- +- do { +- if (received_SIGINT()) +- restart(0); +- +- if (!verify_slab_v2(si, last, s)) { +- list_borked = 1; +- continue; +- } +- last = si->slab - OFFSET(slab_list); +- +- dump_slab_percpu_v2(si); +- +- if (si->found) { +- return; +- } +- +- readmem(si->slab+OFFSET(slab_list), +- KVADDR, &si->slab, sizeof(ulong), +- "slab list", FAULT_ON_ERROR); +- +- si->slab -= OFFSET(slab_list); +- +- } while (si->slab != slab_chains[s] && !list_borked); + } + +- break; ++ if (CRASHDEBUG(1)) ++ if ((pp % (1024*1024)) == 0) ++ console("%lx\n", pp); ++ ++ pp += PAGESIZE(); + } + } + ++ + /* +- * Try to preclude any attempt to translate a bogus slab structure. ++ * Return the next mapped user virtual address page that comes after ++ * the passed-in address. + */ + static int +-verify_slab_v2(struct meminfo *si, ulong last, int s) ++next_upage(struct task_context *tc, ulong vaddr, ulong *nextvaddr) + { +- char slab_buf[BUFSIZE]; +- struct kernel_list_head *list_head; +- unsigned int inuse; +- ulong s_mem; +- char *list; +- int errcnt; ++ ulong vma, total_vm; ++ int found; ++ char *vma_buf; ++ ulong vm_start, vm_end; ++ void *vm_next; + +- list = slab_chain_name_v2[s]; ++ if (!tc->mm_struct) ++ return FALSE; + +- errcnt = 0; ++ fill_mm_struct(tc->mm_struct); ++ vma = ULONG(tt->mm_struct + OFFSET(mm_struct_mmap)); ++ total_vm = ULONG(tt->mm_struct + OFFSET(mm_struct_total_vm)); + +- if (!readmem(si->slab, KVADDR, slab_buf, +- SIZE(slab), "slab buffer", QUIET|RETURN_ON_ERROR)) { +- error(INFO, "%s: %s list: bad slab pointer: %lx\n", +- si->curname, list, si->slab); ++ if (!vma || (total_vm == 0)) + return FALSE; +- } +- +- list_head = (struct kernel_list_head *)(slab_buf + OFFSET(slab_list)); +- if (!IS_KVADDR((ulong)list_head->next) || +- !accessible((ulong)list_head->next)) { +- error(INFO, "%s: %s list: slab: %lx bad next pointer: %lx\n", +- si->curname, list, si->slab, +- (ulong)list_head->next); +- errcnt++; +- } +- +- if (last && (last != (ulong)list_head->prev)) { +- error(INFO, "%s: %s list: slab: %lx bad prev pointer: %lx\n", +- si->curname, list, si->slab, +- (ulong)list_head->prev); +- errcnt++; +- } +- +- inuse = UINT(slab_buf + OFFSET(slab_inuse)); +- if (inuse > si->c_num) { +- error(INFO, "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; +- } +- +- if (!last) +- goto no_inuse_check_v2; + +- switch (s) +- { +- case 0: /* partial */ +- if ((inuse == 0) || (inuse == si->c_num)) { +- error(INFO, +- "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; +- } +- break; ++ vaddr = VIRTPAGEBASE(vaddr) + PAGESIZE(); /* first possible page */ + +- case 1: /* full */ +- if (inuse != si->c_num) { +- error(INFO, +- "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; +- } +- break; ++ for (found = FALSE; vma; vma = (ulong)vm_next) { ++ vma_buf = fill_vma_cache(vma); + +- case 2: /* free */ +- if (inuse > 0) { +- error(INFO, +- "%s: %s list: slab: %lx bad inuse counter: %ld\n", +- si->curname, list, si->slab, inuse); +- errcnt++; +- } +- break; +- } ++ vm_start = ULONG(vma_buf + OFFSET(vm_area_struct_vm_start)); ++ vm_end = ULONG(vma_buf + OFFSET(vm_area_struct_vm_end)); ++ vm_next = VOID_PTR(vma_buf + OFFSET(vm_area_struct_vm_next)); + +-no_inuse_check_v2: +- s_mem = ULONG(slab_buf + OFFSET(slab_s_mem)); +- if (!IS_KVADDR(s_mem) || !accessible(s_mem)) { +- error(INFO, "%s: %s list: slab: %lx bad s_mem pointer: %lx\n", +- si->curname, list, si->slab, s_mem); +- errcnt++; ++ if (vaddr <= vm_start) { ++ *nextvaddr = vm_start; ++ return TRUE; ++ } ++ ++ if ((vaddr > vm_start) && (vaddr < vm_end)) { ++ *nextvaddr = vaddr; ++ return TRUE; ++ } + } + +- return(errcnt ? FALSE : TRUE); ++ return FALSE; + } + + /* +- * If it's a dumpfile, save the essential slab data to avoid re-reading +- * the whole slab chain more than once. This may seem like overkill, but +- * if the problem is a memory leak, or just the over-use of the buffer_head +- * cache, it's painful to wait each time subsequent kmem -s or -i commands +- * simply need the basic slab counts. ++ * Return the next mapped kernel virtual address in the vmlist ++ * that is equal to or comes after the passed-in address. + */ +-struct slab_data { +- ulong cache_addr; +- int num_slabs; +- int inuse; +- ulong cpucached_cache; +-}; +- +-#define NO_SLAB_DATA ((void *)(-1)) +- +-static void +-save_slab_data(struct meminfo *si) ++static ulong ++next_vmlist_vaddr(ulong vaddr) + { +- int i; ++ ulong i, count; ++ struct meminfo meminfo, *mi; + +- if (ACTIVE()) +- return; ++ mi = &meminfo; ++ BZERO(mi, sizeof(struct meminfo)); + +- if (vt->slab_data == NO_SLAB_DATA) +- return; ++ mi->flags = GET_VMLIST_COUNT; ++ dump_vmlist(mi); ++ count = mi->retval; + +- if (!vt->slab_data) { +- if (!(vt->slab_data = (struct slab_data *) +- malloc(sizeof(struct slab_data) * vt->kmem_cache_count))) { +- error(INFO, "cannot malloc slab_data table"); +- vt->slab_data = NO_SLAB_DATA; +- return; +- } +- for (i = 0; i < vt->kmem_cache_count; i++) { +- vt->slab_data[i].cache_addr = (ulong)NO_SLAB_DATA; +- vt->slab_data[i].num_slabs = 0; +- vt->slab_data[i].inuse = 0; +- vt->slab_data[i].cpucached_cache = 0; +- } +- } ++ if (!count) ++ return vaddr; + +- for (i = 0; i < vt->kmem_cache_count; i++) { +- if (vt->slab_data[i].cache_addr == si->cache) +- break; ++ mi->vmlist = (struct vmlist *)GETBUF(sizeof(struct vmlist)*count); ++ mi->flags = GET_VMLIST; ++ dump_vmlist(mi); + +- if (vt->slab_data[i].cache_addr == (ulong)NO_SLAB_DATA) { +- vt->slab_data[i].cache_addr = si->cache; +- vt->slab_data[i].num_slabs = si->num_slabs; +- vt->slab_data[i].inuse = si->inuse; +- vt->slab_data[i].cpucached_cache = si->cpucached_cache; ++ for (i = 0; i < count; i++) { ++ if (vaddr <= mi->vmlist[i].addr) { ++ vaddr = mi->vmlist[i].addr; + break; + } ++ if (vaddr < (mi->vmlist[i].addr + mi->vmlist[i].size)) ++ break; + } ++ ++ FREEBUF(mi->vmlist); ++ ++ return vaddr; + } + +-static int +-slab_data_saved(struct meminfo *si) ++ ++/* ++ * Return the next kernel virtual address page that comes after ++ * the passed-in, untranslatable, address. ++ */ ++static int ++next_kpage(ulong vaddr, ulong *nextvaddr) + { +- int i; ++ ulong vaddr_orig; + +- if (ACTIVE() || !vt->slab_data || (vt->slab_data == NO_SLAB_DATA)) +- return FALSE; ++ vaddr_orig = vaddr; ++ vaddr = VIRTPAGEBASE(vaddr) + PAGESIZE(); /* first possible page */ + +- for (i = 0; i < vt->kmem_cache_count; i++) { +- if (vt->slab_data[i].cache_addr == si->cache) { +- si->inuse = vt->slab_data[i].inuse; +- si->num_slabs = vt->slab_data[i].num_slabs; +- si->cpucached_cache = vt->slab_data[i].cpucached_cache; ++ if (vaddr < vaddr_orig) /* wrapped back to zero? */ ++ return FALSE; ++ ++ if (IS_VMALLOC_ADDR(vaddr_orig) || ++ (machine_type("IA64") && IS_VMALLOC_ADDR(vaddr))) { ++ ++ if (IS_VMALLOC_ADDR(vaddr) && ++ (vaddr < last_vmalloc_address())) { ++ if (machine_type("X86_64")) ++ vaddr = next_vmlist_vaddr(vaddr); ++ *nextvaddr = vaddr; ++ return TRUE; ++ } ++ ++ if (vt->vmalloc_start < machdep->identity_map_base) { ++ *nextvaddr = machdep->identity_map_base; + return TRUE; + } ++ ++ return FALSE; + } + +- return FALSE; ++ if (next_identity_mapping(vaddr, nextvaddr)) ++ return TRUE; ++ ++ if (vt->vmalloc_start > vaddr) { ++ *nextvaddr = vt->vmalloc_start; ++ return TRUE; ++ } else ++ return FALSE; + } + +-static void +-dump_saved_slab_data(void) ++/* ++ * Display swap statistics. ++ */ ++void ++cmd_swap(void) + { +- int i; ++ int c; + +- if (!vt->slab_data || (vt->slab_data == NO_SLAB_DATA)) +- return; ++ while ((c = getopt(argcnt, args, "")) != EOF) { ++ switch(c) ++ { ++ default: ++ argerrs++; ++ break; ++ } ++ } + +- for (i = 0; i < vt->kmem_cache_count; i++) { +- if (vt->slab_data[i].cache_addr == (ulong)NO_SLAB_DATA) +- break; ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); + +- fprintf(fp, +- " cache: %lx inuse: %5d num_slabs: %3d cpucached_cache: %ld\n", +- vt->slab_data[i].cache_addr, +- vt->slab_data[i].inuse, +- vt->slab_data[i].num_slabs, +- vt->slab_data[i].cpucached_cache); +- } ++ dump_swap_info(VERBOSE, NULL, NULL); + } + + /* +- * Dump the contents of a kmem slab. ++ * Do the work for cmd_swap(). + */ + +-static void +-dump_slab(struct meminfo *si) ++#define SWP_USED 1 ++#define SWAP_MAP_BAD 0x8000 ++ ++char *swap_info_hdr = \ ++"FILENAME TYPE SIZE USED PCT PRIORITY\n"; ++ ++static int ++dump_swap_info(ulong swapflags, ulong *totalswap_pages, ulong *totalused_pages) + { +- uint16_t s_offset; ++ int i, j; ++ int flags, swap_device, pages, prio, usedswap; ++ ulong swap_file, max, swap_map, pct; ++ ulong vfsmnt; ++ ulong swap_info; ++ ushort *map; ++ ulong totalswap, totalused; ++ char buf[BUFSIZE]; + +- si->s_mem = ULONG(si->slab_buf + OFFSET(kmem_slab_s_s_mem)); +- si->s_mem = PTOB(BTOP(si->s_mem)); ++ if (!symbol_exists("nr_swapfiles")) ++ error(FATAL, "nr_swapfiles doesn't exist in this kernel!\n"); + +- if (si->flags & ADDRESS_SPECIFIED) { +- if (INSLAB(si->slab, si) && (si->spec_addr >= si->slab) && +- (si->spec_addr < (si->slab+SIZE(kmem_slab_s)))){ +- si->found = KMEM_SLAB_ADDR; +- return; +- } +- if (INSLAB(si->spec_addr, si)) +- si->found = KMEM_ON_SLAB; /* But don't return yet... */ +- else +- return; +- } ++ if (!symbol_exists("swap_info")) ++ error(FATAL, "swap_info doesn't exist in this kernel!\n"); + +- si->s_freep = VOID_PTR(si->slab_buf + OFFSET(kmem_slab_s_s_freep)); +- si->s_inuse = ULONG(si->slab_buf + OFFSET(kmem_slab_s_s_inuse)); +- si->s_index = ULONG_PTR(si->slab_buf + OFFSET(kmem_slab_s_s_index)); +- s_offset = USHORT(si->slab_buf + OFFSET(kmem_slab_s_s_offset)); ++ swap_info = symbol_value("swap_info"); + +- if (!(si->flags & ADDRESS_SPECIFIED)) { +- fprintf(fp, slab_hdr); +- DUMP_SLAB_INFO(); +- } ++ if (swapflags & VERBOSE) ++ fprintf(fp, swap_info_hdr); + +- dump_slab_objects(si); +-} ++ totalswap = totalused = 0; + +-/* +- * dump_slab() adapted for newer percpu slab format. +- */ ++ for (i = 0; i < vt->nr_swapfiles; i++, ++ swap_info += SIZE(swap_info_struct)) { ++ fill_swap_info(swap_info); + +-static void +-dump_slab_percpu_v1(struct meminfo *si) +-{ +- int tmp; ++ flags = INT(vt->swap_info_struct + ++ OFFSET(swap_info_struct_flags)); + +- readmem(si->slab+OFFSET(slab_s_s_mem), +- KVADDR, &si->s_mem, sizeof(ulong), +- "s_mem", FAULT_ON_ERROR); ++ if (!(flags & SWP_USED)) ++ continue; + +- /* +- * Include the array of kmem_bufctl_t's appended to slab. +- */ +- tmp = SIZE(slab_s) + (SIZE(kmem_bufctl_t) * si->c_num); ++ swap_file = ULONG(vt->swap_info_struct + ++ OFFSET(swap_info_struct_swap_file)); + +- if (si->flags & ADDRESS_SPECIFIED) { +- if (INSLAB_PERCPU(si->slab, si) && +- (si->spec_addr >= si->slab) && +- (si->spec_addr < (si->slab+tmp))) { +- if (si->spec_addr >= (si->slab + SIZE(slab_s))) +- si->found = KMEM_BUFCTL_ADDR; +- else +- si->found = KMEM_SLAB_ADDR; +- } else if (INSLAB_PERCPU(si->spec_addr, si)) +- si->found = KMEM_ON_SLAB; /* But don't return yet... */ ++ swap_device = INT(vt->swap_info_struct + ++ OFFSET_OPTION(swap_info_struct_swap_device, ++ swap_info_struct_old_block_size)); ++ ++ pages = INT(vt->swap_info_struct + ++ OFFSET(swap_info_struct_pages)); ++ ++ totalswap += pages; ++ pages <<= (PAGESHIFT() - 10); ++ ++ prio = INT(vt->swap_info_struct + ++ OFFSET(swap_info_struct_prio)); ++ ++ if (MEMBER_SIZE("swap_info_struct", "max") == sizeof(int)) ++ max = UINT(vt->swap_info_struct + ++ OFFSET(swap_info_struct_max)); + else +- return; +- } ++ max = ULONG(vt->swap_info_struct + ++ OFFSET(swap_info_struct_max)); ++ ++ swap_map = ULONG(vt->swap_info_struct + ++ OFFSET(swap_info_struct_swap_map)); ++ ++ if (swap_file) { ++ if (VALID_MEMBER(swap_info_struct_swap_vfsmnt)) { ++ vfsmnt = ULONG(vt->swap_info_struct + ++ OFFSET(swap_info_struct_swap_vfsmnt)); ++ get_pathname(swap_file, buf, BUFSIZE, ++ 1, vfsmnt); ++ } else if (VALID_MEMBER ++ (swap_info_struct_old_block_size)) { ++ get_pathname(file_to_dentry(swap_file), ++ buf, BUFSIZE, 1, file_to_vfsmnt(swap_file)); ++ } else { ++ get_pathname(swap_file, buf, BUFSIZE, 1, 0); ++ } ++ } else ++ sprintf(buf, "(unknown)"); ++ ++ map = (ushort *)GETBUF(sizeof(ushort) * max); ++ ++ if (!readmem(swap_map, KVADDR, map, ++ sizeof(ushort) * max, "swap_info swap_map data", ++ RETURN_ON_ERROR|QUIET)) { ++ if (swapflags & RETURN_ON_ERROR) { ++ *totalswap_pages = swap_map; ++ *totalused_pages = i; ++ return FALSE; ++ } else ++ error(FATAL, ++ "swap_info[%d].swap_map at %lx is unaccessible\n", ++ i, swap_map); ++ } + +- readmem(si->slab+OFFSET(slab_s_inuse), +- KVADDR, &tmp, sizeof(int), +- "inuse", FAULT_ON_ERROR); +- si->s_inuse = tmp; ++ usedswap = 0; ++ for (j = 0; j < max; j++) { ++ switch (map[j]) ++ { ++ case SWAP_MAP_BAD: ++ case 0: ++ continue; ++ default: ++ usedswap++; ++ } ++ } + +- readmem(si->slab+OFFSET(slab_s_free), +- KVADDR, &si->free, SIZE(kmem_bufctl_t), +- "kmem_bufctl_t", FAULT_ON_ERROR); ++ FREEBUF(map); + +- gather_slab_free_list_percpu(si); +- gather_slab_cached_count(si); ++ totalused += usedswap; ++ usedswap <<= (PAGESHIFT() - 10); ++ pct = (usedswap * 100)/pages; + +- if (!(si->flags & ADDRESS_SPECIFIED)) { +- fprintf(fp, slab_hdr); +- DUMP_SLAB_INFO(); ++ if (swapflags & VERBOSE) ++ fprintf(fp, "%-15s %s %7dk %7dk %2ld%% %d\n", ++ buf, swap_device ? "PARTITION" : " FILE ", ++ pages, usedswap, pct, prio); + } + +- dump_slab_objects_percpu(si); +-} ++ if (totalswap_pages) ++ *totalswap_pages = totalswap; ++ if (totalused_pages) ++ *totalused_pages = totalused; + ++ return TRUE; ++} + + /* +- * Updated for 2.6 slab substructure. ++ * Translate a PTE into a swap device and offset string. + */ +-static void +-dump_slab_percpu_v2(struct meminfo *si) ++char * ++swap_location(ulonglong pte, char *buf) + { +- int tmp; +- +- readmem(si->slab+OFFSET(slab_s_mem), +- KVADDR, &si->s_mem, sizeof(ulong), +- "s_mem", FAULT_ON_ERROR); +- +- /* +- * Include the array of kmem_bufctl_t's appended to slab. +- */ +- tmp = SIZE(slab) + (SIZE(kmem_bufctl_t) * si->c_num); +- +- if (si->flags & ADDRESS_SPECIFIED) { +- if (INSLAB_PERCPU(si->slab, si) && +- (si->spec_addr >= si->slab) && +- (si->spec_addr < (si->slab+tmp))) { +- if (si->spec_addr >= (si->slab + SIZE(slab))) +- si->found = KMEM_BUFCTL_ADDR; +- else +- si->found = KMEM_SLAB_ADDR; +- } else if (INSLAB_PERCPU(si->spec_addr, si)) +- si->found = KMEM_ON_SLAB; /* But don't return yet... */ +- else +- return; +- } +- +- readmem(si->slab+OFFSET(slab_inuse), +- KVADDR, &tmp, sizeof(int), +- "inuse", FAULT_ON_ERROR); +- si->s_inuse = tmp; +- +- readmem(si->slab+OFFSET(slab_free), +- KVADDR, &si->free, SIZE(kmem_bufctl_t), +- "kmem_bufctl_t", FAULT_ON_ERROR); ++ char swapdev[BUFSIZE]; + +- gather_slab_free_list_percpu(si); +- gather_slab_cached_count(si); ++ if (!pte) ++ return NULL; + +- if (!(si->flags & ADDRESS_SPECIFIED)) { +- fprintf(fp, slab_hdr); +- DUMP_SLAB_INFO(); +- } ++ if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) ++ sprintf(buf, "%s OFFSET: %lld", ++ get_swapdev(__swp_type(pte), swapdev), __swp_offset(pte)); ++ else ++ sprintf(buf, "%s OFFSET: %llx", ++ get_swapdev(SWP_TYPE(pte), swapdev), SWP_OFFSET(pte)); + +- dump_slab_objects_percpu(si); ++ return buf; + } + +- +- + /* +- * Gather the free objects in a slab into the si->addrlist, checking for +- * specified addresses that are in-slab kmem_bufctls, and making error checks +- * along the way. Object address checks are deferred to dump_slab_objects(). ++ * Given the type field from a PTE, return the name of the swap device. + */ +- +-#define INOBJECT(addr, obj) ((addr >= obj) && (addr < (obj+si->size))) +- +-static void +-gather_slab_free_list(struct meminfo *si) ++static char * ++get_swapdev(ulong type, char *buf) + { +- ulong *next, obj; +- ulong expected, cnt; +- +- BNEG(si->addrlist, sizeof(ulong) * (si->c_num+1)); ++ unsigned int i, swap_info_len; ++ ulong swap_info, swap_file; ++ ulong vfsmnt; + +- if (!si->s_freep) +- return; ++ if (!symbol_exists("nr_swapfiles")) ++ error(FATAL, "nr_swapfiles doesn't exist in this kernel!\n"); + +- cnt = 0; +- expected = si->c_num - si->s_inuse; ++ if (!symbol_exists("swap_info")) ++ error(FATAL, "swap_info doesn't exist in this kernel!\n"); + +- next = si->s_freep; +- do { ++ swap_info = symbol_value("swap_info"); + +- if (cnt == si->c_num) { +- error(INFO, +- "\"%s\" cache: too many objects found in slab free list\n", +- si->curname); +- si->errors++; +- return; +- } ++ swap_info_len = (i = ARRAY_LENGTH(swap_info)) ? ++ i : get_array_length("swap_info", NULL, 0); + +- /* +- * Off-slab kmem_bufctls are contained in arrays of object +- * pointers that point to: +- * 1. next kmem_bufctl (or NULL) if the object is free. +- * 2. to the object if it the object is in use. +- * +- * On-slab kmem_bufctls resides just after the object itself, +- * and point to: +- * 1. next kmem_bufctl (or NULL) if object is free. +- * 2. the containing slab if the object is in use. +- */ ++ sprintf(buf, "(unknown swap location)"); + +- if (si->c_flags & SLAB_CFLGS_BUFCTL) +- obj = si->s_mem + ((next - si->s_index) * si->c_offset); +- else +- obj = (ulong)next - si->c_offset; ++ if (type >= swap_info_len) ++ return buf; + +- si->addrlist[cnt] = obj; ++ swap_info += (SIZE(swap_info_struct) * type); ++ fill_swap_info(swap_info); ++ swap_file = ULONG(vt->swap_info_struct + ++ OFFSET(swap_info_struct_swap_file)); + +- if (si->flags & ADDRESS_SPECIFIED) { +- if (INSLAB(next, si) && +- (si->spec_addr >= (ulong)next) && +- (si->spec_addr < (ulong)(next + 1))) { +- si->found = KMEM_BUFCTL_ADDR; +- return; +- } ++ if (swap_file) { ++ if (VALID_MEMBER(swap_info_struct_swap_vfsmnt)) { ++ vfsmnt = ULONG(vt->swap_info_struct + ++ OFFSET(swap_info_struct_swap_vfsmnt)); ++ get_pathname(swap_file, buf, BUFSIZE, 1, vfsmnt); ++ } else if (VALID_MEMBER (swap_info_struct_old_block_size)) { ++ get_pathname(file_to_dentry(swap_file), ++ buf, BUFSIZE, 1, 0); ++ } else { ++ get_pathname(swap_file, buf, BUFSIZE, 1, 0); + } ++ } + +- cnt++; ++ return buf; ++} + +- if (!INSLAB(obj, si)) { +- error(INFO, +- "\"%s\" cache: address not contained within slab: %lx\n", +- si->curname, obj); +- si->errors++; +- } ++/* ++ * If not currently stashed, cache the passed-in swap_info_struct. ++ */ ++static void ++fill_swap_info(ulong swap_info) ++{ ++ if (vt->last_swap_read == swap_info) ++ return; + +- readmem((ulong)next, KVADDR, &next, sizeof(void *), +- "s_freep chain entry", FAULT_ON_ERROR); +- } while (next); ++ if (!vt->swap_info_struct && !(vt->swap_info_struct = (char *) ++ malloc(SIZE(swap_info_struct)))) ++ error(FATAL, "cannot malloc swap_info_struct space\n"); ++ ++ readmem(swap_info, KVADDR, vt->swap_info_struct, SIZE(swap_info_struct), ++ "fill_swap_info", FAULT_ON_ERROR); + +- if (cnt != expected) { +- error(INFO, +- "\"%s\" cache: free object mismatch: expected: %ld found: %ld\n", +- si->curname, expected, cnt); +- si->errors++; +- } ++ vt->last_swap_read = swap_info; + } + +- + /* +- * gather_slab_free_list() adapted for newer percpu slab format. ++ * If active, clear references to the swap_info references. + */ ++void ++clear_swap_info_cache(void) ++{ ++ if (ACTIVE()) ++ vt->last_swap_read = 0; ++} + +-#define BUFCTL_END 0xffffFFFF + +-static void +-gather_slab_free_list_percpu(struct meminfo *si) ++/* ++ * Translage a vm_area_struct and virtual address into a filename ++ * and offset string. ++ */ ++ ++#define PAGE_CACHE_SHIFT (machdep->pageshift) /* This is supposed to change! */ ++ ++static char * ++vma_file_offset(ulong vma, ulong vaddr, char *buf) + { +- int i; +- ulong obj; +- ulong expected, cnt; +- int free_index; +- ulong kmembp; +- short *kbp; ++ ulong vm_file, vm_start, vm_offset, vm_pgoff, dentry, offset; ++ ulong vfsmnt; ++ char file[BUFSIZE]; ++ char *vma_buf, *file_buf; + +- BNEG(si->addrlist, sizeof(ulong) * (si->c_num+1)); ++ if (!vma) ++ return NULL; + +- if (CRASHDEBUG(1)) +- fprintf(fp, "slab: %lx si->s_inuse: %ld si->c_num: %ld\n", +- si->slab, si->s_inuse, si->c_num); ++ vma_buf = fill_vma_cache(vma); ++ ++ vm_file = ULONG(vma_buf + OFFSET(vm_area_struct_vm_file)); ++ ++ if (!vm_file) ++ goto no_file_offset; ++ ++ file_buf = fill_file_cache(vm_file); ++ dentry = ULONG(file_buf + OFFSET(file_f_dentry)); ++ ++ if (!dentry) ++ goto no_file_offset; + +- if (si->s_inuse == si->c_num ) +- return; ++ file[0] = NULLCHAR; ++ if (VALID_MEMBER(file_f_vfsmnt)) { ++ vfsmnt = ULONG(file_buf + OFFSET(file_f_vfsmnt)); ++ get_pathname(dentry, file, BUFSIZE, 1, vfsmnt); ++ } else ++ get_pathname(dentry, file, BUFSIZE, 1, 0); + +- kmembp = si->slab + SIZE_OPTION(slab_s, slab); +- readmem((ulong)kmembp, KVADDR, si->kmem_bufctl, +- SIZE(kmem_bufctl_t) * si->c_num, +- "kmem_bufctl array", FAULT_ON_ERROR); ++ if (!strlen(file)) ++ goto no_file_offset; + +- if (CRASHDEBUG(1)) { +- for (i = 0; (SIZE(kmem_bufctl_t) == sizeof(int)) && +- (i < si->c_num); i++) +- fprintf(fp, "%d ", si->kmem_bufctl[i]); ++ vm_start = ULONG(vma_buf + OFFSET(vm_area_struct_vm_start)); + +- for (kbp = (short *)&si->kmem_bufctl[0], i = 0; +- (SIZE(kmem_bufctl_t) == sizeof(short)) && (i < si->c_num); +- i++) +- fprintf(fp, "%d ", *(kbp + i)); ++ vm_offset = vm_pgoff = 0xdeadbeef; + +- fprintf(fp, "\n"); ++ if (VALID_MEMBER(vm_area_struct_vm_offset)) ++ vm_offset = ULONG(vma_buf + ++ OFFSET(vm_area_struct_vm_offset)); ++ else if (VALID_MEMBER(vm_area_struct_vm_pgoff)) ++ vm_pgoff = ULONG(vma_buf + ++ OFFSET(vm_area_struct_vm_pgoff)); ++ else ++ goto no_file_offset; ++ ++ if (vm_offset != 0xdeadbeef) ++ offset = VIRTPAGEBASE(vaddr) - vm_start + vm_offset; ++ else if (vm_pgoff != 0xdeadbeef) { ++ offset = ((vaddr - vm_start) >> PAGE_CACHE_SHIFT) + vm_pgoff; ++ offset <<= PAGE_CACHE_SHIFT; + } + +- cnt = 0; +- expected = si->c_num - si->s_inuse; ++ sprintf(buf, "%s OFFSET: %lx", file, offset); + +- if (SIZE(kmem_bufctl_t) == sizeof(int)) { +- for (free_index = si->free; free_index != BUFCTL_END; +- free_index = si->kmem_bufctl[free_index]) { +- +- if (cnt == si->c_num) { +- error(INFO, +- "\"%s\" cache: too many objects found in slab free list\n", +- si->curname); +- si->errors++; +- return; +- } +- +- obj = si->s_mem + (free_index*si->size); +- si->addrlist[cnt] = obj; +- cnt++; +- } +- } else if (SIZE(kmem_bufctl_t) == sizeof(short)) { +- kbp = (short *)&si->kmem_bufctl[0]; ++ return buf; + +- for (free_index = si->free; free_index != BUFCTL_END; +- free_index = (int)*(kbp + free_index)) { ++no_file_offset: ++ return NULL; ++} + +- if (cnt == si->c_num) { +- error(INFO, +- "\"%s\" cache: too many objects found in slab free list\n", si->curname); +- si->errors++; +- return; +- } ++/* ++ * Translate a PTE into its physical address and flags. ++ */ ++void ++cmd_pte(void) ++{ ++ int c; ++ ulonglong pte; + +- obj = si->s_mem + (free_index*si->size); +- si->addrlist[cnt] = obj; +- cnt++; ++ while ((c = getopt(argcnt, args, "")) != EOF) { ++ switch(c) ++ { ++ default: ++ argerrs++; ++ break; + } +- } else +- error(FATAL, +- "size of kmem_bufctl_t (%d) not sizeof(int) or sizeof(short)\n", +- SIZE(kmem_bufctl_t)); ++ } + +- if (cnt != expected) { +- error(INFO, +- "\"%s\" cache: free object mismatch: expected: %ld found: %ld\n", +- si->curname, expected, cnt); +- si->errors++; ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ while (args[optind]) { ++ pte = htoll(args[optind], FAULT_ON_ERROR, NULL); ++ machdep->translate_pte((ulong)pte, NULL, pte); ++ optind++; + } +-} + ++} + ++static char *node_zone_hdr = "ZONE NAME SIZE"; + + /* +- * Dump the FREE, [ALLOCATED] and objects of a slab. +- */ +- +-#define DUMP_SLAB_OBJECT() \ +- for (j = on_free_list = 0; j < si->c_num; j++) { \ +- if (obj == si->addrlist[j]) { \ +- on_free_list = TRUE; \ +- break; \ +- } \ +- } \ +- \ +- if (on_free_list) { \ +- if (!(si->flags & ADDRESS_SPECIFIED)) \ +- fprintf(fp, " %lx\n", obj); \ +- if (si->flags & ADDRESS_SPECIFIED) { \ +- if (INOBJECT(si->spec_addr, obj)) { \ +- si->found = \ +- KMEM_OBJECT_ADDR_FREE; \ +- return; \ +- } \ +- } \ +- } else { \ +- if (!(si->flags & ADDRESS_SPECIFIED)) \ +- fprintf(fp, " [%lx]\n", obj); \ +- cnt++; \ +- if (si->flags & ADDRESS_SPECIFIED) { \ +- if (INOBJECT(si->spec_addr, obj)) { \ +- si->found = \ +- KMEM_OBJECT_ADDR_INUSE; \ +- return; \ +- } \ +- } \ +- } +- ++ * On systems supporting memory nodes, display the basic per-node data. ++ */ + static void +-dump_slab_objects(struct meminfo *si) ++dump_memory_nodes(int initialize) + { + int i, j; +- ulong *next; +- int on_free_list; +- ulong cnt, expected; +- ulong bufctl, obj; +- +- gather_slab_free_list(si); +- +- if ((si->flags & ADDRESS_SPECIFIED) && (si->found & ~KMEM_ON_SLAB)) +- return; +- +- cnt = 0; +- expected = si->s_inuse; ++ int n, id, node, flen, slen, badaddr; ++ ulong node_mem_map; ++ ulong node_start_paddr; ++ ulong node_start_pfn; ++ ulong node_start_mapnr; ++ ulong node_spanned_pages, node_present_pages; ++ ulong free_pages, zone_size, node_size, cum_zone_size; ++ ulong zone_start_paddr, zone_start_mapnr, zone_mem_map; ++ physaddr_t phys; ++ ulong pp; ++ ulong zone_start_pfn; ++ ulong bdata; ++ ulong pgdat; ++ ulong node_zones; ++ ulong value; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; ++ char buf4[BUFSIZE]; ++ char buf5[BUFSIZE]; ++ struct node_table *nt; + +- if (CRASHDEBUG(1)) +- for (i = 0; i < si->c_num; i++) { +- fprintf(fp, "si->addrlist[%d]: %lx\n", +- i, si->addrlist[i]); ++ if (!(vt->flags & (NODES|NODES_ONLINE)) && initialize) { ++ nt = &vt->node_table[0]; ++ nt->node_id = 0; ++ if (symbol_exists("contig_page_data")) ++ nt->pgdat = symbol_value("contig_page_data"); ++ else ++ nt->pgdat = 0; ++ nt->size = vt->total_pages; ++ nt->mem_map = vt->mem_map; ++ nt->start_paddr = 0; ++ nt->start_mapnr = 0; ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "node_table[%d]: \n", 0); ++ fprintf(fp, " id: %d\n", nt->node_id); ++ fprintf(fp, " pgdat: %lx\n", nt->pgdat); ++ fprintf(fp, " size: %ld\n", nt->size); ++ fprintf(fp, " present: %ld\n", nt->present); ++ fprintf(fp, " mem_map: %lx\n", nt->mem_map); ++ fprintf(fp, " start_paddr: %llx\n", nt->start_paddr); ++ fprintf(fp, " start_mapnr: %ld\n", nt->start_mapnr); + } ++ return; ++ } + +- if (!(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, free_inuse_hdr); +- +- /* For on-slab bufctls, c_offset is the distance between the start of +- * an obj and its related bufctl. For off-slab bufctls, c_offset is +- * the distance between objs in the slab. +- */ +- +- if (si->c_flags & SLAB_CFLGS_BUFCTL) { +- for (i = 0, next = si->s_index; i < si->c_num; i++, next++){ +- obj = si->s_mem + +- ((next - si->s_index) * si->c_offset); +- DUMP_SLAB_OBJECT(); +- } +- } else { ++ if (initialize) { ++ pgdat = UNINITIALIZED; + /* +- * Get the "real" s_mem, i.e., without the offset stripped off. +- * It contains the address of the first object. ++ * This order may have to change based upon architecture... + */ +- readmem(si->slab+OFFSET(kmem_slab_s_s_mem), +- KVADDR, &obj, sizeof(ulong), +- "s_mem", FAULT_ON_ERROR); ++ if (symbol_exists("pgdat_list") && ++ (VALID_MEMBER(pglist_data_node_next) || ++ VALID_MEMBER(pglist_data_pgdat_next))) { ++ get_symbol_data("pgdat_list", sizeof(void *), &pgdat); ++ vt->flags &= ~NODES_ONLINE; ++ } else if (vt->flags & NODES_ONLINE) { ++ if ((node = next_online_node(0)) < 0) { ++ error(WARNING, ++ "cannot determine first node from node_online_map\n\n"); ++ return; ++ } ++ if (!(pgdat = next_online_pgdat(node))) { ++ error(WARNING, ++ "cannot determine pgdat list for this kernel/architecture\n\n"); ++ return; ++ } ++ } ++ } else ++ pgdat = vt->node_table[0].pgdat; + +- for (i = 0; i < si->c_num; i++) { +- DUMP_SLAB_OBJECT(); ++ if (initialize && (pgdat == UNINITIALIZED)) { ++ error(WARNING, "cannot initialize pgdat list\n\n"); ++ return; ++ } + +- if (si->flags & ADDRESS_SPECIFIED) { +- bufctl = obj + si->c_offset; ++ for (n = 0, badaddr = FALSE; pgdat; n++) { ++ if (n >= vt->numnodes) ++ error(FATAL, "numnodes out of sync with pgdat_list?\n"); + +- if ((si->spec_addr >= bufctl) && +- (si->spec_addr < +- (bufctl + SIZE(kmem_bufctl_t)))) { +- si->found = KMEM_BUFCTL_ADDR; +- return; +- } +- } ++ nt = &vt->node_table[n]; + +- obj += (si->c_offset + SIZE(kmem_bufctl_t)); ++ readmem(pgdat+OFFSET(pglist_data_node_id), KVADDR, &id, ++ sizeof(int), "pglist node_id", FAULT_ON_ERROR); ++ ++ if (VALID_MEMBER(pglist_data_node_mem_map)) { ++ readmem(pgdat+OFFSET(pglist_data_node_mem_map), KVADDR, ++ &node_mem_map, sizeof(ulong), ++ "node_mem_map", FAULT_ON_ERROR); ++ } else { ++ node_mem_map = BADADDR; ++ badaddr = TRUE; + } +- } + +- if (cnt != expected) { +- error(INFO, +- "\"%s\" cache: inuse object mismatch: expected: %ld found: %ld\n", +- si->curname, expected, cnt); +- si->errors++; +- } ++ if (VALID_MEMBER(pglist_data_node_start_paddr)) ++ readmem(pgdat+OFFSET(pglist_data_node_start_paddr), ++ KVADDR, &node_start_paddr, sizeof(ulong), ++ "pglist node_start_paddr", FAULT_ON_ERROR); ++ else if (VALID_MEMBER(pglist_data_node_start_pfn)) { ++ readmem(pgdat+OFFSET(pglist_data_node_start_pfn), ++ KVADDR, &node_start_pfn, sizeof(ulong), ++ "pglist node_start_pfn", FAULT_ON_ERROR); ++ node_start_mapnr = node_start_pfn; ++ node_start_paddr = PTOB(node_start_pfn); ++ if (badaddr && IS_SPARSEMEM()) { ++ phys = PTOB(node_start_pfn); ++ if (phys_to_page(phys, &pp)) ++ node_mem_map = pp; ++ } ++ } else error(INFO, ++ "cannot determine zone starting physical address\n"); + +-} ++ if (VALID_MEMBER(pglist_data_node_start_mapnr)) ++ readmem(pgdat+OFFSET(pglist_data_node_start_mapnr), ++ KVADDR, &node_start_mapnr, sizeof(ulong), ++ "pglist node_start_mapnr", FAULT_ON_ERROR); + ++ if (VALID_MEMBER(pglist_data_node_size)) ++ readmem(pgdat+OFFSET(pglist_data_node_size), ++ KVADDR, &node_size, sizeof(ulong), ++ "pglist node_size", FAULT_ON_ERROR); ++ else if (VALID_MEMBER(pglist_data_node_spanned_pages)) { ++ readmem(pgdat+OFFSET(pglist_data_node_spanned_pages), ++ KVADDR, &node_spanned_pages, sizeof(ulong), ++ "pglist node_spanned_pages", FAULT_ON_ERROR); ++ node_size = node_spanned_pages; ++ } else error(INFO, "cannot determine zone size\n"); + +-/* +- * dump_slab_objects() adapted for newer percpu slab format. +- */ ++ if (VALID_MEMBER(pglist_data_node_present_pages)) ++ readmem(pgdat+OFFSET(pglist_data_node_present_pages), ++ KVADDR, &node_present_pages, sizeof(ulong), ++ "pglist node_present_pages", FAULT_ON_ERROR); ++ else ++ node_present_pages = 0; + +-static void +-dump_slab_objects_percpu(struct meminfo *si) +-{ +- int i, j; +- int on_free_list, on_cpudata_list; +- ulong cnt, expected; +- ulong obj; ++ readmem(pgdat+OFFSET(pglist_data_bdata), KVADDR, &bdata, ++ sizeof(ulong), "pglist bdata", FAULT_ON_ERROR); + +- if ((si->flags & ADDRESS_SPECIFIED) && (si->found & ~KMEM_ON_SLAB)) +- return; ++ if (initialize) { ++ nt->node_id = id; ++ nt->pgdat = pgdat; ++ if (VALID_MEMBER(zone_struct_memsize)) ++ nt->size = 0; /* initialize below */ ++ else ++ nt->size = node_size; ++ nt->present = node_present_pages; ++ nt->mem_map = node_mem_map; ++ nt->start_paddr = node_start_paddr; ++ nt->start_mapnr = node_start_mapnr; + +- cnt = 0; +- expected = si->s_inuse; ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "node_table[%d]: \n", n); ++ fprintf(fp, " id: %d\n", nt->node_id); ++ fprintf(fp, " pgdat: %lx\n", nt->pgdat); ++ fprintf(fp, " size: %ld\n", nt->size); ++ fprintf(fp, " present: %ld\n", nt->present); ++ fprintf(fp, " mem_map: %lx\n", nt->mem_map); ++ fprintf(fp, " start_paddr: %llx\n", nt->start_paddr); ++ fprintf(fp, " start_mapnr: %ld\n", nt->start_mapnr); ++ } ++ } + +- if (CRASHDEBUG(1)) +- for (i = 0; i < si->c_num; i++) { +- fprintf(fp, "si->addrlist[%d]: %lx\n", +- i, si->addrlist[i]); +- } ++ if (!initialize) { ++ if (n) { ++ fprintf(fp, "\n"); ++ pad_line(fp, slen, '-'); ++ } ++ flen = MAX(VADDR_PRLEN, strlen("BOOTMEM_DATA")); ++ fprintf(fp, "%sNODE %s %s %s %s\n", ++ n ? "\n\n" : "", ++ mkstring(buf1, 8, CENTER, "SIZE"), ++ mkstring(buf2, flen, CENTER|LJUST, "PGLIST_DATA"), ++ mkstring(buf3, flen, CENTER|LJUST, "BOOTMEM_DATA"), ++ mkstring(buf4, flen, CENTER|LJUST, "NODE_ZONES")); + +- if (!(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, free_inuse_hdr); ++ node_zones = pgdat + OFFSET(pglist_data_node_zones); ++ sprintf(buf5, " %2d %s %s %s %s\n", id, ++ mkstring(buf1, 8, CENTER|LJUST|LONG_DEC, ++ MKSTR(node_size)), ++ mkstring(buf2, flen, CENTER|LJUST|LONG_HEX, ++ MKSTR(pgdat)), ++ mkstring(buf3, flen, CENTER|LONG_HEX, ++ MKSTR(bdata)), ++ mkstring(buf4, flen, CENTER|LJUST|LONG_HEX, ++ MKSTR(node_zones))); ++ fprintf(fp, "%s", buf5); + +- for (i = 0, obj = si->s_mem; i < si->c_num; i++, obj += si->size) { +- on_free_list = FALSE; +- on_cpudata_list = FALSE; ++ j = 12 + strlen(buf1) + strlen(buf2) + strlen(buf3) + ++ count_leading_spaces(buf4); ++ for (i = 1; i < vt->nr_zones; i++) { ++ node_zones += SIZE_OPTION(zone_struct, zone); ++ INDENT(j); ++ fprintf(fp, "%lx\n", node_zones); ++ } ++ ++ fprintf(fp, "%s START_PADDR START_MAPNR\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, ++ "MEM_MAP")); ++ fprintf(fp, "%s %s %s\n", ++ mkstring(buf1, VADDR_PRLEN, ++ CENTER|LONG_HEX, MKSTR(node_mem_map)), ++ mkstring(buf2, strlen("START_PADDR"), ++ CENTER|LONG_HEX|RJUST, MKSTR(node_start_paddr)), ++ mkstring(buf3, strlen("START_MAPNR"), ++ CENTER|LONG_DEC|RJUST, ++ MKSTR(node_start_mapnr))); ++ ++ sprintf(buf2, "%s %s START_PADDR START_MAPNR", ++ node_zone_hdr, ++ mkstring(buf1, VADDR_PRLEN, CENTER|RJUST, ++ "MEM_MAP")); ++ slen = strlen(buf2); ++ fprintf(fp, "\n%s\n", buf2); ++ } + +- for (j = 0; j < si->c_num; j++) { +- if (obj == si->addrlist[j]) { +- on_free_list = TRUE; +- break; +- } +- } ++ node_zones = pgdat + OFFSET(pglist_data_node_zones); ++ cum_zone_size = 0; ++ for (i = 0; i < vt->nr_zones; i++) { ++ if (CRASHDEBUG(7)) ++ fprintf(fp, "zone %d at %lx\n", i, node_zones); + +- on_cpudata_list = check_cpudata_list(si, obj); ++ if (VALID_MEMBER(zone_struct_size)) ++ readmem(node_zones+OFFSET(zone_struct_size), ++ KVADDR, &zone_size, sizeof(ulong), ++ "zone_struct size", FAULT_ON_ERROR); ++ else if (VALID_MEMBER(zone_struct_memsize)) { ++ readmem(node_zones+OFFSET(zone_struct_memsize), ++ KVADDR, &zone_size, sizeof(ulong), ++ "zone_struct memsize", FAULT_ON_ERROR); ++ nt->size += zone_size; ++ } else if (VALID_MEMBER(zone_spanned_pages)) { ++ readmem(node_zones+ OFFSET(zone_spanned_pages), ++ KVADDR, &zone_size, sizeof(ulong), ++ "zone spanned_pages", FAULT_ON_ERROR); ++ } else error(FATAL, ++ "zone_struct has neither size nor memsize field\n"); + +- if (on_free_list && on_cpudata_list) { +- error(INFO, +- "\"%s\" cache: object %lx on both free and cpudata lists\n", +- si->curname, obj); +- si->errors++; +- } +- +- if (on_free_list) { +- if (!(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, " %lx\n", obj); +- if (si->flags & ADDRESS_SPECIFIED) { +- if (INOBJECT(si->spec_addr, obj)) { +- si->found = +- KMEM_OBJECT_ADDR_FREE; +- return; +- } +- } +- } else if (on_cpudata_list) { +- if (!(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, " %lx (cpu %d cache)\n", obj, +- si->cpu); +- cnt++; +- if (si->flags & ADDRESS_SPECIFIED) { +- if (INOBJECT(si->spec_addr, obj)) { +- si->found = +- KMEM_OBJECT_ADDR_CACHED; +- return; +- } +- } +- } else { +- if (!(si->flags & ADDRESS_SPECIFIED)) +- fprintf(fp, " [%lx]\n", obj); +- cnt++; +- if (si->flags & ADDRESS_SPECIFIED) { +- if (INOBJECT(si->spec_addr, obj)) { +- si->found = +- KMEM_OBJECT_ADDR_INUSE; +- return; +- } +- } +- } +- } ++ readmem(node_zones+ ++ OFFSET_OPTION(zone_struct_free_pages, ++ zone_free_pages), KVADDR, &free_pages, ++ sizeof(ulong), "zone[_struct] free_pages", ++ FAULT_ON_ERROR); ++ readmem(node_zones+OFFSET_OPTION(zone_struct_name, ++ zone_name), KVADDR, &value, sizeof(void *), ++ "zone[_struct] name", FAULT_ON_ERROR); ++ if (!read_string(value, buf1, BUFSIZE-1)) ++ sprintf(buf1, "(unknown) "); ++ if (VALID_STRUCT(zone_struct)) { ++ if (VALID_MEMBER(zone_struct_zone_start_paddr)) ++ { ++ readmem(node_zones+OFFSET ++ (zone_struct_zone_start_paddr), ++ KVADDR, &zone_start_paddr, ++ sizeof(ulong), ++ "node_zones zone_start_paddr", ++ FAULT_ON_ERROR); ++ } else { ++ readmem(node_zones+ ++ OFFSET(zone_struct_zone_start_pfn), ++ KVADDR, &zone_start_pfn, ++ sizeof(ulong), ++ "node_zones zone_start_pfn", ++ FAULT_ON_ERROR); ++ zone_start_paddr = ++ PTOB(zone_start_pfn); ++ } ++ readmem(node_zones+ ++ OFFSET(zone_struct_zone_start_mapnr), ++ KVADDR, &zone_start_mapnr, ++ sizeof(ulong), ++ "node_zones zone_start_mapnr", ++ FAULT_ON_ERROR); ++ } else { ++ readmem(node_zones+ ++ OFFSET(zone_zone_start_pfn), ++ KVADDR, &zone_start_pfn, ++ sizeof(ulong), ++ "node_zones zone_start_pfn", ++ FAULT_ON_ERROR); ++ zone_start_paddr = PTOB(zone_start_pfn); + +- if (cnt != expected) { +- error(INFO, +- "\"%s\" cache: inuse object mismatch: expected: %ld found: %ld\n", +- si->curname, expected, cnt); +- si->errors++; +- } +-} ++ if (IS_SPARSEMEM()) { ++ zone_mem_map = 0; ++ zone_start_mapnr = 0; ++ if (zone_size) { ++ phys = PTOB(zone_start_pfn); ++ zone_start_mapnr = phys/PAGESIZE(); ++ } + +-/* +- * Determine how many of the "inuse" slab objects are actually cached +- * in the kmem_cache_s header. Set the per-slab count and update the +- * cumulative per-cache count. +- */ ++ } else if (!(vt->flags & NODES) && ++ INVALID_MEMBER(zone_zone_mem_map)) { ++ readmem(pgdat+OFFSET(pglist_data_node_mem_map), ++ KVADDR, &zone_mem_map, sizeof(void *), ++ "contig_page_data mem_map", FAULT_ON_ERROR); ++ if (zone_size) ++ zone_mem_map += cum_zone_size * SIZE(page); ++ } else readmem(node_zones+ ++ OFFSET(zone_zone_mem_map), ++ KVADDR, &zone_mem_map, ++ sizeof(ulong), ++ "node_zones zone_mem_map", ++ FAULT_ON_ERROR); + +-static void +-gather_slab_cached_count(struct meminfo *si) +-{ +- int i; +- ulong obj; ++ if (zone_mem_map) ++ zone_start_mapnr = ++ (zone_mem_map - node_mem_map) / ++ SIZE(page); ++ else if (!IS_SPARSEMEM()) ++ zone_start_mapnr = 0; ++ } + +- si->cpucached_slab = 0; ++ if (IS_SPARSEMEM()) { ++ zone_mem_map = 0; ++ if (zone_size) { ++ phys = PTOB(zone_start_pfn); ++ if (phys_to_page(phys, &pp)) ++ zone_mem_map = pp; ++ } ++ } else if (!(vt->flags & NODES) && ++ INVALID_MEMBER(zone_struct_zone_mem_map) && ++ INVALID_MEMBER(zone_zone_mem_map)) { ++ readmem(pgdat+OFFSET(pglist_data_node_mem_map), ++ KVADDR, &zone_mem_map, sizeof(void *), ++ "contig_page_data mem_map", FAULT_ON_ERROR); ++ if (zone_size) ++ zone_mem_map += cum_zone_size * SIZE(page); ++ else ++ zone_mem_map = 0; ++ } else ++ readmem(node_zones+ ++ OFFSET_OPTION(zone_struct_zone_mem_map, ++ zone_zone_mem_map), KVADDR, &zone_mem_map, ++ sizeof(ulong), "node_zones zone_mem_map", ++ FAULT_ON_ERROR); + +- for (i = 0, obj = si->s_mem; i < si->c_num; i++, obj += si->size) { +- if (check_cpudata_list(si, obj)) { +- si->cpucached_slab++; +- if (si->flags & SLAB_GET_COUNTS) { +- si->cpucached_cache++; ++ if (!initialize) { ++ fprintf(fp, " %2d %-9s %7ld ", ++ i, buf1, zone_size); ++ cum_zone_size += zone_size; ++ fprintf(fp, "%s %s %s\n", ++ mkstring(buf1, VADDR_PRLEN, ++ RJUST|LONG_HEX,MKSTR(zone_mem_map)), ++ mkstring(buf2, strlen("START_PADDR"), ++ LONG_HEX|RJUST,MKSTR(zone_start_paddr)), ++ mkstring(buf3, strlen("START_MAPNR"), ++ LONG_DEC|RJUST, ++ MKSTR(zone_start_mapnr))); + } ++ ++ node_zones += SIZE_OPTION(zone_struct, zone); ++ } ++ ++ if (initialize) { ++ if (vt->flags & NODES_ONLINE) { ++ if ((node = next_online_node(node+1)) < 0) ++ pgdat = 0; ++ else if (!(pgdat = next_online_pgdat(node))) { ++ error(WARNING, ++ "cannot determine pgdat list for this kernel/architecture (node %d)\n\n", ++ node); ++ pgdat = 0; ++ } ++ } else ++ readmem(pgdat + OFFSET_OPTION(pglist_data_node_next, ++ pglist_data_pgdat_next), KVADDR, ++ &pgdat, sizeof(void *), "pglist_data node_next", ++ FAULT_ON_ERROR); ++ } else { ++ if ((n+1) < vt->numnodes) ++ pgdat = vt->node_table[n+1].pgdat; ++ else ++ pgdat = 0; + } ++ } ++ ++ if (n != vt->numnodes) { ++ if (CRASHDEBUG(2)) ++ error(NOTE, "changing numnodes from %d to %d\n", ++ vt->numnodes, n); ++ vt->numnodes = n; + } +-} + +-/* +- * Populate the percpu object list for a given slab. +- */ ++ if (!initialize && IS_SPARSEMEM()) ++ dump_mem_sections(); ++} + + static void +-gather_cpudata_list_v1(struct meminfo *si) ++dump_zone_stats(void) + { +- int i, j; +- int avail; +- ulong cpudata[NR_CPUS]; ++ int i, n; ++ ulong pgdat, node_zones; ++ char *zonebuf; ++ char buf1[BUFSIZE]; ++ int ivalue; ++ ulong value1; ++ ulong value2; ++ ulong value3; ++ ulong value4; ++ ulong value5; ++ ulong value6; ++ ++ pgdat = vt->node_table[0].pgdat; ++ zonebuf = GETBUF(SIZE_OPTION(zone_struct, zone)); ++ vm_stat_init(); + +- if (INVALID_MEMBER(kmem_cache_s_cpudata)) +- return; ++ for (n = 0; pgdat; n++) { ++ node_zones = pgdat + OFFSET(pglist_data_node_zones); + +- readmem(si->cache+OFFSET(kmem_cache_s_cpudata), +- KVADDR, &cpudata[0], +- sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_cpudata), +- "cpudata array", FAULT_ON_ERROR); ++ for (i = 0; i < vt->nr_zones; i++) { + +- for (i = 0; (i < ARRAY_LENGTH(kmem_cache_s_cpudata)) && +- cpudata[i]; i++) { +- BZERO(si->cpudata[i], sizeof(ulong) * vt->kmem_max_limit); ++ if (!readmem(node_zones, KVADDR, zonebuf, ++ SIZE_OPTION(zone_struct, zone), ++ "zone buffer", FAULT_ON_ERROR)) ++ break; + +- readmem(cpudata[i]+OFFSET(cpucache_s_avail), +- KVADDR, &avail, sizeof(int), +- "cpucache avail", FAULT_ON_ERROR); ++ value1 = ULONG(zonebuf + ++ OFFSET_OPTION(zone_struct_name, zone_name)); + +- if (!avail) +- continue; ++ if (!read_string(value1, buf1, BUFSIZE-1)) ++ sprintf(buf1, "(unknown) "); + +- if (avail > vt->kmem_max_limit) { +- error(INFO, +- "\"%s\" cache: cpucache_s.avail %d greater than limit %ld\n", +- si->curname, avail, vt->kmem_max_limit); +- si->errors++; ++ if (VALID_MEMBER(zone_struct_size)) ++ value1 = value6 = ULONG(zonebuf + ++ OFFSET(zone_struct_size)); ++ else if (VALID_MEMBER(zone_struct_memsize)) { ++ value1 = value6 = ULONG(zonebuf + ++ OFFSET(zone_struct_memsize)); ++ } else if (VALID_MEMBER(zone_spanned_pages)) { ++ value1 = ULONG(zonebuf + ++ OFFSET(zone_spanned_pages)); ++ value6 = ULONG(zonebuf + ++ OFFSET(zone_present_pages)); ++ } else error(FATAL, ++ "zone struct has unknown size field\n"); ++ ++ value2 = ULONG(zonebuf + OFFSET_OPTION(zone_pages_min, ++ zone_struct_pages_min)); ++ value3 = ULONG(zonebuf + OFFSET_OPTION(zone_pages_low, ++ zone_struct_pages_low)); ++ value4 = ULONG(zonebuf + OFFSET_OPTION(zone_pages_high, ++ zone_struct_pages_high)); ++ value5 = ULONG(zonebuf + OFFSET_OPTION(zone_free_pages, ++ zone_struct_free_pages)); ++ ++ fprintf(fp, ++ "NODE: %d ZONE: %d ADDR: %lx NAME: \"%s\"\n", ++ n, i, node_zones, buf1); ++ ++ if (!value1) { ++ fprintf(fp, " [unpopulated]\n"); ++ goto next_zone; ++ } ++ fprintf(fp, " SIZE: %ld", value1); ++ if (value6 < value1) ++ fprintf(fp, " PRESENT: %ld", value6); ++ fprintf(fp, " MIN/LOW/HIGH: %ld/%ld/%ld", ++ value2, value3, value4); ++ ++ if (VALID_MEMBER(zone_vm_stat)) ++ dump_vm_stat("NR_FREE_PAGES", (long *)&value5, ++ node_zones + OFFSET(zone_vm_stat)); ++ ++ if (VALID_MEMBER(zone_nr_active) && ++ VALID_MEMBER(zone_nr_inactive)) { ++ value1 = ULONG(zonebuf + ++ OFFSET(zone_nr_active)); ++ value2 = ULONG(zonebuf + ++ OFFSET(zone_nr_inactive)); ++ fprintf(fp, ++ "\n NR_ACTIVE: %ld NR_INACTIVE: %ld FREE: %ld\n", ++ value1, value2, value5); ++ if (VALID_MEMBER(zone_vm_stat)) { ++ fprintf(fp, " VM_STAT:\n"); ++ dump_vm_stat(NULL, NULL, node_zones + ++ OFFSET(zone_vm_stat)); ++ } ++ } else if (VALID_MEMBER(zone_vm_stat) && ++ dump_vm_stat("NR_ACTIVE", (long *)&value1, ++ node_zones + OFFSET(zone_vm_stat)) && ++ dump_vm_stat("NR_INACTIVE", (long *)&value2, ++ node_zones + OFFSET(zone_vm_stat))) { ++ fprintf(fp, "\n VM_STAT:\n"); ++ dump_vm_stat(NULL, NULL, node_zones + ++ OFFSET(zone_vm_stat)); ++ } else { ++ fprintf(fp, " FREE: %ld\n", value5); ++ goto next_zone; ++ } ++ ++ if (VALID_MEMBER(zone_all_unreclaimable)) { ++ ivalue = UINT(zonebuf + ++ OFFSET(zone_all_unreclaimable)); ++ fprintf(fp, " ALL_UNRECLAIMABLE: %s ", ++ ivalue ? "yes" : "no"); ++ } else if (VALID_MEMBER(zone_flags) && ++ enumerator_value("ZONE_ALL_UNRECLAIMABLE", ++ (long *)&value1)) { ++ value2 = ULONG(zonebuf + OFFSET(zone_flags)); ++ value3 = value2 & (1 << value1); ++ fprintf(fp, " ALL_UNRECLAIMABLE: %s ", ++ value3 ? "yes" : "no"); ++ } ++ ++ if (VALID_MEMBER(zone_pages_scanned)) { ++ value1 = ULONG(zonebuf + ++ OFFSET(zone_pages_scanned)); ++ fprintf(fp, "PAGES_SCANNED: %ld ", value1); ++ } ++ fprintf(fp, "\n"); ++ ++next_zone: ++ fprintf(fp, "\n"); ++ node_zones += SIZE_OPTION(zone_struct, zone); + } + +- if (CRASHDEBUG(2)) +- fprintf(fp, "%s: cpu[%d] avail: %d\n", +- si->curname, i, avail); ++ if ((n+1) < vt->numnodes) ++ pgdat = vt->node_table[n+1].pgdat; ++ else ++ pgdat = 0; ++ } + +- readmem(cpudata[i]+SIZE(cpucache_s), +- KVADDR, si->cpudata[i], +- sizeof(void *) * avail, +- "cpucache avail", FAULT_ON_ERROR); ++ FREEBUF(zonebuf); + +- if (CRASHDEBUG(2)) +- for (j = 0; j < avail; j++) +- fprintf(fp, " %lx\n", si->cpudata[i][j]); +- } + } + + /* +- * Updated for 2.6 slab percpu data structure. ++ * Gather essential information regarding each memory node. + */ + static void +-gather_cpudata_list_v2(struct meminfo *si) ++node_table_init(void) + { +- int i, j; +- int avail; +- ulong cpudata[NR_CPUS]; +- +- readmem(si->cache+OFFSET(kmem_cache_s_array), +- KVADDR, &cpudata[0], +- sizeof(ulong) * ARRAY_LENGTH(kmem_cache_s_array), +- "array_cache array", FAULT_ON_ERROR); ++ int n; ++ ulong pgdat; + +- for (i = 0; (i < ARRAY_LENGTH(kmem_cache_s_array)) && +- cpudata[i]; i++) { +- BZERO(si->cpudata[i], sizeof(ulong) * vt->kmem_max_limit); ++ /* ++ * Override numnodes -- some kernels may leave it at 1 on a system ++ * with multiple memory nodes. ++ */ ++ if ((vt->flags & NODES) && (VALID_MEMBER(pglist_data_node_next) || ++ VALID_MEMBER(pglist_data_pgdat_next))) { + +- readmem(cpudata[i]+OFFSET(array_cache_avail), +- KVADDR, &avail, sizeof(int), +- "array cache avail", FAULT_ON_ERROR); ++ get_symbol_data("pgdat_list", sizeof(void *), &pgdat); ++ ++ for (n = 0; pgdat; n++) { ++ readmem(pgdat + OFFSET_OPTION(pglist_data_node_next, ++ pglist_data_pgdat_next), KVADDR, ++ &pgdat, sizeof(void *), "pglist_data node_next", ++ FAULT_ON_ERROR); ++ } ++ if (n != vt->numnodes) { ++ if (CRASHDEBUG(2)) ++ error(NOTE, "changing numnodes from %d to %d\n", ++ vt->numnodes, n); ++ vt->numnodes = n; ++ } ++ } else ++ vt->flags &= ~NODES; + +- if (!avail) +- continue; ++ if (!(vt->node_table = (struct node_table *) ++ malloc(sizeof(struct node_table) * vt->numnodes))) ++ error(FATAL, "cannot malloc node_table %s(%d nodes)", ++ vt->numnodes > 1 ? "array " : "", vt->numnodes); + +- if (avail > vt->kmem_max_limit) { +- error(INFO, +- "\"%s\" cache: array_cache.avail %d greater than limit %ld\n", +- si->curname, avail, vt->kmem_max_limit); +- si->errors++; +- } ++ BZERO(vt->node_table, sizeof(struct node_table) * vt->numnodes); + +- if (CRASHDEBUG(2)) +- fprintf(fp, "%s: cpu[%d] avail: %d\n", +- si->curname, i, avail); ++ dump_memory_nodes(MEMORY_NODES_INITIALIZE); + +- readmem(cpudata[i]+SIZE(array_cache), +- KVADDR, si->cpudata[i], +- sizeof(void *) * avail, +- "array_cache avail", FAULT_ON_ERROR); ++ qsort((void *)vt->node_table, (size_t)vt->numnodes, ++ sizeof(struct node_table), compare_node_data); + +- if (CRASHDEBUG(2)) +- for (j = 0; j < avail; j++) +- fprintf(fp, " %lx\n", si->cpudata[i][j]); +- } ++ if (CRASHDEBUG(2)) ++ dump_memory_nodes(MEMORY_NODES_DUMP); + } + + /* +- * Check whether a given address is contained in the previously-gathered +- * percpu object cache. ++ * 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 +-check_cpudata_list(struct meminfo *si, ulong obj) ++compare_node_data(const void *v1, const void *v2) + { +- int i, j; ++ struct node_table *t1, *t2; + +- for (i = 0; i < vt->kmem_max_cpus; i++) { +- for (j = 0; si->cpudata[i][j]; j++) +- if (si->cpudata[i][j] == obj) { +- si->cpu = i; +- return TRUE; +- } +- } ++ t1 = (struct node_table *)v1; ++ t2 = (struct node_table *)v2; + +- return FALSE; ++ return (t1->node_id < t2->node_id ? -1 : ++ t1->node_id == t2->node_id ? 0 : 1); + } + + + /* +- * Search the various memory subsystems for instances of this address. +- * Start with the most specific areas, ending up with at least the +- * mem_map page data. ++ * Depending upon the processor, and whether we're running live or on a ++ * dumpfile, get the system page size. + */ +-static void +-kmem_search(struct meminfo *mi) ++uint ++memory_page_size(void) + { +- struct syment *sp; +- struct meminfo tmp_meminfo; +- char buf[BUFSIZE]; +- ulong vaddr, orig_flags; +- physaddr_t paddr; +- ulong offset; +- +- switch (mi->memtype) +- { +- case KVADDR: +- vaddr = mi->spec_addr; +- break; ++ uint psz; + +- case PHYSADDR: +- vaddr = mi->spec_addr < VTOP(vt->high_memory) ? +- PTOV(mi->spec_addr) : BADADDR; +- break; +- } ++ if (machdep->pagesize) ++ return machdep->pagesize; + +- orig_flags = mi->flags; +- mi->retval = 0; ++ if (REMOTE_MEMSRC()) ++ return remote_page_size(); + +- /* +- * Check first for a possible symbolic display of the virtual +- * address associated with mi->spec_addr or PTOV(mi->spec_addr). +- */ +- if (((vaddr >= kt->stext) && (vaddr <= kt->end)) || +- IS_MODULE_VADDR(mi->spec_addr)) { +- if ((sp = value_search(vaddr, &offset))) { +- show_symbol(sp, offset, SHOW_LINENUM | SHOW_RADIX()); +- fprintf(fp, "\n"); +- } +- } ++ switch (pc->flags & MEMORY_SOURCES) ++ { ++ case DISKDUMP: ++ psz = diskdump_page_size(); ++ break; + +- /* +- * Check for a valid mapped address. +- */ +- if ((mi->memtype == KVADDR) && IS_VMALLOC_ADDR(mi->spec_addr)) { +- if (kvtop(NULL, mi->spec_addr, &paddr, 0)) { +- mi->flags = orig_flags; +- dump_vmlist(mi); +- fprintf(fp, "\n"); +- mi->spec_addr = paddr; +- mi->memtype = PHYSADDR; +- } +- goto mem_map; +- } +- /* +- * If the address is physical, check whether it's in vmalloc space. +- */ ++ case XENDUMP: ++ psz = xendump_page_size(); ++ break; + +- if (mi->memtype == PHYSADDR) { +- mi->flags = orig_flags; +- mi->flags |= GET_PHYS_TO_VMALLOC; +- mi->retval = 0; +- dump_vmlist(mi); +- mi->flags &= ~GET_PHYS_TO_VMALLOC; ++ case KDUMP: ++ psz = kdump_page_size(); ++ break; + +- if (mi->retval) { +- if ((sp = value_search(mi->retval, &offset))) { +- show_symbol(sp, offset, +- SHOW_LINENUM | SHOW_RADIX()); +- fprintf(fp, "\n"); +- } +- dump_vmlist(mi); +- fprintf(fp, "\n"); +- goto mem_map; +- } +- } ++ case NETDUMP: ++ psz = netdump_page_size(); ++ break; + +- /* +- * Check whether the containing page belongs to the slab subsystem. +- */ +- mi->flags = orig_flags; +- mi->retval = 0; +- if ((vaddr != BADADDR) && vaddr_to_kmem_cache(vaddr, buf)) { +- BZERO(&tmp_meminfo, sizeof(struct meminfo)); +- tmp_meminfo.spec_addr = vaddr; +- tmp_meminfo.memtype = KVADDR; +- tmp_meminfo.flags = mi->flags; +- vt->dump_kmem_cache(&tmp_meminfo); +- fprintf(fp, "\n"); +- } ++ case MCLXCD: ++ psz = (uint)mclx_page_size(); ++ break; + +- /* +- * Check free list. +- */ +- mi->flags = orig_flags; +- mi->retval = 0; +- vt->dump_free_pages(mi); +- if (mi->retval) +- fprintf(fp, "\n"); ++ case LKCD: ++#if 0 /* REMIND: */ ++ psz = lkcd_page_size(); /* dh_dump_page_size is HW page size; should add dh_page_size */ ++#else ++ psz = (uint)getpagesize(); ++#endif ++ break; + +- if (vt->page_hash_table) { +- /* +- * Check the page cache. +- */ +- mi->flags = orig_flags; +- mi->retval = 0; +- dump_page_hash_table(mi); +- if (mi->retval) +- fprintf(fp, "\n"); +- } ++ case DEVMEM: ++ case MEMMOD: ++ case CRASHBUILTIN: ++ psz = (uint)getpagesize(); ++ break; + +-mem_map: +- mi->flags = orig_flags; +- dump_mem_map(mi); ++ case S390D: ++ psz = s390_page_size(); ++ break; + +- if (!mi->retval) +- fprintf(fp, "%llx: address not found\n", mi->spec_addr); ++ default: ++ error(FATAL, "memory_page_size: invalid pc->flags: %lx\n", ++ pc->flags & MEMORY_SOURCES); ++ } + ++ return psz; + } + + /* +- * Determine whether an address is a page pointer from the mem_map[] array. +- * If the caller requests it, return the associated physical address. ++ * If the page size cannot be determined by the dumpfile (like kdump), ++ * and the processor default cannot be used, allow the force-feeding ++ * of a crash command-line page size option. + */ +-int +-is_page_ptr(ulong addr, physaddr_t *phys) ++void ++force_page_size(char *s) + { +- int n; +- ulong ppstart, ppend; +- struct node_table *nt; +- ulong pgnum, node_size; ++ int k, err; ++ ulong psize; + +- for (n = 0; n < vt->numnodes; n++) { +- nt = &vt->node_table[n]; +- if ((vt->flags & V_MEM_MAP) && (vt->numnodes == 1)) +- node_size = vt->max_mapnr; +- else +- node_size = nt->size; ++ k = 1; ++ err = FALSE; + +- ppstart = nt->mem_map; +- ppend = ppstart + (node_size * SIZE(page)); ++ switch (LASTCHAR(s)) ++ { ++ case 'k': ++ case 'K': ++ LASTCHAR(s) = NULLCHAR; ++ if (!decimal(s, 0)) { ++ err = TRUE; ++ break; ++ } ++ k = 1024; + +- if ((addr < ppstart) || (addr >= ppend)) +- continue; ++ /* FALLTHROUGH */ + +- /* +- * We're in the mem_map range -- but it is a page pointer? +- */ +- if ((addr - ppstart) % SIZE(page)) +- return FALSE; ++ default: ++ if (decimal(s, 0)) ++ psize = dtol(s, QUIET|RETURN_ON_ERROR, &err); ++ else if (hexadecimal(s, 0)) ++ psize = htol(s, QUIET|RETURN_ON_ERROR, &err); ++ else ++ err = TRUE; ++ break; ++ } + +- if (phys) { +- pgnum = (addr - nt->mem_map) / SIZE(page); +- *phys = (pgnum * PAGESIZE()) + nt->start_paddr; +- } ++ if (err) ++ error(INFO, "invalid page size: %s\n", s); ++ else ++ machdep->pagesize = psize * k; ++} + +- return TRUE; +- } + +- return FALSE; ++/* ++ * Return the vmalloc address referenced by the first vm_struct ++ * on the vmlist. This can normally be used by the machine-specific ++ * xxx_vmalloc_start() routines. ++ */ + +-#ifdef PRE_NODES +- ppstart = vt->mem_map; +- ppend = ppstart + (vt->total_pages * vt->page_struct_len); ++ulong ++first_vmalloc_address(void) ++{ ++ ulong vmlist, addr; + +- if ((addr < ppstart) || (addr >= ppend)) +- return FALSE; ++ get_symbol_data("vmlist", sizeof(void *), &vmlist); + +- if ((addr - ppstart) % vt->page_struct_len) +- return FALSE; ++ if (!vmlist) ++ return 0; + +- return TRUE; +-#endif ++ if (!readmem(vmlist+OFFSET(vm_struct_addr), KVADDR, &addr, ++ sizeof(void *), "first vmlist addr", RETURN_ON_ERROR)) ++ non_matching_kernel(); ++ ++ return addr; + } + + /* +- * Return the physical address associated with this page pointer. ++ * Return the current vmalloc address limit, storing it ++ * if it's a dumpfile. + */ +-static int +-page_to_phys(ulong pp, physaddr_t *phys) ++ ++static ulong ++last_vmalloc_address(void) + { +- return(is_page_ptr(pp, phys)); +-} ++ struct meminfo meminfo; ++ static ulong vmalloc_limit = 0; ++ ++ if (!vmalloc_limit) { ++ BZERO(&meminfo, sizeof(struct meminfo)); ++ meminfo.memtype = KVADDR; ++ meminfo.spec_addr = 0; ++ meminfo.flags = (ADDRESS_SPECIFIED|GET_HIGHEST); ++ dump_vmlist(&meminfo); ++ vmalloc_limit = meminfo.retval; ++ } + ++ return vmalloc_limit; ++} + + /* +- * Return the page pointer associated with this physical address. ++ * Determine whether an identity-mapped virtual address ++ * refers to an existant physical page, and if not bump ++ * it up to the next node. + */ +-static int +-phys_to_page(physaddr_t phys, ulong *pp) ++static int ++next_identity_mapping(ulong vaddr, ulong *nextvaddr) + { + int n; +- ulong pgnum; + struct node_table *nt; +- physaddr_t pstart, pend; ++ ulonglong paddr, pstart, pend; + ulong node_size; + ++ paddr = VTOP(vaddr); ++ + for (n = 0; n < vt->numnodes; n++) { + nt = &vt->node_table[n]; + if ((vt->flags & V_MEM_MAP) && (vt->numnodes == 1)) + node_size = vt->max_mapnr; + else +- node_size = nt->size; ++ node_size = nt->size; + + pstart = nt->start_paddr; + pend = pstart + ((ulonglong)node_size * PAGESIZE()); + +- if ((phys < pstart) || (phys >= pend)) ++ /* ++ * Check the next node. ++ */ ++ if (paddr >= pend) ++ continue; ++ /* ++ * Bump up to the next node. ++ */ ++ if (paddr < pstart) { ++ *nextvaddr = PTOV(paddr); + continue; ++ } + /* +- * We're in the physical range -- calculate the page. ++ * We're in the physical range. + */ +- pgnum = BTOP(phys - pstart); +- *pp = nt->mem_map + (pgnum * SIZE(page)); +- ++ *nextvaddr = vaddr; + return TRUE; + } + + return FALSE; ++} + +-#ifdef PRE_NODES +- if (phys >= (vt->total_pages * PAGESIZE())) +- return FALSE; + +- pgnum = PTOB(BTOP(phys)) / PAGESIZE(); +- *pp = vt->mem_map + (pgnum * vt->page_struct_len); +- +- return TRUE; +-#endif +-} ++/* ++ * Return the L1 cache size in bytes, which can be found stored in the ++ * cache_cache. ++ */ ++ ++int ++l1_cache_size(void) ++{ ++ ulong cache; ++ ulong c_align; ++ int colour_off; ++ int retval; ++ ++ retval = -1; ++ ++ if (VALID_MEMBER(kmem_cache_s_c_align)) { ++ cache = symbol_value("cache_cache"); ++ readmem(cache+OFFSET(kmem_cache_s_c_align), ++ KVADDR, &c_align, sizeof(ulong), ++ "c_align", FAULT_ON_ERROR); ++ retval = (int)c_align; ++ } else if (VALID_MEMBER(kmem_cache_s_colour_off)) { ++ cache = symbol_value("cache_cache"); ++ readmem(cache+OFFSET(kmem_cache_s_colour_off), ++ KVADDR, &colour_off, sizeof(int), ++ "colour_off", FAULT_ON_ERROR); ++ retval = colour_off; ++ } + ++ return retval; ++} + + /* +- * Try to read a string of non-NULL characters from a memory location, +- * returning the number of characters read. ++ * Multi-purpose routine used to query/control dumpfile memory usage. + */ + int +-read_string(ulong kvaddr, char *buf, int maxlen) ++dumpfile_memory(int cmd) + { +- char strbuf[MIN_PAGE_SIZE]; +- ulong kp; +- char *bufptr; +- long cnt, size; ++ int retval; + +- BZERO(buf, maxlen); +- BZERO(strbuf, MIN_PAGE_SIZE); ++ retval = 0; + +- kp = kvaddr; +- bufptr = strbuf; +- size = maxlen; ++ if (!DUMPFILE()) ++ return retval; + +- while (size > 0) { +- cnt = MIN_PAGE_SIZE - (kp & (MIN_PAGE_SIZE-1)); +- +- if (cnt > size) +- cnt = size; ++ switch (cmd) ++ { ++ case DUMPFILE_MEM_USED: ++ if (REMOTE_DUMPFILE()) ++ retval = remote_memory_used(); ++ else if (pc->flags & NETDUMP) ++ retval = netdump_memory_used(); ++ else if (pc->flags & KDUMP) ++ retval = kdump_memory_used(); ++ else if (pc->flags & XENDUMP) ++ retval = xendump_memory_used(); ++ else if (pc->flags & DISKDUMP) ++ retval = diskdump_memory_used(); ++ else if (pc->flags & LKCD) ++ retval = lkcd_memory_used(); ++ else if (pc->flags & MCLXCD) ++ retval = vas_memory_used(); ++ else if (pc->flags & S390D) ++ retval = s390_memory_used(); ++ break; + +- if (!readmem(kp, KVADDR, bufptr, cnt, +- "readstring characters", QUIET|RETURN_ON_ERROR)) +- break; ++ case DUMPFILE_FREE_MEM: ++ if (REMOTE_DUMPFILE()) ++ retval = remote_free_memory(); ++ else if (pc->flags & NETDUMP) ++ retval = netdump_free_memory(); ++ else if (pc->flags & KDUMP) ++ retval = kdump_free_memory(); ++ else if (pc->flags & XENDUMP) ++ retval = xendump_free_memory(); ++ else if (pc->flags & DISKDUMP) ++ retval = diskdump_free_memory(); ++ else if (pc->flags & LKCD) ++ retval = lkcd_free_memory(); ++ else if (pc->flags & MCLXCD) ++ retval = vas_free_memory(NULL); ++ else if (pc->flags & S390D) ++ retval = s390_free_memory(); ++ break; + +- if (count_buffer_chars(bufptr, NULLCHAR, cnt)) +- break; ++ case DUMPFILE_MEM_DUMP: ++ if (REMOTE_DUMPFILE()) ++ retval = remote_memory_dump(0); ++ else if (pc->flags & NETDUMP) ++ retval = netdump_memory_dump(fp); ++ else if (pc->flags & KDUMP) ++ retval = kdump_memory_dump(fp); ++ else if (pc->flags & XENDUMP) ++ retval = xendump_memory_dump(fp); ++ else if (pc->flags & DISKDUMP) ++ retval = diskdump_memory_dump(fp); ++ else if (pc->flags & LKCD) ++ retval = lkcd_memory_dump(set_lkcd_fp(fp)); ++ else if (pc->flags & MCLXCD) ++ retval = vas_memory_dump(fp); ++ else if (pc->flags & S390D) ++ retval = s390_memory_dump(fp); ++ break; ++ ++ case DUMPFILE_ENVIRONMENT: ++ if (pc->flags & LKCD) { ++ set_lkcd_fp(fp); ++ dump_lkcd_environment(0); ++ } else if (pc->flags & REM_LKCD) ++ retval = remote_memory_dump(VERBOSE); ++ break; ++ } ++ ++ return retval; ++} ++ ++/* ++ * Functions for sparse mem support ++ */ ++ulong ++sparse_decode_mem_map(ulong coded_mem_map, ulong section_nr) ++{ ++ return coded_mem_map + ++ (section_nr_to_pfn(section_nr) * SIZE(page)); ++} ++ ++void ++sparse_mem_init(void) ++{ ++ ulong addr; ++ ulong mem_section_size; ++ int dimension; ++ ++ if (!IS_SPARSEMEM()) ++ return; ++ ++ MEMBER_OFFSET_INIT(mem_section_section_mem_map, "mem_section", ++ "section_mem_map"); ++ STRUCT_SIZE_INIT(mem_section, "mem_section"); ++ ++ if (!MAX_PHYSMEM_BITS()) ++ error(FATAL, ++ "CONFIG_SPARSEMEM kernels not supported for this architecture\n"); + +- kp += cnt; +- bufptr += cnt; +- size -= cnt; ++ if ((get_array_length("mem_section", &dimension, 0) == ++ (NR_MEM_SECTIONS() / _SECTIONS_PER_ROOT_EXTREME())) || !dimension) ++ vt->flags |= SPARSEMEM_EX; ++ ++ if (IS_SPARSEMEM_EX()) { ++ machdep->sections_per_root = _SECTIONS_PER_ROOT_EXTREME(); ++ mem_section_size = sizeof(void *) * NR_SECTION_ROOTS(); ++ } else { ++ machdep->sections_per_root = _SECTIONS_PER_ROOT(); ++ mem_section_size = SIZE(mem_section) * NR_SECTION_ROOTS(); + } + +- strcpy(buf, strbuf); +- return (strlen(buf)); ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "PAGESIZE=%d\n",PAGESIZE()); ++ fprintf(fp,"mem_section_size = %ld\n", mem_section_size); ++ fprintf(fp, "NR_SECTION_ROOTS = %ld\n", NR_SECTION_ROOTS()); ++ fprintf(fp, "NR_MEM_SECTIONS = %ld\n", NR_MEM_SECTIONS()); ++ fprintf(fp, "SECTIONS_PER_ROOT = %ld\n", SECTIONS_PER_ROOT() ); ++ fprintf(fp, "SECTION_ROOT_MASK = 0x%lx\n", SECTION_ROOT_MASK()); ++ fprintf(fp, "PAGES_PER_SECTION = %ld\n", PAGES_PER_SECTION()); ++ } ++ ++ if (!(vt->mem_sec = (void *)malloc(mem_section_size))) ++ error(FATAL, "cannot malloc mem_sec cache\n"); ++ if (!(vt->mem_section = (char *)malloc(SIZE(mem_section)))) ++ error(FATAL, "cannot malloc mem_section cache\n"); ++ ++ addr = symbol_value("mem_section"); ++ readmem(addr, KVADDR,vt->mem_sec ,mem_section_size, ++ "memory section root table", FAULT_ON_ERROR); + } + +-/* +- * "help -v" output +- */ +-void +-dump_vm_table(int verbose) ++char * ++read_mem_section(ulong addr) + { +- int i; +- struct node_table *nt; +- int others; ++ if ((addr == 0) || !IS_KVADDR(addr)) ++ return 0; ++ ++ readmem(addr, KVADDR, vt->mem_section, SIZE(mem_section), ++ "memory section", FAULT_ON_ERROR); + +- others = 0; +- fprintf(fp, " flags: %lx %s(", +- vt->flags, count_bits_long(vt->flags) > 4 ? "\n " : ""); +- if (vt->flags & NODES) +- fprintf(fp, "%sNODES", others++ ? "|" : ""); +- if (vt->flags & ZONES) +- fprintf(fp, "%sZONES", others++ ? "|" : ""); +- if (vt->flags & PERCPU_KMALLOC_V1) +- fprintf(fp, "%sPERCPU_KMALLOC_V1", others++ ? "|" : ""); +- if (vt->flags & PERCPU_KMALLOC_V2) +- fprintf(fp, "%sPERCPU_KMALLOC_V2", others++ ? "|" : ""); +- if (vt->flags & COMMON_VADDR) +- fprintf(fp, "%sCOMMON_VADDR", others++ ? "|" : ""); +- if (vt->flags & KMEM_CACHE_INIT) +- fprintf(fp, "%sKMEM_CACHE_INIT", others++ ? "|" : ""); +- if (vt->flags & V_MEM_MAP) +- fprintf(fp, "%sV_MEM_MAP", others++ ? "|" : ""); +- if (vt->flags & KMEM_CACHE_UNAVAIL) +- fprintf(fp, "%sKMEM_CACHE_UNAVAIL", others++ ? "|" : ""); +- if (vt->flags & DISCONTIGMEM) +- fprintf(fp, "%sDISCONTIGMEM", others++ ? "|" : ""); +- fprintf(fp, ")\n"); +- if (vt->kernel_pgd[0] == vt->kernel_pgd[1]) +- fprintf(fp, " kernel_pgd[NR_CPUS]: %lx ...\n", +- vt->kernel_pgd[0]); +- else { +- fprintf(fp, " kernel_pgd[NR_CPUS]: "); +- for (i = 0; i < NR_CPUS; i++) { +- if ((i % 4) == 0) +- fprintf(fp, "\n "); +- fprintf(fp, "%lx ", vt->kernel_pgd[i]); +- } +- fprintf(fp, "\n"); +- } +- fprintf(fp, " high_memory: %lx\n", vt->high_memory); +- fprintf(fp, " vmalloc_start: %lx\n", vt->vmalloc_start); +- fprintf(fp, " mem_map: %lx\n", vt->mem_map); +- fprintf(fp, " total_pages: %ld\n", vt->total_pages); +- fprintf(fp, " max_mapnr: %ld\n", vt->max_mapnr); +- fprintf(fp, " totalram_pages: %ld\n", vt->totalram_pages); +- fprintf(fp, " totalhigh_pages: %ld\n", vt->totalhigh_pages); +- fprintf(fp, " num_physpages: %ld\n", vt->num_physpages); +- fprintf(fp, " page_hash_table: %lx\n", vt->page_hash_table); +- fprintf(fp, "page_hash_table_len: %d\n", vt->page_hash_table_len); +- fprintf(fp, " kmem_max_c_num: %ld\n", vt->kmem_max_c_num); +- fprintf(fp, " kmem_max_limit: %ld\n", vt->kmem_max_limit); +- fprintf(fp, " kmem_max_cpus: %ld\n", vt->kmem_max_cpus); +- fprintf(fp, " kmem_cache_count: %ld\n", vt->kmem_cache_count); +- fprintf(fp, " kmem_cache_namelen: %d\n", vt->kmem_cache_namelen); +- fprintf(fp, " PG_reserved: %lx\n", vt->PG_reserved); +- fprintf(fp, " PG_slab: %ld\n", vt->PG_slab); +- fprintf(fp, " paddr_prlen: %d\n", vt->paddr_prlen); +- fprintf(fp, " numnodes: %d\n", vt->numnodes); +- fprintf(fp, " nr_zones: %d\n", vt->nr_zones); +- fprintf(fp, " nr_free_areas: %d\n", vt->nr_free_areas); +- for (i = 0; i < vt->numnodes; i++) { +- nt = &vt->node_table[i]; +- fprintf(fp, " node_table[%d]: \n", i); +- fprintf(fp, " id: %d\n", nt->node_id); +- fprintf(fp, " pgdat: %lx\n", nt->pgdat); +- fprintf(fp, " size: %ld\n", nt->size); +- fprintf(fp, " mem_map: %lx\n", nt->mem_map); +- fprintf(fp, " start_paddr: %llx\n", nt->start_paddr); +- fprintf(fp, " start_mapnr: %ld\n", nt->start_mapnr); +- } ++ return vt->mem_section; ++} + +- fprintf(fp, " dump_free_pages: "); +- if (vt->dump_free_pages == dump_free_pages) +- fprintf(fp, "dump_free_pages()\n"); +- else if (vt->dump_free_pages == dump_free_pages_zones_v1) +- fprintf(fp, "dump_free_pages_zones_v1()\n"); +- else if (vt->dump_free_pages == dump_free_pages_zones_v2) +- fprintf(fp, "dump_free_pages_zones_v2()\n"); +- else if (vt->dump_free_pages == dump_multidimensional_free_pages) +- fprintf(fp, "dump_multidimensional_free_pages()\n"); +- else +- fprintf(fp, "%lx (unknown)\n", (ulong)vt->dump_free_pages); ++ulong ++nr_to_section(ulong nr) ++{ ++ ulong addr; ++ ulong *mem_sec = vt->mem_sec; + +- fprintf(fp, " dump_kmem_cache: "); +- if (vt->dump_kmem_cache == dump_kmem_cache) +- fprintf(fp, "dump_kmem_cache()\n"); +- else if (vt->dump_kmem_cache == dump_kmem_cache_percpu_v1) +- fprintf(fp, "dump_kmem_cache_percpu_v1()\n"); +- else if (vt->dump_kmem_cache == dump_kmem_cache_percpu_v2) +- fprintf(fp, "dump_kmem_cache_percpu_v2()\n"); ++ if ((mem_sec[SECTION_NR_TO_ROOT(nr)] == 0) || ++ !IS_KVADDR(mem_sec[SECTION_NR_TO_ROOT(nr)])) ++ return 0; ++ ++ if (IS_SPARSEMEM_EX()) ++ addr = mem_sec[SECTION_NR_TO_ROOT(nr)] + ++ (nr & SECTION_ROOT_MASK()) * SIZE(mem_section); + else +- fprintf(fp, "%lx (unknown)\n", (ulong)vt->dump_kmem_cache); +- fprintf(fp, " slab_data: %lx\n", (ulong)vt->slab_data); +- if (verbose) +- dump_saved_slab_data(); +- fprintf(fp, " nr_swapfiles: %d\n", vt->nr_swapfiles); +- fprintf(fp, " last_swap_read: %lx\n", vt->last_swap_read); +- fprintf(fp, " swap_info_struct: %lx\n", (ulong)vt->swap_info_struct); ++ addr = symbol_value("mem_section") + ++ (SECTIONS_PER_ROOT() * SECTION_NR_TO_ROOT(nr) + ++ (nr & SECTION_ROOT_MASK())) * SIZE(mem_section); + +- dump_vma_cache(VERBOSE); ++ if (!IS_KVADDR(addr)) ++ return 0; ++ ++ return addr; + } + + /* +- * Calculate the amount of memory referenced in the kernel-specific "nodes". ++ * We use the lower bits of the mem_map pointer to store ++ * a little bit of information. There should be at least ++ * 3 bits here due to 32-bit alignment. + */ +-uint64_t +-total_node_memory() ++#define SECTION_MARKED_PRESENT (1UL<<0) ++#define SECTION_HAS_MEM_MAP (1UL<<1) ++#define SECTION_MAP_LAST_BIT (1UL<<2) ++#define SECTION_MAP_MASK (~(SECTION_MAP_LAST_BIT-1)) ++ ++ ++int ++valid_section(ulong addr) + { +- int i; +- struct node_table *nt; +- uint64_t total; ++ char *mem_section; + +- for (i = total = 0; i < vt->numnodes; i++) { +- nt = &vt->node_table[i]; ++ if ((mem_section = read_mem_section(addr))) ++ return (ULONG(mem_section + ++ OFFSET(mem_section_section_mem_map)) && ++ SECTION_MARKED_PRESENT); ++ return 0; ++} + +- if (CRASHDEBUG(1)) { +- console("node_table[%d]: \n", i); +- console(" id: %d\n", nt->node_id); +- console(" pgdat: %lx\n", nt->pgdat); +- console(" size: %ld\n", nt->size); +- console(" mem_map: %lx\n", nt->mem_map); +- console(" start_paddr: %lx\n", nt->start_paddr); +- console(" start_mapnr: %ld\n", nt->start_mapnr); +- } ++int ++section_has_mem_map(ulong addr) ++{ ++ char *mem_section; + +- total += (uint64_t)((uint64_t)nt->size * (uint64_t)PAGESIZE()); +- } ++ if ((mem_section = read_mem_section(addr))) ++ return (ULONG(mem_section + ++ OFFSET(mem_section_section_mem_map)) ++ && SECTION_HAS_MEM_MAP); ++ return 0; ++} + +- return total; ++ulong ++section_mem_map_addr(ulong addr) ++{ ++ char *mem_section; ++ ulong map; ++ ++ if ((mem_section = read_mem_section(addr))) { ++ map = ULONG(mem_section + ++ OFFSET(mem_section_section_mem_map)); ++ map &= SECTION_MAP_MASK; ++ return map; ++ } ++ return 0; + } + +-/* +- * Dump just the vm_area_struct cache table data so that it can be +- * called from above or for debug purposes. +- */ +-void +-dump_vma_cache(ulong verbose) ++ ++ulong ++valid_section_nr(ulong nr) + { +- int i; +- ulong vhits; ++ ulong addr = nr_to_section(nr); + +- if (!verbose) +- goto show_hits; ++ if (valid_section(addr)) ++ return addr; + +- for (i = 0; i < VMA_CACHE; i++) +- fprintf(fp, " cached_vma[%2d]: %lx (%ld)\n", +- i, vt->cached_vma[i], +- vt->cached_vma_hits[i]); +- fprintf(fp, " vma_cache: %lx\n", (ulong)vt->vma_cache); +- fprintf(fp, " vma_cache_index: %d\n", vt->vma_cache_index); +- fprintf(fp, " vma_cache_fills: %ld\n", vt->vma_cache_fills); +- fflush(fp); ++ return 0; ++} + +-show_hits: +- if (vt->vma_cache_fills) { +- for (i = vhits = 0; i < VMA_CACHE; i++) +- vhits += vt->cached_vma_hits[i]; ++ulong ++pfn_to_map(ulong pfn) ++{ ++ ulong section, page_offset; ++ ulong section_nr; ++ ulong coded_mem_map, mem_map; + +- fprintf(stderr, "%s vma hit rate: %2ld%% (%ld of %ld)\n", +- verbose ? "" : " ", +- (vhits * 100)/vt->vma_cache_fills, +- vhits, vt->vma_cache_fills); +- } ++ section_nr = pfn_to_section_nr(pfn); ++ if (!(section = valid_section_nr(section_nr))) ++ return 0; ++ ++ if (section_has_mem_map(section)) { ++ page_offset = pfn - section_nr_to_pfn(section_nr); ++ coded_mem_map = section_mem_map_addr(section); ++ mem_map = sparse_decode_mem_map(coded_mem_map, section_nr) + ++ (page_offset * SIZE(page)); ++ return mem_map; ++ } ++ ++ return 0; + } + +-/* +- * Guess at the "real" amount of physical memory installed, formatting +- * it in a MB or GB based string. +- */ +-char * +-get_memory_size(char *buf) ++void ++dump_mem_sections(void) + { +- uint64_t total; +- ulong next_gig; +-#ifdef OLDWAY +- ulong mbs, gbs; +-#endif ++ ulong nr,addr; ++ ulong nr_mem_sections; ++ ulong coded_mem_map, mem_map, pfn; ++ char buf1[BUFSIZE]; ++ char buf2[BUFSIZE]; ++ char buf3[BUFSIZE]; ++ char buf4[BUFSIZE]; + +- total = machdep->memory_size(); ++ nr_mem_sections = NR_MEM_SECTIONS(); + +- if ((next_gig = roundup(total, GIGABYTES(1)))) { +- if ((next_gig - total) <= MEGABYTES(64)) +- total = next_gig; ++ fprintf(fp, "\n"); ++ pad_line(fp, BITS32() ? 59 : 67, '-'); ++ fprintf(fp, "\n\nNR %s %s %s PFN\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "SECTION"), ++ mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "CODED_MEM_MAP"), ++ mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "MEM_MAP")); ++ ++ for (nr = 0; nr <= nr_mem_sections ; nr++) { ++ if ((addr = valid_section_nr(nr))) { ++ coded_mem_map = section_mem_map_addr(addr); ++ mem_map = sparse_decode_mem_map(coded_mem_map,nr); ++ pfn = section_nr_to_pfn(nr); ++ ++ fprintf(fp, "%2ld %s %s %s %s\n", ++ nr, ++ mkstring(buf1, VADDR_PRLEN, ++ CENTER|LONG_HEX, MKSTR(addr)), ++ mkstring(buf2, VADDR_PRLEN, ++ CENTER|LONG_HEX|RJUST, MKSTR(coded_mem_map)), ++ mkstring(buf3, VADDR_PRLEN, ++ CENTER|LONG_HEX|RJUST, MKSTR(mem_map)), ++ pc->output_radix == 10 ? ++ mkstring(buf4, VADDR_PRLEN, ++ LONG_DEC|LJUST, MKSTR(pfn)) : ++ mkstring(buf4, VADDR_PRLEN, ++ LONG_HEX|LJUST, MKSTR(pfn))); ++ } + } ++} + +- return (pages_to_size((ulong)(total/PAGESIZE()), buf)); ++void ++list_mem_sections(void) ++{ ++ ulong nr,addr; ++ ulong nr_mem_sections = NR_MEM_SECTIONS(); ++ ulong coded_mem_map; ++ ++ for (nr = 0; nr <= nr_mem_sections ; nr++) { ++ if ((addr = valid_section_nr(nr))) { ++ coded_mem_map = section_mem_map_addr(addr); ++ fprintf(fp, ++ "nr=%ld section = %lx coded_mem_map=%lx pfn=%ld mem_map=%lx\n", ++ nr, ++ addr, ++ coded_mem_map, ++ section_nr_to_pfn(nr), ++ sparse_decode_mem_map(coded_mem_map,nr)); ++ } ++ } ++} + +-#ifdef OLDWAY +- gbs = (ulong)(total/GIGABYTES(1)); +- mbs = (ulong)(total/MEGABYTES(1)); +- if (gbs) +- mbs = (total % GIGABYTES(1))/MEGABYTES(1); ++/* ++ * For kernels containing the node_online_map or node_states[], ++ * return the number of online node bits set. ++ */ ++static int ++get_nodes_online(void) ++{ ++ int i, len, online; ++ struct gnu_request req; ++ ulong *maskptr; ++ long N_ONLINE; ++ ulong mapaddr; + +- if (total%MEGABYTES(1)) +- mbs++; ++ if (!symbol_exists("node_online_map") && ++ !symbol_exists("node_states")) ++ return 0; + +- if (gbs) +- sprintf(buf, mbs ? "%ld GB %ld MB" : "%ld GB", gbs, mbs); +- else +- sprintf(buf, "%ld MB", mbs); ++ if (symbol_exists("node_online_map")) { ++ if (LKCD_KERNTYPES()) { ++ if ((len = STRUCT_SIZE("nodemask_t")) < 0) ++ error(FATAL, ++ "cannot determine type nodemask_t\n"); ++ mapaddr = symbol_value("node_online_map"); ++ } else { ++ len = get_symbol_type("node_online_map", NULL, &req) ++ == TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; ++ mapaddr = symbol_value("node_online_map"); ++ } ++ } else if (symbol_exists("node_states")) { ++ if ((get_symbol_type("node_states", NULL, &req) != TYPE_CODE_ARRAY) || ++ !(len = get_array_length("node_states", NULL, 0)) || ++ !enumerator_value("N_ONLINE", &N_ONLINE)) ++ return 0; ++ len = req.length / len; ++ mapaddr = symbol_value("node_states") + (N_ONLINE * len); ++ } ++ ++ if (!(vt->node_online_map = (ulong *)malloc(len))) ++ error(FATAL, "cannot malloc node_online_map\n"); ++ ++ if (!readmem(mapaddr, KVADDR, ++ (void *)&vt->node_online_map[0], len, "node_online_map", ++ QUIET|RETURN_ON_ERROR)) ++ error(FATAL, "cannot read node_online_map/node_states\n"); ++ ++ vt->node_online_map_len = len/sizeof(ulong); ++ ++ online = 0; ++ ++ maskptr = (ulong *)vt->node_online_map; ++ for (i = 0; i < vt->node_online_map_len; i++, maskptr++) ++ online += count_bits_long(*maskptr); + +- return buf; +-#endif ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "node_online_map: ["); ++ for (i = 0; i < vt->node_online_map_len; i++) ++ fprintf(fp, "%s%lx", i ? ", " : "", vt->node_online_map[i]); ++ fprintf(fp, "] -> nodes online: %d\n", online); ++ } ++ ++ if (online) ++ vt->numnodes = online; ++ ++ return online; + } + + /* +- * For use by architectures not having machine-specific manners for +- * best determining physical memory size. +- */ +-uint64_t +-generic_memory_size(void) ++ * Return the next node index, with "first" being the first acceptable node. ++ */ ++static int ++next_online_node(int first) + { +- if (machdep->memsize) +- return machdep->memsize; ++ int i, j, node; ++ ulong mask, *maskptr; + +- return (machdep->memsize = total_node_memory()); ++ if ((first/BITS_PER_LONG) >= vt->node_online_map_len) { ++ error(INFO, "next_online_node: %d is too large!\n", first); ++ return -1; ++ } ++ ++ maskptr = (ulong *)vt->node_online_map; ++ for (i = node = 0; i < vt->node_online_map_len; i++, maskptr++) { ++ mask = *maskptr; ++ for (j = 0; j < BITS_PER_LONG; j++, node++) { ++ if (mask & 1) { ++ if (node >= first) ++ return node; ++ } ++ mask >>= 1; ++ } ++ } ++ ++ return -1; + } + + /* +- * Determine whether a virtual address is user or kernel or ambiguous. +- */ +-int +-vaddr_type(ulong vaddr, struct task_context *tc) ++ * Modify appropriately for architecture/kernel nuances. ++ */ ++static ulong ++next_online_pgdat(int node) + { +- int memtype, found; ++ char buf[BUFSIZE]; ++ ulong pgdat; + +- if (!tc) +- tc = CURRENT_CONTEXT(); +- memtype = found = 0; ++ /* ++ * Default -- look for type: struct pglist_data node_data[] ++ */ ++ if (LKCD_KERNTYPES()) { ++ if (!kernel_symbol_exists("node_data")) ++ goto pgdat2; ++ /* ++ * Just index into node_data[] without checking that it is ++ * an array; kerntypes have no such symbol information. ++ */ ++ } else { ++ if (get_symbol_type("node_data", NULL, NULL) != TYPE_CODE_ARRAY) ++ goto pgdat2; ++ ++ open_tmpfile(); ++ sprintf(buf, "whatis node_data"); ++ if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { ++ close_tmpfile(); ++ goto pgdat2; ++ } ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (STRNEQ(buf, "type = ")) ++ break; ++ } ++ close_tmpfile(); ++ ++ if ((!strstr(buf, "struct pglist_data *") && ++ !strstr(buf, "pg_data_t *")) || ++ (count_chars(buf, '[') != 1) || ++ (count_chars(buf, ']') != 1)) ++ goto pgdat2; ++ } ++ ++ if (!readmem(symbol_value("node_data") + (node * sizeof(void *)), ++ KVADDR, &pgdat, sizeof(void *), "node_data", RETURN_ON_ERROR) || ++ !IS_KVADDR(pgdat)) ++ goto pgdat2; ++ ++ return pgdat; ++ ++pgdat2: ++ if (LKCD_KERNTYPES()) { ++ if (!kernel_symbol_exists("pgdat_list")) ++ goto pgdat3; ++ } else { ++ if (get_symbol_type("pgdat_list",NULL,NULL) != TYPE_CODE_ARRAY) ++ goto pgdat3; + +- if (machdep->is_uvaddr(vaddr, tc)) { +- memtype |= UVADDR; +- found++; +- } ++ open_tmpfile(); ++ sprintf(buf, "whatis pgdat_list"); ++ if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { ++ close_tmpfile(); ++ goto pgdat3; ++ } ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (STRNEQ(buf, "type = ")) ++ break; ++ } ++ close_tmpfile(); + +- if (machdep->is_kvaddr(vaddr)) { +- memtype |= KVADDR; +- found++; ++ if ((!strstr(buf, "struct pglist_data *") && ++ !strstr(buf, "pg_data_t *")) || ++ (count_chars(buf, '[') != 1) || ++ (count_chars(buf, ']') != 1)) ++ goto pgdat3; + } + +- if (found == 1) +- return memtype; +- else +- return AMBIGUOUS; +-} ++ if (!readmem(symbol_value("pgdat_list") + (node * sizeof(void *)), ++ KVADDR, &pgdat, sizeof(void *), "pgdat_list", RETURN_ON_ERROR) || ++ !IS_KVADDR(pgdat)) ++ goto pgdat3; + +-/* +- * Determine the first valid user space address +- */ +-static int +-address_space_start(struct task_context *tc, ulong *addr) +-{ +- ulong vma; +- char *vma_buf; ++ return pgdat; + +- if (!tc->mm_struct) +- return FALSE; ++pgdat3: ++ if (symbol_exists("contig_page_data") && (node == 0)) ++ return symbol_value("contig_page_data"); + +- fill_mm_struct(tc->mm_struct); +- vma = ULONG(tt->mm_struct + OFFSET(mm_struct_mmap)); +- if (!vma) +- return FALSE; +- vma_buf = fill_vma_cache(vma); +- *addr = ULONG(vma_buf + OFFSET(vm_area_struct_vm_start)); +- +- return TRUE; ++ return 0; + } + + /* +- * Search for a given value between a starting and ending address range, +- * applying an optional mask for "don't care" bits. As an alternative +- * to entering the starting address value, -k means "start of kernel address +- * space". For processors with ambiguous user/kernel address spaces, +- * -u or -k must be used (with or without -s) as a differentiator. ++ * Make the vm_stat[] array contents easily accessible. + */ +-void +-cmd_search(void) ++static int ++vm_stat_init(void) + { +- int c; +- ulong start, end, mask, memtype, len; +- ulong uvaddr_end; +- int sflag; +- struct meminfo meminfo; +- ulong value_array[MAXARGS]; +- struct syment *sp; ++ char buf[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ int i, c, stringlen, total; ++ struct gnu_request *req; ++ char *start; + +- start = end = mask = sflag = memtype = len = 0; +- uvaddr_end = COMMON_VADDR_SPACE() ? (ulong)(-1) : machdep->kvbase; +- BZERO(value_array, sizeof(ulong) * MAXARGS); ++ if (vt->flags & VM_STAT) ++ return TRUE; + +- while ((c = getopt(argcnt, args, "l:uks:e:v:m:")) != EOF) { +- switch(c) +- { +- case 'u': +- if (!sflag) { +- address_space_start(CURRENT_CONTEXT(),&start); +- sflag++; +- } +- memtype = UVADDR; +- sflag++; +- break; ++ if ((vt->nr_vm_stat_items == -1) || !symbol_exists("vm_stat")) ++ goto bailout; + +- case 'k': +- if (!sflag) { +- start = machdep->kvbase; +- sflag++; +- } +- memtype = KVADDR; +- sflag++; +- break; ++ /* ++ * look for type: type = atomic_long_t [] ++ */ ++ if (LKCD_KERNTYPES()) { ++ if (!symbol_exists("vm_stat")) ++ goto bailout; ++ /* ++ * Just assume that vm_stat is an array; there is ++ * no symbol info in a kerntypes file. ++ */ ++ } else { ++ if (!symbol_exists("vm_stat") || ++ get_symbol_type("vm_stat", NULL, NULL) != TYPE_CODE_ARRAY) ++ goto bailout; ++ ++ open_tmpfile(); ++ sprintf(buf, "whatis vm_stat"); ++ if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { ++ close_tmpfile(); ++ goto bailout; ++ } ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (STRNEQ(buf, "type = ")) ++ break; ++ } ++ close_tmpfile(); + +- case 's': +- if ((sp = symbol_search(optarg))) +- start = sp->value; +- else +- start = htol(optarg, FAULT_ON_ERROR, NULL); +- sflag++; +- break; ++ if (!strstr(buf, "atomic_long_t") || ++ (count_chars(buf, '[') != 1) || ++ (count_chars(buf, ']') != 1)) ++ goto bailout; ++ } ++ ++ open_tmpfile(); ++ req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); ++ req->command = GNU_GET_DATATYPE; ++ req->name = "zone_stat_item"; ++ req->flags = GNU_PRINT_ENUMERATORS; ++ gdb_interface(req); ++ FREEBUF(req); + +- case 'e': +- if ((sp = symbol_search(optarg))) +- end = sp->value; +- else +- end = htol(optarg, FAULT_ON_ERROR, NULL); +- break; ++ stringlen = 1; + +- case 'l': +- len = stol(optarg, FAULT_ON_ERROR, NULL); ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "{") || strstr(buf, "}")) ++ continue; ++ clean_line(buf); ++ c = parse_line(buf, arglist); ++ if (STREQ(arglist[0], "NR_VM_ZONE_STAT_ITEMS")) { ++ vt->nr_vm_stat_items = atoi(arglist[2]); + break; +- +- case 'm': +- mask = htol(optarg, FAULT_ON_ERROR, NULL); +- break; +- +- default: +- argerrs++; +- break; +- } ++ } else ++ stringlen += strlen(arglist[0]); + } + +- if (argerrs || !sflag || !args[optind] || (len && end)) +- cmd_usage(pc->curcmd, SYNOPSIS); ++ total = stringlen + vt->nr_vm_stat_items + ++ (sizeof(void *) * vt->nr_vm_stat_items); ++ if (!(vt->vm_stat_items = (char **)malloc(total))) { ++ close_tmpfile(); ++ error(FATAL, "cannot malloc vm_stat_items cache\n"); ++ } + +- if (!memtype) +- memtype = vaddr_type(start, CURRENT_CONTEXT()); ++ start = (char *)&vt->vm_stat_items[vt->nr_vm_stat_items]; + +- switch (memtype) +- { +- case UVADDR: +- if (!IS_UVADDR(start, CURRENT_CONTEXT())) { +- error(INFO, "invalid user virtual address: %lx\n", +- start); +- cmd_usage(pc->curcmd, SYNOPSIS); ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "{") || strstr(buf, "}")) ++ continue; ++ c = parse_line(buf, arglist); ++ i = atoi(arglist[2]); ++ if (i < vt->nr_vm_stat_items) { ++ vt->vm_stat_items[i] = start; ++ strcpy(start, arglist[0]); ++ start += strlen(arglist[0]) + 1; + } +- break; ++ } ++ close_tmpfile(); + +- case KVADDR: +- if (!IS_KVADDR(start)) { +- error(INFO, "invalid kernel virtual address: %lx\n", +- start); +- cmd_usage(pc->curcmd, SYNOPSIS); +- } +- break; ++ vt->flags |= VM_STAT; ++ return TRUE; + +- case AMBIGUOUS: +- error(INFO, +- "ambiguous virtual address: %lx (requires -u or -k)\n", +- start); +- cmd_usage(pc->curcmd, SYNOPSIS); ++bailout: ++ vt->nr_vm_stat_items = -1; ++ return FALSE; ++} ++ ++/* ++ * Either dump all vm_stat entries, or return the value of ++ * the specified vm_stat item. Use the global counter unless ++ * a zone-specific address is passed. ++ */ ++static int ++dump_vm_stat(char *item, long *retval, ulong zone) ++{ ++ char *buf; ++ ulong *vp; ++ ulong location; ++ int i; ++ ++ if (!vm_stat_init()) { ++ if (!item) ++ if (CRASHDEBUG(1)) ++ error(INFO, ++ "vm_stat not available in this kernel\n"); ++ return FALSE; + } + +- if (!end && !len) { +- switch (memtype) +- { +- case UVADDR: +- end = uvaddr_end; +- break; ++ buf = GETBUF(sizeof(ulong) * vt->nr_vm_stat_items); + +- case KVADDR: +- if (vt->vmalloc_start < machdep->identity_map_base) +- end = (ulong)(-1); +- else { +- meminfo.memtype = KVADDR; +- meminfo.spec_addr = 0; +- meminfo.flags = (ADDRESS_SPECIFIED|GET_HIGHEST); +- dump_vmlist(&meminfo); +- end = meminfo.retval; +- } +- break; +- } +- } else if (len) +- end = start + len; ++ location = zone ? zone : symbol_value("vm_stat"); + +- switch (memtype) +- { +- case UVADDR: +- if (end > uvaddr_end) { +- error(INFO, +- "address range starts in user space and ends kernel space\n"); +- cmd_usage(pc->curcmd, SYNOPSIS); +- } +- /* FALLTHROUGH */ +- case KVADDR: +- if (end < start) { +- error(INFO, +- "ending address %lx is below starting address %lx\n", +- end, start); +- cmd_usage(pc->curcmd, SYNOPSIS); +- } +- break; ++ readmem(location, KVADDR, buf, ++ sizeof(ulong) * vt->nr_vm_stat_items, ++ "vm_stat", FAULT_ON_ERROR); ++ ++ if (!item) { ++ if (!zone) ++ fprintf(fp, " VM_STAT:\n"); ++ vp = (ulong *)buf; ++ for (i = 0; i < vt->nr_vm_stat_items; i++) ++ fprintf(fp, "%23s: %ld\n", vt->vm_stat_items[i], vp[i]); ++ return TRUE; + } + +- c = 0; +- while (args[optind]) { +- value_array[c] = htol(args[optind], FAULT_ON_ERROR, NULL); +- c++; +- optind++; ++ vp = (ulong *)buf; ++ for (i = 0; i < vt->nr_vm_stat_items; i++) { ++ if (STREQ(vt->vm_stat_items[i], item)) { ++ *retval = vp[i]; ++ return TRUE; ++ } + } + +- search(start, end, mask, memtype, value_array, c); ++ return FALSE; + } + + /* +- * Do the work for cmd_search(). ++ * Dump the cumulative totals of the per_cpu__page_states counters. + */ ++int ++dump_page_states(void) ++{ ++ struct syment *sp; ++ ulong addr, value; ++ int i, c, fd, len, instance, members; ++ char buf[BUFSIZE]; ++ char *arglist[MAXARGS]; ++ struct entry { ++ char *name; ++ ulong value; ++ } *entry_list; ++ struct stat stat; ++ char *namebuf, *nameptr; + +-#define SEARCHMASK(X) ((X) | mask) ++ if (!(sp = symbol_search("per_cpu__page_states"))) { ++ if (CRASHDEBUG(1)) ++ error(INFO, "per_cpu__page_states" ++ "not available in this kernel\n"); ++ return FALSE; ++ } + +-static void +-search(ulong start, ulong end, ulong mask, int memtype, ulong *value, int vcnt) +-{ +- int i, j; +- ulong pp, next, *ubp; +- int wordcnt, lastpage; +- ulong page; +- physaddr_t paddr; +- char *pagebuf; ++ instance = members = len = 0; + +- if (start & (sizeof(long)-1)) { +- start &= ~(sizeof(long)-1); +- error(INFO, "rounding down start address to: %lx\n", start); ++ sprintf(buf, "ptype struct page_state"); ++ ++ open_tmpfile(); ++ if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { ++ close_tmpfile(); ++ return FALSE; + } + +- pagebuf = GETBUF(PAGESIZE()); +- next = start; ++ fflush(pc->tmpfile); ++ fd = fileno(pc->tmpfile); ++ fstat(fd, &stat); ++ namebuf = GETBUF(stat.st_size); ++ nameptr = namebuf; + +- for (pp = VIRTPAGEBASE(start); next < end; next = pp) { +- lastpage = (VIRTPAGEBASE(next) == VIRTPAGEBASE(end)); +- if (LKCD_DUMPFILE()) +- set_lkcd_nohash(); ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "struct page_state") || ++ strstr(buf, "}")) ++ continue; ++ members++; ++ } + +- switch (memtype) +- { +- case UVADDR: +- if (!uvtop(CURRENT_CONTEXT(), pp, &paddr, 0) || +- !phys_to_page(paddr, &page)) { +- if (!next_upage(CURRENT_CONTEXT(), pp, &pp)) +- return; +- continue; +- } +- break; ++ entry_list = (struct entry *) ++ GETBUF(sizeof(struct entry) * members); + +- case KVADDR: +- if (!kvtop(CURRENT_CONTEXT(), pp, &paddr, 0) || +- !phys_to_page(paddr, &page)) { +- if (!next_kpage(pp, &pp)) +- return; +- continue; +- } +- break; +- } ++ rewind(pc->tmpfile); ++ i = 0; ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "struct page_state") || ++ strstr(buf, "}")) ++ continue; ++ strip_ending_char(strip_linefeeds(buf), ';'); ++ c = parse_line(buf, arglist); ++ strcpy(nameptr, arglist[c-1]); ++ entry_list[i].name = nameptr; ++ if (strlen(nameptr) > len) ++ len = strlen(nameptr); ++ nameptr += strlen(nameptr)+2; ++ i++; ++ } ++ close_tmpfile(); ++ ++ open_tmpfile(); ++ ++ for (c = 0; c < kt->cpus; c++) { ++ addr = sp->value + kt->__per_cpu_offset[c]; ++ dump_struct("page_state", addr, RADIX(16)); ++ } + +- if (!readmem(paddr, PHYSADDR, pagebuf, PAGESIZE(), +- "search page", RETURN_ON_ERROR|QUIET)) { +- pp += PAGESIZE(); ++ i = 0; ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "struct page_state")) { ++ instance++; ++ i = 0; + continue; + } ++ if (strstr(buf, "}")) ++ continue; ++ strip_linefeeds(buf); ++ extract_hex(buf, &value, ',', TRUE); ++ entry_list[i].value += value; ++ i++; ++ } + +- ubp = (ulong *)&pagebuf[next - pp]; +- if (lastpage) { +- if (end == (ulong)(-1)) +- wordcnt = PAGESIZE()/sizeof(long); +- else +- wordcnt = (end - next)/sizeof(long); +- } else +- wordcnt = (PAGESIZE() - (next - pp))/sizeof(long); ++ close_tmpfile(); + +- for (i = 0; i < wordcnt; i++, ubp++, next += sizeof(long)) { +- for (j = 0; j < vcnt; j++) { +- if (SEARCHMASK(*ubp) == SEARCHMASK(value[j])) +- fprintf(fp, "%lx: %lx\n", next, *ubp); +- } +- } ++ fprintf(fp, " PAGE_STATES:\n"); ++ for (i = 0; i < members; i++) { ++ sprintf(buf, "%s", entry_list[i].name); ++ fprintf(fp, "%s", mkstring(buf, len+2, RJUST, 0)); ++ fprintf(fp, ": %ld\n", entry_list[i].value); ++ } + +- if (CRASHDEBUG(1)) +- if ((pp % (1024*1024)) == 0) +- console("%lx\n", pp); ++ FREEBUF(namebuf); ++ FREEBUF(entry_list); + +- pp += PAGESIZE(); +- } ++ return TRUE; + } + + +-/* +- * Return the next mapped user virtual address page that comes after +- * the passed-in address. ++/* ++ * Dump the cumulative totals of the per_cpu__vm_event_state ++ * counters. + */ +-static int +-next_upage(struct task_context *tc, ulong vaddr, ulong *nextvaddr) ++static int ++dump_vm_event_state(void) + { +- ulong vma, total_vm; +- int found; +- char *vma_buf; +- ulong vm_start, vm_end; +- void *vm_next; +- +- if (!tc->mm_struct) +- return FALSE; +- +- fill_mm_struct(tc->mm_struct); +- vma = ULONG(tt->mm_struct + OFFSET(mm_struct_mmap)); +- total_vm = ULONG(tt->mm_struct + OFFSET(mm_struct_total_vm)); ++ int i, c; ++ struct syment *sp; ++ ulong addr; ++ ulong *events, *cumulative; + +- if (!vma || (total_vm == 0)) ++ if (!vm_event_state_init()) + return FALSE; + +- vaddr = VIRTPAGEBASE(vaddr) + PAGESIZE(); /* first possible page */ +- +- for (found = FALSE; vma; vma = (ulong)vm_next) { +- vma_buf = fill_vma_cache(vma); ++ events = (ulong *)GETBUF((sizeof(ulong) * vt->nr_vm_event_items) * 2); ++ cumulative = &events[vt->nr_vm_event_items]; + +- vm_start = ULONG(vma_buf + OFFSET(vm_area_struct_vm_start)); +- vm_end = ULONG(vma_buf + OFFSET(vm_area_struct_vm_end)); +- vm_next = VOID_PTR(vma_buf + OFFSET(vm_area_struct_vm_next)); ++ sp = symbol_search("per_cpu__vm_event_states"); + +- if (vaddr <= vm_start) { +- *nextvaddr = vm_start; +- return TRUE; ++ for (c = 0; c < kt->cpus; c++) { ++ addr = sp->value + kt->__per_cpu_offset[c]; ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "[%d]: %lx\n", c, addr); ++ dump_struct("vm_event_state", addr, RADIX(16)); + } ++ readmem(addr, KVADDR, events, ++ sizeof(ulong) * vt->nr_vm_event_items, ++ "vm_event_states buffer", FAULT_ON_ERROR); ++ for (i = 0; i < vt->nr_vm_event_items; i++) ++ cumulative[i] += events[i]; ++ } + +- if ((vaddr > vm_start) && (vaddr < vm_end)) { +- *nextvaddr = vaddr; +- return TRUE; +- } +- } ++ fprintf(fp, "\n VM_EVENT_STATES:\n"); ++ for (i = 0; i < vt->nr_vm_event_items; i++) ++ fprintf(fp, "%23s: %ld\n", vt->vm_event_items[i], cumulative[i]); + +- return FALSE; ++ FREEBUF(events); ++ ++ return TRUE; + } + +-/* +- * Return the next kernel virtual address page that comes after +- * the passed-in address. +- */ + static int +-next_kpage(ulong vaddr, ulong *nextvaddr) ++vm_event_state_init(void) + { +- int n; +- ulong paddr, vaddr_orig, node_size; +- struct node_table *nt; +- ulonglong pstart, pend; +- ulong vmalloc_limit; +- struct meminfo meminfo; ++ int i, c, stringlen, total; ++ long count; ++ struct gnu_request *req; ++ char *arglist[MAXARGS]; ++ char buf[BUFSIZE]; ++ char *start; + +- vaddr_orig = vaddr; +- vaddr = VIRTPAGEBASE(vaddr) + PAGESIZE(); /* first possible page */ ++ if (vt->flags & VM_EVENT) ++ return TRUE; + +- if (vaddr < vaddr_orig) /* wrapped back to zero? */ +- return FALSE; ++ if ((vt->nr_vm_event_items == -1) || ++ !symbol_exists("per_cpu__vm_event_states")) ++ goto bailout; + +- meminfo.memtype = KVADDR; +- meminfo.spec_addr = 0; +- meminfo.flags = (ADDRESS_SPECIFIED|GET_HIGHEST); +- dump_vmlist(&meminfo); +- vmalloc_limit = meminfo.retval; ++ if (!enumerator_value("NR_VM_EVENT_ITEMS", &count)) ++ return FALSE; + +- if (IS_VMALLOC_ADDR(vaddr_orig)) { +- if (IS_VMALLOC_ADDR(vaddr) && (vaddr < vmalloc_limit)) { +- *nextvaddr = vaddr; +- return TRUE; +- } ++ vt->nr_vm_event_items = count; + +- if (vt->vmalloc_start < machdep->identity_map_base) { +- *nextvaddr = machdep->identity_map_base; +- return TRUE; +- } ++ open_tmpfile(); ++ req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); ++ req->command = GNU_GET_DATATYPE; ++ req->name = "vm_event_item"; ++ req->flags = GNU_PRINT_ENUMERATORS; ++ gdb_interface(req); ++ FREEBUF(req); + +- return FALSE; +- } ++ stringlen = 1; + +- paddr = VTOP(vaddr); ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "{") || strstr(buf, "}")) ++ continue; ++ clean_line(buf); ++ c = parse_line(buf, arglist); ++ if (STREQ(arglist[0], "NR_VM_EVENT_ITEMS")) ++ break; ++ else ++ stringlen += strlen(arglist[0]); ++ } + +- for (n = 0; n < vt->numnodes; n++) { +- nt = &vt->node_table[n]; +- if ((vt->flags & V_MEM_MAP) && (vt->numnodes == 1)) +- node_size = vt->max_mapnr; +- else +- node_size = nt->size; ++ total = stringlen + vt->nr_vm_event_items + ++ (sizeof(void *) * vt->nr_vm_event_items); ++ if (!(vt->vm_event_items = (char **)malloc(total))) { ++ close_tmpfile(); ++ error(FATAL, "cannot malloc vm_event_items cache\n"); ++ } + +- pstart = nt->start_paddr; +- pend = pstart + ((ulonglong)node_size * PAGESIZE()); ++ start = (char *)&vt->vm_event_items[vt->nr_vm_event_items]; + +- if ((paddr < pstart) || (paddr >= pend)) ++ rewind(pc->tmpfile); ++ while (fgets(buf, BUFSIZE, pc->tmpfile)) { ++ if (strstr(buf, "{") || strstr(buf, "}")) + continue; +- /* +- * We're in the physical range. +- */ +- return TRUE; ++ c = parse_line(buf, arglist); ++ i = atoi(arglist[2]); ++ if (i < vt->nr_vm_event_items) { ++ vt->vm_event_items[i] = start; ++ strcpy(start, arglist[0]); ++ start += strlen(arglist[0]) + 1; ++ } + } ++ close_tmpfile(); + +- if (vt->vmalloc_start > vaddr) { +- *nextvaddr = vt->vmalloc_start; +- return TRUE; +- } else +- return FALSE; ++ vt->flags |= VM_EVENT; ++ return TRUE; ++ ++bailout: ++ vt->nr_vm_event_items = -1; ++ return FALSE; + } + ++ + /* +- * Display swap statistics. ++ * Support for slub.c slab cache. + */ +-void +-cmd_swap(void) ++static void ++kmem_cache_init_slub(void) + { +- int c; +- +- while ((c = getopt(argcnt, args, "")) != EOF) { +- switch(c) +- { +- default: +- argerrs++; +- break; +- } +- } ++ if (CRASHDEBUG(1) && ++ !(vt->flags & CONFIG_NUMA) && (vt->numnodes > 1)) ++ error(WARNING, ++ "kmem_cache_init_slub: numnodes: %d without CONFIG_NUMA\n", ++ vt->numnodes); + +- if (argerrs) +- cmd_usage(pc->curcmd, SYNOPSIS); ++ vt->cpu_slab_type = MEMBER_TYPE("kmem_cache", "cpu_slab"); + +- dump_swap_info(VERBOSE, NULL, NULL); ++ vt->flags |= KMEM_CACHE_INIT; + } + +-/* +- * Do the work for cmd_swap(). +- */ +- +-#define SWP_USED 1 +-#define SWAP_MAP_BAD 0x8000 +- +-char *swap_info_hdr = \ +-"FILENAME TYPE SIZE USED PCT PRIORITY\n"; +- +-static int +-dump_swap_info(ulong swapflags, ulong *totalswap_pages, ulong *totalused_pages) ++static void ++kmem_cache_list_slub(void) + { +- int i, j; +- int flags, swap_device, pages, prio, usedswap; +- ulong swap_file, max, swap_map, pct; +- ulong vfsmnt; +- ulong swap_info; +- ushort *map; +- ulong totalswap, totalused; ++ int i, cnt; ++ ulong *cache_list; ++ ulong name; ++ char *cache_buf; + char buf[BUFSIZE]; + +- if (!symbol_exists("nr_swapfiles")) +- error(FATAL, "nr_swapfiles doesn't exist in this kernel!\n"); ++ cnt = get_kmem_cache_list(&cache_list); ++ cache_buf = GETBUF(SIZE(kmem_cache)); + +- if (!symbol_exists("swap_info")) +- error(FATAL, "swap_info doesn't exist in this kernel!\n"); ++ for (i = 0; i < cnt; i++) { ++ fprintf(fp, "%lx ", cache_list[i]); + +- swap_info = symbol_value("swap_info"); ++ readmem(cache_list[i], KVADDR, cache_buf, ++ SIZE(kmem_cache), "kmem_cache buffer", ++ FAULT_ON_ERROR); + +- if (swapflags & VERBOSE) +- fprintf(fp, swap_info_hdr); ++ name = ULONG(cache_buf + OFFSET(kmem_cache_name)); ++ if (!read_string(name, buf, BUFSIZE-1)) ++ sprintf(buf, "(unknown)\n"); ++ ++ fprintf(fp, "%s\n", buf); ++ } + +- totalswap = totalused = 0; ++ FREEBUF(cache_list); ++ FREEBUF(cache_buf); ++} + +- for (i = 0; i < vt->nr_swapfiles; i++, +- swap_info += SIZE(swap_info_struct)){ +- fill_swap_info(swap_info); ++#define DUMP_KMEM_CACHE_INFO_SLUB() dump_kmem_cache_info_slub(si) + +- flags = INT(vt->swap_info_struct + +- OFFSET(swap_info_struct_flags)); ++static void ++dump_kmem_cache_info_slub(struct meminfo *si) ++{ ++ char b1[BUFSIZE]; ++ char b2[BUFSIZE]; ++ int namelen, sizelen, spacelen; + +- if (!(flags & SWP_USED)) +- continue; ++ fprintf(fp, "%s ", ++ mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->cache))); + +- swap_file = ULONG(vt->swap_info_struct + +- OFFSET(swap_info_struct_swap_file)); ++ namelen = strlen(si->curname); ++ sprintf(b2, "%ld", si->objsize); ++ sizelen = strlen(b2); ++ spacelen = 0; + +- swap_device = INT(vt->swap_info_struct + +- OFFSET_OPTION(swap_info_struct_swap_device, +- swap_info_struct_old_block_size)); ++ if (namelen++ > 18) { ++ spacelen = 29 - namelen - sizelen; ++ fprintf(fp, "%s%s%ld ", si->curname, ++ space(spacelen <= 0 ? 1 : spacelen), si->objsize); ++ if (spacelen > 0) ++ spacelen = 1; ++ sprintf(b1, "%c%dld ", '%', 9 + spacelen - 1); ++ } else { ++ fprintf(fp, "%-18s %8ld ", si->curname, si->objsize); ++ sprintf(b1, "%c%dld ", '%', 9); ++ } + +- pages = INT(vt->swap_info_struct + +- OFFSET(swap_info_struct_pages)); ++ fprintf(fp, b1, si->inuse); + +- totalswap += pages; +- pages <<= (PAGESHIFT() - 10); ++ fprintf(fp, "%8ld %5ld %4ldk\n", ++ si->num_slabs * si->objects, ++ si->num_slabs, si->slabsize/1024); ++} + +- prio = INT(vt->swap_info_struct + +- OFFSET(swap_info_struct_prio)); ++static void ++dump_kmem_cache_slub(struct meminfo *si) ++{ ++ int i; ++ ulong name, oo; ++ unsigned int size, objsize, objects, order, offset; ++ char *reqname, *p1; ++ char kbuf[BUFSIZE]; ++ char buf[BUFSIZE]; + +- max = ULONG(vt->swap_info_struct + +- OFFSET(swap_info_struct_max)); ++ if (INVALID_MEMBER(kmem_cache_node_nr_slabs)) { ++ error(INFO, ++ "option requires kmem_cache_node.nr_slabs member!\n" ++ "(the kernel must be built with CONFIG_SLUB_DEBUG)\n"); ++ return; ++ } + +- swap_map = ULONG(vt->swap_info_struct + +- OFFSET(swap_info_struct_swap_map)); ++ si->cache_count = get_kmem_cache_list(&si->cache_list); ++ si->cache_buf = GETBUF(SIZE(kmem_cache)); + +- if (swap_file) { +- if (VALID_MEMBER(swap_info_struct_swap_vfsmnt)) { +- vfsmnt = ULONG(vt->swap_info_struct + +- OFFSET(swap_info_struct_swap_vfsmnt)); +- get_pathname(swap_file, buf, BUFSIZE, +- 1, vfsmnt); +- } else if (VALID_MEMBER +- (swap_info_struct_old_block_size)) { +- get_pathname(file_to_dentry(swap_file), +- buf, BUFSIZE, 1, 0); +- } else { +- get_pathname(swap_file, buf, BUFSIZE, 1, 0); +- } +- } else +- sprintf(buf, "(unknown)"); ++ if (!si->reqname && ++ !(si->flags & (ADDRESS_SPECIFIED|GET_SLAB_PAGES))) ++ fprintf(fp, kmem_cache_hdr); ++ ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if ((p1 = is_slab_page(si, kbuf))) { ++ si->flags |= VERBOSE; ++ si->slab = (ulong)si->spec_addr; ++ } else if (!(p1 = vaddr_to_kmem_cache(si->spec_addr, kbuf, ++ VERBOSE))) { ++ error(INFO, ++ "address is not allocated in slab subsystem: %lx\n", ++ si->spec_addr); ++ goto bailout; ++ } ++ ++ if (si->reqname && (si->reqname != p1)) ++ error(INFO, ++ "ignoring pre-selected %s cache for address: %lx\n", ++ si->reqname, si->spec_addr, si->reqname); ++ reqname = p1; ++ } else ++ reqname = si->reqname; + +- map = (ushort *)GETBUF(sizeof(ushort) * max); ++ for (i = 0; i < si->cache_count; i++) { ++ if (!readmem(si->cache_list[i], KVADDR, si->cache_buf, ++ SIZE(kmem_cache), "kmem_cache buffer", RETURN_ON_ERROR)) ++ goto next_cache; + +- if (!readmem(swap_map, KVADDR, map, +- sizeof(ushort) * max, "swap_info swap_map data", +- RETURN_ON_ERROR|QUIET)) { +- if (swapflags & RETURN_ON_ERROR) { +- *totalswap_pages = swap_map; +- *totalused_pages = i; +- return FALSE; +- } else +- error(FATAL, +- "swap_info[%d].swap_map at %lx is unaccessible\n", +- i, swap_map); ++ name = ULONG(si->cache_buf + OFFSET(kmem_cache_name)); ++ if (!read_string(name, buf, BUFSIZE-1)) ++ sprintf(buf, "(unknown)"); ++ if (reqname) { ++ if (!STREQ(reqname, buf)) ++ continue; ++ fprintf(fp, kmem_cache_hdr); + } +- +- usedswap = 0; +- for (j = 0; j < max; j++) { +- switch (map[j]) +- { +- case SWAP_MAP_BAD: +- case 0: +- continue; +- default: +- usedswap++; +- } ++ if (ignore_cache(si, buf)) { ++ fprintf(fp, "%lx %-18s [IGNORED]\n", ++ si->cache_list[i], buf); ++ goto next_cache; + } + +- FREEBUF(map); ++ objsize = UINT(si->cache_buf + OFFSET(kmem_cache_objsize)); ++ size = UINT(si->cache_buf + OFFSET(kmem_cache_size)); ++ offset = UINT(si->cache_buf + OFFSET(kmem_cache_offset)); ++ if (VALID_MEMBER(kmem_cache_objects)) { ++ objects = UINT(si->cache_buf + ++ OFFSET(kmem_cache_objects)); ++ order = UINT(si->cache_buf + OFFSET(kmem_cache_order)); ++ } else if (VALID_MEMBER(kmem_cache_oo)) { ++ oo = ULONG(si->cache_buf + OFFSET(kmem_cache_oo)); ++ objects = oo_objects(oo); ++ order = oo_order(oo); ++ } else ++ error(FATAL, "cannot determine " ++ "kmem_cache objects/order values\n"); + +- totalused += usedswap; +- usedswap <<= (PAGESHIFT() - 10); +- pct = (usedswap * 100)/pages; ++ si->cache = si->cache_list[i]; ++ si->curname = buf; ++ si->objsize = objsize; ++ si->size = size; ++ si->objects = objects; ++ si->slabsize = (PAGESIZE() << order); ++ si->inuse = si->num_slabs = 0; ++ si->slab_offset = offset; ++ if (!get_kmem_cache_slub_data(GET_SLUB_SLABS, si) || ++ !get_kmem_cache_slub_data(GET_SLUB_OBJECTS, si)) ++ goto next_cache; + +- if (swapflags & VERBOSE) +- fprintf(fp, "%-15s %s %7dk %7dk %2ld%% %d\n", +- buf, swap_device ? "PARTITION" : " FILE ", +- pages, usedswap, pct, prio); +- } ++ DUMP_KMEM_CACHE_INFO_SLUB(); + +- if (totalswap_pages) +- *totalswap_pages = totalswap; +- if (totalused_pages) +- *totalused_pages = totalused; ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if (!si->slab) ++ si->slab = vaddr_to_slab(si->spec_addr); ++ do_slab_slub(si, VERBOSE); ++ } else if (si->flags & VERBOSE) { ++ do_kmem_cache_slub(si); ++ if (!reqname && ((i+1) < si->cache_count)) ++ fprintf(fp, kmem_cache_hdr); ++ } + +- return TRUE; ++next_cache: ++ if (reqname) ++ break; ++ } ++ ++bailout: ++ FREEBUF(si->cache_list); ++ FREEBUF(si->cache_buf); + } + + /* +- * Translate a PTE into a swap device and offset string. +- */ +-char * +-swap_location(ulonglong pte, char *buf) ++ * Emulate the total count calculation done by the ++ * slab_objects() sysfs function in slub.c. ++ */ ++static int ++get_kmem_cache_slub_data(long cmd, struct meminfo *si) + { +- char swapdev[BUFSIZE]; ++ int i, n, node; ++ ulong total_objects, total_slabs; ++ ulong cpu_slab_ptr, node_ptr; ++ ulong node_nr_partial, node_nr_slabs; ++ int full_slabs, objects; ++ long p; ++ short inuse; ++ ulong *nodes, *per_cpu; + +- if (!pte) +- return NULL; ++ /* ++ * nodes[n] is not being used (for now) ++ * per_cpu[n] is a count of cpu_slab pages per node. ++ */ ++ nodes = (ulong *)GETBUF(2 * sizeof(ulong) * vt->numnodes); ++ per_cpu = nodes + vt->numnodes; + +- sprintf(buf, "%s OFFSET: %lld", +- get_swapdev(SWP_TYPE(pte), swapdev), SWP_OFFSET(pte)); ++ total_slabs = total_objects = 0; + +- return buf; +-} ++ for (i = 0; i < kt->cpus; i++) { ++ cpu_slab_ptr = get_cpu_slab_ptr(si, i, NULL); + +-/* +- * Given the type field from a PTE, return the name of the swap device. +- */ +-static char * +-get_swapdev(ulong type, char *buf) +-{ +- unsigned int i, swap_info_len; +- ulong swap_info, swap_file; +- ulong vfsmnt; ++ if (!cpu_slab_ptr) ++ continue; + +- if (!symbol_exists("nr_swapfiles")) +- error(FATAL, "nr_swapfiles doesn't exist in this kernel!\n"); ++ if ((node = page_to_nid(cpu_slab_ptr)) < 0) ++ goto bailout; + +- if (!symbol_exists("swap_info")) +- error(FATAL, "swap_info doesn't exist in this kernel!\n"); ++ switch (cmd) ++ { ++ case GET_SLUB_OBJECTS: ++ if (!readmem(cpu_slab_ptr + OFFSET(page_inuse), ++ KVADDR, &inuse, sizeof(short), ++ "page inuse", RETURN_ON_ERROR)) ++ return FALSE; ++ total_objects += inuse; ++ break; + +- swap_info = symbol_value("swap_info"); ++ case GET_SLUB_SLABS: ++ total_slabs++; ++ break; ++ } ++ per_cpu[node]++; ++ } ++ ++ for (n = 0; n < vt->numnodes; n++) { ++ if (vt->flags & CONFIG_NUMA) ++ node_ptr = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_node) + ++ (sizeof(void *)*n)); ++ else ++ node_ptr = si->cache + ++ OFFSET(kmem_cache_local_node); + +- swap_info_len = (i = ARRAY_LENGTH(swap_info)) ? +- i : get_array_length("swap_info", NULL, 0); ++ if (!readmem(node_ptr + OFFSET(kmem_cache_node_nr_partial), ++ KVADDR, &node_nr_partial, sizeof(ulong), ++ "kmem_cache_node nr_partial", RETURN_ON_ERROR)) ++ goto bailout; ++ if (!readmem(node_ptr + OFFSET(kmem_cache_node_nr_slabs), ++ KVADDR, &node_nr_slabs, sizeof(ulong), ++ "kmem_cache_node nr_slabs", RETURN_ON_ERROR)) ++ goto bailout; + +- sprintf(buf, "(unknown swap location)"); ++ switch (cmd) ++ { ++ case GET_SLUB_OBJECTS: ++ if ((p = count_partial(node_ptr)) < 0) ++ return FALSE; ++ total_objects += p; ++ break; + +- if (type >= swap_info_len) +- return buf; ++ case GET_SLUB_SLABS: ++ total_slabs += node_nr_partial; ++ break; ++ } + +- swap_info += (SIZE(swap_info_struct) * type); +- fill_swap_info(swap_info); +- swap_file = ULONG(vt->swap_info_struct + +- OFFSET(swap_info_struct_swap_file)); ++ full_slabs = node_nr_slabs - per_cpu[n] - node_nr_partial; ++ objects = si->objects; + +- if (swap_file) { +- if (VALID_MEMBER(swap_info_struct_swap_vfsmnt)) { +- vfsmnt = ULONG(vt->swap_info_struct + +- OFFSET(swap_info_struct_swap_vfsmnt)); +- get_pathname(swap_file, buf, BUFSIZE, 1, vfsmnt); +- } else if (VALID_MEMBER (swap_info_struct_old_block_size)) { +- get_pathname(file_to_dentry(swap_file), +- buf, BUFSIZE, 1, 0); +- } else { +- get_pathname(swap_file, buf, BUFSIZE, 1, 0); ++ switch (cmd) ++ { ++ case GET_SLUB_OBJECTS: ++ total_objects += (full_slabs * objects); ++ break; ++ ++ case GET_SLUB_SLABS: ++ total_slabs += full_slabs; ++ break; + } +- } + +- return buf; +-} ++ if (!(vt->flags & CONFIG_NUMA)) ++ break; ++ } + +-/* +- * If not currently stashed, cache the passed-in swap_info_struct. +- */ +-static void +-fill_swap_info(ulong swap_info) +-{ +- if (vt->last_swap_read == swap_info) +- return; ++ switch (cmd) ++ { ++ case GET_SLUB_OBJECTS: ++ si->inuse = total_objects; ++ break; + +- if (!vt->swap_info_struct && !(vt->swap_info_struct = (char *) +- malloc(SIZE(swap_info_struct)))) +- error(FATAL, "cannot malloc swap_info_struct space\n"); +- +- readmem(swap_info, KVADDR, vt->swap_info_struct, SIZE(swap_info_struct), +- "fill_swap_info", FAULT_ON_ERROR); ++ case GET_SLUB_SLABS: ++ si->num_slabs = total_slabs; ++ break; ++ } + +- vt->last_swap_read = swap_info; ++ FREEBUF(nodes); ++ return TRUE; ++ ++bailout: ++ FREEBUF(nodes); ++ return FALSE; + } + +-/* +- * If active, clear references to the swap_info references. +- */ +-void +-clear_swap_info_cache(void) ++ ++static void ++do_kmem_cache_slub(struct meminfo *si) + { +- if (ACTIVE()) +- vt->last_swap_read = 0; +-} ++ int i, n; ++ ulong cpu_slab_ptr, node_ptr; ++ ulong node_nr_partial, node_nr_slabs; ++ ulong *per_cpu; + ++ per_cpu = (ulong *)GETBUF(sizeof(ulong) * vt->numnodes); + +-/* +- * Translage a vm_area_struct and virtual address into a filename +- * and offset string. +- */ ++ for (i = 0; i < kt->cpus; i++) { ++ cpu_slab_ptr = get_cpu_slab_ptr(si, i, NULL); + +-#define PAGE_CACHE_SHIFT (machdep->pageshift) /* This is supposed to change! */ ++ fprintf(fp, "CPU %d SLAB:\n%s", i, ++ cpu_slab_ptr ? "" : " (empty)\n"); + +-static char * +-vma_file_offset(ulong vma, ulong vaddr, char *buf) +-{ +- ulong vm_file, vm_start, vm_offset, vm_pgoff, dentry, offset; +- ulong vfsmnt; +- char file[BUFSIZE]; +- char *vma_buf, *file_buf; ++ if (!cpu_slab_ptr) ++ continue; + +- if (!vma) +- return NULL; ++ if ((n = page_to_nid(cpu_slab_ptr)) >= 0) ++ per_cpu[n]++; + +- vma_buf = fill_vma_cache(vma); ++ si->slab = cpu_slab_ptr; ++ do_slab_slub(si, VERBOSE); + +- vm_file = ULONG(vma_buf + OFFSET(vm_area_struct_vm_file)); ++ if (received_SIGINT()) ++ restart(0); ++ } + +- if (!vm_file) +- goto no_file_offset; ++ for (n = 0; n < vt->numnodes; n++) { ++ if (vt->flags & CONFIG_NUMA) ++ node_ptr = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_node) + ++ (sizeof(void *)*n)); ++ else ++ node_ptr = si->cache + ++ OFFSET(kmem_cache_local_node); + +- file_buf = fill_file_cache(vm_file); +- dentry = ULONG(file_buf + OFFSET(file_f_dentry)); ++ if (!readmem(node_ptr + OFFSET(kmem_cache_node_nr_partial), ++ KVADDR, &node_nr_partial, sizeof(ulong), ++ "kmem_cache_node nr_partial", RETURN_ON_ERROR)) ++ break; ++ if (!readmem(node_ptr + OFFSET(kmem_cache_node_nr_slabs), ++ KVADDR, &node_nr_slabs, sizeof(ulong), ++ "kmem_cache_node nr_slabs", RETURN_ON_ERROR)) ++ break; + +- if (!dentry) +- goto no_file_offset; ++ fprintf(fp, "KMEM_CACHE_NODE NODE SLABS PARTIAL PER-CPU\n"); + +- file[0] = NULLCHAR; +- if (VALID_MEMBER(file_f_vfsmnt)) { +- vfsmnt = ULONG(file_buf + OFFSET(file_f_vfsmnt)); +- get_pathname(dentry, file, BUFSIZE, 1, vfsmnt); +- } else +- get_pathname(dentry, file, BUFSIZE, 1, 0); ++ fprintf(fp, "%lx%s", node_ptr, space(VADDR_PRLEN > 8 ? 2 : 10)); ++ fprintf(fp, "%4d %5ld %7ld %7ld\n", ++ n, node_nr_slabs, node_nr_partial, per_cpu[n]); + +- if (!strlen(file)) +- goto no_file_offset; ++ do_node_lists_slub(si, node_ptr, n); + +- vm_start = ULONG(vma_buf + OFFSET(vm_area_struct_vm_start)); ++ if (!(vt->flags & CONFIG_NUMA)) ++ break; ++ } + +- vm_offset = vm_pgoff = 0xdeadbeef; ++ fprintf(fp, "\n"); + +- if (VALID_MEMBER(vm_area_struct_vm_offset)) +- vm_offset = ULONG(vma_buf + +- OFFSET(vm_area_struct_vm_offset)); +- else if (VALID_MEMBER(vm_area_struct_vm_pgoff)) +- vm_pgoff = ULONG(vma_buf + +- OFFSET(vm_area_struct_vm_pgoff)); +- else +- goto no_file_offset; ++ FREEBUF(per_cpu); ++} + +- if (vm_offset != 0xdeadbeef) +- offset = VIRTPAGEBASE(vaddr) - vm_start + vm_offset; +- else if (vm_pgoff != 0xdeadbeef) { +- offset = ((vaddr - vm_start) >> PAGE_CACHE_SHIFT) + vm_pgoff; +- offset <<= PAGE_CACHE_SHIFT; ++#define DUMP_SLAB_INFO_SLUB() \ ++ { \ ++ char b1[BUFSIZE], b2[BUFSIZE]; \ ++ fprintf(fp, " %s %s %4d %5d %9d %4d\n", \ ++ mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->slab)), \ ++ mkstring(b2, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(vaddr)), \ ++ node, objects, inuse, objects - inuse); \ ++ } ++ ++static void ++do_slab_slub(struct meminfo *si, int verbose) ++{ ++ physaddr_t paddr; ++ ulong vaddr; ++ ushort inuse, objects; ++ ulong freelist, cpu_freelist, cpu_slab_ptr; ++ int i, cpu_slab, is_free, node; ++ ulong p, q; ++ ++ if (!si->slab) { ++ if (CRASHDEBUG(1)) ++ error(INFO, "-S option not supported for CONFIG_SLUB\n"); ++ return; + } + +- sprintf(buf, "%s OFFSET: %lx", file, offset); ++ if (!page_to_phys(si->slab, &paddr)) { ++ error(WARNING, ++ "%lx: cannot tranlate slab page to physical address\n", ++ si->slab); ++ return; ++ } + +- return buf; ++ node = page_to_nid(si->slab); + +-no_file_offset: +- return NULL; +-} ++ vaddr = PTOV(paddr); + +-/* +- * Translate a PTE into its physical address and flags. +- */ +-void +-cmd_pte(void) +-{ +- int c; +- ulonglong pte; ++ if (verbose) ++ fprintf(fp, " %s", slab_hdr); + +- while ((c = getopt(argcnt, args, "")) != EOF) { +- switch(c) +- { +- default: +- argerrs++; +- break; +- } +- } ++ if (!readmem(si->slab + OFFSET(page_inuse), KVADDR, &inuse, ++ sizeof(ushort), "page.inuse", RETURN_ON_ERROR)) ++ return; ++ if (!readmem(si->slab + OFFSET(page_freelist), KVADDR, &freelist, ++ sizeof(void *), "page.freelist", RETURN_ON_ERROR)) ++ return; ++ /* ++ * Pre-2.6.27, the object count and order were fixed in the ++ * kmem_cache structure. Now they may change, say if a high ++ * order slab allocation fails, so the per-slab object count ++ * is kept in the slab. ++ */ ++ if (VALID_MEMBER(page_objects)) { ++ if (!readmem(si->slab + OFFSET(page_objects), KVADDR, &objects, ++ sizeof(ushort), "page.objects", RETURN_ON_ERROR)) ++ return; + +- if (argerrs) +- cmd_usage(pc->curcmd, SYNOPSIS); ++ if (CRASHDEBUG(1) && (objects != si->objects)) ++ error(NOTE, "%s: slab: %lx oo objects: %ld " ++ "slab objects: %d\n", ++ si->curname, si->slab, ++ si->objects, objects); ++ } else ++ objects = (ushort)si->objects; + +- while (args[optind]) { +- pte = htoll(args[optind], FAULT_ON_ERROR, NULL); +- machdep->translate_pte((ulong)pte, NULL, pte); +- optind++; ++ if (!verbose) { ++ DUMP_SLAB_INFO_SLUB(); ++ return; + } + +-} ++ for (i = 0, cpu_slab = -1; i < kt->cpus; i++) { ++ cpu_slab_ptr = get_cpu_slab_ptr(si, i, &cpu_freelist); + +-static char *node_zone_hdr = "ZONE NAME SIZE"; ++ if (!cpu_slab_ptr) ++ continue; ++ if (cpu_slab_ptr == si->slab) { ++ cpu_slab = i; ++ /* ++ * Later slub scheme uses the per-cpu freelist ++ * and keeps page->inuse maxed out, so count ++ * the free objects by hand. ++ */ ++ if (cpu_freelist) ++ freelist = cpu_freelist; ++ if ((si->objects - inuse) == 0) ++ inuse = si->objects - ++ count_free_objects(si, freelist); ++ break; ++ } ++ } + +-/* +- * On systems supporting memory nodes, display the basic per-node data. +- */ +-static void +-dump_memory_nodes(int initialize) +-{ +- int i, j; +- int n, id, flen, slen; +- ulong node_mem_map; +- ulong node_start_paddr; +- ulong node_start_pfn; +- ulong node_start_mapnr; +- ulong node_spanned_pages; +- ulong free_pages, zone_size, node_size; +- ulong zone_start_paddr, zone_start_mapnr, zone_mem_map; +- ulong zone_start_pfn; +- ulong bdata; +- ulong pgdat; +- ulong node_zones; +- ulong value; +- char buf1[BUFSIZE]; +- char buf2[BUFSIZE]; +- char buf3[BUFSIZE]; +- char buf4[BUFSIZE]; +- char buf5[BUFSIZE]; +- struct node_table *nt; ++ DUMP_SLAB_INFO_SLUB(); + +- if (!(vt->flags & NODES)) { +- if (!initialize) +- error(FATAL, +- "memory nodes not supported by this kernel\n\n"); +- else { +- nt = &vt->node_table[0]; +- nt->node_id = 0; +- if (symbol_exists("contig_page_data")) +- nt->pgdat = symbol_value("contig_page_data"); +- else +- nt->pgdat = 0; +- nt->size = vt->total_pages; +- nt->mem_map = vt->mem_map; +- nt->start_paddr = 0; +- nt->start_mapnr = 0; +- return; ++ fprintf(fp, " %s", free_inuse_hdr); ++ ++#define PAGE_MAPPING_ANON 1 ++ ++ if (CRASHDEBUG(1)) { ++ fprintf(fp, "< SLUB: free list START: >\n"); ++ i = 0; ++ for (q = freelist; q; q = get_freepointer(si, (void *)q)) { ++ if (q & PAGE_MAPPING_ANON) { ++ fprintf(fp, ++ "< SLUB: free list END: %lx (%d found) >\n", ++ q, i); ++ break; ++ } ++ fprintf(fp, " %lx\n", q); ++ i++; + } ++ if (!q) ++ fprintf(fp, "< SLUB: free list END (%d found) >\n", i); + } + +- if (initialize) +- get_symbol_data("pgdat_list", sizeof(void *), &pgdat); +- else +- pgdat = vt->node_table[0].pgdat; ++ for (p = vaddr; p < vaddr + objects * si->size; p += si->size) { ++ is_free = FALSE; ++ for (is_free = 0, q = freelist; q; ++ q = get_freepointer(si, (void *)q)) { ++ if (q == BADADDR) ++ return; ++ if (q & PAGE_MAPPING_ANON) ++ break; ++ if (p == q) { ++ is_free = TRUE; ++ break; ++ } ++ } + +- for (n = 0; pgdat; n++) { +- if (n >= vt->numnodes) +- error(FATAL, "numnodes out of sync with pgdat_list?\n"); ++ if (si->flags & ADDRESS_SPECIFIED) { ++ if ((si->spec_addr < p) || ++ (si->spec_addr >= (p + si->size))) { ++ if (!(si->flags & VERBOSE)) ++ continue; ++ } ++ } + +- nt = &vt->node_table[n]; ++ fprintf(fp, " %s%lx%s", ++ is_free ? " " : "[", ++ p, is_free ? " " : "]"); ++ if (is_free && (cpu_slab >= 0)) ++ fprintf(fp, "(cpu %d cache)", cpu_slab); ++ fprintf(fp, "\n"); + +- readmem(pgdat+OFFSET(pglist_data_node_id), KVADDR, &id, +- sizeof(int), "pglist node_id", FAULT_ON_ERROR); ++ } ++} + +- readmem(pgdat+OFFSET(pglist_data_node_mem_map), KVADDR, +- &node_mem_map, sizeof(ulong), +- "node_mem_map", FAULT_ON_ERROR); ++static int ++count_free_objects(struct meminfo *si, ulong freelist) ++{ ++ int c; ++ ulong q; + +- if (VALID_MEMBER(pglist_data_node_start_paddr)) +- readmem(pgdat+OFFSET(pglist_data_node_start_paddr), +- KVADDR, &node_start_paddr, sizeof(ulong), +- "pglist node_start_paddr", FAULT_ON_ERROR); +- else if (VALID_MEMBER(pglist_data_node_start_pfn)) { +- readmem(pgdat+OFFSET(pglist_data_node_start_pfn), +- KVADDR, &node_start_pfn, sizeof(ulong), +- "pglist node_start_pfn", FAULT_ON_ERROR); +- node_start_mapnr = node_start_pfn; +- node_start_paddr = PTOB(node_start_pfn); +- } else error(INFO, +- "cannot determine zone starting physical address\n"); ++ c = 0; ++ for (q = freelist; q; q = get_freepointer(si, (void *)q)) { ++ if (q & PAGE_MAPPING_ANON) ++ break; ++ c++; ++ } + +- if (VALID_MEMBER(pglist_data_node_start_mapnr)) +- readmem(pgdat+OFFSET(pglist_data_node_start_mapnr), +- KVADDR, &node_start_mapnr, sizeof(ulong), +- "pglist node_start_mapnr", FAULT_ON_ERROR); ++ return c; ++} + +- if (VALID_MEMBER(pglist_data_node_size)) +- readmem(pgdat+OFFSET(pglist_data_node_size), +- KVADDR, &node_size, sizeof(ulong), +- "pglist node_size", FAULT_ON_ERROR); +- else if (VALID_MEMBER(pglist_data_node_spanned_pages)) { +- readmem(pgdat+OFFSET(pglist_data_node_spanned_pages), +- KVADDR, &node_spanned_pages, sizeof(ulong), +- "pglist node_spanned_pages", FAULT_ON_ERROR); +- node_size = node_spanned_pages; +- } else error(INFO, "cannot determine zone size\n"); + +- readmem(pgdat+OFFSET(pglist_data_bdata), KVADDR, &bdata, +- sizeof(ulong), "pglist bdata", FAULT_ON_ERROR); ++static ulong ++get_freepointer(struct meminfo *si, void *object) ++{ ++ ulong vaddr, nextfree; ++ ++ vaddr = (ulong)(object + si->slab_offset); ++ if (!readmem(vaddr, KVADDR, &nextfree, ++ sizeof(void *), "get_freepointer", RETURN_ON_ERROR)) ++ return BADADDR; + +- if (initialize) { +- nt->node_id = id; +- nt->pgdat = pgdat; +- if (VALID_MEMBER(zone_struct_memsize)) +- nt->size = 0; /* initialize below */ +- else +- nt->size = node_size; +- nt->mem_map = node_mem_map; +- nt->start_paddr = node_start_paddr; +- nt->start_mapnr = node_start_mapnr; +- } ++ return nextfree; ++} + +- if (!initialize) { +- if (n) { +- fprintf(fp, "\n"); +- pad_line(fp, slen, '-'); +- } +- flen = MAX(VADDR_PRLEN, strlen("BOOTMEM_DATA")); +- fprintf(fp, "%sNODE %s %s %s %s\n", +- n ? "\n\n" : "", +- mkstring(buf1, 8, CENTER, "SIZE"), +- mkstring(buf2, flen, CENTER|LJUST, "PGLIST_DATA"), +- mkstring(buf3, flen, CENTER|LJUST, "BOOTMEM_DATA"), +- mkstring(buf4, flen, CENTER|LJUST, "NODE_ZONES")); ++static void ++do_node_lists_slub(struct meminfo *si, ulong node_ptr, int node) ++{ ++ ulong next, list_head, flags; ++ int first; + +- node_zones = pgdat + OFFSET(pglist_data_node_zones); +- sprintf(buf5, " %2d %s %s %s %s\n", id, +- mkstring(buf1, 8, CENTER|LJUST|LONG_DEC, +- MKSTR(node_size)), +- mkstring(buf2, flen, CENTER|LJUST|LONG_HEX, +- MKSTR(pgdat)), +- mkstring(buf3, flen, CENTER|LONG_HEX, +- MKSTR(bdata)), +- mkstring(buf4, flen, CENTER|LJUST|LONG_HEX, +- MKSTR(node_zones))); +- fprintf(fp, "%s", buf5); ++ list_head = node_ptr + OFFSET(kmem_cache_node_partial); ++ if (!readmem(list_head, KVADDR, &next, sizeof(ulong), ++ "kmem_cache_node partial", RETURN_ON_ERROR)) ++ return; + +- j = 12 + strlen(buf1) + strlen(buf2) + strlen(buf3) + +- count_leading_spaces(buf4); +- for (i = 1; i < vt->nr_zones; i++) { +- node_zones += SIZE_OPTION(zone_struct, zone); +- INDENT(j); +- fprintf(fp, "%lx\n", node_zones); +- } +- +- fprintf(fp, "%s START_PADDR START_MAPNR\n", +- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, +- "MEM_MAP")); +- fprintf(fp, "%s %s %s\n", +- mkstring(buf1, VADDR_PRLEN, +- CENTER|LONG_HEX, MKSTR(node_mem_map)), +- mkstring(buf2, strlen("START_PADDR"), +- CENTER|LONG_HEX|RJUST, MKSTR(node_start_paddr)), +- mkstring(buf3, strlen("START_MAPNR"), +- CENTER|LONG_DEC|RJUST, +- MKSTR(node_start_mapnr))); ++ fprintf(fp, "NODE %d PARTIAL:\n%s", node, ++ next == list_head ? " (empty)\n" : ""); ++ first = 0; ++ while (next != list_head) { ++ si->slab = next - OFFSET(page_lru); ++ if (first++ == 0) ++ fprintf(fp, " %s", slab_hdr); ++ do_slab_slub(si, !VERBOSE); ++ ++ if (received_SIGINT()) ++ restart(0); ++ ++ if (!readmem(next, KVADDR, &next, sizeof(ulong), ++ "page.lru.next", RETURN_ON_ERROR)) ++ return; ++ } ++ ++#define SLAB_STORE_USER (0x00010000UL) ++ flags = ULONG(si->cache_buf + OFFSET(kmem_cache_flags)); + +- sprintf(buf2, "%s %s START_PADDR START_MAPNR", +- node_zone_hdr, +- mkstring(buf1, VADDR_PRLEN, CENTER|RJUST, +- "MEM_MAP")); +- slen = strlen(buf2); +- fprintf(fp, "\n%s\n", buf2); +- } ++ if (INVALID_MEMBER(kmem_cache_node_full) || ++ !(flags & SLAB_STORE_USER)) { ++ fprintf(fp, "NODE %d FULL:\n (not tracked)\n", node); ++ return; ++ } + +- node_zones = pgdat + OFFSET(pglist_data_node_zones); +- for (i = 0; i < vt->nr_zones; i++) { +- if (CRASHDEBUG(7)) +- fprintf(fp, "zone at %lx\n", node_zones); ++ list_head = node_ptr + OFFSET(kmem_cache_node_full); ++ if (!readmem(list_head, KVADDR, &next, sizeof(ulong), ++ "kmem_cache_node full", RETURN_ON_ERROR)) ++ return; + +- if (VALID_MEMBER(zone_struct_size)) +- readmem(node_zones+OFFSET(zone_struct_size), +- KVADDR, &zone_size, sizeof(ulong), +- "zone_struct size", FAULT_ON_ERROR); +- else if (VALID_MEMBER(zone_struct_memsize)) { +- readmem(node_zones+OFFSET(zone_struct_memsize), +- KVADDR, &zone_size, sizeof(ulong), +- "zone_struct memsize", FAULT_ON_ERROR); +- nt->size += zone_size; +- } else if (VALID_MEMBER(zone_spanned_pages)) { +- readmem(node_zones+ OFFSET(zone_spanned_pages), +- KVADDR, &zone_size, sizeof(ulong), +- "zone spanned_pages", FAULT_ON_ERROR); +- } else error(FATAL, +- "zone_struct has neither size nor memsize field\n"); +- readmem(node_zones+ +- OFFSET_OPTION(zone_struct_free_pages, +- zone_free_pages), KVADDR, &free_pages, +- sizeof(ulong), "zone[_struct] free_pages", +- FAULT_ON_ERROR); +- readmem(node_zones+OFFSET_OPTION(zone_struct_name, +- zone_name), KVADDR, &value, sizeof(void *), +- "zone[_struct] name", FAULT_ON_ERROR); +- if (!read_string(value, buf1, BUFSIZE-1)) +- sprintf(buf1, "(unknown) "); +- if (VALID_STRUCT(zone_struct)) { +- readmem(node_zones+ +- OFFSET(zone_struct_zone_start_paddr), +- KVADDR, &zone_start_paddr, +- sizeof(ulong), +- "node_zones zone_start_paddr", +- FAULT_ON_ERROR); +- readmem(node_zones+ +- OFFSET(zone_struct_zone_start_mapnr), +- KVADDR, &zone_start_mapnr, +- sizeof(ulong), +- "node_zones zone_start_mapnr", +- FAULT_ON_ERROR); +- } else { +- readmem(node_zones+ +- OFFSET(zone_zone_start_pfn), +- KVADDR, &zone_start_pfn, +- sizeof(ulong), +- "node_zones zone_start_pfn", +- FAULT_ON_ERROR); +- zone_start_paddr = PTOB(zone_start_pfn); +- readmem(node_zones+ +- OFFSET(zone_zone_mem_map), +- KVADDR, &zone_mem_map, +- sizeof(ulong), +- "node_zones zone_mem_map", +- FAULT_ON_ERROR); +- if (zone_mem_map) +- zone_start_mapnr = +- (zone_mem_map - node_mem_map) / +- SIZE(page); +- else +- zone_start_mapnr = 0; +- } +- readmem(node_zones+ +- OFFSET_OPTION(zone_struct_zone_mem_map, +- zone_zone_mem_map), KVADDR, &zone_mem_map, +- sizeof(ulong), "node_zones zone_mem_map", +- FAULT_ON_ERROR); ++ fprintf(fp, "NODE %d FULL:\n%s", node, ++ next == list_head ? " (empty)\n" : ""); ++ first = 0; ++ while (next != list_head) { ++ si->slab = next - OFFSET(page_lru); ++ if (first++ == 0) ++ fprintf(fp, " %s", slab_hdr); ++ do_slab_slub(si, !VERBOSE); + +- if (!initialize) { +- fprintf(fp, " %2d %-9s %7ld ", +- i, buf1, zone_size); +- fprintf(fp, "%s %s %s\n", +- mkstring(buf1, VADDR_PRLEN, +- RJUST|LONG_HEX,MKSTR(zone_mem_map)), +- mkstring(buf2, strlen("START_PADDR"), +- LONG_HEX|RJUST,MKSTR(zone_start_paddr)), +- mkstring(buf3, strlen("START_MAPNR"), +- LONG_DEC|RJUST, +- MKSTR(zone_start_mapnr))); +- } ++ if (received_SIGINT()) ++ restart(0); ++ ++ if (!readmem(next, KVADDR, &next, sizeof(ulong), ++ "page.lru.next", RETURN_ON_ERROR)) ++ return; ++ } ++} + +- node_zones += SIZE_OPTION(zone_struct, zone); +- } + +- if (initialize) +- readmem(pgdat + OFFSET_OPTION(pglist_data_node_next, +- pglist_data_pgdat_next), KVADDR, +- &pgdat, sizeof(void *), "pglist_data node_next", +- FAULT_ON_ERROR); +- else { +- if ((n+1) < vt->numnodes) +- pgdat = vt->node_table[n+1].pgdat; +- else +- pgdat = 0; +- } +- } ++static char * ++is_kmem_cache_addr_slub(ulong vaddr, char *kbuf) ++{ ++ int i, cnt; ++ ulong *cache_list; ++ ulong name; ++ char *cache_buf; ++ int found; ++ ++ cnt = get_kmem_cache_list(&cache_list); ++ cache_buf = GETBUF(SIZE(kmem_cache)); ++ ++ for (i = 0, found = FALSE; i < cnt; i++) { ++ if (cache_list[i] != vaddr) ++ continue; ++ ++ if (!readmem(cache_list[i], KVADDR, cache_buf, ++ SIZE(kmem_cache), "kmem_cache buffer", ++ RETURN_ON_ERROR)) ++ break; ++ ++ name = ULONG(cache_buf + OFFSET(kmem_cache_name)); ++ if (!read_string(name, kbuf, BUFSIZE-1)) ++ sprintf(kbuf, "(unknown)"); ++ ++ found = TRUE; ++ break; ++ } ++ ++ FREEBUF(cache_list); ++ FREEBUF(cache_buf); + +- if (n != vt->numnodes) +- error(FATAL, "numnodes out of sync with pgdat_list?\n"); ++ return (found ? kbuf : NULL); + } + + /* +- * Gather essential information regarding each memory node. ++ * Kernel-config-neutral page-to-node evaluator. + */ +-static void +-node_table_init(void) ++static int ++page_to_nid(ulong page) + { +- int n; +- ulong pgdat; +- +- /* +- * Override numnodes -- some kernels may leave it at 1 on a system +- * with multiple memory nodes. +- */ +- get_symbol_data("pgdat_list", sizeof(void *), &pgdat); ++ int i; ++ physaddr_t paddr; ++ struct node_table *nt; ++ physaddr_t end_paddr; + +- for (n = 0; pgdat; n++) { +- readmem(pgdat + OFFSET_OPTION(pglist_data_node_next, +- pglist_data_pgdat_next), KVADDR, +- &pgdat, sizeof(void *), "pglist_data node_next", +- FAULT_ON_ERROR); +- } +- if (n != vt->numnodes) { +- if (CRASHDEBUG(2)) +- error(NOTE, "changing numnodes from %d to %d\n", +- vt->numnodes, n); +- vt->numnodes = n; ++ if (!page_to_phys(page, &paddr)) { ++ error(INFO, "page_to_nid: invalid page: %lx\n", page); ++ return -1; + } + +- if (!(vt->node_table = (struct node_table *) +- malloc(sizeof(struct node_table) * vt->numnodes))) +- error(FATAL, "cannot malloc node_table %s(%d nodes)", +- vt->numnodes > 1 ? "array " : "", vt->numnodes); +- +- BZERO(vt->node_table, sizeof(struct node_table) * vt->numnodes); ++ for (i = 0; i < vt->numnodes; i++) { ++ nt = &vt->node_table[i]; + +- dump_memory_nodes(MEMORY_NODES_INITIALIZE); ++ end_paddr = nt->start_paddr + ++ ((physaddr_t)nt->size * (physaddr_t)PAGESIZE()); ++ ++ if ((paddr >= nt->start_paddr) && (paddr < end_paddr)) ++ return i; ++ } + +- qsort((void *)vt->node_table, (size_t)vt->numnodes, +- sizeof(struct node_table), compare_node_data); ++ error(INFO, "page_to_nid: cannot determine node for pages: %lx\n", ++ page); + +- if (CRASHDEBUG(2)) +- dump_memory_nodes(MEMORY_NODES_DUMP); ++ return -1; + } + + /* +- * 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. ++ * Allocate and fill the passed-in buffer with a list of ++ * the current kmem_cache structures. + */ +- + static int +-compare_node_data(const void *v1, const void *v2) ++get_kmem_cache_list(ulong **cache_buf) + { +- struct node_table *t1, *t2; ++ int cnt; ++ ulong vaddr; ++ struct list_data list_data, *ld; + +- t1 = (struct node_table *)v1; +- t2 = (struct node_table *)v2; ++ get_symbol_data("slab_caches", sizeof(void *), &vaddr); + +- return (t1->node_id < t2->node_id ? -1 : +- t1->node_id == t2->node_id ? 0 : 1); ++ ld = &list_data; ++ BZERO(ld, sizeof(struct list_data)); ++ ld->start = vaddr; ++ ld->list_head_offset = OFFSET(kmem_cache_list); ++ ld->end = symbol_value("slab_caches"); ++ if (CRASHDEBUG(3)) ++ ld->flags |= VERBOSE; ++ ++ hq_open(); ++ cnt = do_list(ld); ++ *cache_buf = (ulong *)GETBUF(cnt * sizeof(ulong)); ++ cnt = retrieve_list(*cache_buf, cnt); ++ hq_close(); ++ ++ return cnt; + } + + + /* +- * Depending upon the processor, and whether we're running live or on a +- * dumpfile, get the system page size. ++ * Get the address of the head page of a compound page. + */ +-uint +-memory_page_size(void) ++static ulong ++compound_head(ulong page) + { +- uint psz; ++ ulong flags, first_page;; + +- if (REMOTE_MEMSRC()) +- return remote_page_size(); ++ first_page = page; + +- switch (pc->flags & MEMORY_SOURCES) +- { +- case DISKDUMP: +- psz = diskdump_page_size(); +- break; ++ if (!readmem(page+OFFSET(page_flags), KVADDR, &flags, sizeof(ulong), ++ "page.flags", RETURN_ON_ERROR)) ++ return first_page; + +- case NETDUMP: +- psz = netdump_page_size(); +- break; ++ if ((flags & vt->PG_head_tail_mask) == vt->PG_head_tail_mask) ++ readmem(page+OFFSET(page_first_page), KVADDR, &first_page, ++ sizeof(ulong), "page.first_page", RETURN_ON_ERROR); ++ ++ return first_page; ++} + +- case MCLXCD: +- psz = (uint)mclx_page_size(); +- break; ++long ++count_partial(ulong node) ++{ ++ ulong list_head, next; ++ short inuse; ++ ulong total_inuse; ++ ++ total_inuse = 0; ++ list_head = node + OFFSET(kmem_cache_node_partial); ++ if (!readmem(list_head, KVADDR, &next, sizeof(ulong), ++ "kmem_cache_node.partial", RETURN_ON_ERROR)) ++ return -1; ++ ++ while (next != list_head) { ++ if (!readmem(next - OFFSET(page_lru) + OFFSET(page_inuse), KVADDR, &inuse, ++ sizeof(ushort), "page.inuse", RETURN_ON_ERROR)) ++ return -1; ++ total_inuse += inuse; ++ if (!readmem(next, KVADDR, &next, sizeof(ulong), ++ "page.lru.next", RETURN_ON_ERROR)) ++ return -1; ++ } ++ return total_inuse; ++} + +- case LKCD: +-#if 0 /* REMIND: */ +- psz = lkcd_page_size(); /* dh_dump_page_size is HW page size; should add dh_page_size */ +-#else +- psz = (uint)getpagesize(); +-#endif +- break; ++char * ++is_slab_page(struct meminfo *si, char *buf) ++{ ++ int i, cnt; ++ ulong page_slab, page_flags, name; ++ ulong *cache_list; ++ char *cache_buf, *retval; + +- case DEVMEM: +- case MEMMOD: +- psz = (uint)getpagesize(); +- break; ++ if (!(vt->flags & KMALLOC_SLUB)) ++ return NULL; + +- case S390D: +- psz = s390_page_size(); +- break; ++ if (!is_page_ptr((ulong)si->spec_addr, NULL)) ++ return NULL; + +- default: +- error(FATAL, "memory_page_size: invalid pc->flags: %lx\n", +- pc->flags & MEMORY_SOURCES); +- } ++ if (!readmem(si->spec_addr + OFFSET(page_flags), KVADDR, ++ &page_flags, sizeof(ulong), "page.flags", ++ RETURN_ON_ERROR|QUIET)) ++ return NULL; + +- return psz; +-} ++ if (!(page_flags & (1 << vt->PG_slab))) ++ return NULL; + +-/* +- * Return the vmalloc address referenced by the first vm_struct +- * on the vmlist. This can normally be used by the machine-specific +- * xxx_vmalloc_start() routines. +- */ ++ if (!readmem(si->spec_addr + OFFSET(page_slab), KVADDR, ++ &page_slab, sizeof(ulong), "page.slab", ++ RETURN_ON_ERROR|QUIET)) ++ return NULL; + +-ulong +-first_vmalloc_address(void) +-{ +- ulong vmlist, addr; ++ retval = NULL; ++ cnt = get_kmem_cache_list(&cache_list); ++ cache_buf = GETBUF(SIZE(kmem_cache)); ++ ++ for (i = 0; i < cnt; i++) { ++ if (page_slab == cache_list[i]) { ++ if (!readmem(cache_list[i], KVADDR, cache_buf, ++ SIZE(kmem_cache), "kmem_cache buffer", ++ QUIET|RETURN_ON_ERROR)) ++ goto bailout; ++ ++ name = ULONG(cache_buf + OFFSET(kmem_cache_name)); ++ if (!read_string(name, buf, BUFSIZE-1)) ++ goto bailout; + +- get_symbol_data("vmlist", sizeof(void *), &vmlist); ++ retval = buf; ++ break; ++ } ++ } + +- if (!readmem(vmlist+OFFSET(vm_struct_addr), KVADDR, &addr, +- sizeof(void *), "first vmlist addr", RETURN_ON_ERROR)) +- non_matching_kernel(); ++bailout: ++ FREEBUF(cache_list); ++ FREEBUF(cache_buf); + +- return addr; ++ return retval; + } + + /* +- * Return the L1 cache size in bytes, which can be found stored in the +- * cache_cache. ++ * Figure out which of the kmem_cache.cpu_slab declarations ++ * is used by this kernel, and return a pointer to the slab ++ * page being used. Return the kmem_cache_cpu.freelist pointer ++ * if requested. + */ +- +-int +-l1_cache_size(void) ++static ulong ++get_cpu_slab_ptr(struct meminfo *si, int cpu, ulong *cpu_freelist) + { +- ulong cache_cache; +- ulong c_align; +- int colour_off; +- int retval; ++ ulong cpu_slab_ptr, page, freelist; + +- cache_cache = symbol_value("cache_cache"); ++ if (cpu_freelist) ++ *cpu_freelist = 0; + +- retval = -1; ++ switch (vt->cpu_slab_type) ++ { ++ case TYPE_CODE_STRUCT: ++ cpu_slab_ptr = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_cpu_slab) + ++ OFFSET(kmem_cache_cpu_page)); ++ if (cpu_freelist && VALID_MEMBER(kmem_cache_cpu_freelist)) ++ *cpu_freelist = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_cpu_slab) + ++ OFFSET(kmem_cache_cpu_freelist)); ++ break; ++ ++ case TYPE_CODE_ARRAY: ++ cpu_slab_ptr = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_cpu_slab) + (sizeof(void *)*cpu)); ++ ++ if (cpu_slab_ptr && cpu_freelist && ++ VALID_MEMBER(kmem_cache_cpu_freelist)) { ++ if (readmem(cpu_slab_ptr + OFFSET(kmem_cache_cpu_freelist), ++ KVADDR, &freelist, sizeof(void *), ++ "kmem_cache_cpu.freelist", RETURN_ON_ERROR)) ++ *cpu_freelist = freelist; ++ } ++ ++ if (cpu_slab_ptr && VALID_MEMBER(kmem_cache_cpu_page)) { ++ if (!readmem(cpu_slab_ptr + OFFSET(kmem_cache_cpu_page), ++ KVADDR, &page, sizeof(void *), ++ "kmem_cache_cpu.page", RETURN_ON_ERROR)) ++ cpu_slab_ptr = 0; ++ else ++ cpu_slab_ptr = page; ++ } ++ break; + +- if (VALID_MEMBER(kmem_cache_s_c_align)) { +- readmem(cache_cache+OFFSET(kmem_cache_s_c_align), +- KVADDR, &c_align, sizeof(ulong), +- "c_align", FAULT_ON_ERROR); +- retval = (int)c_align; +- } else if (VALID_MEMBER(kmem_cache_s_colour_off)) { +- readmem(cache_cache+OFFSET(kmem_cache_s_colour_off), +- KVADDR, &colour_off, sizeof(int), +- "colour_off", FAULT_ON_ERROR); +- retval = colour_off; ++ default: ++ error(FATAL, "cannot determine location of kmem_cache.cpu_slab page\n"); + } + +- return retval; ++ return cpu_slab_ptr; + } + + /* +- * Multi-purpose routine used to query/control dumpfile memory usage. ++ * In 2.6.27 kmem_cache.order and kmem_cache.objects were merged ++ * into the kmem_cache.oo, a kmem_cache_order_objects structure. ++ * oo_order() and oo_objects() emulate the kernel functions ++ * of the same name. + */ +-int +-dumpfile_memory(int cmd) ++static unsigned int oo_order(ulong oo) + { +- int retval; ++ return (oo >> 16); ++} + +- retval = 0; ++static unsigned int oo_objects(ulong oo) ++{ ++ return (oo & ((1 << 16) - 1)); ++} + +- if (!DUMPFILE()) +- return retval; ++#ifdef NOT_USED ++ulong ++slab_to_kmem_cache_node(struct meminfo *si, ulong slab_page) ++{ ++ int node; ++ ulong node_ptr; + +- switch (cmd) +- { +- case DUMPFILE_MEM_USED: +- if (REMOTE_DUMPFILE()) +- retval = remote_memory_used(); +- else if (pc->flags & NETDUMP) +- retval = netdump_memory_used(); +- else if (pc->flags & DISKDUMP) +- retval = diskdump_memory_used(); +- else if (pc->flags & LKCD) +- retval = lkcd_memory_used(); +- else if (pc->flags & MCLXCD) +- retval = vas_memory_used(); +- else if (pc->flags & S390D) +- retval = s390_memory_used(); +- break; ++ if (vt->flags & CONFIG_NUMA) { ++ node = page_to_nid(slab_page); ++ node_ptr = ULONG(si->cache_buf + ++ OFFSET(kmem_cache_node) + ++ (sizeof(void *)*node)); ++ } else ++ node_ptr = si->cache + OFFSET(kmem_cache_local_node); + +- case DUMPFILE_FREE_MEM: +- if (REMOTE_DUMPFILE()) +- retval = remote_free_memory(); +- else if (pc->flags & NETDUMP) +- retval = netdump_free_memory(); +- else if (pc->flags & DISKDUMP) +- retval = diskdump_free_memory(); +- else if (pc->flags & LKCD) +- retval = lkcd_free_memory(); +- else if (pc->flags & MCLXCD) +- retval = vas_free_memory(NULL); +- else if (pc->flags & S390D) +- retval = s390_free_memory(); +- break; ++ return node_ptr; ++} + +- case DUMPFILE_MEM_DUMP: +- if (REMOTE_DUMPFILE()) +- retval = remote_memory_dump(0); +- else if (pc->flags & NETDUMP) +- retval = netdump_memory_dump(fp); +- else if (pc->flags & DISKDUMP) +- retval = diskdump_memory_dump(fp); +- else if (pc->flags & LKCD) +- retval = lkcd_memory_dump(set_lkcd_fp(fp)); +- else if (pc->flags & MCLXCD) +- retval = vas_memory_dump(fp); +- else if (pc->flags & S390D) +- retval = s390_memory_dump(fp); +- break; +- +- case DUMPFILE_ENVIRONMENT: +- if (pc->flags & LKCD) { +- set_lkcd_fp(fp); +- dump_lkcd_environment(0); +- } else if (pc->flags & REM_LKCD) +- retval = remote_memory_dump(VERBOSE); +- break; +- } ++ulong ++get_kmem_cache_by_name(char *request) ++{ ++ int i, cnt; ++ ulong *cache_list; ++ ulong name; ++ char *cache_buf; ++ char buf[BUFSIZE]; ++ ulong found; ++ ++ cnt = get_kmem_cache_list(&cache_list); ++ cache_buf = GETBUF(SIZE(kmem_cache)); ++ found = 0; ++ ++ for (i = 0; i < cnt; i++) { ++ readmem(cache_list[i], KVADDR, cache_buf, ++ SIZE(kmem_cache), "kmem_cache buffer", ++ FAULT_ON_ERROR); + +- return retval; +-} ++ name = ULONG(cache_buf + OFFSET(kmem_cache_name)); ++ if (!read_string(name, buf, BUFSIZE-1)) ++ continue; ++ ++ if (STREQ(buf, request)) { ++ found = cache_list[i]; ++ break; ++ } ++ } + ++ FREEBUF(cache_list); ++ FREEBUF(cache_buf); ++ ++ return found; ++} ++#endif /* NOT_USED */ +--- crash/lkcd_fix_mem.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_fix_mem.c 2007-11-15 10:44:38.000000000 -0500 +@@ -20,21 +20,13 @@ + + #define LKCD_COMMON + #include "defs.h" +-#include "lkcd_fix_mem.h" ++#include "lkcd_dump_v8.h" + + static int fix_addr(dump_header_asm_t *); + + int +-fix_addr_v8(int fd) ++fix_addr_v8(dump_header_asm_t *dha) + { +- static dump_header_asm_t dump_header_asm_v8 = { 0 }; +- dump_header_asm_t *dha; +- dha = &dump_header_asm_v8; +- +- if (read(lkcd->fd, dha, sizeof(dump_header_asm_t)) != +- sizeof(dump_header_asm_t)) +- return -1; +- + fix_addr(dha); + + return 0; +@@ -59,14 +51,6 @@ + static int + fix_addr(dump_header_asm_t *dha) + { +- +- +- if (dha->dha_header_size != sizeof(dump_header_asm_t)) { +- error(INFO, "LKCD machine specific dump header doesn't match crash version\n"); +- error(INFO, "traceback of currently executing threads may not work\n\n"); +- } +- +- + lkcd->dump_header_asm = dha; + + +@@ -83,7 +67,7 @@ + if (dha->dha_stack[i] && dha->dha_smp_current_task[i]) { + lkcd->fix_addr[i].task = (ulong)dha->dha_smp_current_task[i]; + lkcd->fix_addr[i].saddr = (ulong)dha->dha_stack[i]; +- lkcd->fix_addr[i].sw = (ulong)dha->dha_switch_stack[i]; ++ lkcd->fix_addr[i].sw = (ulong)dha->dha_stack_ptr[i]; + /* remember the highest non-zero entry */ + lkcd->fix_addr_num = i + 1; + } else { +@@ -113,4 +97,14 @@ + return 0; + } + ++int lkcd_get_kernel_start_v8(ulong *addr) ++{ ++ if (!addr) ++ return 0; ++ ++ *addr = ((dump_header_asm_t *)lkcd->dump_header_asm)->dha_kernel_addr; ++ ++ return 1; ++} ++ + #endif // IA64 +--- crash/tools.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/tools.c 2009-01-26 14:54:54.000000000 -0500 +@@ -1,8 +1,8 @@ + /* tools.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -18,12 +18,12 @@ + #include "defs.h" + #include + +-static int calculate(char *, ulong *, ulonglong *, ulong); + static void print_number(struct number_option *, int, int); + static long alloc_hq_entry(void); + struct hq_entry; + static void dealloc_hq_entry(struct hq_entry *); + static void show_options(void); ++static void dump_struct_members(struct list_data *, int, ulong); + + /* + * General purpose error reporting routine. Type INFO prints the message +@@ -41,11 +41,11 @@ + { + int end_of_line, new_line; + char buf[BUFSIZE]; +- ulong retaddr[4] = { 0 }; ++ ulong retaddr[NUMBER_STACKFRAMES] = { 0 }; + va_list ap; + + if (CRASHDEBUG(1) || (pc->flags & DROP_CORE)) { +- save_return_address(retaddr); ++ SAVE_RETURN_ADDRESS(retaddr); + console("error() trace: %lx => %lx => %lx => %lx\n", + retaddr[3], retaddr[2], retaddr[1], retaddr[0]); + } +@@ -63,6 +63,8 @@ + + if ((new_line = (buf[0] == '\n'))) + shift_string_left(buf, 1); ++ else if (pc->flags & PLEASE_WAIT) ++ new_line = TRUE; + + if (pc->stdpipe) { + fprintf(pc->stdpipe, "%s%s: %s%s", +@@ -1532,7 +1534,7 @@ + int right; + char buf[BUFSIZE]; + +- switch (flags & (LONG_DEC|LONG_HEX|INT_HEX|INT_DEC|LONGLONG_HEX)) ++ switch (flags & (LONG_DEC|LONG_HEX|INT_HEX|INT_DEC|LONGLONG_HEX|ZERO_FILL)) + { + case LONG_DEC: + sprintf(s, "%lu", (ulong)opt); +@@ -1540,6 +1542,12 @@ + case LONG_HEX: + sprintf(s, "%lx", (ulong)opt); + break; ++ case (LONG_HEX|ZERO_FILL): ++ if (VADDR_PRLEN == 8) ++ sprintf(s, "%08lx", (ulong)opt); ++ else if (VADDR_PRLEN == 16) ++ sprintf(s, "%016lx", (ulong)opt); ++ break; + case INT_DEC: + sprintf(s, "%u", (uint)((ulong)opt)); + break; +@@ -1671,6 +1679,9 @@ + switch(c) + { + case 'c': ++ if (XEN_HYPER_MODE()) ++ option_not_supported(c); ++ + if (!runtime) { + error(INFO, + "cpu setting not allowed from .%src\n", +@@ -1687,6 +1698,9 @@ + return; + + case 'p': ++ if (XEN_HYPER_MODE()) ++ option_not_supported(c); ++ + if (!runtime) + return; + +@@ -1721,7 +1735,10 @@ + } + + if (!args[optind]) { +- if (runtime) ++ if (XEN_HYPER_MODE()) ++ error(INFO, ++ "requires an option with the Xen hypervisor\n"); ++ else if (runtime) + show_context(CURRENT_CONTEXT()); + return; + } +@@ -1770,6 +1787,42 @@ + pc->flags & HASH ? "on" : "off"); + return; + ++ } else if (STREQ(args[optind], "unwind")) { ++ if (args[optind+1]) { ++ optind++; ++ if (STREQ(args[optind], "on")) { ++ if ((kt->flags & DWARF_UNWIND_CAPABLE) || ++ !runtime) { ++ kt->flags |= DWARF_UNWIND; ++ kt->flags &= ~NO_DWARF_UNWIND; ++ } ++ } else if (STREQ(args[optind], "off")) { ++ kt->flags &= ~DWARF_UNWIND; ++ if (!runtime) ++ kt->flags |= NO_DWARF_UNWIND; ++ } else if (IS_A_NUMBER(args[optind])) { ++ value = stol(args[optind], ++ FAULT_ON_ERROR, NULL); ++ if (value) { ++ if ((kt->flags & DWARF_UNWIND_CAPABLE) || ++ !runtime) { ++ kt->flags |= DWARF_UNWIND; ++ kt->flags &= ~NO_DWARF_UNWIND; ++ } ++ } else { ++ kt->flags &= ~DWARF_UNWIND; ++ if (!runtime) ++ kt->flags |= NO_DWARF_UNWIND; ++ } ++ } else ++ goto invalid_set_command; ++ } ++ ++ if (runtime) ++ fprintf(fp, "unwind: %s\n", ++ kt->flags & DWARF_UNWIND ? "on" : "off"); ++ return; ++ + } else if (STREQ(args[optind], "refresh")) { + if (args[optind+1]) { + optind++; +@@ -1806,7 +1859,14 @@ + pc->flags |= SCROLL; + else if (STREQ(args[optind], "off")) + pc->flags &= ~SCROLL; +- else if (IS_A_NUMBER(args[optind])) { ++ else if (STREQ(args[optind], "more")) ++ pc->scroll_command = SCROLL_MORE; ++ else if (STREQ(args[optind], "less")) ++ pc->scroll_command = SCROLL_LESS; ++ else if (STREQ(args[optind], "CRASHPAGER")) { ++ if (CRASHPAGER_valid()) ++ pc->scroll_command = SCROLL_CRASHPAGER; ++ } else if (IS_A_NUMBER(args[optind])) { + value = stol(args[optind], + FAULT_ON_ERROR, NULL); + if (value) +@@ -1817,9 +1877,25 @@ + goto invalid_set_command; + } + +- if (runtime) +- fprintf(fp, "scroll: %s\n", +- pc->flags & SCROLL ? "on" : "off"); ++ if (runtime) { ++ fprintf(fp, "scroll: %s ", ++ pc->flags & SCROLL ? "on" : "off"); ++ switch (pc->scroll_command) ++ { ++ case SCROLL_LESS: ++ fprintf(fp, "(/usr/bin/less)\n"); ++ break; ++ case SCROLL_MORE: ++ fprintf(fp, "(/bin/more)\n"); ++ break; ++ case SCROLL_NONE: ++ fprintf(fp, "(none)\n"); ++ break; ++ case SCROLL_CRASHPAGER: ++ fprintf(fp, "(CRASHPAGER: %s)\n", getenv("CRASHPAGER")); ++ break; ++ } ++ } + + return; + +@@ -1998,12 +2074,38 @@ + fprintf(fp, "print_max: %d\n", print_max); + return; + ++ } else if (STREQ(args[optind], "null-stop")) { ++ optind++; ++ if (args[optind]) { ++ if (STREQ(args[optind], "on")) ++ stop_print_at_null = 1; ++ else if (STREQ(args[optind], "off")) ++ stop_print_at_null = 0; ++ else if (IS_A_NUMBER(args[optind])) { ++ value = stol(args[optind], ++ FAULT_ON_ERROR, NULL); ++ if (value) ++ stop_print_at_null = 1; ++ else ++ stop_print_at_null = 0; ++ } else ++ goto invalid_set_command; ++ } ++ if (runtime) ++ fprintf(fp, "null-stop: %s\n", ++ stop_print_at_null ? "on" : "off"); ++ return; ++ + } else if (STREQ(args[optind], "dumpfile")) { + optind++; + if (!runtime && args[optind]) { + pc->flags &= ~(DUMPFILE_TYPES); + if (is_netdump(args[optind], NETDUMP_LOCAL)) + pc->flags |= NETDUMP; ++ else if (is_kdump(args[optind], KDUMP_LOCAL)) ++ pc->flags |= KDUMP; ++ else if (is_xendump(args[optind])) ++ pc->flags |= XENDUMP; + else if (is_diskdump(args[optind])) + pc->flags |= DISKDUMP; + else if (is_lkcd_compressed_dump(args[optind])) +@@ -2054,6 +2156,33 @@ + pc->flags |= DATADEBUG; + return; + ++ } else if (STREQ(args[optind], "zero_excluded")) { ++ ++ if (args[optind+1]) { ++ optind++; ++ if (STREQ(args[optind], "on")) ++ *diskdump_flags |= ZERO_EXCLUDED; ++ else if (STREQ(args[optind], "off")) ++ *diskdump_flags &= ~ZERO_EXCLUDED; ++ else if (IS_A_NUMBER(args[optind])) { ++ value = stol(args[optind], ++ FAULT_ON_ERROR, NULL); ++ if (value) ++ *diskdump_flags |= ZERO_EXCLUDED; ++ else ++ *diskdump_flags &= ~ZERO_EXCLUDED; ++ } else ++ goto invalid_set_command; ++ } ++ ++ if (runtime) ++ fprintf(fp, "zero_excluded: %s\n", ++ *diskdump_flags & ZERO_EXCLUDED ? ++ "on" : "off"); ++ return; ++ ++ } else if (XEN_HYPER_MODE()) { ++ error(FATAL, "invalid argument for the Xen hypervisor\n"); + } else if (runtime) { + ulong pid, task; + +@@ -2106,7 +2235,23 @@ + static void + show_options(void) + { +- fprintf(fp, " scroll: %s\n", pc->flags & SCROLL ? "on" : "off"); ++ fprintf(fp, " scroll: %s ", ++ pc->flags & SCROLL ? "on" : "off"); ++ switch (pc->scroll_command) ++ { ++ case SCROLL_LESS: ++ fprintf(fp, "(/usr/bin/less)\n"); ++ break; ++ case SCROLL_MORE: ++ fprintf(fp, "(/bin/more)\n"); ++ break; ++ case SCROLL_NONE: ++ fprintf(fp, "(none)\n"); ++ break; ++ case SCROLL_CRASHPAGER: ++ fprintf(fp, "(CRASHPAGER: %s)\n", getenv("CRASHPAGER")); ++ break; ++ } + fprintf(fp, " radix: %d (%s)\n", pc->output_radix, + pc->output_radix == 10 ? "decimal" : + pc->output_radix == 16 ? "hexadecimal" : "unknown"); +@@ -2121,6 +2266,9 @@ + fprintf(fp, " edit: %s\n", pc->editing_mode); + fprintf(fp, " namelist: %s\n", pc->namelist); + fprintf(fp, " dumpfile: %s\n", pc->dumpfile); ++ fprintf(fp, " unwind: %s\n", kt->flags & DWARF_UNWIND ? "on" : "off"); ++ fprintf(fp, " zero_excluded: %s\n", *diskdump_flags & ZERO_EXCLUDED ? "on" : "off"); ++ fprintf(fp, " null-stop: %s\n", stop_print_at_null ? "on" : "off"); + } + + +@@ -2336,6 +2484,7 @@ + char *element2; + struct syment *sp; + ++ opcode = 0; + value1 = value2 = 0; + ll_value1 = ll_value2 = 0; + +@@ -2550,7 +2699,7 @@ + * its real value. The allowable multipliers are k, K, m, M, g and G, for + * kilobytes, megabytes and gigabytes. + */ +-static int ++int + calculate(char *s, ulong *value, ulonglong *llvalue, ulong flags) + { + ulong factor, bias; +@@ -2832,7 +2981,9 @@ + break; + + case 's': +- ld->structname = optarg; ++ if (ld->structname_args++ == 0) ++ hq_open(); ++ hq_enter((ulong)optarg); + break; + + case 'o': +@@ -2871,6 +3022,12 @@ + cmd_usage(pc->curcmd, SYNOPSIS); + } + ++ if (ld->structname_args) { ++ ld->structname = (char **)GETBUF(sizeof(char *) * ld->structname_args); ++ retrieve_list((ulong *)ld->structname, ld->structname_args); ++ hq_close(); ++ } ++ + while (args[optind]) { + if (strstr(args[optind], ".") && + arg_to_datatype(args[optind], sm, RETURN_ON_ERROR) > 1) { +@@ -2896,11 +3053,25 @@ + } + + /* +- * If it's not a symbol nor a number, bail out. ++ * If it's not a symbol nor a number, bail out if it ++ * cannot be evaluated as a start address. + */ +- if (!IS_A_NUMBER(args[optind])) ++ if (!IS_A_NUMBER(args[optind])) { ++ if (can_eval(args[optind])) { ++ value = eval(args[optind], FAULT_ON_ERROR, NULL); ++ if (IS_KVADDR(value)) { ++ if (ld->flags & LIST_START_ENTERED) ++ error(FATAL, ++ "list start already entered\n"); ++ ld->start = value; ++ ld->flags |= LIST_START_ENTERED; ++ goto next_arg; ++ } ++ } ++ + error(FATAL, "invalid argument: %s\n", + args[optind]); ++ } + + /* + * If the start is known, it's got to be an offset. +@@ -2941,7 +3112,8 @@ + ld->member_offset = value; + ld->flags |= LIST_OFFSET_ENTERED; + goto next_arg; +- } else if (!IS_A_NUMBER(args[optind+1]) && ++ } else if ((!IS_A_NUMBER(args[optind+1]) && ++ !can_eval(args[optind+1])) && + !strstr(args[optind+1], ".")) + error(FATAL, "symbol not found: %s\n", + args[optind+1]); +@@ -3002,8 +3174,12 @@ + hq_open(); + c = do_list(ld); + hq_close(); ++ ++ if (ld->structname_args) ++ FREEBUF(ld->structname); + } + ++ + /* + * Does the work for cmd_list() and any other function that requires the + * contents of a linked list. See cmd_list description above for details. +@@ -3013,7 +3189,7 @@ + { + ulong next, last, first; + ulong searchfor, readflag; +- int count, others; ++ int i, count, others; + + if (CRASHDEBUG(1)) { + others = 0; +@@ -3038,7 +3214,11 @@ + console("list_head_offset: %ld\n", ld->list_head_offset); + console(" end: %lx\n", ld->end); + console(" searchfor: %lx\n", ld->searchfor); +- console(" structname: %s\n", ld->structname); ++ console(" structname_args: %lx\n", ld->structname_args); ++ if (!ld->structname_args) ++ console(" structname: (unused)\n"); ++ for (i = 0; i < ld->structname_args; i++) ++ console(" structname[%d]: %s\n", i, ld->structname[i]); + console(" header: %s\n", ld->header); + } + +@@ -3065,20 +3245,21 @@ + fprintf(fp, "%lx\n", next - ld->list_head_offset); + + if (ld->structname) { +- switch (count_chars(ld->structname, '.')) +- { +- case 0: +- dump_struct(ld->structname, +- next - ld->list_head_offset, 0); +- break; +- case 1: +- dump_struct_member(ld->structname, +- next - ld->list_head_offset, 0); +- break; +- default: +- error(FATAL, +- "invalid structure reference: %s\n", +- ld->structname); ++ for (i = 0; i < ld->structname_args; i++) { ++ switch (count_chars(ld->structname[i], '.')) ++ { ++ case 0: ++ dump_struct(ld->structname[i], ++ next - ld->list_head_offset, 0); ++ break; ++ case 1: ++ dump_struct_members(ld, i, next); ++ break; ++ default: ++ error(FATAL, ++ "invalid structure reference: %s\n", ++ ld->structname[i]); ++ } + } + } + } +@@ -3148,6 +3329,42 @@ + } + + /* ++ * Issue a dump_struct_member() call for one or more structure ++ * members. Multiple members are passed in a comma-separated ++ * list using the the format: ++ * ++ * struct.member1,member2,member3 ++ */ ++void ++dump_struct_members(struct list_data *ld, int idx, ulong next) ++{ ++ int i, argc; ++ char *p1, *p2; ++ char *structname, *members; ++ char *arglist[MAXARGS]; ++ ++ structname = GETBUF(strlen(ld->structname[idx])+1); ++ members = GETBUF(strlen(ld->structname[idx])+1); ++ ++ strcpy(structname, ld->structname[idx]); ++ p1 = strstr(structname, ".") + 1; ++ ++ p2 = strstr(ld->structname[idx], ".") + 1; ++ strcpy(members, p2); ++ replace_string(members, ",", ' '); ++ argc = parse_line(members, arglist); ++ ++ for (i = 0; i < argc; i++) { ++ *p1 = NULLCHAR; ++ strcat(structname, arglist[i]); ++ dump_struct_member(structname, next - ld->list_head_offset, 0); ++ } ++ ++ FREEBUF(structname); ++ FREEBUF(members); ++} ++ ++/* + * The next set of functions are a general purpose hashing tool used to + * identify duplicate entries in a set of passed-in data, and if found, + * to fail the entry attempt. When a command wishes to verify a list +@@ -3552,6 +3769,52 @@ + return(-1); + } + ++/* ++ * For a given value, check to see if a hash queue entry exists. If an ++ * entry is found, return TRUE; for all other possibilities return FALSE. ++ */ ++int ++hq_entry_exists(ulong value) ++{ ++ struct hash_table *ht; ++ struct hq_entry *list_entry; ++ long hqi; ++ ++ if (!(pc->flags & HASH)) ++ return FALSE; ++ ++ ht = &hash_table; ++ ++ if (ht->flags & (HASH_QUEUE_NONE)) ++ return FALSE; ++ ++ if (!(ht->flags & HASH_QUEUE_OPEN)) ++ return FALSE; ++ ++ hqi = HQ_INDEX(value); ++ list_entry = ht->memptr + ht->queue_heads[hqi].next; ++ ++ while (TRUE) { ++ if (list_entry->value == value) ++ return TRUE; ++ ++ if (list_entry->next >= ht->count) { ++ error(INFO, corrupt_hq, ++ list_entry->value, ++ list_entry->next, ++ list_entry->order); ++ ht->flags |= HASH_QUEUE_NONE; ++ return FALSE; ++ } ++ ++ if (list_entry->next == 0) ++ break; ++ ++ list_entry = ht->memptr + list_entry->next; ++ } ++ ++ return FALSE; ++} + + /* + * K&R power function for integers +@@ -4210,6 +4473,14 @@ + { + ulonglong total, days, hours, minutes, seconds; + ++ if (CRASHDEBUG(2)) ++ error(INFO, "convert_time: %lld (%llx)\n", count, count); ++ ++ if (!machdep->hz) { ++ sprintf(buf, "(cannot calculate: unknown HZ value)"); ++ return buf; ++ } ++ + total = (count)/(ulonglong)machdep->hz; + + days = total / SEC_DAYS; +@@ -4297,15 +4568,142 @@ + return STREQ(MACHINE_TYPE, type); + } + ++int ++machine_type_mismatch(char *file, char *e_machine, char *alt, ulong query) ++{ ++ if (machine_type(e_machine) || machine_type(alt)) ++ return FALSE; ++ ++ if (query == KDUMP_LOCAL) /* already printed by NETDUMP_LOCAL */ ++ return TRUE; ++ ++ error(WARNING, "machine type mismatch:\n"); ++ ++ fprintf(fp, " crash utility: %s\n", MACHINE_TYPE); ++ fprintf(fp, " %s: %s%s%s\n\n", file, e_machine, ++ alt ? " or " : "", alt ? alt : ""); ++ ++ return TRUE; ++} + void + command_not_supported() + { +- error(FATAL, "command not supported on this architecture\n"); ++ error(FATAL, ++ "command not supported or applicable on this architecture or kernel\n"); + } + + void + option_not_supported(int c) + { +- error(FATAL, "-%c option not supported on this architecture\n", ++ error(FATAL, ++ "-%c option not supported or applicable on this architecture or kernel\n", + (char)c); + } ++ ++void ++please_wait(char *s) ++{ ++ if ((pc->flags & SILENT) || !(pc->flags & TTY) || ++ !DUMPFILE() || (pc->flags & RUNTIME)) ++ return; ++ ++ pc->flags |= PLEASE_WAIT; ++ ++ fprintf(fp, "\rplease wait... (%s)", s); ++ fflush(fp); ++} ++ ++void ++please_wait_done(void) ++{ ++ if ((pc->flags & SILENT) || !(pc->flags & TTY) || ++ !DUMPFILE() || (pc->flags & RUNTIME)) ++ return; ++ ++ pc->flags &= ~PLEASE_WAIT; ++ ++ fprintf(fp, "\r \r"); ++ fflush(fp); ++} ++ ++/* ++ * Compare two pathnames. ++ */ ++int ++pathcmp(char *p1, char *p2) ++{ ++ char c1, c2; ++ ++ do { ++ if ((c1 = *p1++) == '/') ++ while (*p1 == '/') { p1++; } ++ if ((c2 = *p2++) == '/') ++ while (*p2 == '/') { p2++; } ++ if (c1 == '\0') ++ return ((c2 == '/') && (*p2 == '\0')) ? 0 : c1 - c2; ++ } while (c1 == c2); ++ ++ return ((c2 == '\0') && (c1 == '/') && (*p1 == '\0')) ? 0 : c1 - c2; ++} ++ ++#include ++ ++/* ++ * Check the byte-order of an ELF file vs. the host byte order. ++ */ ++int ++endian_mismatch(char *file, char dumpfile_endian, ulong query) ++{ ++ char *endian; ++ ++ switch (dumpfile_endian) ++ { ++ case ELFDATA2LSB: ++ if (__BYTE_ORDER == __LITTLE_ENDIAN) ++ return FALSE; ++ endian = "big-endian"; ++ break; ++ case ELFDATA2MSB: ++ if (__BYTE_ORDER == __BIG_ENDIAN) ++ return FALSE; ++ endian = "little-endian"; ++ break; ++ default: ++ endian = "unknown"; ++ break; ++ } ++ ++ if (query == KDUMP_LOCAL) /* already printed by NETDUMP_LOCAL */ ++ return TRUE; ++ ++ error(WARNING, "endian mismatch:\n"); ++ ++ fprintf(fp, " crash utility: %s\n", ++ (__BYTE_ORDER == __LITTLE_ENDIAN) ? ++ "little-endian" : "big-endian"); ++ fprintf(fp, " %s: %s\n\n", file, endian); ++ ++ return TRUE; ++} ++ ++uint16_t ++swap16(uint16_t val, int swap) ++{ ++ if (swap) ++ return (((val & 0x00ff) << 8) | ++ ((val & 0xff00) >> 8)); ++ else ++ return val; ++} ++ ++uint32_t ++swap32(uint32_t val, int swap) ++{ ++ if (swap) ++ return (((val & 0x000000ffU) << 24) | ++ ((val & 0x0000ff00U) << 8) | ++ ((val & 0x00ff0000U) >> 8) | ++ ((val & 0xff000000U) >> 24)); ++ else ++ return val; ++} +--- crash/unwind_x86.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/unwind_x86.h 2006-10-20 14:58:14.000000000 -0400 +@@ -0,0 +1,2 @@ ++ ++ +--- crash/ia64.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/ia64.c 2008-09-25 14:56:20.000000000 -0400 +@@ -1,8 +1,8 @@ + /* ia64.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 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 +@@ -16,6 +16,8 @@ + */ + #ifdef IA64 + #include "defs.h" ++#include "xen_hyper_defs.h" ++#include + + static int ia64_verify_symbol(const char *, ulong, char); + static int ia64_eframe_search(struct bt_info *); +@@ -25,6 +27,8 @@ + static void try_old_unwind(struct bt_info *); + static void ia64_dump_irq(int); + static ulong ia64_processor_speed(void); ++static int ia64_vtop_4l(ulong, physaddr_t *paddr, ulong *pgd, int, int); ++static int ia64_vtop(ulong, physaddr_t *paddr, ulong *pgd, int, int); + static int ia64_uvtop(struct task_context *, ulong, physaddr_t *, int); + static int ia64_kvtop(struct task_context *, ulong, physaddr_t *, int); + static ulong ia64_get_task_pgd(ulong); +@@ -47,10 +51,12 @@ + static int ia64_verify_paddr(uint64_t); + static int ia64_available_memory(struct efi_memory_desc_t *); + static void ia64_post_init(void); ++static ulong ia64_in_per_cpu_mca_stack(void); + static struct line_number_hook ia64_line_number_hooks[]; + static ulong ia64_get_stackbase(ulong); + static ulong ia64_get_stacktop(ulong); + static void parse_cmdline_arg(void); ++static void ia64_calc_phys_start(void); + + struct unw_frame_info; + static void dump_unw_frame_info(struct unw_frame_info *); +@@ -62,6 +68,17 @@ + static ulong rse_read_reg(struct unw_frame_info *, int, int *); + static void rse_function_params(struct unw_frame_info *, char *); + ++static int ia64_vtop_4l_xen_wpt(ulong, physaddr_t *paddr, ulong *pgd, int, int); ++static int ia64_vtop_xen_wpt(ulong, physaddr_t *paddr, ulong *pgd, int, int); ++static int ia64_xen_kdump_p2m_create(struct xen_kdump_data *); ++static int ia64_xendump_p2m_create(struct xendump_data *); ++static void ia64_debug_dump_page(FILE *, char *, char *); ++static char *ia64_xendump_load_page(ulong, struct xendump_data *); ++static int ia64_xendump_page_index(ulong, struct xendump_data *); ++static ulong ia64_xendump_panic_task(struct xendump_data *); ++static void ia64_get_xendump_regs(struct xendump_data *, struct bt_info *, ulong *, ulong *); ++ ++static void ia64_init_hyper(int); + + struct machine_specific ia64_machine_specific = { 0 }; + +@@ -70,8 +87,22 @@ + { + struct syment *sp, *spn; + ++ if (XEN_HYPER_MODE()) { ++ ia64_init_hyper(when); ++ return; ++ } ++ + switch (when) + { ++ case SETUP_ENV: ++#if defined(PR_SET_FPEMU) && defined(PR_FPEMU_NOPRINT) ++ prctl(PR_SET_FPEMU, PR_FPEMU_NOPRINT, 0, 0, 0); ++#endif ++#if defined(PR_SET_UNALIGN) && defined(PR_UNALIGN_NOPRINT) ++ prctl(PR_SET_UNALIGN, PR_UNALIGN_NOPRINT, 0, 0, 0); ++#endif ++ break; ++ + case PRE_SYMTAB: + machdep->verify_symbol = ia64_verify_symbol; + machdep->machspec = &ia64_machine_specific; +@@ -92,17 +123,23 @@ + case 16384: + machdep->stacksize = (power(2, 1) * PAGESIZE()); + break; ++ case 65536: ++ machdep->stacksize = (power(2, 0) * PAGESIZE()); ++ break; + default: + machdep->stacksize = 32*1024; + break; + } + if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pgd space."); ++ if ((machdep->pud = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pud space."); + if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pmd space."); + if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc ptbl space."); + machdep->last_pgd_read = 0; ++ machdep->last_pud_read = 0; + machdep->last_pmd_read = 0; + machdep->last_ptbl_read = 0; + machdep->verify_paddr = ia64_verify_paddr; +@@ -115,14 +152,17 @@ + break; + + case PRE_GDB: ++ + if (pc->flags & KERNEL_DEBUG_QUERY) + return; ++ + /* + * Until the kernel core dump and va_server library code + * do the right thing with respect to the configured page size, + * try to recognize a fatal inequity between the compiled-in + * page size and the page size used by the kernel. + */ ++ + + if ((sp = symbol_search("empty_zero_page")) && + (spn = next_symbol(NULL, sp)) && +@@ -169,10 +209,14 @@ + machdep->machspec->kernel_start + + GIGABYTES((ulong)(4)); + if (machdep->machspec->phys_start == UNKNOWN_PHYS_START) +- machdep->machspec->phys_start = +- DEFAULT_PHYS_START; ++ ia64_calc_phys_start(); + } else + machdep->machspec->vmalloc_start = KERNEL_VMALLOC_BASE; ++ ++ machdep->xen_kdump_p2m_create = ia64_xen_kdump_p2m_create; ++ machdep->xendump_p2m_create = ia64_xendump_p2m_create; ++ machdep->xendump_panic_task = ia64_xendump_panic_task; ++ machdep->get_xendump_regs = ia64_get_xendump_regs; + break; + + case POST_GDB: +@@ -202,7 +246,10 @@ + else if (symbol_exists("_irq_desc")) + ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc, + "_irq_desc", NULL, 0); +- machdep->hz = 1024; ++ if (!machdep->hz) ++ machdep->hz = 1024; ++ machdep->section_size_bits = _SECTION_SIZE_BITS; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; + ia64_create_memmap(); + break; + +@@ -228,8 +275,10 @@ + char *arglist[MAXARGS]; + ulong value; + struct machine_specific *ms; ++ int vm_flag; + + ms = &ia64_machine_specific; ++ vm_flag = 0; + + if (!strstr(machdep->cmdline_arg, "=")) { + errflag = 0; +@@ -284,11 +333,37 @@ + continue; + } + } ++ } else if (STRNEQ(arglist[i], "vm=")) { ++ vm_flag++; ++ p = arglist[i] + strlen("vm="); ++ if (strlen(p)) { ++ if (STREQ(p, "4l")) { ++ machdep->flags |= VM_4_LEVEL; ++ continue; ++ } ++ } + } + + error(WARNING, "ignoring --machdep option: %s\n", arglist[i]); + } + ++ if (vm_flag) { ++ switch (machdep->flags & (VM_4_LEVEL)) ++ { ++ case VM_4_LEVEL: ++ error(NOTE, "using 4-level pagetable\n"); ++ c++; ++ break; ++ ++ default: ++ error(WARNING, "invalid vm= option\n"); ++ c++; ++ machdep->flags &= ~(VM_4_LEVEL); ++ break; ++ } ++ } ++ ++ + if (c) + fprintf(fp, "\n"); + } +@@ -314,6 +389,58 @@ + return TRUE; + } + ++ ++static ulong ++ia64_in_per_cpu_mca_stack(void) ++{ ++ int plen, i; ++ ulong flag; ++ ulong vaddr, paddr, stackbase, stacktop; ++ ulong *__per_cpu_mca; ++ struct task_context *tc; ++ ++ tc = CURRENT_CONTEXT(); ++ ++ if (STRNEQ(CURRENT_COMM(), "INIT")) ++ flag = INIT; ++ else if (STRNEQ(CURRENT_COMM(), "MCA")) ++ flag = MCA; ++ else ++ return 0; ++ ++ if (!symbol_exists("__per_cpu_mca") || ++ !(plen = get_array_length("__per_cpu_mca", NULL, 0)) || ++ (plen < kt->cpus)) ++ return 0; ++ ++ vaddr = SWITCH_STACK_ADDR(CURRENT_TASK()); ++ if (VADDR_REGION(vaddr) != KERNEL_CACHED_REGION) ++ return 0; ++ paddr = ia64_VTOP(vaddr); ++ ++ __per_cpu_mca = (ulong *)GETBUF(sizeof(ulong) * kt->cpus); ++ ++ if (!readmem(symbol_value("__per_cpu_mca"), KVADDR, __per_cpu_mca, ++ sizeof(ulong) * kt->cpus, "__per_cpu_mca", RETURN_ON_ERROR|QUIET)) ++ return 0; ++ ++ if (CRASHDEBUG(1)) { ++ for (i = 0; i < kt->cpus; i++) { ++ fprintf(fp, "__per_cpu_mca[%d]: %lx\n", ++ i, __per_cpu_mca[i]); ++ } ++ } ++ ++ stackbase = __per_cpu_mca[tc->processor]; ++ stacktop = stackbase + (STACKSIZE() * 2); ++ FREEBUF(__per_cpu_mca); ++ ++ if ((paddr >= stackbase) && (paddr < stacktop)) ++ return flag; ++ else ++ return 0; ++} ++ + void + ia64_dump_machdep_table(ulong arg) + { +@@ -401,12 +528,14 @@ + fprintf(fp, "%sUNW_R0", others++ ? "|" : ""); + if (machdep->flags & MEM_LIMIT) + fprintf(fp, "%sMEM_LIMIT", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + if (machdep->flags & DEVMEMRD) + fprintf(fp, "%sDEVMEMRD", others++ ? "|" : ""); + if (machdep->flags & INIT) + fprintf(fp, "%sINIT", others++ ? "|" : ""); ++ if (machdep->flags & MCA) ++ fprintf(fp, "%sMCA", others++ ? "|" : ""); ++ if (machdep->flags & VM_4_LEVEL) ++ fprintf(fp, "%sVM_4_LEVEL", others++ ? "|" : ""); + fprintf(fp, ")\n"); + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); + fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base); +@@ -445,16 +574,25 @@ + (machdep->verify_paddr == ia64_verify_paddr) ? + "ia64_verify_paddr" : "generic_verify_paddr"); + fprintf(fp, " init_kernel_pgd: NULL\n"); ++ fprintf(fp, "xen_kdump_p2m_create: ia64_xen_kdump_p2m_create()\n"); ++ fprintf(fp, " xendump_p2m_create: ia64_xendump_p2m_create()\n"); ++ fprintf(fp, " xendump_panic_task: ia64_xendump_panic_task()\n"); ++ fprintf(fp, " get_xendump_regs: ia64_get_xendump_regs()\n"); + fprintf(fp, " value_to_symbol: generic_machdep_value_to_symbol()\n"); + fprintf(fp, " line_number_hooks: ia64_line_number_hooks\n"); + fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); ++ fprintf(fp, " last_pud_read: %lx\n", machdep->last_pud_read); + fprintf(fp, " last_pmd_read: %lx\n", machdep->last_pmd_read); + fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read); + fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); ++ fprintf(fp, " pud: %lx\n", (ulong)machdep->pud); + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); + fprintf(fp, " cmdline_arg: %s\n", machdep->cmdline_arg); ++ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); ++ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); ++ fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); + fprintf(fp, " machspec: ia64_machine_specific\n"); + fprintf(fp, " cpu_data_address: %lx\n", + machdep->machspec->cpu_data_address); +@@ -565,9 +703,9 @@ + if (CRASHDEBUG(8)) + fprintf(fp, "%016lx %s\n", value, name); + +- if (STREQ(name, "phys_start") && type == 'A') +- if (machdep->machspec->phys_start == UNKNOWN_PHYS_START) +- machdep->machspec->phys_start = value; ++// if (STREQ(name, "phys_start") && type == 'A') ++// if (machdep->machspec->phys_start == UNKNOWN_PHYS_START) ++// machdep->machspec->phys_start = value; + + region = VADDR_REGION(value); + +@@ -665,74 +803,148 @@ + return (machdep->mhz = mhz); + } + +- +-/* +- * Translates a user virtual address to its physical address. cmd_vtop() +- * sets the verbose flag so that the pte translation gets displayed; all +- * other callers quietly accept the translation. +- * +- * This routine can also take mapped kernel virtual addresses if the -u flag +- * was passed to cmd_vtop(). If so, it makes the translation using the +- * swapper_pg_dir, making it irrelevant in this processor's case. ++/* Generic abstraction to translate user or kernel virtual ++ * addresses to physical using a 4 level page table. + */ + static int +-ia64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) ++ia64_vtop_4l(ulong vaddr, physaddr_t *paddr, ulong *pgd, int verbose, int usr) + { +- ulong mm; +- ulong *pgd; + ulong *page_dir; ++ ulong *page_upper; + ulong *page_middle; + ulong *page_table; + ulong pgd_pte; ++ ulong pud_pte; + ulong pmd_pte; + ulong pte; + ulong region, offset; + +- if (!tc) +- error(FATAL, "current context invalid\n"); +- +- *paddr = 0; +- region = VADDR_REGION(uvaddr); ++ if (usr) { ++ region = VADDR_REGION(vaddr); ++ offset = (vaddr >> PGDIR_SHIFT) & ((PTRS_PER_PGD >> 3) - 1); ++ offset |= (region << (PAGESHIFT() - 6)); ++ page_dir = pgd + offset; ++ } else { ++ if (!(pgd = (ulong *)vt->kernel_pgd[0])) ++ error(FATAL, "cannot determine kernel pgd pointer\n"); ++ page_dir = pgd + ((vaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)); ++ } + +- if (IS_KVADDR(uvaddr)) +- return ia64_kvtop(tc, uvaddr, paddr, verbose); ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); + +- if ((mm = task_mm(tc->task, TRUE))) +- pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); +- else +- readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, +- sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); ++ FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE()); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); + ++ if (!(pgd_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PUD_SHIFT) & (PTRS_PER_PUD - 1); ++ page_upper = (ulong *)(PTOV(pgd_pte & _PFN_MASK)) + offset; ++ ++ FILL_PUD(PAGEBASE(page_upper), KVADDR, PAGESIZE()); ++ pud_pte = ULONG(machdep->pud + PAGEOFFSET(page_upper)); ++ + if (verbose) +- fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ fprintf(fp, " PUD: %lx => %lx\n", (ulong)page_upper, pud_pte); ++ ++ if (!(pud_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1); ++ page_middle = (ulong *)(PTOV(pud_pte & _PFN_MASK)) + offset; ++ ++ FILL_PMD(PAGEBASE(page_middle), KVADDR, PAGESIZE()); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); ++ ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte); ++ ++ if (!(pmd_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PAGESHIFT()) & (PTRS_PER_PTE - 1); ++ page_table = (ulong *)(PTOV(pmd_pte & _PFN_MASK)) + offset; ++ ++ FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE()); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); + +- offset = (uvaddr >> PGDIR_SHIFT) & ((PTRS_PER_PGD >> 3) - 1); +- offset |= (region << (PAGESHIFT() - 6)); +- page_dir = pgd + offset; ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); ++ ++ if (!(pte & (_PAGE_P))) { ++ if (usr) ++ *paddr = pte; ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ ia64_translate_pte(pte, 0, 0); ++ } ++ return FALSE; ++ } ++ ++ *paddr = (pte & _PFN_MASK) + PAGEOFFSET(vaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ ia64_translate_pte(pte, 0, 0); ++ } ++ ++ return TRUE; ++} ++ ++/* Generic abstraction to translate user or kernel virtual ++ * addresses to physical using a 3 level page table. ++ */ ++static int ++ia64_vtop(ulong vaddr, physaddr_t *paddr, ulong *pgd, int verbose, int usr) ++{ ++ ulong *page_dir; ++ ulong *page_middle; ++ ulong *page_table; ++ ulong pgd_pte; ++ ulong pmd_pte; ++ ulong pte; ++ ulong region, offset; ++ ++ if (usr) { ++ region = VADDR_REGION(vaddr); ++ offset = (vaddr >> PGDIR_SHIFT_3L) & ((PTRS_PER_PGD >> 3) - 1); ++ offset |= (region << (PAGESHIFT() - 6)); ++ page_dir = pgd + offset; ++ } else { ++ if (!(pgd = (ulong *)vt->kernel_pgd[0])) ++ error(FATAL, "cannot determine kernel pgd pointer\n"); ++ page_dir = pgd + ((vaddr >> PGDIR_SHIFT_3L) & (PTRS_PER_PGD - 1)); ++ } + ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ + FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE()); + pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); + +- if (verbose) { ++ if (verbose) + fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); +- } + + if (!(pgd_pte)) +- goto no_upage; ++ return FALSE; + +- offset = (uvaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1); ++ offset = (vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1); + page_middle = (ulong *)(PTOV(pgd_pte & _PFN_MASK)) + offset; + + FILL_PMD(PAGEBASE(page_middle), KVADDR, PAGESIZE()); + pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); + + if (verbose) +- fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle,pmd_pte); ++ fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte); + + if (!(pmd_pte)) +- goto no_upage; ++ return FALSE; + +- offset = (uvaddr >> PAGESHIFT()) & (PTRS_PER_PTE - 1); ++ offset = (vaddr >> PAGESHIFT()) & (PTRS_PER_PTE - 1); + page_table = (ulong *)(PTOV(pmd_pte & _PFN_MASK)) + offset; + + FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE()); +@@ -742,15 +954,16 @@ + fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); + + if (!(pte & (_PAGE_P))) { +- *paddr = pte; ++ if (usr) ++ *paddr = pte; + if (pte && verbose) { + fprintf(fp, "\n"); + ia64_translate_pte(pte, 0, 0); + } +- goto no_upage; ++ return FALSE; + } + +- *paddr = (pte & _PFN_MASK) + PAGEOFFSET(uvaddr); ++ *paddr = (pte & _PFN_MASK) + PAGEOFFSET(vaddr); + + if (verbose) { + fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); +@@ -758,10 +971,50 @@ + } + + return TRUE; ++} + +-no_upage: + +- return FALSE; ++/* ++ * Translates a user virtual address to its physical address. cmd_vtop() ++ * sets the verbose flag so that the pte translation gets displayed; all ++ * other callers quietly accept the translation. ++ * ++ * This routine can also take mapped kernel virtual addresses if the -u flag ++ * was passed to cmd_vtop(). If so, it makes the translation using the ++ * swapper_pg_dir, making it irrelevant in this processor's case. ++ */ ++static int ++ia64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) ++{ ++ ulong mm; ++ ulong *pgd; ++ ++ if (!tc) ++ error(FATAL, "current context invalid\n"); ++ ++ *paddr = 0; ++ ++ if (IS_KVADDR(uvaddr)) ++ return ia64_kvtop(tc, uvaddr, paddr, verbose); ++ ++ if ((mm = task_mm(tc->task, TRUE))) ++ pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); ++ else ++ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, ++ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); ++ ++ if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) { ++ if (machdep->flags & VM_4_LEVEL) ++ return ia64_vtop_4l_xen_wpt(uvaddr, paddr, pgd, verbose, 1); ++ else ++ return ia64_vtop_xen_wpt(uvaddr, paddr, pgd, verbose, 1); ++ } else { ++ if (machdep->flags & VM_4_LEVEL) ++ return ia64_vtop_4l(uvaddr, paddr, pgd, verbose, 1); ++ else ++ return ia64_vtop(uvaddr, paddr, pgd, verbose, 1); ++ } ++ + } + + +@@ -774,13 +1027,6 @@ + ia64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) + { + ulong *pgd; +- ulong *page_dir; +- ulong *page_middle; +- ulong *page_table; +- ulong pgd_pte; +- ulong pmd_pte; +- ulong pte; +- ulong offset; + + if (!IS_KVADDR(kvaddr)) + return FALSE; +@@ -807,72 +1053,33 @@ + case KERNEL_VMALLOC_REGION: + if (ia64_IS_VMALLOC_ADDR(kvaddr)) + break; ++ if ((kvaddr < machdep->machspec->kernel_start) && ++ (machdep->machspec->kernel_region == ++ KERNEL_VMALLOC_REGION)) { ++ *paddr = PADDR_NOT_AVAILABLE; ++ return FALSE; ++ } + *paddr = ia64_VTOP(kvaddr); + if (verbose) + fprintf(fp, "[MAPPED IN TRANSLATION REGISTER]\n"); + return TRUE; + } + +- pgd = (ulong *)vt->kernel_pgd[0]; +- +- if (verbose) { +- fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); +- } +- +- page_dir = pgd + ((kvaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)); +- +- FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE()); +- pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); +- +- if (verbose) { +- fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); +- } +- +- if (!(pgd_pte)) +- goto no_kpage; +- +- offset = (kvaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1); +- page_middle = (ulong *)(PTOV(pgd_pte & _PFN_MASK)) + offset; +- +- FILL_PMD(PAGEBASE(page_middle), KVADDR, PAGESIZE()); +- pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); +- +- if (verbose) +- fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, +- pmd_pte); +- +- if (!(pmd_pte)) +- goto no_kpage; +- +- offset = (kvaddr >> PAGESHIFT()) & (PTRS_PER_PTE - 1); +- page_table = (ulong *)(PTOV(pmd_pte & _PFN_MASK)) + offset; +- +- FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE()); +- pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); +- +- if (verbose) +- fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); +- +- if (!(pte & (_PAGE_P))) { +- if (pte && verbose) { +- fprintf(fp, "\n"); +- ia64_translate_pte(pte, 0, 0); +- } +- goto no_kpage; +- } +- +- *paddr = (pte & _PFN_MASK) + PAGEOFFSET(kvaddr); ++ if (!(pgd = (ulong *)vt->kernel_pgd[0])) ++ error(FATAL, "cannot determine kernel pgd pointer\n"); + +- if (verbose) { +- fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); +- ia64_translate_pte(pte, 0, 0); ++ if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) { ++ if (machdep->flags & VM_4_LEVEL) ++ return ia64_vtop_4l_xen_wpt(kvaddr, paddr, pgd, verbose, 0); ++ else ++ return ia64_vtop_xen_wpt(kvaddr, paddr, pgd, verbose, 0); ++ } else { ++ if (machdep->flags & VM_4_LEVEL) ++ return ia64_vtop_4l(kvaddr, paddr, pgd, verbose, 0); ++ else ++ return ia64_vtop(kvaddr, paddr, pgd, verbose, 0); + } + +- return TRUE; +- +-no_kpage: +- +- return FALSE; + } + + /* +@@ -958,9 +1165,15 @@ + { + ulong ksp; + +- readmem(task + OFFSET(task_struct_thread_ksp), KVADDR, +- &ksp, sizeof(void *), +- "thread_struct ksp", FAULT_ON_ERROR); ++ if (XEN_HYPER_MODE()) { ++ readmem(task + XEN_HYPER_OFFSET(vcpu_thread_ksp), KVADDR, ++ &ksp, sizeof(void *), ++ "vcpu thread ksp", FAULT_ON_ERROR); ++ } else { ++ readmem(task + OFFSET(task_struct_thread_ksp), KVADDR, ++ &ksp, sizeof(void *), ++ "thread_struct ksp", FAULT_ON_ERROR); ++ } + + return ksp; + } +@@ -1315,7 +1528,10 @@ + BZERO(&eframe, sizeof(ulong) * NUM_PT_REGS); + + open_tmpfile(); +- dump_struct("pt_regs", addr, RADIX(16)); ++ if (XEN_HYPER_MODE()) ++ dump_struct("cpu_user_regs", addr, RADIX(16)); ++ else ++ dump_struct("pt_regs", addr, RADIX(16)); + rewind(pc->tmpfile); + + fval = 0; +@@ -1571,6 +1787,12 @@ + + fprintf(fp, " EFRAME: %lx\n", addr); + ++ if (bt->flags & BT_INCOMPLETE_USER_EFRAME) { ++ fprintf(fp, ++ " [exception frame incomplete -- check salinfo for complete context]\n"); ++ bt->flags &= ~BT_INCOMPLETE_USER_EFRAME; ++ } ++ + fprintf(fp, " B0: %016lx CR_IIP: %016lx\n", + eframe[P_b0], eframe[P_cr_iip]); + /** +@@ -2099,7 +2321,7 @@ + fprintf(fp, "(unknown)\n"); + fprintf(fp, " HZ: %d\n", machdep->hz); + fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); +- fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); ++// fprintf(fp, " L1 CACHE SIZE: %d\n", l1_cache_size()); + fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); + fprintf(fp, " KERNEL CACHED REGION: %lx\n", + (ulong)KERNEL_CACHED_REGION << REGION_SHIFT); +@@ -2371,9 +2593,10 @@ + !readmem(ia64_boot_param+ + MEMBER_OFFSET("ia64_boot_param", "efi_memmap"), + KVADDR, &efi_memmap, sizeof(uint64_t), "efi_memmap", +- RETURN_ON_ERROR)) { +- error(WARNING, "cannot read ia64_boot_param: " +- "memory verification will not be performed\n\n"); ++ QUIET|RETURN_ON_ERROR)) { ++ if (!XEN() || CRASHDEBUG(1)) ++ error(WARNING, "cannot read ia64_boot_param: " ++ "memory verification will not be performed\n\n"); + return; + } + +@@ -2391,9 +2614,11 @@ + + if ((ms->mem_limit && (efi_memmap >= ms->mem_limit)) || + !readmem(PTOV(efi_memmap), KVADDR, memmap, +- ms->efi_memmap_size, "efi_mmap contents", RETURN_ON_ERROR)) { +- error(WARNING, "cannot read efi_mmap: " +- "memory verification will not be performed\n"); ++ ms->efi_memmap_size, "efi_mmap contents", ++ QUIET|RETURN_ON_ERROR)) { ++ if (!XEN() || (XEN() && CRASHDEBUG(1))) ++ error(WARNING, "cannot read efi_mmap: " ++ "EFI memory verification will not be performed\n\n"); + free(memmap); + return; + } +@@ -2605,6 +2830,8 @@ + ia64_post_init(void) + { + struct machine_specific *ms; ++ struct gnu_request req; ++ ulong flag; + + ms = &ia64_machine_specific; + +@@ -2677,12 +2904,16 @@ + } + } + +- if (symbol_exists("ia64_init_stack") && !ms->ia64_init_stack_size) +- ms->ia64_init_stack_size = get_array_length("ia64_init_stack", +- NULL, 0); ++ if (symbol_exists("ia64_init_stack") && !ms->ia64_init_stack_size) { ++ get_symbol_type("ia64_init_stack", NULL, &req); ++ ms->ia64_init_stack_size = req.length; ++ } + + if (DUMPFILE() && ia64_in_init_stack(SWITCH_STACK_ADDR(CURRENT_TASK()))) + machdep->flags |= INIT; ++ ++ if (DUMPFILE() && (flag = ia64_in_per_cpu_mca_stack())) ++ machdep->flags |= flag; + } + + /* +@@ -3326,4 +3557,775 @@ + (vaddr < (ulong)KERNEL_UNCACHED_BASE)); + } + ++/* Generic abstraction to translate user or kernel virtual ++ * addresses to physical using a 4 level page table. ++ */ ++static int ++ia64_vtop_4l_xen_wpt(ulong vaddr, physaddr_t *paddr, ulong *pgd, int verbose, int usr) ++{ ++ error(FATAL, "ia64_vtop_4l_xen_wpt: TBD\n"); ++ return FALSE; ++#ifdef TBD ++ ulong *page_dir; ++ ulong *page_upper; ++ ulong *page_middle; ++ ulong *page_table; ++ ulong pgd_pte; ++ ulong pud_pte; ++ ulong pmd_pte; ++ ulong pte; ++ ulong region, offset; ++ ++ ++ if (usr) { ++ region = VADDR_REGION(vaddr); ++ offset = (vaddr >> PGDIR_SHIFT) & ((PTRS_PER_PGD >> 3) - 1); ++ offset |= (region << (PAGESHIFT() - 6)); ++ page_dir = pgd + offset; ++ } else { ++ if (!(pgd = (ulong *)vt->kernel_pgd[0])) ++ error(FATAL, "cannot determine kernel pgd pointer\n"); ++ page_dir = pgd + ((vaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)); ++ } ++ ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ ++ FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE()); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); ++ ++ if (!(pgd_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PUD_SHIFT) & (PTRS_PER_PUD - 1); ++ page_upper = (ulong *)(PTOV(pgd_pte & _PFN_MASK)) + offset; ++ ++ FILL_PUD(PAGEBASE(page_upper), KVADDR, PAGESIZE()); ++ pud_pte = ULONG(machdep->pud + PAGEOFFSET(page_upper)); ++ ++ if (verbose) ++ fprintf(fp, " PUD: %lx => %lx\n", (ulong)page_upper, pud_pte); ++ ++ if (!(pud_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1); ++ page_middle = (ulong *)(PTOV(pud_pte & _PFN_MASK)) + offset; ++ ++ FILL_PMD(PAGEBASE(page_middle), KVADDR, PAGESIZE()); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); ++ ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte); ++ ++ if (!(pmd_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PAGESHIFT()) & (PTRS_PER_PTE - 1); ++ page_table = (ulong *)(PTOV(pmd_pte & _PFN_MASK)) + offset; ++ ++ FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE()); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); ++ ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); ++ ++ if (!(pte & (_PAGE_P))) { ++ if (usr) ++ *paddr = pte; ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ ia64_translate_pte(pte, 0, 0); ++ } ++ return FALSE; ++ } ++ ++ *paddr = (pte & _PFN_MASK) + PAGEOFFSET(vaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ ia64_translate_pte(pte, 0, 0); ++ } ++ ++ return TRUE; ++#endif ++} ++ ++/* Generic abstraction to translate user or kernel virtual ++ * addresses to physical using a 3 level page table. ++ */ ++static int ++ia64_vtop_xen_wpt(ulong vaddr, physaddr_t *paddr, ulong *pgd, int verbose, int usr) ++{ ++ error(FATAL, "ia64_vtop_xen_wpt: TBD\n"); ++ return FALSE; ++#ifdef TBD ++ ulong *page_dir; ++ ulong *page_middle; ++ ulong *page_table; ++ ulong pgd_pte; ++ ulong pmd_pte; ++ ulong pte; ++ ulong region, offset; ++ ++ ++ if (usr) { ++ region = VADDR_REGION(vaddr); ++ offset = (vaddr >> PGDIR_SHIFT) & ((PTRS_PER_PGD >> 3) - 1); ++ offset |= (region << (PAGESHIFT() - 6)); ++ page_dir = pgd + offset; ++ } else { ++ if (!(pgd = (ulong *)vt->kernel_pgd[0])) ++ error(FATAL, "cannot determine kernel pgd pointer\n"); ++ page_dir = pgd + ((vaddr >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)); ++ } ++ ++ if (verbose) ++ fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); ++ ++ FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE()); ++ pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); ++ ++ if (verbose) ++ fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); ++ ++ if (!(pgd_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1); ++ page_middle = (ulong *)(PTOV(pgd_pte & _PFN_MASK)) + offset; ++ ++ FILL_PMD(PAGEBASE(page_middle), KVADDR, PAGESIZE()); ++ pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); ++ ++ if (verbose) ++ fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte); ++ ++ if (!(pmd_pte)) ++ return FALSE; ++ ++ offset = (vaddr >> PAGESHIFT()) & (PTRS_PER_PTE - 1); ++ page_table = (ulong *)(PTOV(pmd_pte & _PFN_MASK)) + offset; ++ ++ FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE()); ++ pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); ++ ++ if (verbose) ++ fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); ++ ++ if (!(pte & (_PAGE_P))) { ++ if (usr) ++ *paddr = pte; ++ if (pte && verbose) { ++ fprintf(fp, "\n"); ++ ia64_translate_pte(pte, 0, 0); ++ } ++ return FALSE; ++ } ++ ++ *paddr = (pte & _PFN_MASK) + PAGEOFFSET(vaddr); ++ ++ if (verbose) { ++ fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); ++ ia64_translate_pte(pte, 0, 0); ++ } ++ ++ return TRUE; ++#endif ++} ++ ++#include "netdump.h" ++ ++/* ++ * Determine the relocatable physical address base. ++ */ ++static void ++ia64_calc_phys_start(void) ++{ ++ FILE *iomem; ++ int i, found, errflag; ++ char buf[BUFSIZE]; ++ char *p1; ++ ulong kernel_code_start; ++ struct vmcore_data *vd; ++ Elf64_Phdr *phdr; ++ ulong phys_start, text_start; ++ ++ /* ++ * Default to 64MB. ++ */ ++ machdep->machspec->phys_start = DEFAULT_PHYS_START; ++ ++ text_start = symbol_exists("_text") ? symbol_value("_text") : BADADDR; ++ ++ if (ACTIVE()) { ++ if ((iomem = fopen("/proc/iomem", "r")) == NULL) ++ return; ++ ++ errflag = 1; ++ while (fgets(buf, BUFSIZE, iomem)) { ++ if (strstr(buf, ": Kernel code")) { ++ clean_line(buf); ++ errflag = 0; ++ break; ++ } ++ } ++ fclose(iomem); ++ ++ if (errflag) ++ return; ++ ++ if (!(p1 = strstr(buf, "-"))) ++ return; ++ else ++ *p1 = NULLCHAR; ++ ++ errflag = 0; ++ kernel_code_start = htol(buf, RETURN_ON_ERROR|QUIET, &errflag); ++ if (errflag) ++ return; ++ ++ machdep->machspec->phys_start = kernel_code_start; ++ ++ if (CRASHDEBUG(1)) { ++ if (text_start == BADADDR) ++ fprintf(fp, "_text: (unknown) "); ++ else ++ fprintf(fp, "_text: %lx ", text_start); ++ fprintf(fp, "Kernel code: %lx -> ", kernel_code_start); ++ fprintf(fp, "phys_start: %lx\n\n", ++ machdep->machspec->phys_start); ++ } ++ ++ return; ++ } ++ ++ /* ++ * Get relocation value from whatever dumpfile format is being used. ++ */ ++ ++ if (DISKDUMP_DUMPFILE()) { ++ if (diskdump_phys_base(&phys_start)) { ++ machdep->machspec->phys_start = phys_start; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "compressed kdump: phys_start: %lx\n", ++ phys_start); ++ } ++ return; ++ } else if (LKCD_DUMPFILE()) { ++ ++ if (lkcd_get_kernel_start(&phys_start)) { ++ machdep->machspec->phys_start = phys_start; ++ if (CRASHDEBUG(1)) ++ fprintf(fp, ++ "LKCD dump: phys_start: %lx\n", ++ phys_start); ++ } ++ } ++ ++ if ((vd = get_kdump_vmcore_data())) { ++ /* ++ * There should be at most one region 5 region, and it ++ * should be equal to "_text". If not, take whatever ++ * region 5 address comes first and hope for the best. ++ */ ++ for (i = found = 0; i < vd->num_pt_load_segments; i++) { ++ phdr = vd->load64 + i; ++ if (phdr->p_vaddr == text_start) { ++ machdep->machspec->phys_start = phdr->p_paddr; ++ found++; ++ break; ++ } ++ } ++ ++ for (i = 0; !found && (i < vd->num_pt_load_segments); i++) { ++ phdr = vd->load64 + i; ++ if (VADDR_REGION(phdr->p_vaddr) == KERNEL_VMALLOC_REGION) { ++ machdep->machspec->phys_start = phdr->p_paddr; ++ found++; ++ break; ++ } ++ } ++ ++ if (found && CRASHDEBUG(1)) { ++ if (text_start == BADADDR) ++ fprintf(fp, "_text: (unknown) "); ++ else ++ fprintf(fp, "_text: %lx ", text_start); ++ fprintf(fp, "p_vaddr: %lx p_paddr: %lx\n", ++ phdr->p_vaddr, phdr->p_paddr); ++ } ++ ++ return; ++ } ++} ++ ++/* ++ * From the xen vmcore, create an index of mfns for each page that makes ++ * up the dom0 kernel's complete phys_to_machine_mapping[max_pfn] array. ++ */ ++static int ++ia64_xen_kdump_p2m_create(struct xen_kdump_data *xkd) ++{ ++ /* ++ * Temporarily read physical (machine) addresses from vmcore by ++ * going directly to read_netdump() instead of via read_kdump(). ++ */ ++ pc->readmem = read_netdump; ++ ++ if (CRASHDEBUG(1)) ++ fprintf(fp, "ia64_xen_kdump_p2m_create: p2m_mfn: %lx\n", xkd->p2m_mfn); ++ ++ if ((xkd->p2m_mfn_frame_list = (ulong *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc p2m_frame_list"); ++ ++ if (!readmem(PTOB(xkd->p2m_mfn), PHYSADDR, xkd->p2m_mfn_frame_list, PAGESIZE(), ++ "xen kdump p2m mfn page", RETURN_ON_ERROR)) ++ error(FATAL, "cannot read xen kdump p2m mfn page\n"); ++ ++ xkd->p2m_frames = PAGESIZE()/sizeof(ulong); ++ ++ pc->readmem = read_kdump; ++ ++ return TRUE; ++} ++ ++physaddr_t ++ia64_xen_kdump_p2m(struct xen_kdump_data *xkd, physaddr_t pseudo) ++{ ++ ulong pgd_idx, pte_idx; ++ ulong pmd, pte; ++ physaddr_t paddr; ++ ++ /* ++ * Temporarily read physical (machine) addresses from vmcore by ++ * going directly to read_netdump() instead of via read_kdump(). ++ */ ++ pc->readmem = read_netdump; ++ ++ xkd->accesses += 2; ++ ++ pgd_idx = (pseudo >> PGDIR_SHIFT_3L) & (PTRS_PER_PGD - 1); ++ pmd = xkd->p2m_mfn_frame_list[pgd_idx] & _PFN_MASK; ++ if (!pmd) { ++ paddr = P2M_FAILURE; ++ goto out; ++ } ++ ++ pmd += ((pseudo >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) * sizeof(ulong); ++ if (pmd != xkd->last_pmd_read) { ++ if (!readmem(pmd, PHYSADDR, &pte, sizeof(ulong), ++ "ia64_xen_kdump_p2m pmd", RETURN_ON_ERROR)) { ++ xkd->last_pmd_read = BADADDR; ++ xkd->last_mfn_read = BADADDR; ++ paddr = P2M_FAILURE; ++ goto out; ++ } ++ xkd->last_pmd_read = pmd; ++ } else { ++ pte = xkd->last_mfn_read; ++ xkd->cache_hits++; ++ } ++ pte = pte & _PFN_MASK; ++ if (!pte) { ++ paddr = P2M_FAILURE; ++ goto out; ++ } ++ ++ if (pte != xkd->last_mfn_read) { ++ if (!readmem(pte, PHYSADDR, xkd->page, PAGESIZE(), ++ "ia64_xen_kdump_p2m pte page", RETURN_ON_ERROR)) { ++ xkd->last_pmd_read = BADADDR; ++ xkd->last_mfn_read = BADADDR; ++ paddr = P2M_FAILURE; ++ goto out; ++ } ++ xkd->last_mfn_read = pte; ++ } else ++ xkd->cache_hits++; ++ ++ pte_idx = (pseudo >> PAGESHIFT()) & (PTRS_PER_PTE - 1); ++ paddr = *(((ulong *)xkd->page) + pte_idx); ++ if (!(paddr & _PAGE_P)) { ++ paddr = P2M_FAILURE; ++ goto out; ++ } ++ paddr = (paddr & _PFN_MASK) | PAGEOFFSET(pseudo); ++ ++out: ++ pc->readmem = read_kdump; ++ return paddr; ++} ++ ++#include "xendump.h" ++ ++/* ++ * Create an index of mfns for each page that makes up the ++ * kernel's complete phys_to_machine_mapping[max_pfn] array. ++ */ ++static int ++ia64_xendump_p2m_create(struct xendump_data *xd) ++{ ++ if (!symbol_exists("phys_to_machine_mapping")) { ++ xd->flags |= XC_CORE_NO_P2M; ++ return TRUE; ++ } ++ ++ error(FATAL, "ia64_xendump_p2m_create: TBD\n"); ++ ++ /* dummy calls for clean "make [wW]arn" */ ++ ia64_debug_dump_page(NULL, NULL, NULL); ++ ia64_xendump_load_page(0, xd); ++ ia64_xendump_page_index(0, xd); ++ ia64_xendump_panic_task(xd); /* externally called */ ++ ia64_get_xendump_regs(xd, NULL, NULL, NULL); /* externally called */ ++ ++ return FALSE; ++} ++ ++static void ++ia64_debug_dump_page(FILE *ofp, char *page, char *name) ++{ ++ int i; ++ ulong *up; ++ ++ fprintf(ofp, "%s\n", name); ++ ++ up = (ulong *)page; ++ for (i = 0; i < 1024; i++) { ++ fprintf(ofp, "%016lx: %016lx %016lx\n", ++ (ulong)((i * 2) * sizeof(ulong)), ++ *up, *(up+1)); ++ up += 2; ++ } ++} ++ ++/* ++ * Find the page associate with the kvaddr, and read its contents ++ * into the passed-in buffer. ++ */ ++static char * ++ia64_xendump_load_page(ulong kvaddr, struct xendump_data *xd) ++{ ++ error(FATAL, "ia64_xendump_load_page: TBD\n"); ++ ++ return NULL; ++} ++ ++/* ++ * Find the dumpfile page index associated with the kvaddr. ++ */ ++static int ++ia64_xendump_page_index(ulong kvaddr, struct xendump_data *xd) ++{ ++ error(FATAL, "ia64_xendump_page_index: TBD\n"); ++ ++ return 0; ++} ++ ++static ulong ++ia64_xendump_panic_task(struct xendump_data *xd) ++{ ++ if (CRASHDEBUG(1)) ++ error(INFO, "ia64_xendump_panic_task: TBD\n"); ++ ++ return NO_TASK; ++} ++ ++static void ++ia64_get_xendump_regs(struct xendump_data *xd, struct bt_info *bt, ulong *rip, ulong *rsp) ++{ ++ machdep->get_stack_frame(bt, rip, rsp); ++ ++ if (is_task_active(bt->task) && ++ !(bt->flags & (BT_TEXT_SYMBOLS_ALL|BT_TEXT_SYMBOLS)) && ++ STREQ(closest_symbol(*rip), "schedule")) ++ error(INFO, ++ "xendump: switch_stack possibly not saved -- try \"bt -t\"\n"); ++} ++ ++/* for XEN Hypervisor analysis */ ++ ++static int ++ia64_is_kvaddr_hyper(ulong addr) ++{ ++ return (addr >= HYPERVISOR_VIRT_START && addr < HYPERVISOR_VIRT_END); ++} ++ ++static int ++ia64_kvtop_hyper(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) ++{ ++ ulong virt_percpu_start, phys_percpu_start; ++ ulong addr, dirp, entry; ++ ++ if (!IS_KVADDR(kvaddr)) ++ return FALSE; ++ ++ if (PERCPU_VIRT_ADDR(kvaddr)) { ++ virt_percpu_start = symbol_value("__phys_per_cpu_start"); ++ phys_percpu_start = virt_percpu_start - DIRECTMAP_VIRT_START; ++ *paddr = kvaddr - PERCPU_ADDR + phys_percpu_start; ++ return TRUE; ++ } else if (DIRECTMAP_VIRT_ADDR(kvaddr)) { ++ *paddr = kvaddr - DIRECTMAP_VIRT_START; ++ return TRUE; ++ } else if (!FRAME_TABLE_VIRT_ADDR(kvaddr)) { ++ return FALSE; ++ } ++ ++ /* frametable virtual address */ ++ addr = kvaddr - xhmachdep->frame_table; ++ ++ dirp = symbol_value("frametable_pg_dir"); ++ dirp += ((addr >> PGDIR_SHIFT_3L) & (PTRS_PER_PGD - 1)) * sizeof(ulong); ++ readmem(dirp, KVADDR, &entry, sizeof(ulong), ++ "frametable_pg_dir", FAULT_ON_ERROR); ++ ++ dirp = entry & _PFN_MASK; ++ if (!dirp) ++ return FALSE; ++ dirp += ((addr >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) * sizeof(ulong); ++ readmem(dirp, PHYSADDR, &entry, sizeof(ulong), ++ "frametable pmd", FAULT_ON_ERROR); ++ ++ dirp = entry & _PFN_MASK; ++ if (!dirp) ++ return FALSE; ++ dirp += ((addr >> PAGESHIFT()) & (PTRS_PER_PTE - 1)) * sizeof(ulong); ++ readmem(dirp, PHYSADDR, &entry, sizeof(ulong), ++ "frametable pte", FAULT_ON_ERROR); ++ ++ if (!(entry & _PAGE_P)) ++ return FALSE; ++ ++ *paddr = (entry & _PFN_MASK) + (kvaddr & (PAGESIZE() - 1)); ++ return TRUE; ++} ++ ++static void ++ia64_post_init_hyper(void) ++{ ++ struct machine_specific *ms; ++ ulong frame_table; ++ ++ ms = &ia64_machine_specific; ++ ++ if (symbol_exists("unw_init_frame_info")) { ++ machdep->flags |= NEW_UNWIND; ++ if (MEMBER_EXISTS("unw_frame_info", "pt")) { ++ if (MEMBER_EXISTS("cpu_user_regs", "ar_csd")) { ++ machdep->flags |= NEW_UNW_V3; ++ ms->unwind_init = unwind_init_v3; ++ ms->unwind = unwind_v3; ++ ms->unwind_debug = unwind_debug_v3; ++ ms->dump_unwind_stats = dump_unwind_stats_v3; ++ } else { ++ machdep->flags |= NEW_UNW_V2; ++ ms->unwind_init = unwind_init_v2; ++ ms->unwind = unwind_v2; ++ ms->unwind_debug = unwind_debug_v2; ++ ms->dump_unwind_stats = dump_unwind_stats_v2; ++ } ++ } else { ++ machdep->flags |= NEW_UNW_V1; ++ ms->unwind_init = unwind_init_v1; ++ ms->unwind = unwind_v1; ++ ms->unwind_debug = unwind_debug_v1; ++ ms->dump_unwind_stats = dump_unwind_stats_v1; ++ } ++ } else { ++ machdep->flags |= OLD_UNWIND; ++ ms->unwind_init = ia64_old_unwind_init; ++ ms->unwind = ia64_old_unwind; ++ } ++ ms->unwind_init(); ++ ++ if (symbol_exists("frame_table")) { ++ frame_table = symbol_value("frame_table"); ++ readmem(frame_table, KVADDR, &xhmachdep->frame_table, sizeof(ulong), ++ "frame_table virtual address", FAULT_ON_ERROR); ++ } else { ++ error(FATAL, "cannot find frame_table virtual address."); ++ } ++} ++ ++int ++ia64_in_mca_stack_hyper(ulong addr, struct bt_info *bt) ++{ ++ int plen, i; ++ ulong paddr, stackbase, stacktop; ++ ulong *__per_cpu_mca; ++ struct xen_hyper_vcpu_context *vcc; ++ ++ vcc = xen_hyper_vcpu_to_vcpu_context(bt->task); ++ if (!vcc) ++ return 0; ++ ++ if (!symbol_exists("__per_cpu_mca") || ++ !(plen = get_array_length("__per_cpu_mca", NULL, 0)) || ++ (plen < xht->pcpus)) ++ return 0; ++ ++ if (!machdep->kvtop(NULL, addr, &paddr, 0)) ++ return 0; ++ ++ __per_cpu_mca = (ulong *)GETBUF(sizeof(ulong) * plen); ++ ++ if (!readmem(symbol_value("__per_cpu_mca"), KVADDR, __per_cpu_mca, ++ sizeof(ulong) * plen, "__per_cpu_mca", RETURN_ON_ERROR|QUIET)) ++ return 0; ++ ++ if (CRASHDEBUG(1)) { ++ for (i = 0; i < plen; i++) { ++ fprintf(fp, "__per_cpu_mca[%d]: %lx\n", ++ i, __per_cpu_mca[i]); ++ } ++ } ++ ++ stackbase = __per_cpu_mca[vcc->processor]; ++ stacktop = stackbase + (STACKSIZE() * 2); ++ FREEBUF(__per_cpu_mca); ++ ++ if ((paddr >= stackbase) && (paddr < stacktop)) ++ return 1; ++ else ++ return 0; ++} ++ ++static void ++ia64_init_hyper(int when) ++{ ++ struct syment *sp; ++ ++ switch (when) ++ { ++ case SETUP_ENV: ++#if defined(PR_SET_FPEMU) && defined(PR_FPEMU_NOPRINT) ++ prctl(PR_SET_FPEMU, PR_FPEMU_NOPRINT, 0, 0, 0); ++#endif ++#if defined(PR_SET_UNALIGN) && defined(PR_UNALIGN_NOPRINT) ++ prctl(PR_SET_UNALIGN, PR_UNALIGN_NOPRINT, 0, 0, 0); ++#endif ++ break; ++ ++ case PRE_SYMTAB: ++ machdep->verify_symbol = ia64_verify_symbol; ++ machdep->machspec = &ia64_machine_specific; ++ if (pc->flags & KERNEL_DEBUG_QUERY) ++ return; ++ machdep->pagesize = memory_page_size(); ++ machdep->pageshift = ffs(machdep->pagesize) - 1; ++ machdep->pageoffset = machdep->pagesize - 1; ++ machdep->pagemask = ~(machdep->pageoffset); ++ switch (machdep->pagesize) ++ { ++ case 4096: ++ machdep->stacksize = (power(2, 3) * PAGESIZE()); ++ break; ++ case 8192: ++ machdep->stacksize = (power(2, 2) * PAGESIZE()); ++ break; ++ case 16384: ++ machdep->stacksize = (power(2, 1) * PAGESIZE()); ++ break; ++ case 65536: ++ machdep->stacksize = (power(2, 0) * PAGESIZE()); ++ break; ++ default: ++ machdep->stacksize = 32*1024; ++ break; ++ } ++ if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pgd space."); ++ if ((machdep->pud = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pud space."); ++ if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc pmd space."); ++ if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) ++ error(FATAL, "cannot malloc ptbl space."); ++ machdep->last_pgd_read = 0; ++ machdep->last_pud_read = 0; ++ machdep->last_pmd_read = 0; ++ machdep->last_ptbl_read = 0; ++ machdep->verify_paddr = ia64_verify_paddr; ++ machdep->ptrs_per_pgd = PTRS_PER_PGD; ++ machdep->machspec->phys_start = UNKNOWN_PHYS_START; ++ /* ODA: if need make hyper version ++ if (machdep->cmdline_arg) ++ parse_cmdline_arg(); */ ++ break; ++ ++ case PRE_GDB: ++ ++ if (pc->flags & KERNEL_DEBUG_QUERY) ++ return; ++ ++ machdep->kvbase = HYPERVISOR_VIRT_START; ++ machdep->identity_map_base = HYPERVISOR_VIRT_START; ++ machdep->is_kvaddr = ia64_is_kvaddr_hyper; ++ machdep->is_uvaddr = generic_is_uvaddr; ++ machdep->eframe_search = ia64_eframe_search; ++ machdep->back_trace = ia64_back_trace_cmd; ++ machdep->processor_speed = xen_hyper_ia64_processor_speed; ++ machdep->uvtop = ia64_uvtop; ++ machdep->kvtop = ia64_kvtop_hyper; ++ machdep->get_stack_frame = ia64_get_stack_frame; ++ machdep->get_stackbase = ia64_get_stackbase; ++ machdep->get_stacktop = ia64_get_stacktop; ++ machdep->translate_pte = ia64_translate_pte; ++ machdep->memory_size = xen_hyper_ia64_memory_size; ++ machdep->dis_filter = ia64_dis_filter; ++ machdep->cmd_mach = ia64_cmd_mach; ++ machdep->get_smp_cpus = xen_hyper_ia64_get_smp_cpus; ++ machdep->line_number_hooks = ia64_line_number_hooks; ++ machdep->value_to_symbol = generic_machdep_value_to_symbol; ++ machdep->init_kernel_pgd = NULL; ++ ++ if ((sp = symbol_search("_stext"))) { ++ machdep->machspec->kernel_region = ++ VADDR_REGION(sp->value); ++ machdep->machspec->kernel_start = sp->value; ++ } else { ++// machdep->machspec->kernel_region = KERNEL_CACHED_REGION; ++// machdep->machspec->kernel_start = KERNEL_CACHED_BASE; ++ } ++ ++ /* machdep table for Xen Hypervisor */ ++ xhmachdep->pcpu_init = xen_hyper_ia64_pcpu_init; ++ break; ++ ++ case POST_GDB: ++ STRUCT_SIZE_INIT(switch_stack, "switch_stack"); ++ MEMBER_OFFSET_INIT(thread_struct_fph, "thread_struct", "fph"); ++ MEMBER_OFFSET_INIT(switch_stack_b0, "switch_stack", "b0"); ++ MEMBER_OFFSET_INIT(switch_stack_ar_bspstore, ++ "switch_stack", "ar_bspstore"); ++ MEMBER_OFFSET_INIT(switch_stack_ar_pfs, ++ "switch_stack", "ar_pfs"); ++ MEMBER_OFFSET_INIT(switch_stack_ar_rnat, ++ "switch_stack", "ar_rnat"); ++ MEMBER_OFFSET_INIT(switch_stack_pr, ++ "switch_stack", "pr"); ++ ++ XEN_HYPER_STRUCT_SIZE_INIT(cpuinfo_ia64, "cpuinfo_ia64"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpuinfo_ia64_proc_freq, "cpuinfo_ia64", "proc_freq"); ++ XEN_HYPER_MEMBER_OFFSET_INIT(cpuinfo_ia64_vendor, "cpuinfo_ia64", "vendor"); ++ if (symbol_exists("per_cpu__cpu_info")) { ++ xht->cpu_data_address = symbol_value("per_cpu__cpu_info"); ++ } ++ /* kakuma Can this be calculated? */ ++ if (!machdep->hz) { ++ machdep->hz = XEN_HYPER_HZ; ++ } ++ break; ++ ++ case POST_INIT: ++ ia64_post_init_hyper(); ++ break; ++ } ++} + #endif +--- crash/alpha.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/alpha.c 2006-10-11 09:14:35.000000000 -0400 +@@ -1,8 +1,8 @@ + /* alpha.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 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 +@@ -186,7 +186,8 @@ + "irq_desc", NULL, 0); + else + machdep->nr_irqs = 0; +- machdep->hz = HZ; ++ if (!machdep->hz) ++ machdep->hz = HZ; + break; + + case POST_INIT: +@@ -1858,8 +1859,6 @@ + fprintf(fp, " flags: %lx (", machdep->flags); + if (machdep->flags & HWRESET) + fprintf(fp, "%sHWRESET", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + fprintf(fp, ")\n"); + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); + fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base); +--- crash/Makefile.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/Makefile 2009-02-12 09:31:03.000000000 -0500 +@@ -3,8 +3,8 @@ + # Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. + # www.missioncriticallinux.com, info@missioncriticallinux.com + # +-# Copyright (C) 2002, 2003, 2004, 2005 David Anderson +-# Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 David Anderson ++# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 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 +@@ -35,10 +35,12 @@ + # + # GDB, GDB_FILES and GDB_OFILES will be configured automatically by configure + # +-GDB=gdb-6.1 +-GDB_FILES=${GDB_6.1_FILES} ++GDB= ++GDB_FILES= + GDB_OFILES= + ++GDB_PATCH_FILES=gdb-6.1.patch ++ + # + # Default installation directory + # +@@ -60,34 +62,81 @@ + # (2) Or invoke make like so: + # make LDFLAGS=-static NAT_CLIBS="-lc -lresolv" GDBSERVER_LIBS="-lc -lresolv" + +-GENERIC_HFILES=defs.h ++GENERIC_HFILES=defs.h xen_hyper_defs.h + MCORE_HFILES=va_server.h vas_crash.h +-REDHAT_HFILES=netdump.h diskdump.h ++REDHAT_HFILES=netdump.h diskdump.h xendump.h + LKCD_DUMP_HFILES=lkcd_vmdump_v1.h lkcd_vmdump_v2_v3.h lkcd_dump_v5.h \ +- lkcd_dump_v7.h lkcd_dump_v8.h lkcd_fix_mem.h ++ lkcd_dump_v7.h lkcd_dump_v8.h ++LKCD_OBSOLETE_HFILES=lkcd_fix_mem.h + LKCD_TRACE_HFILES=lkcd_x86_trace.h + IBM_HFILES=ibm_common.h +-UNWIND_HFILES=unwind.h unwind_i.h rse.h ++UNWIND_HFILES=unwind.h unwind_i.h rse.h unwind_x86.h unwind_x86_64.h + + CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \ + kernel.c test.c gdb_interface.c configure.c net.c dev.c \ +- alpha.c x86.c ppc.c ia64.c s390.c s390x.c ppc64.c x86_64.c \ ++ alpha.c x86.c ppc.c ia64.c s390.c s390x.c s390dbf.c ppc64.c x86_64.c \ + extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c \ + lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c\ + lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c \ +- netdump.c diskdump.c unwind.c unwind_decoder.c ++ netdump.c diskdump.c xendump.c unwind.c unwind_decoder.c \ ++ unwind_x86_32_64.c \ ++ xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \ ++ xen_hyper_dump_tables.c + + SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \ + ${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \ +- ${LKCD_DUMP_HFILES} ${LKCD_TRACE_HFILES} ${IBM_HFILES} ++ ${LKCD_DUMP_HFILES} ${LKCD_TRACE_HFILES} ${LKCD_OBSOLETE_HFILES}\ ++ ${IBM_HFILES} + + OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \ + build_data.o kernel.o test.o gdb_interface.o net.o dev.o \ +- alpha.o x86.o ppc.o ia64.o s390.o s390x.o ppc64.o x86_64.o \ ++ alpha.o x86.o ppc.o ia64.o s390.o s390x.o s390dbf.o ppc64.o x86_64.o \ + extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o \ + lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \ +- lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o \ +- lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o ++ lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o xendump.o \ ++ lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o \ ++ unwind_x86_32_64.o \ ++ xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \ ++ xen_hyper_dump_tables.o ++ ++# These are the current set of crash extensions sources. They are not built ++# by default unless the third command line of the "all:" stanza is uncommented. ++# Alternatively, they can be built by entering "make extensions" from this ++# directory. ++ ++EXTENSIONS=extensions ++EXTENSION_SOURCE_FILES=${EXTENSIONS}/Makefile ${EXTENSIONS}/echo.c ${EXTENSIONS}/dminfo.c \ ++ ${EXTENSIONS}/libsial/Makefile \ ++ ${EXTENSIONS}/libsial/mkbaseop.c \ ++ ${EXTENSIONS}/libsial/README \ ++ ${EXTENSIONS}/libsial/README.sial \ ++ ${EXTENSIONS}/libsial/sial_alloc.c \ ++ ${EXTENSIONS}/libsial/sial_api.c \ ++ ${EXTENSIONS}/libsial/sial_api.h \ ++ ${EXTENSIONS}/libsial/sial_builtin.c \ ++ ${EXTENSIONS}/libsial/sial_case.c \ ++ ${EXTENSIONS}/libsial/sial_define.c \ ++ ${EXTENSIONS}/libsial/sial_func.c \ ++ ${EXTENSIONS}/libsial/sial.h \ ++ ${EXTENSIONS}/libsial/sial_input.c \ ++ ${EXTENSIONS}/libsial/sial.l \ ++ ${EXTENSIONS}/libsial/sial-lsed \ ++ ${EXTENSIONS}/libsial/sial_member.c \ ++ ${EXTENSIONS}/libsial/sial_node.c \ ++ ${EXTENSIONS}/libsial/sial_num.c \ ++ ${EXTENSIONS}/libsial/sial_op.c \ ++ ${EXTENSIONS}/libsial/sialpp.l \ ++ ${EXTENSIONS}/libsial/sialpp-lsed \ ++ ${EXTENSIONS}/libsial/sialpp.y \ ++ ${EXTENSIONS}/libsial/sial_print.c \ ++ ${EXTENSIONS}/libsial/sial_stat.c \ ++ ${EXTENSIONS}/libsial/sial_str.c \ ++ ${EXTENSIONS}/libsial/sial_type.c \ ++ ${EXTENSIONS}/libsial/sial_util.c \ ++ ${EXTENSIONS}/libsial/sial_var.c \ ++ ${EXTENSIONS}/libsial/sial.y \ ++ ${EXTENSIONS}/sial.c \ ++ ${EXTENSIONS}/sial.mk + + DAEMON_OBJECT_FILES=remote_daemon.o va_server.o va_server_v1.o \ + lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \ +@@ -150,10 +199,11 @@ + ${GDB}/gdb/main.c ${GDB}/gdb/symtab.c ${GDB}/gdb/target.c \ + ${GDB}/gdb/symfile.c ${GDB}/gdb/elfread.c \ + ${GDB}/gdb/ui-file.c ${GDB}/gdb/utils.c ${GDB}/gdb/dwarf2read.c \ +- ${GDB}/include/obstack.h ++ ${GDB}/include/obstack.h ${GDB}/gdb/ppc-linux-tdep.c + GDB_6.1_OFILES=${GDB}/gdb/main.o ${GDB}/gdb/symtab.o \ + ${GDB}/gdb/target.o ${GDB}/gdb/symfile.o ${GDB}/gdb/elfread.o \ +- ${GDB}/gdb/ui-file.o ${GDB}/gdb/utils.o ${GDB}/gdb/dwarf2read.o ++ ${GDB}/gdb/ui-file.o ${GDB}/gdb/utils.o ${GDB}/gdb/dwarf2read.o \ ++ ${GDB}/gdb/ppc-linux-tdep.o + + # + # GDB_FLAGS is passed up from the gdb Makefile. +@@ -175,7 +225,8 @@ + + CFLAGS=-g -D${TARGET} ${TARGET_CFLAGS} + +-TAR_FILES=${SOURCE_FILES} Makefile COPYING README .rh_rpm_package crash.8 ++TAR_FILES=${SOURCE_FILES} Makefile COPYING README .rh_rpm_package crash.8 \ ++ ${EXTENSION_SOURCE_FILES} + CSCOPE_FILES=${SOURCE_FILES} + + READLINE_DIRECTORY=./${GDB}/readline +@@ -184,9 +235,13 @@ + + REDHATFLAGS=-DREDHAT + ++# To build the extensions library by default, uncomment the third command ++# line below. Otherwise they can be built by entering "make extensions". ++ + all: make_configure + @./configure -p "RPMPKG=${RPMPKG}" -b + @make --no-print-directory gdb_merge ++# @make --no-print-directory extensions + + gdb_merge: force + @if [ ! -f ${GDB}/README ]; then \ +@@ -206,6 +261,11 @@ + @for FILE in ${GDB_FILES}; do\ + echo $$FILE >> gdb.files; done + @tar --exclude-from gdb.files -xvzmf ${GDB}.tar.gz ++ @make --no-print-directory gdb_patch ++ ++gdb_patch: ++ if [ -f ${GDB}.patch ] && [ -s ${GDB}.patch ]; then \ ++ patch -p0 < ${GDB}.patch; fi + + library: make_build_data ${OBJECT_FILES} + ar -rs ${PROGRAM}lib.a ${OBJECT_FILES} +@@ -223,6 +283,7 @@ + + clean: + rm -f ${OBJECT_FILES} ${DAEMON_OBJECT_FILES} ${PROGRAM} ${PROGRAM}lib.a ${GDB_OFILES} ++ @(cd extensions; make --no-print-directory -i clean) + + make_build_data: force + cc -c ${CFLAGS} build_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} +@@ -318,7 +379,7 @@ + remote_daemon.o: ${GENERIC_HFILES} remote.c + cc -c ${CFLAGS} -DDAEMON remote.c -o remote_daemon.o ${WARNING_OPTIONS} ${WARNING_ERROR} + +-x86.o: ${GENERIC_HFILES} x86.c ++x86.o: ${GENERIC_HFILES} ${REDHAT_HFILES} x86.c + cc -c ${CFLAGS} -DMCLX x86.c ${WARNING_OPTIONS} ${WARNING_ERROR} + + alpha.o: ${GENERIC_HFILES} alpha.c +@@ -327,13 +388,13 @@ + ppc.o: ${GENERIC_HFILES} ppc.c + cc -c ${CFLAGS} ppc.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +-ia64.o: ${GENERIC_HFILES} ia64.c ++ia64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} ia64.c + cc -c ${CFLAGS} ia64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + + ppc64.o: ${GENERIC_HFILES} ppc64.c + cc -c ${CFLAGS} ppc64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +-x86_64.o: ${GENERIC_HFILES} x86_64.c ++x86_64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} x86_64.c + cc -c ${CFLAGS} x86_64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + + s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c +@@ -342,6 +403,9 @@ + s390x.o: ${GENERIC_HFILES} ${IBM_HFILES} s390x.c + cc -c ${CFLAGS} s390x.c ${WARNING_OPTIONS} ${WARNING_ERROR} + ++s390dbf.o: ${GENERIC_HFILES} ${IBM_HFILES} s390dbf.c ++ cc -c ${CFLAGS} s390dbf.c ${WARNING_OPTIONS} ${WARNING_ERROR} ++ + s390_dump.o: ${GENERIC_HFILES} ${IBM_HFILES} s390_dump.c + cc -c ${CFLAGS} s390_dump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +@@ -353,12 +417,18 @@ + diskdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} diskdump.c + cc -c ${CFLAGS} diskdump.c ${WARNING_OPTIONS} ${WARNING_ERROR} + ++xendump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} xendump.c ++ cc -c ${CFLAGS} xendump.c ${WARNING_OPTIONS} ${WARNING_ERROR} ++ + extensions.o: ${GENERIC_HFILES} extensions.c + cc -c ${CFLAGS} extensions.c ${WARNING_OPTIONS} ${WARNING_ERROR} + + lkcd_x86_trace.o: ${GENERIC_HFILES} ${LKCD_TRACE_HFILES} lkcd_x86_trace.c + cc -c ${CFLAGS} -DREDHAT lkcd_x86_trace.c ${WARNING_OPTIONS} ${WARNING_ERROR} + ++unwind_x86_32_64.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind_x86_32_64.c ++ cc -c ${CFLAGS} unwind_x86_32_64.c -o unwind_x86_32_64.o ${WARNING_OPTIONS} ${WARNING_ERROR} ++ + unwind_v1.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind.c unwind_decoder.c + cc -c ${CFLAGS} -DREDHAT -DUNWIND_V1 unwind.c -o unwind_v1.o ${WARNING_OPTIONS} ${WARNING_ERROR} + +@@ -369,7 +439,19 @@ + cc -c ${CFLAGS} -DREDHAT -DUNWIND_V3 unwind.c -o unwind_v3.o ${WARNING_OPTIONS} ${WARNING_ERROR} + + lkcd_fix_mem.o: ${GENERIC_HFILES} ${LKCD_HFILES} lkcd_fix_mem.c +- cc -c ${CFLAGS} lkcd_fix_mem.c ${WARNING_OPTIONS} ${WARNING_ERROR} ++ cc -c ${CFLAGS} -DMCLX lkcd_fix_mem.c ${WARNING_OPTIONS} ${WARNING_ERROR} ++ ++xen_hyper.o: ${GENERIC_HFILES} xen_hyper.c ++ cc -c ${CFLAGS} xen_hyper.c ${WARNING_OPTIONS} ${WARNING_ERROR} ++ ++xen_hyper_command.o: ${GENERIC_HFILES} xen_hyper_command.c ++ cc -c ${CFLAGS} xen_hyper_command.c ${WARNING_OPTIONS} ${WARNING_ERROR} ++ ++xen_hyper_global_data.o: ${GENERIC_HFILES} xen_hyper_global_data.c ++ cc -c ${CFLAGS} xen_hyper_global_data.c ${WARNING_OPTIONS} ${WARNING_ERROR} ++ ++xen_hyper_dump_tables.o: ${GENERIC_HFILES} xen_hyper_dump_tables.c ++ cc -c ${CFLAGS} xen_hyper_dump_tables.c ${WARNING_OPTIONS} ${WARNING_ERROR} + + ${PROGRAM}: force + @make --no-print-directory all +@@ -393,13 +475,13 @@ + + gdb_files: make_configure + @./configure -q -b +- @echo ${GDB_FILES} ++ @echo ${GDB_FILES} ${GDB_PATCH_FILES} + + show_files: + @if [ -f ${PROGRAM} ]; then \ +- ./${PROGRAM} --no_crashrc -h README > README; fi +- @echo ${SOURCE_FILES} Makefile ${GDB_FILES} COPYING README \ +- .rh_rpm_package crash.8 ++ ./${PROGRAM} --no_scroll --no_crashrc -h README > README; echo $?; fi ++ @echo ${SOURCE_FILES} Makefile ${GDB_FILES} ${GDB_PATCH_FILES} COPYING README \ ++ .rh_rpm_package crash.8 ${EXTENSION_SOURCE_FILES} + + ctags: + ctags ${SOURCE_FILES} +@@ -410,8 +492,8 @@ + + do_tar: + @if [ -f ${PROGRAM} ]; then \ +- ./${PROGRAM} --no_crashrc -h README > README; fi +- tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES} ++ ./${PROGRAM} --no_scroll --no_crashrc -h README > README; fi ++ tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES} ${GDB_PATCH_FILES} + @echo; ls -l ${PROGRAM}.tar.gz + + # To create a base tar file for Red Hat RPM packaging, pass the base RPM +@@ -421,12 +503,12 @@ + # spec file will have its own release number, which will in turn get passed + # to the "all" target upon the initial build. + +-RELEASE=4.0 ++RELEASE= + + release: make_configure + @if [ "`id --user`" != "0" ]; then \ + echo "make release: must be super-user"; exit 1; fi +- @./configure -p "RPMPKG=${RPMPKG}" -u -g ++ @./configure -P "RPMPKG=${RPMPKG}" -u -g + @make --no-print-directory release_configure + @echo + @echo "cvs tag this release if necessary" +@@ -446,10 +528,10 @@ + @rm -f ${PROGRAM}-${RELEASE}.tar.gz + @rm -f ${PROGRAM}-${RELEASE}.src.rpm + @chown root ./RELDIR/${PROGRAM}-${RELEASE} +- @tar cf - ${SOURCE_FILES} Makefile ${GDB_FILES} COPYING \ +- .rh_rpm_package crash.8 | (cd ./RELDIR/${PROGRAM}-${RELEASE}; tar xf -) ++ @tar cf - ${SOURCE_FILES} Makefile ${GDB_FILES} ${GDB_PATCH_FILES} COPYING \ ++ .rh_rpm_package crash.8 ${EXTENSION_SOURCE_FILES} | (cd ./RELDIR/${PROGRAM}-${RELEASE}; tar xf -) + @cp ${GDB}.tar.gz ./RELDIR/${PROGRAM}-${RELEASE} +- @./${PROGRAM} --no_crashrc -h README > ./RELDIR/${PROGRAM}-${RELEASE}/README ++ @./${PROGRAM} --no_scroll --no_crashrc -h README > ./RELDIR/${PROGRAM}-${RELEASE}/README + @(cd ./RELDIR; find . -exec chown root {} ";") + @(cd ./RELDIR; find . -exec chgrp root {} ";") + @(cd ./RELDIR; find . -exec touch {} ";") +@@ -464,7 +546,7 @@ + cp ${PROGRAM}-${RELEASE}.tar.gz /usr/src/redhat/SOURCES; \ + /usr/bin/rpmbuild -bs ${PROGRAM}.spec > /dev/null; \ + rm -f /usr/src/redhat/SOURCES/${PROGRAM}-${RELEASE}.tar.gz; \ +- cp /usr/src/redhat/SRPMS/${PROGRAM}-${RELEASE}.src.rpm . ; \ ++ mv /usr/src/redhat/SRPMS/${PROGRAM}-${RELEASE}.src.rpm . ; \ + ls -l ${PROGRAM}-${RELEASE}.src.rpm; \ + exit 0; fi + +@@ -488,3 +570,10 @@ + + dis: + objdump --disassemble --line-numbers ${PROGRAM} > ${PROGRAM}.dis ++ ++extensions: make_configure ++ @./configure -q -b ++ @make --no-print-directory do_extensions ++ ++do_extensions: ++ @(cd extensions; make -i TARGET=$(TARGET) TARGET_CFLAGS=$(TARGET_CFLAGS)) +--- crash/extensions/sial.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/sial.c 2008-10-28 14:53:10.000000000 -0400 +@@ -0,0 +1,1021 @@ ++/* ++ * $Id$ ++ * ++ * This file is part of lcrash, an analysis tool for Linux memory dumps. ++ * ++ * Created by Silicon Graphics, Inc. ++ * Contributions by IBM, and others ++ * ++ * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. ++ * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation ++ * ++ * 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. See the file COPYING for more ++ * information. ++ */ ++ ++#include "gdb-6.1/gdb/defs.h" ++#include "target.h" ++#include "symtab.h" ++#include "gdbtypes.h" ++#include "gdbcore.h" ++#include "frame.h" ++#include "value.h" ++#include "symfile.h" ++#include "objfiles.h" ++#include "gdbcmd.h" ++#include "call-cmds.h" ++#include "gdb_regex.h" ++#include "expression.h" ++#include "language.h" ++#include "demangle.h" ++#include "inferior.h" ++#include "linespec.h" ++#include "source.h" ++#include "filenames.h" /* for FILENAME_CMP */ ++#include "objc-lang.h" ++ ++#include "hashtab.h" ++ ++#include "gdb_obstack.h" ++#include "block.h" ++#include "dictionary.h" ++ ++#include ++#include ++#include "gdb_string.h" ++#include "gdb_stat.h" ++#include ++#include "cp-abi.h" ++ ++#include ++#include ++#include ++#include ++ ++///////////////////////////////////////////////////////////////////////// ++// some stuff from crash's defs.h, file which cannot be included here. ++// Hate to do this but this is a quick port. ++// If anyone cares to work on the include and defs structure to make ++// this work cleanly... ++// ++/* ++ * Global data (global_data.c) ++ */ ++extern char *args[]; ++extern int argcnt; ++extern int argerrs; ++#define SYNOPSIS (0x1) ++#define COMPLETE_HELP (0x2) ++#define PIPE_TO_LESS (0x4) ++#define KVADDR (0x1) ++#define QUIET (0x4) ++ ++typedef void (*cmd_func_t)(void); ++ ++struct command_table_entry { /* one for each command in menu */ ++ char *name; ++ cmd_func_t func; ++ char **help_data; ++ ulong flags; ++}; ++extern FILE *fp; ++extern char *crash_global_cmd(); ++ ++// ++///////////////////////////////////////////////////////////////////////// ++/* ++ This is the glue between the sial interpreter and crash. ++*/ ++ ++static int ++apigetmem(ull iaddr, void *p, int nbytes) ++{ ++ return readmem(iaddr, KVADDR, p, nbytes, NULL, QUIET); ++} ++ ++// Since crash is target dependant (build for the ++static uint8_t apigetuint8(void* ptr) ++{ ++uint8_t val; ++ if(!readmem((unsigned long)ptr, KVADDR, (char*)&val, sizeof val, NULL, QUIET)) return (uint8_t)-1; ++ return val; ++} ++ ++static uint16_t apigetuint16(void* ptr) ++{ ++uint16_t val; ++ if(!readmem((unsigned long)ptr, KVADDR, (char*)&val, sizeof val, NULL, QUIET)) return (uint16_t)-1; ++ return val; ++} ++ ++static uint32_t apigetuint32(void* ptr) ++{ ++uint32_t val; ++ if(!readmem((unsigned long)ptr, KVADDR, (char*)&val, sizeof val, NULL, QUIET)) return (uint32_t)-1; ++ return val; ++} ++ ++static uint64_t apigetuint64(void* ptr) ++{ ++uint64_t val; ++ if(!readmem((unsigned long)ptr, KVADDR, (char*)&val, sizeof val, NULL, QUIET)) return (uint64_t)-1; ++ return val; ++} ++ ++static int ++apiputmem(ull iaddr, void *p, int nbytes) ++{ ++ return 1; ++} ++ ++/* extract a complex type (struct, union and enum) */ ++static int ++apigetctype(int ctype, char *name, TYPE_S *tout) ++{ ++ struct symbol *sym; ++ struct type *type; ++ int v=0; ++ ++ sial_dbg_named(DBG_TYPE, name, 2, "Looking for type %d name [%s] in struct domain...", ctype, name); ++ sym = lookup_symbol(name, 0, STRUCT_DOMAIN, 0, (struct symtab **) NULL); ++ if(!sym) { ++ sial_dbg_named(DBG_TYPE, name, 2, "Not found.\nLooking for type %d name [%s] in var domain...", ctype, name); ++ sym = lookup_symbol(name, 0, VAR_DOMAIN, 0, (struct symtab **) NULL); ++ if(sym) { ++ sial_dbg_named(DBG_TYPE, name, 2, "found class=%d\n", sym->aclass); ++ if(sym->aclass == LOC_TYPEDEF) v=1; ++ } ++ } ++ ++ if (sym) { ++ type=sym->type; ++ if(sial_is_typedef(ctype) && v) goto match; ++ switch(TYPE_CODE(type)) { ++ case TYPE_CODE_TYPEDEF: case TYPE_CODE_INT: ++ if(sial_is_typedef(ctype)) goto match; break; ++ case TYPE_CODE_ENUM: if(sial_is_enum(ctype)) goto match; break; ++ case TYPE_CODE_STRUCT: if(sial_is_struct(ctype)) goto match; break; ++ case TYPE_CODE_UNION: if(sial_is_union(ctype)) goto match; break; ++ } ++ sial_dbg_named(DBG_TYPE, name, 2, "Found but no match.\n"); ++ } ++ else sial_dbg_named(DBG_TYPE, name, 2, "Not Found.\n"); ++ ++ return 0; ++ ++match: ++ sial_dbg_named(DBG_TYPE, name, 2, "Found.\n"); ++ /* populate */ ++ sial_type_settype(tout, ctype); ++ sial_type_setsize(tout, TYPE_LENGTH(type)); ++ sial_type_setidx(tout, (ull)(unsigned long)type); ++ sial_pushref(tout, 0); ++ return 1; ++} ++ ++/* set idx value to actual array indexes from specified size */ ++static void ++sial_setupidx(TYPE_S*t, int ref, int nidx, int *idxlst) ++{ ++ /* put the idxlst in index size format */ ++ if(nidx) { ++ ++ int i; ++ ++ for(i=0;itype; ++ } ++ } ++ ++ switch(TYPE_CODE(type)) { ++ ++ /* typedef inserts a level of reference to the 1'dactual type */ ++ case TYPE_CODE_PTR: ++ ++ ref++; ++ type=TYPE_TARGET_TYPE(type); ++ /* this could be a void*, in which case the drill down stops here */ ++ if(!type) { ++ ++ /* make it a char* */ ++ sial_parsetype("char", t, ref); ++ return 0; ++ ++ } ++ break; ++ ++ /* handle pointer to functions */ ++ case TYPE_CODE_FUNC: ++ ++ fctflg=1; ++ type=TYPE_TARGET_TYPE(type); ++ break; ++ ++ /* Is this an array ? if so then just skip this type info and ++ we only need information on the elements themselves */ ++ case TYPE_CODE_ARRAY: ++ if(!idxlst) idxlst=sial_calloc(sizeof(int)*(MAXIDX+1)); ++ if(nidx >= MAXIDX) sial_error("Too many indexes! max=%d\n", MAXIDX); ++ if (TYPE_LENGTH (type) > 0 && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0) ++ { ++ idxlst[nidx++]=TYPE_LENGTH (type) / TYPE_LENGTH (check_typedef(TYPE_TARGET_TYPE (type))); ++ } ++ type=TYPE_TARGET_TYPE(type); ++ break; ++ ++ /* typedef points to a typedef itself */ ++ case TYPE_CODE_TYPEDEF: ++ type=TYPE_TARGET_TYPE(type); ++ break; ++ ++ case TYPE_CODE_INT: ++ ++ sial_parsetype(tstr=TYPE_NAME(type), t, 0); ++ type=0; ++ break; ++ ++ case TYPE_CODE_UNION: ++ sial_type_mkunion(t); ++ goto label; ++ ++ case TYPE_CODE_ENUM: ++ sial_type_mkenum(t); ++ goto label; ++ ++ case TYPE_CODE_STRUCT: ++ { ++ sial_type_mkstruct(t); ++ ++label: ++ sial_type_setsize(t, TYPE_LENGTH(type)); ++ sial_type_setidx(t, (ull)(unsigned long)type); ++ tstr=TYPE_TAG_NAME(type); ++ type=0; ++ } ++ break; ++ ++ /* we don;t have all the info about it */ ++ case TYPE_CODE_VOID: ++ sial_parsetype("int", t, 0); ++ type=0; ++ break; ++ ++ ++ default: ++ sial_error("Oops drilldowntype"); ++ break; ++ } ++ ++ ++ } ++ sial_setupidx(t, ref, nidx, idxlst); ++ if(fctflg) sial_type_setfct(t, 1); ++ sial_pushref(t, ref+(nidx?1:0)); ++ if(tstr) return sial_strdup(tstr); ++ return sial_strdup(""); ++} ++ ++static char * ++apigetrtype(ull idx, TYPE_S *t) ++{ ++ return drilldowntype((struct type*)(unsigned long)(idx), t); ++} ++ ++/* ++ Return the name of a symbol at an address (if any) ++*/ ++static char* ++apifindsym(char *p) ++{ ++ return NULL; ++} ++ ++ ++/* ++ Get the type, size and position information for a member of a structure. ++*/ ++static char* ++apimember(char *mname, ull tnum, TYPE_S *tm, MEMBER_S *m, ull *lnum) ++{ ++struct type *type=(struct type*)(unsigned long)tnum; ++int midx; ++#define LASTNUM (*lnum) ++ ++ /* if we're being asked the next member in a getfirst/getnext sequence */ ++ if(mname && !mname[0] && LASTNUM) { ++ ++ midx = LASTNUM; ++ ++ } else { ++ ++ if (TYPE_CODE(type) == TYPE_CODE_TYPEDEF) { ++ return 0; ++ } ++ if ((TYPE_CODE(type) != TYPE_CODE_STRUCT) && (TYPE_CODE(type) != TYPE_CODE_UNION)) { ++ return 0; ++ } ++ midx=0; ++ } ++ while(midx < TYPE_NFIELDS(type)) { ++ ++ if (!mname || !mname[0] || !strcmp(mname, TYPE_FIELD_NAME(type, midx))) { ++ ++ check_typedef(TYPE_FIELD_TYPE(type, midx)); ++ sial_member_soffset(m, TYPE_FIELD_BITPOS(type, midx)/8); ++ sial_member_ssize(m, TYPE_FIELD_TYPE(type, midx)->length); ++ sial_member_snbits(m, TYPE_FIELD_BITSIZE(type, midx)); ++ sial_member_sfbit(m, TYPE_FIELD_BITPOS(type, midx)%8); ++ sial_member_sname(m, TYPE_FIELD_NAME(type, midx)); ++ LASTNUM=midx+1; ++ return drilldowntype(TYPE_FIELD_TYPE(type, midx), tm); ++ } ++ midx++; ++ } ++ return 0; ++} ++ ++/* ++ This function gets the proper allignment value for a type. ++*/ ++static int ++apialignment(ull idx) ++{ ++struct type *type=(struct type *)(unsigned long)idx; ++ ++ while(1) ++ { ++ switch(TYPE_CODE(type)) { ++ ++ case TYPE_CODE_ARRAY: case TYPE_CODE_TYPEDEF: ++ type=TYPE_TARGET_TYPE(type); ++ break; ++ ++ case TYPE_CODE_STRUCT: ++ case TYPE_CODE_UNION: ++ { ++ int max=0, cur; ++ int midx=0; ++ ++ while(midx < TYPE_NFIELDS(type)) { ++ cur=apialignment((ull)(unsigned long)TYPE_FIELD_TYPE(type, midx)); ++ if(cur > max) max=cur; ++ midx++; ++ } ++ return max; ++ } ++ ++ ++ case TYPE_CODE_PTR: ++ case TYPE_CODE_ENUM: ++ case TYPE_CODE_INT: ++ ++ return TYPE_LENGTH (type); ++ ++ default: ++ ++ sial_error("Oops apialignment"); ++ } ++ } ++} ++ ++/* get the value of a symbol */ ++static int ++apigetval(char *name, ull *val) ++{ ++ if (symbol_exists(name)) { ++ *val=symbol_value(name); ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ Get the list of enum symbols. ++*/ ++ENUM_S* ++apigetenum(char *name) ++{ ++ struct symbol *sym; ++ ++ sym = lookup_symbol(name, 0, STRUCT_DOMAIN, 0, (struct symtab **) NULL); ++ if (sym && TYPE_CODE(sym->type)==TYPE_CODE_ENUM) { ++ ENUM_S *et=0; ++ struct type *type=sym->type; ++ int n=0; ++ while(n < TYPE_NFIELDS (type)) { ++ et=sial_add_enum(et, sial_strdup(TYPE_FIELD_NAME(type, n)), TYPE_FIELD_BITPOS(type, n)); ++ n++; ++ } ++ return et; ++ } ++ return 0; ++} ++ ++/* ++ Return the list of preprocessor defines. ++ For Irix we have to get the die for a startup.c file. ++ Of dwarf type DW_TAG_compile_unit. ++ the DW_AT_producer will contain the compile line. ++ ++ We then need to parse that line to get all the -Dname[=value] ++*/ ++DEF_S * ++apigetdefs(void) ++{ ++DEF_S *dt=0; ++int i; ++static struct linuxdefs_s { ++ ++ char *name; ++ char *value; ++ ++} linuxdefs[] = { ++ ++ {"crash", "1"}, ++ {"linux", "1"}, ++ {"__linux", "1"}, ++ {"__linux__", "1"}, ++ {"unix", "1"}, ++ {"__unix", "1"}, ++ {"__unix__", "1"}, ++ // helper macros ++ {"LINUX_2_2_16", "(LINUX_RELEASE==0x020210)"}, ++ {"LINUX_2_2_17", "(LINUX_RELEASE==0x020211)"}, ++ {"LINUX_2_4_0", "(LINUX_RELEASE==0x020400)"}, ++ {"LINUX_2_2_X", "(((LINUX_RELEASE) & 0xffff00) == 0x020200)"}, ++ {"LINUX_2_4_X", "(((LINUX_RELEASE) & 0xffff00) == 0x020400)"}, ++ {"LINUX_2_6_X", "(((LINUX_RELEASE) & 0xffff00) == 0x020600)"}, ++#ifdef i386 ++ {"i386", "1"}, ++ {"__i386", "1"}, ++ {"__i386__", "1"}, ++#endif ++#ifdef s390 ++ {"s390", "1"}, ++ {"__s390", "1"}, ++ {"__s390__", "1"}, ++#endif ++#ifdef s390x ++ {"s390x", "1"}, ++ {"__s390x", "1"}, ++ {"__s390x__", "1"}, ++#endif ++#ifdef __ia64__ ++ {"ia64", "1"}, ++ {"__ia64", "1"}, ++ {"__ia64__", "1"}, ++ {"__LP64__", "1"}, ++ {"_LONGLONG", "1"}, ++ {"__LONG_MAX__", "9223372036854775807L"}, ++#endif ++#ifdef ppc64 ++ {"ppc64", "1"}, ++ {"__ppc64", "1"}, ++ {"__ppc64__", "1"}, ++#endif ++ }; ++ ++static char *untdef[] = { ++ "clock", ++ "mode", ++ "pid", ++ "uid", ++ "xtime", ++ "init_task", ++ "size", ++ "type", ++ "level", ++ 0 ++}; ++ ++#if 0 ++How to extract basic set of -D flags from the kernel image ++ ++ prod=sial_strdup(kl_getproducer()); ++ for(p=prod; *p; p++) { ++ ++ if(*p=='-' && *(p+1)=='D') { ++ ++ char *def=p+2; ++ ++ while(*p && *p != '=' && *p != ' ') p++; ++ ++ if(!*p || *p == ' ') { ++ ++ *p='\0'; ++ dt=sial_add_def(dt, sial_strdup(def), sial_strdup("1")); ++ ++ } else { ++ ++ char *val=p+1; ++ ++ *p++='\0'; ++ while(*p && *p != ' ') p++; ++ *p='\0'; ++ ++ dt=sial_add_def(dt, sial_strdup(def), sial_strdup(val)); ++ } ++ } ++ } ++#endif ++ ++ /* remove some tdef with very usual identifier. ++ could also be cases where the kernel defined a type and variable with same name e.g. xtime. ++ the same can be accomplished in source using #undef or forcing the evaluation of ++ a indentifier as a variable name ex: __var(xtime). ++ ++ I tried to make the grammar as unambiqguous as I could. ++ ++ If this becomes to much of a problem I might diable usage of all image typedefs usage in sial! ++ */ ++ { ++ char **tdefname=untdef; ++ while(*tdefname) sial_addneg(*tdefname++);; ++ ++ } ++ ++ /* insert constant defines from list above */ ++ for(i=0;i\n" ++ , S_MAJOR, S_MINOR); ++} ++ ++static void ++run_callback(void) ++{ ++extern char *crash_global_cmd(); ++FILE *ofp = NULL; ++ ++ if (fp) { ++ ofp = sial_getofile(); ++ sial_setofile(fp); ++ } ++ ++ sial_cmd(crash_global_cmd(), args, argcnt); ++ ++ if (ofp) ++ sial_setofile(ofp); ++} ++ ++ ++void ++edit_cmd(void) ++{ ++int c, file=0; ++ while ((c = getopt(argcnt, args, "lf")) != EOF) { ++ switch(c) ++ { ++ case 'l': ++ sial_vilast(); ++ return; ++ break; ++ case 'f': ++ file++; ++ break; ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(crash_global_cmd(), SYNOPSIS); ++ ++ else if(args[optind]) { ++ while(args[optind]) { ++ sial_vi(args[optind++], file); ++ } ++ } ++ else cmd_usage(crash_global_cmd(), SYNOPSIS); ++} ++ ++char *edit_help[]={ ++ "edit", ++ "Start a $EDITOR session of a sial function or file", ++ "<-f fileName>|", ++ "This command can be use during a tight development cycle", ++ "where frequent editing->run->editing sequences are executed.", ++ "To edit a known sial macro file use the -f option. To edit the file", ++ "at the location of a known function's declaration omit the -f option.", ++ "Use a single -l option to be brought to the last compile error location.", ++ "", ++ "EXAMPLES:", ++ " %s> edit -f ps", ++ " %s> edit ps", ++ " %s> edit ps_opt", ++ " %s> edit -l", ++ NULL ++}; ++ ++ ++// these control debug mode when parsing (pre-processor and compile) ++int sialdebug=0, sialppdebug=0; ++ ++void ++load_cmd(void) ++{ ++ if(argcnt< 2) cmd_usage(crash_global_cmd(), SYNOPSIS); ++ else { ++ sial_setofile(fp); ++ sial_loadunload(1, args[1], 0); ++ } ++} ++ ++char *load_help[]={ ++ "load", ++ "Load a sial file", ++ "|", ++ " Load a file or a directory. In the case of a directory", ++ " all files in that directory will be loaded.", ++ NULL ++ ++}; ++ ++void ++unload_cmd(void) ++{ ++ if(argcnt < 2) cmd_usage(crash_global_cmd(), SYNOPSIS); ++ else sial_loadunload(0, args[1], 0); ++} ++ ++char *unload_help[]={ ++ "unload", ++ "Unload a sial file", ++ "|", ++ " Unload a file or a directory. In the case of a directory", ++ " all files in that directory will be unloaded.", ++ NULL ++}; ++ ++void ++sdebug_cmd(void) ++{ ++ if(argcnt < 2) sial_msg("Current sial debug level is %d\n", sial_getdbg()); ++ else sial_setdbg(atoi(args[1])); ++} ++ ++char *sdebug_help[]={ ++ "sdebug", ++ "Print or set sial debug level", ++ "", ++ " Set the debug of sial. Without any parameter, shows the current debug level.", ++ NULL ++}; ++ ++void ++sname_cmd(void) ++{ ++ if(argcnt < 2) { ++ if(sial_getname()) sial_msg("Current sial name match is '%s'\n", sial_getname()); ++ else sial_msg("No name match specified yet.\n"); ++ } else sial_setname(args[1]); ++} ++ ++char *sname_help[]={ ++ "sname", ++ "Print or set sial name match.", ++ "", ++ " Set sial name string for matches. Debug messages that are object oriented", ++ " will only be displayed if the object name (struct, type, ...) matches this", ++ " value.", ++ NULL ++}; ++ ++void ++sclass_cmd(void) ++{ ++ if(argcnt < 2) { ++ char **classes=sial_getclass(); ++ sial_msg("Current sial classes are :"); ++ while(*classes) sial_msg("'%s' ", *classes++); ++ sial_msg("\n"); ++ ++ } ++ else { ++ int i; ++ for(i=1; i[, ]", ++ " Set sial debug classes. Only debug messages that are in the specified classes", ++ " will be displayed.", ++ NULL ++}; ++ ++#define NCMDS 200 ++static struct command_table_entry command_table[NCMDS] = { ++ ++ {"edit", edit_cmd, edit_help}, ++ {"load", load_cmd, load_help}, ++ {"unload", unload_cmd, unload_help}, ++ {"sdebug", sdebug_cmd, sdebug_help}, ++ {"sname", sname_cmd, sname_help}, ++ {"sclass", sclass_cmd, sclass_help}, ++ {(char *)0 } ++}; ++ ++static void ++add_sial_cmd(char *name, void (*cmd)(void), char **help, int flags) ++{ ++struct command_table_entry *cp; ++struct command_table_entry *crash_cmd_table(); ++ ++ // check for a clash with native commands ++ for (cp = crash_cmd_table(); cp->name; cp++) { ++ if (!strcmp(cp->name, name)) { ++ sial_msg("Sial command name '%s' conflicts with native crash command.\n", name); ++ return; ++ } ++ } ++ ++ // make sure we have enough space for the new command ++ if(!command_table[NCMDS-2].name) { ++ for (cp = command_table; cp->name; cp++); ++ cp->name=sial_strdup(name); ++ cp->func=cmd; ++ cp->help_data=help; ++ cp->flags=flags; ++ } ++} ++ ++static void ++rm_sial_cmd(char *name) ++{ ++struct command_table_entry *cp, *end; ++ ++ for (cp = command_table; cp->name; cp++) { ++ if (!strcmp(cp->name, name)) { ++ sial_free(cp->name); ++ memmove(cp, cp+1, sizeof *cp *(NCMDS-(cp-command_table)-1)); ++ break; ++ } ++ } ++} ++ ++/* ++ This function is called for every new function ++ generated by a load command. This enables us to ++ register new commands. ++ ++ We check here is the functions: ++ ++ fname_help() ++ fname_opt() ++ and ++ fname_usage() ++ ++ exist, and if so then we have a new command. ++ Then we associated (register) a function with ++ the standard sial callbacks. ++*/ ++void ++reg_callback(char *name, int load) ++{ ++char fname[MAX_SYMNAMELEN+sizeof("_usage")+1]; ++char *help_str, *opt_str; ++char **help=malloc(sizeof *help * 5); ++ ++ if(!help) return; ++ snprintf(fname, sizeof(fname), "%s_help", name); ++ if(sial_chkfname(fname, 0)) { ++ help_str=sial_strdup((char*)(unsigned long)sial_exefunc(fname, 0)); ++ snprintf(fname, sizeof(fname), "%s_usage", name); ++ if(sial_chkfname(fname, 0)) { ++ if(load) { ++ opt_str=sial_strdup((char*)(unsigned long)sial_exefunc(fname, 0)); ++ help[0]=sial_strdup(name); ++ help[1]=""; ++ help[2]=sial_strdup(opt_str); ++ help[3]=sial_strdup(help_str); ++ help[4]=0; ++ add_sial_cmd(name, run_callback, help, 0); ++ return; ++ } ++ else rm_sial_cmd(name); ++ } ++ sial_free(help_str); ++ } ++ free(help); ++ return; ++} ++ ++/* ++ * The _fini() function is called if the shared object is unloaded. ++ * If desired, perform any cleanups here. ++ */ ++void _fini() ++{ ++ // need to unload any files we have loaded ++ ++} ++ ++VALUE_S *curtask(VALUE_S *v, ...) ++{ ++unsigned long get_curtask(); ++ return sial_makebtype((ull)get_curtask()); ++} ++ ++_init() /* Register the command set. */ ++{ ++#define LCDIR "/usr/share/sial/crash" ++#define LCIDIR "include" ++#define LCUDIR ".sial" ++ ++ ++ if(sial_open() >= 0) { ++ ++ char *path, *ipath; ++ char *homepath=0; ++ char *home=getenv("HOME"); ++ ++ /* set api, default size, and default sign for types */ ++#ifdef i386 ++#define SIAL_ABI ABI_INTEL_X86 ++#else ++#ifdef __ia64__ ++#define SIAL_ABI ABI_INTEL_IA ++#else ++#ifdef __x86_64__ ++#define SIAL_ABI ABI_INTEL_IA ++#else ++#ifdef __s390__ ++#define SIAL_ABI ABI_S390 ++#else ++#ifdef __s390x__ ++#define SIAL_ABI ABI_S390X ++#else ++#ifdef PPC64 ++#define SIAL_ABI ABI_PPC64 ++#else ++#error sial: Unkown ABI ++#endif ++#endif ++#endif ++#endif ++#endif ++#endif ++ sial_apiset(&icops, SIAL_ABI, sizeof(long), 0); ++ ++ sial_version(); ++ ++ /* set the macro search path */ ++ if(!(path=getenv("SIAL_MPATH"))) { ++ ++ if(home) { ++ ++ path=sial_alloc(strlen(home)+sizeof(LCUDIR)+sizeof(LCDIR)+4); ++ homepath=sial_alloc(strlen(home)+sizeof(LCUDIR)+2); ++ ++ /* build a path for call to sial_load() */ ++ strcpy(homepath, home); ++ strcat(homepath, "/"); ++ strcat(homepath, LCUDIR); ++ ++ /* built the official path */ ++ strcpy(path, LCDIR); ++ strcat(path, ":"); ++ strcat(path, home); ++ strcat(path, "/"); ++ strcat(path, LCUDIR); ++ } ++ else path=LCDIR; ++ } ++ sial_setmpath(path); ++ ++ fprintf(fp, "\tLoading sial commands from %s .... ", ++ path); ++ ++ /* include path */ ++ if(!(ipath=getenv("SIAL_IPATH"))) { ++ ++ if(home) { ++ ++ ipath=sial_alloc(strlen(home)+sizeof(LCDIR)+sizeof(LCUDIR)+(sizeof(LCIDIR)*2)+(sizeof("/usr/include")+2)+6); ++ ++ /* built the official path */ ++ strcpy(ipath, LCDIR); ++ strcat(ipath, "/"LCIDIR":"); ++ strcat(ipath, home); ++ strcat(ipath, "/"); ++ strcat(ipath, LCUDIR); ++ strcat(ipath, "/"LCIDIR); ++ strcat(ipath, ":/usr/include"); ++ } ++ else ipath=LCDIR"/"LCIDIR; ++ } ++ sial_setipath(ipath); ++ ++ /* set the new function callback */ ++ sial_setcallback(reg_callback); ++ ++ /* load the default macros */ ++ sial_loadall(); ++ ++ /* load some sial specific commands */ ++ register_extension(command_table); ++ ++ /* some builtins */ ++ sial_builtin("int curtask()", curtask); ++ ++ fprintf(fp, "Done.\n"); ++ } ++ return 1; ++} +--- crash/extensions/dminfo.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/dminfo.c 2007-11-09 16:57:28.000000000 -0500 +@@ -0,0 +1,1534 @@ ++/* dminfo.c - crash extension module for device-mapper analysis ++ * ++ * Copyright (C) 2005 NEC Corporation ++ * Copyright (C) 2005 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" /* From the crash source top-level directory */ ++ ++int _init(void); ++int _fini(void); ++ ++/* ++ * Indices of size-offset array (Used by GET_xxx macros) ++ * ++ * DM__ ++ */ ++enum { ++ DM_hash_cell_name_list = 0, ++ DM_hash_cell_name, ++ DM_hash_cell_md, ++ ++ DM_mapped_device_disk, ++ DM_mapped_device_map, ++ ++ DM_gendisk_major, ++ DM_gendisk_first_minor, ++ DM_gendisk_disk_name, ++ ++ DM_dm_table_num_targets, ++ DM_dm_table_targets, ++ DM_dm_table_devices, ++ ++ DM_dm_target_type, ++ DM_dm_target_begin, ++ DM_dm_target_len, ++ DM_dm_target_private, ++ ++ DM_dm_dev_count, ++ DM_dm_dev_bdev, ++ DM_dm_dev_name, ++ ++ DM_dm_io_md, ++ DM_dm_io_bio, ++ ++ DM_target_type_name, ++ ++ DM_target_io_io, ++ ++ DM_block_device_bd_disk, ++ ++ DM_bio_bi_private, ++ ++ DM_bio_list_head, ++ ++ DM_linear_c_dev, ++ DM_linear_c_start, ++ ++ DM_multipath_hw_handler, ++ DM_multipath_nr_priority_groups, ++ DM_multipath_priority_groups, ++ DM_multipath_nr_valid_paths, ++ DM_multipath_current_pg, ++ DM_multipath_queue_if_no_path, ++ DM_multipath_queue_size, ++ ++ DM_hw_handler_type, ++ DM_hw_handler_type_name, ++ ++ DM_priority_group_ps, ++ DM_priority_group_pg_num, ++ DM_priority_group_bypassed, ++ DM_priority_group_nr_pgpaths, ++ DM_priority_group_pgpaths, ++ ++ DM_path_selector_type, ++ DM_path_selector_type_name, ++ ++ DM_pgpath_fail_count, ++ DM_pgpath_path, ++ ++ DM_path_dev, ++ DM_path_is_active, ++ ++ DM_mirror_set_rh, ++ DM_mirror_set_reads, ++ DM_mirror_set_writes, ++ DM_mirror_set_in_sync, ++ DM_mirror_set_nr_mirrors, ++ DM_mirror_set_mirror, ++ ++ DM_region_hash_log, ++ DM_region_hash_quiesced_regions, ++ DM_region_hash_recovered_regions, ++ ++ DM_dirty_log_type, ++ DM_dirty_log_type_name, ++ ++ DM_mirror_error_count, ++ DM_mirror_dev, ++ DM_mirror_offset, ++ ++ DM_crypt_config_dev, ++ DM_crypt_config_iv_mode, ++ DM_crypt_config_tfm, ++ DM_crypt_config_key_size, ++ DM_crypt_config_key, ++ ++ DM_crypto_tfm_crt_u, ++ DM_crypto_tfm___crt_alg, ++ ++ DM_crypto_alg_cra_name, ++ ++ DM_cipher_tfm_cit_mode, ++ ++ DM_stripe_c_stripes, ++ DM_stripe_c_chunk_mask, ++ DM_stripe_c_stripe, ++ ++ DM_stripe_dev, ++ ++ DM_dm_snapshot_origin, ++ DM_dm_snapshot_cow, ++ DM_dm_snapshot_chunk_size, ++ DM_dm_snapshot_valid, ++ DM_dm_snapshot_type, ++ ++ NR_DMINFO_MEMBER_TABLE_ENTRY ++}; ++ ++/* Size-offset array for structure's member */ ++static struct dminfo_member_entry { ++ unsigned long offset; ++ unsigned long size; ++} mbr_ary[NR_DMINFO_MEMBER_TABLE_ENTRY]; ++ ++/* ++ * Macros to retrieve data of given structure's member ++ * ++ * Macros except for the MSG assume 'struct s' is at 'addr' ++ */ ++#define MSG(msg, s, m) msg ": " s "." m ++ ++/* Initialize the size-offset array */ ++#define INIT_MBR_TABLE(s, m) \ ++ do { \ ++ if (!mbr_ary[DM_##s##_##m].size) { \ ++ mbr_ary[DM_##s##_##m].offset = MEMBER_OFFSET("struct " #s, #m); \ ++ mbr_ary[DM_##s##_##m].size = MEMBER_SIZE("struct " #s, #m); \ ++ } \ ++ } while (0) ++ ++/* ++ * Store the data of member m in ret. ++ * Initialize the size-offset array for the member m if needed. ++ */ ++#define GET_VALUE(addr, s, m, ret) \ ++ do { \ ++ INIT_MBR_TABLE(s, m); \ ++ if (sizeof(ret) < mbr_ary[DM_##s##_##m].size) \ ++ fprintf(fp, "%s\n", \ ++ MSG("ERROR: GET_VALUE size_check", #s, #m)); \ ++ readmem(addr + mbr_ary[DM_##s##_##m].offset, KVADDR, &ret, \ ++ mbr_ary[DM_##s##_##m].size, MSG("GET_VALUE", #s, #m), \ ++ FAULT_ON_ERROR);\ ++ } while (0) ++ ++/* ++ * Store the address of member m in ret. ++ * Initialize the size-offset array for the member m if needed. ++ */ ++#define GET_ADDR(addr, s, m, ret) \ ++ do { \ ++ INIT_MBR_TABLE(s, m); \ ++ ret = addr + mbr_ary[DM_##s##_##m].offset; \ ++ } while (0) ++ ++/* ++ * Store the string data of member m in ret. ++ * Initialize the size-offset array for the member m if needed. ++ */ ++#define GET_STR(addr, s, m, ret, len) \ ++ do { \ ++ INIT_MBR_TABLE(s, m); \ ++ if (!read_string(addr + mbr_ary[DM_##s##_##m].offset, ret, len - 1)) \ ++ fprintf(fp, "%s\n", MSG("ERROR: GET_STR", #s, #m)); \ ++ } while (0) ++ ++/* ++ * Store the string data pointed by member m in ret. ++ * Initialize the size-offset array for the member m if needed. ++ */ ++#define GET_PTR_STR(addr, s, m, ret, len) \ ++ do { \ ++ unsigned long tmp; \ ++ INIT_MBR_TABLE(s, m); \ ++ readmem(addr + mbr_ary[DM_##s##_##m].offset, KVADDR, &tmp, \ ++ mbr_ary[DM_##s##_##m].size, MSG("GET_PTR_STR", #s, #m),\ ++ FAULT_ON_ERROR);\ ++ if (!read_string(tmp, ret, len - 1)) \ ++ fprintf(fp, "%s\n", MSG("ERROR: GET_PTR_STR", #s, #m));\ ++ } while (0) ++ ++/* ++ * Utility function/macro to walk the list ++ */ ++static unsigned long ++get_next_from_list_head(unsigned long addr) ++{ ++ unsigned long ret; ++ ++ readmem(addr + OFFSET(list_head_next), KVADDR, &ret, sizeof(void *), ++ MSG("get_next_from_list_head", "list_head", "next"), ++ FAULT_ON_ERROR); ++ ++ return ret; ++} ++ ++#define list_for_each(next, head, last) \ ++ for (next = get_next_from_list_head(head), last = 0UL; \ ++ next && next != head && next != last; \ ++ last = next, next = get_next_from_list_head(next)) ++ ++/* ++ * device-mapper target analyzer ++ * ++ * device-mapper has various target driver: linear, mirror, multipath, etc. ++ * Information specific to target is stored in its own way. ++ * Target-specific analyzer is provided for each target driver for this reason. ++ */ ++static struct dminfo_target_analyzer { ++ struct dminfo_target_analyzer *next; ++ char *target_name; ++ int (*ready) (void); /* returns true if analyzer is available */ ++ void (*show_table) (unsigned long); /* display table info */ ++ void (*show_status) (unsigned long); /* display status info */ ++ void (*show_queue) (unsigned long); /* display queued I/O info */ ++} analyzers_head; ++ ++static void ++dminfo_register_target_analyzer(struct dminfo_target_analyzer *ta) ++{ ++ ta->next = analyzers_head.next; ++ analyzers_head.next = ta; ++} ++ ++static struct ++dminfo_target_analyzer *find_target_analyzer(char *target_type) ++{ ++ struct dminfo_target_analyzer *ta; ++ ++ for (ta = analyzers_head.next; ta; ta = ta->next) ++ if (!strcmp(ta->target_name, target_type)) ++ return ta; ++ ++ return NULL; ++} ++ ++/* ++ * zero target ++ */ ++static int ++zero_ready(void) ++{ ++ return 1; ++} ++ ++static void ++zero_show_table(unsigned long target) ++{ ++ unsigned long long start, len; ++ ++ /* Get target information */ ++ GET_VALUE(target, dm_target, begin, start); ++ GET_VALUE(target, dm_target, len, len); ++ ++ fprintf(fp, " begin:%llu len:%llu", start, len); ++} ++ ++static void ++zero_show_status(unsigned long target) ++{ ++ /* zero target has no status */ ++ fprintf(fp, " No status info"); ++} ++ ++static void ++zero_show_queue(unsigned long target) ++{ ++ /* zero target has no queue */ ++ fprintf(fp, " No queue info"); ++} ++ ++static struct dminfo_target_analyzer zero_analyzer = { ++ .target_name = "zero", ++ .ready = zero_ready, ++ .show_table = zero_show_table, ++ .show_status = zero_show_status, ++ .show_queue = zero_show_queue ++}; ++ ++/* ++ * error target ++ */ ++static int ++error_ready(void) ++{ ++ return 1; ++} ++ ++static void ++error_show_table(unsigned long target) ++{ ++ unsigned long long start, len; ++ ++ /* Get target information */ ++ GET_VALUE(target, dm_target, begin, start); ++ GET_VALUE(target, dm_target, len, len); ++ ++ fprintf(fp, " begin:%llu len:%llu", start, len); ++} ++ ++static void ++error_show_status(unsigned long target) ++{ ++ /* error target has no status */ ++ fprintf(fp, " No status info"); ++} ++ ++static void ++error_show_queue(unsigned long target) ++{ ++ /* error target has no queue */ ++ fprintf(fp, " No queue info"); ++} ++ ++static struct dminfo_target_analyzer error_analyzer = { ++ .target_name = "error", ++ .ready = error_ready, ++ .show_table = error_show_table, ++ .show_status = error_show_status, ++ .show_queue = error_show_queue ++}; ++ ++/* ++ * linear target ++ */ ++static int ++linear_ready(void) ++{ ++ static int debuginfo = 0; ++ ++ if (debuginfo) ++ return 1; ++ ++ if (STRUCT_EXISTS("struct linear_c")) { ++ debuginfo = 1; ++ return 1; ++ } else ++ fprintf(fp, "No such struct info: linear_c"); ++ ++ return 0; ++} ++ ++static void ++linear_show_table(unsigned long target) ++{ ++ unsigned long lc, dm_dev; ++ unsigned long long start, len, offset; ++ char devt[BUFSIZE]; ++ ++ /* Get target information */ ++ GET_VALUE(target, dm_target, begin, start); ++ GET_VALUE(target, dm_target, len, len); ++ GET_VALUE(target, dm_target, private, lc); ++ GET_VALUE(lc, linear_c, dev, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, devt, BUFSIZE); ++ GET_VALUE(lc, linear_c, start, offset); ++ ++ fprintf(fp, " begin:%llu len:%llu dev:%s offset:%llu", ++ start, len, devt, offset); ++} ++ ++static void ++linear_show_status(unsigned long target) ++{ ++ /* linear target has no status */ ++ fprintf(fp, " No status info"); ++} ++ ++static void ++linear_show_queue(unsigned long target) ++{ ++ /* linear target has no I/O queue */ ++ fprintf(fp, " No queue info"); ++} ++ ++static struct dminfo_target_analyzer linear_analyzer = { ++ .target_name = "linear", ++ .ready = linear_ready, ++ .show_table = linear_show_table, ++ .show_status = linear_show_status, ++ .show_queue = linear_show_queue ++}; ++ ++/* ++ * mirror target ++ */ ++static int ++mirror_ready(void) ++{ ++ static int debuginfo = 0; ++ ++ if (debuginfo) ++ return 1; ++ ++ if (STRUCT_EXISTS("struct mirror_set")) { ++ debuginfo = 1; ++ return 1; ++ } else ++ fprintf(fp, "No such struct info: mirror_set"); ++ ++ return 0; ++} ++ ++static void ++mirror_show_table(unsigned long target) ++{ ++ unsigned int i, nr_mir; ++ unsigned long ms, rh, log, log_type, mir_size, mir_head, mir, dm_dev; ++ unsigned long long offset; ++ char buf[BUFSIZE]; ++ ++ /* Get the address of struct mirror_set */ ++ GET_VALUE(target, dm_target, private, ms); ++ ++ /* Get the log-type name of the mirror_set */ ++ GET_ADDR(ms, mirror_set, rh, rh); ++ GET_VALUE(rh, region_hash, log, log); ++ GET_VALUE(log, dirty_log, type, log_type); ++ GET_PTR_STR(log_type, dirty_log_type, name, buf, BUFSIZE); ++ fprintf(fp, " log:%s", buf); ++ ++ /* ++ * Display information for each mirror disks. ++ * ++ * mir_head = mirror_set.mirror. ++ * This is the head of struct mirror array. ++ */ ++ fprintf(fp, " dev:"); ++ mir_size = STRUCT_SIZE("struct mirror"); ++ GET_ADDR(ms, mirror_set, mirror, mir_head); ++ GET_VALUE(ms, mirror_set, nr_mirrors, nr_mir); ++ for (i = 0; i < nr_mir; i++) { ++ mir = mir_head + mir_size * i; /* Get next mirror */ ++ ++ /* Get the devt of the mirror disk */ ++ GET_VALUE(mir, mirror, dev, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE); ++ ++ /* Get the offset of the mirror disk */ ++ GET_VALUE(mir, mirror, offset, offset); ++ ++ fprintf(fp, "%s(%llu)%s", buf, offset, ++ i == nr_mir - 1 ? "" : ","); ++ } ++ if (i != nr_mir) ++ fprintf(fp, " ERROR: dev are less than nr_mir:%d", nr_mir); ++} ++ ++static void ++mirror_show_status(unsigned long target) ++{ ++ unsigned int i, nr_mir, synced, nr_error; ++ unsigned long ms, mir_size, mir_head, mir, dm_dev; ++ char buf[BUFSIZE]; ++ ++ /* Get the address of struct mirror_set */ ++ GET_VALUE(target, dm_target, private, ms); ++ ++ /* Get the status info of the mirror_set */ ++ GET_VALUE(ms, mirror_set, in_sync, synced); ++ fprintf(fp, " in_sync:%d", synced); ++ ++ /* ++ * Display information for each mirror disks. ++ * ++ * mir_head = mirror_set.mirror. ++ * This is the head of struct mirror array. ++ */ ++ fprintf(fp, " dev:"); ++ mir_size = STRUCT_SIZE("struct mirror"); ++ GET_ADDR(ms, mirror_set, mirror, mir_head); ++ GET_VALUE(ms, mirror_set, nr_mirrors, nr_mir); ++ for (i = 0; i < nr_mir; i++) { ++ mir = mir_head + mir_size * i; /* Get next mirror */ ++ ++ /* Get the devt of the mirror disk */ ++ GET_VALUE(mir, mirror, dev, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE); ++ ++ /* Get the offset of the mirror disk */ ++ GET_VALUE(mir, mirror, error_count, nr_error); ++ ++ fprintf(fp, "%s(%c,%d)%s", buf, nr_error ? 'D' : 'A', nr_error, ++ i == nr_mir - 1 ? "" : ","); ++ } ++ if (i != nr_mir) ++ fprintf(fp, " ERROR: dev are less than nr_mir:%d", nr_mir); ++} ++ ++static void ++mirror_show_queue(unsigned long target) ++{ ++ unsigned long ms, rlist, wlist, rhead, whead; ++ unsigned long rh, quis_head, rcov_head, quis_next, rcov_next; ++ ++ /* Get the address of struct mirror_set */ ++ GET_VALUE(target, dm_target, private, ms); ++ ++ /* Get the address of queued I/O lists in struct mirror_set */ ++ GET_ADDR(ms, mirror_set, reads, rlist); ++ GET_ADDR(ms, mirror_set, writes, wlist); ++ ++ /* Get the head of queued I/O lists */ ++ GET_VALUE(rlist, bio_list, head, rhead); ++ GET_VALUE(wlist, bio_list, head, whead); ++ fprintf(fp, " %s", rhead ? "reads" : "(reads)"); ++ fprintf(fp, " %s", whead ? "writes" : "(writes)"); ++ ++ /* Get the address of the struct region_hash */ ++ GET_ADDR(ms, mirror_set, rh, rh); ++ ++ /* Get the address of recover region lists in struct region_hash */ ++ GET_ADDR(rh, region_hash, quiesced_regions, quis_head); ++ GET_ADDR(rh, region_hash, recovered_regions, rcov_head); ++ ++ /* Get the head of recover region lists */ ++ quis_next = get_next_from_list_head(quis_head); ++ rcov_next = get_next_from_list_head(rcov_head); ++ ++ fprintf(fp, " %s", quis_next != quis_head ? "quiesced" : "(quiesced)"); ++ fprintf(fp, " %s", rcov_next != rcov_head ? "recovered" : "(recovered)"); ++} ++ ++static struct dminfo_target_analyzer mirror_analyzer = { ++ .target_name = "mirror", ++ .ready = mirror_ready, ++ .show_table = mirror_show_table, ++ .show_status = mirror_show_status, ++ .show_queue = mirror_show_queue ++}; ++ ++/* ++ * multipath target ++ */ ++static int ++multipath_ready(void) ++{ ++ static int debuginfo = 0; ++ ++ if (debuginfo) ++ return 1; ++ ++ if (STRUCT_EXISTS("struct multipath")) { ++ debuginfo = 1; ++ return 1; ++ } else ++ fprintf(fp, "No such struct info: multipath"); ++ ++ return 0; ++} ++ ++static void ++multipath_show_table(unsigned long target) ++{ ++ int i, j; ++ unsigned int queue_if_no_path, nr_pgs, pg_id, nr_paths; ++ unsigned long mp, hwh, hwh_type, ps, ps_type, path, dm_dev; ++ unsigned long pg_head, pg_next, pg_last; ++ unsigned long path_head, path_next, path_last; ++ char name[BUFSIZE]; ++ ++ /* Get the address of struct multipath */ ++ GET_VALUE(target, dm_target, private, mp); ++ ++ /* Get features information */ ++ GET_VALUE(mp, multipath, queue_if_no_path, queue_if_no_path); ++ ++ /* Get the hardware-handler information */ ++ GET_ADDR(mp, multipath, hw_handler, hwh); ++ GET_VALUE(hwh, hw_handler, type, hwh_type); ++ if (hwh_type) ++ GET_PTR_STR(hwh_type, hw_handler_type, name, name, BUFSIZE); ++ else ++ strcpy(name, "none"); ++ ++ /* Get the number of priority groups */ ++ GET_VALUE(mp, multipath, nr_priority_groups, nr_pgs); ++ ++ fprintf(fp, " queue_if_no_path:%d hwh:%s nr_pgs:%d\n", ++ queue_if_no_path, name, nr_pgs); ++ ++ /* Display information for each priority group */ ++ fprintf(fp, " %-2s %-13s %-8s %s", ++ "PG", "PATH_SELECTOR", "NR_PATHS", "PATHS"); ++ GET_ADDR(mp, multipath, priority_groups, pg_head); ++ i = 0; ++ list_for_each (pg_next, pg_head, pg_last) { ++ /* pg_next == struct priority_group */ ++ ++ /* Get the index of the priority group */ ++ GET_VALUE(pg_next, priority_group, pg_num, pg_id); ++ ++ /* Get the name of path selector */ ++ GET_ADDR(pg_next, priority_group, ps, ps); ++ GET_VALUE(ps, path_selector, type, ps_type); ++ GET_PTR_STR(ps_type, path_selector_type, name, name, BUFSIZE); ++ ++ /* Get the number of paths in the priority group */ ++ GET_VALUE(pg_next, priority_group, nr_pgpaths, nr_paths); ++ ++ fprintf(fp, "\n %-2d %-13s %-8d ", pg_id, name, nr_paths); ++ ++ /* Display information for each path */ ++ GET_ADDR(pg_next, priority_group, pgpaths, path_head); ++ j = 0; ++ list_for_each (path_next, path_head, path_last) { ++ /* path_next == struct pgpath */ ++ ++ /* Get the devt of the pgpath */ ++ GET_ADDR(path_next, pgpath, path, path); ++ GET_VALUE(path, path, dev, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, name, BUFSIZE); ++ ++ fprintf(fp, " %s", name); ++ j++; ++ } ++ if (j != nr_paths) ++ fprintf(fp, " ERROR: paths are less than nr_paths:%d", ++ nr_paths); ++ i++; ++ } ++ if (i != nr_pgs) ++ fprintf(fp, " ERROR: pgs are less than nr_pgs:%d", nr_pgs); ++} ++ ++static void ++multipath_show_status(unsigned long target) ++{ ++ int i, j; ++ unsigned int queue_if_no_path, nr_pgs, pg_id, nr_paths; ++ unsigned int bypassed_pg, path_active, nr_fails; ++ unsigned long mp, hwh, hwh_type, cur_pg, path, dm_dev; ++ unsigned long pg_head, pg_next, pg_last; ++ unsigned long path_head, path_next, path_last; ++ char buf[BUFSIZE], path_status; ++ ++ /* Get the address of struct multipath */ ++ GET_VALUE(target, dm_target, private, mp); ++ ++ /* Get features information */ ++ GET_VALUE(mp, multipath, queue_if_no_path, queue_if_no_path); ++ ++ /* Get the hardware-handler information */ ++ GET_ADDR(mp, multipath, hw_handler, hwh); ++ GET_VALUE(hwh, hw_handler, type, hwh_type); ++ if (hwh_type) ++ GET_PTR_STR(hwh_type, hw_handler_type, name, buf, BUFSIZE); ++ else ++ strcpy(buf, "none"); ++ ++ /* Get the number of priority groups */ ++ GET_VALUE(mp, multipath, nr_priority_groups, nr_pgs); ++ ++ fprintf(fp, " queue_if_no_path:%d hwh:%s nr_pgs:%d\n", ++ queue_if_no_path, buf, nr_pgs); ++ ++ /* Display information for each priority group */ ++ fprintf(fp, " %-2s %-9s %-8s %s", ++ "PG", "PG_STATUS", "NR_PATHS", "PATHS"); ++ GET_ADDR(mp, multipath, priority_groups, pg_head); ++ i = 0; ++ list_for_each (pg_next, pg_head, pg_last) { ++ /* pg_next == struct priority_group */ ++ ++ /* Get the index of the priority group */ ++ GET_VALUE(pg_next, priority_group, pg_num, pg_id); ++ ++ /* Get the status of the priority group */ ++ GET_VALUE(pg_next, priority_group, bypassed, bypassed_pg); ++ if (bypassed_pg) ++ strcpy(buf, "disabled"); ++ else { ++ GET_VALUE(mp, multipath, current_pg, cur_pg); ++ if (pg_next == cur_pg) ++ strcpy(buf, "active"); ++ else ++ strcpy(buf, "enabled"); ++ } ++ ++ /* Get the number of paths in the priority group */ ++ GET_VALUE(pg_next, priority_group, nr_pgpaths, nr_paths); ++ ++ fprintf(fp, "\n %-2d %-9s %-8d ", pg_id, buf, nr_paths); ++ ++ /* Display information for each path */ ++ GET_ADDR(pg_next, priority_group, pgpaths, path_head); ++ j = 0; ++ list_for_each (path_next, path_head, path_last) { ++ /* path_next == struct pgpath */ ++ ++ /* Get the devt of the pgpath */ ++ GET_ADDR(path_next, pgpath, path, path); ++ GET_VALUE(path, path, dev, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE); ++ ++ /* Get the status of the path */ ++ GET_VALUE(path, path, is_active, path_active); ++ GET_VALUE(path_next, pgpath, fail_count, nr_fails); ++ path_status = path_active ? 'A' : 'F'; ++ ++ fprintf(fp, " %s(%c,%u)", buf, path_status, nr_fails); ++ j++; ++ } ++ if (j != nr_paths) ++ fprintf(fp, " ERROR: paths are less than nr_paths:%d", ++ nr_paths); ++ i++; ++ } ++ if (i != nr_pgs) ++ fprintf(fp, " ERROR: pgs are less than nr_pgs:%d", nr_pgs); ++} ++ ++static void ++multipath_show_queue(unsigned long target) ++{ ++ unsigned int queue_size; ++ unsigned long mp; ++ ++ /* Get the address of struct multipath */ ++ GET_VALUE(target, dm_target, private, mp); ++ ++ /* Get the size of queued I/Os in this 'target' */ ++ GET_VALUE(mp, multipath, queue_size, queue_size); ++ ++ fprintf(fp, " queue_size:%d", queue_size); ++} ++ ++static struct dminfo_target_analyzer multipath_analyzer = { ++ .target_name = "multipath", ++ .ready = multipath_ready, ++ .show_table = multipath_show_table, ++ .show_status = multipath_show_status, ++ .show_queue = multipath_show_queue ++}; ++ ++/* ++ * crypt target ++ */ ++static int ++crypt_ready(void) ++{ ++ static int debuginfo = 0; ++ ++ if (debuginfo) ++ return 1; ++ ++ if (STRUCT_EXISTS("struct crypt_config")) { ++ debuginfo = 1; ++ return 1; ++ } else ++ fprintf(fp, "No such struct info: crypt_config"); ++ ++ return 0; ++} ++ ++#define DMINFO_CRYPTO_TFM_MODE_ECB 0x00000001 ++#define DMINFO_CRYPTO_TFM_MODE_CBC 0x00000002 ++ ++static void ++crypt_show_table(unsigned long target) ++{ ++ int i, cit_mode, key_size; ++ unsigned long cc, tfm, crt_alg, cipher, iv_mode, dm_dev; ++ char buf[BUFSIZE], *chainmode; ++ ++ /* Get the address of struct crypt_config */ ++ GET_VALUE(target, dm_target, private, cc); ++ ++ /* Get the cipher name of the crypt_tfm */ ++ GET_VALUE(cc, crypt_config, tfm, tfm); ++ GET_VALUE(tfm, crypto_tfm, __crt_alg, crt_alg); ++ GET_STR(crt_alg, crypto_alg, cra_name, buf, BUFSIZE); ++ fprintf(fp, " type:%s", buf); ++ ++ /* Get the cit_mode of the crypt_tfm */ ++ GET_ADDR(tfm, crypto_tfm, crt_u, cipher); ++ GET_VALUE(cipher, cipher_tfm, cit_mode, cit_mode); ++ ++ if (MEMBER_EXISTS("struct crypt_config", "iv_mode")) { ++ if (cit_mode == DMINFO_CRYPTO_TFM_MODE_CBC) ++ chainmode = "cbc"; ++ else if (cit_mode == DMINFO_CRYPTO_TFM_MODE_ECB) ++ chainmode = "ecb"; ++ else ++ chainmode = "unknown"; ++ ++ /* Get the iv_mode of the crypt_config */ ++ GET_VALUE(cc, crypt_config, iv_mode, iv_mode); ++ if (iv_mode) { ++ GET_PTR_STR(cc, crypt_config, iv_mode, buf, BUFSIZE); ++ fprintf(fp, "-%s-%s", chainmode, buf); ++ } else ++ fprintf(fp, "-%s", chainmode); ++ ++ } else { ++ /* Compatibility mode for old dm-crypt cipher strings */ ++ if (cit_mode == DMINFO_CRYPTO_TFM_MODE_CBC) ++ chainmode = "plain"; ++ else if (cit_mode == DMINFO_CRYPTO_TFM_MODE_ECB) ++ chainmode = "ecb"; ++ else ++ chainmode = "unknown"; ++ ++ fprintf(fp, "-%s", chainmode); ++ } ++ ++ /* Get the devt of the crypt_config */ ++ GET_VALUE(cc, crypt_config, dev, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE); ++ fprintf(fp, " dev:%s", buf); ++ ++ /* ++ * Get the key of the crypt_config. ++ */ ++ GET_VALUE(cc, crypt_config, key_size, key_size); ++ GET_STR(cc, crypt_config, key, buf, MIN(key_size + 1, BUFSIZE)); ++ fprintf(fp, " key:"); ++ for (i = 0; i < key_size; i++) ++ fprintf(fp, "%02x", (unsigned char)buf[i]); ++} ++ ++static void ++crypt_show_status(unsigned long target) ++{ ++ /* crypt target has no status */ ++ fprintf(fp, " No status info"); ++} ++ ++static void ++crypt_show_queue(unsigned long target) ++{ ++ /* crypt target has no queue */ ++ fprintf(fp, " No queue info"); ++} ++ ++static struct dminfo_target_analyzer crypt_analyzer = { ++ .target_name = "crypt", ++ .ready = crypt_ready, ++ .show_table = crypt_show_table, ++ .show_status = crypt_show_status, ++ .show_queue = crypt_show_queue ++}; ++ ++/* ++ * stripe target ++ */ ++static int ++stripe_ready(void) ++{ ++ static int debuginfo = 0; ++ ++ if (debuginfo) ++ return 1; ++ ++ if (STRUCT_EXISTS("struct stripe_c")) { ++ debuginfo = 1; ++ return 1; ++ } else ++ fprintf(fp, "No such struct info: stripe_c"); ++ ++ return 0; ++} ++ ++static void ++stripe_show_table(unsigned long target) ++{ ++ unsigned int i, n_stripe; ++ unsigned long sc, stripe_size, s, head, dm_dev; ++ unsigned long long mask; ++ char buf[BUFSIZE]; ++ ++ /* Get the address of struct stripe_c */ ++ GET_VALUE(target, dm_target, private, sc); ++ ++ /* Get the chunk_size of the stripe_c */ ++ GET_VALUE(sc, stripe_c, chunk_mask, mask); ++ fprintf(fp, " chunk_size:%llu", mask + 1); ++ ++ /* ++ * Display the information of each stripe disks. ++ * ++ * head = stripe_c.stripe. ++ * This is the head of struct stripe array. ++ */ ++ stripe_size = STRUCT_SIZE("struct stripe"); ++ GET_ADDR(sc, stripe_c, stripe, head); ++ GET_VALUE(sc, stripe_c, stripes, n_stripe); ++ fprintf(fp, " dev:"); ++ for (i = 0; i < n_stripe; i++) { ++ s = head + stripe_size * i; /* Get next stripe */ ++ ++ /* Get the devt of the stripe disk */ ++ GET_VALUE(s, stripe, dev, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE); ++ ++ fprintf(fp, "%s%s", buf, i == n_stripe - 1 ? "" : ","); ++ } ++ if (i != n_stripe) ++ fprintf(fp, " ERROR: dev are less than n_stripe:%d", n_stripe); ++} ++ ++static void ++stripe_show_status(unsigned long target) ++{ ++ /* stripe target has no status */ ++ fprintf(fp, " No status info"); ++} ++ ++static void ++stripe_show_queue(unsigned long target) ++{ ++ /* stripe target has no queue */ ++ fprintf(fp, " No queue info"); ++} ++ ++static struct dminfo_target_analyzer stripe_analyzer = { ++ .target_name = "striped", ++ .ready = stripe_ready, ++ .show_table = stripe_show_table, ++ .show_status = stripe_show_status, ++ .show_queue = stripe_show_queue ++}; ++ ++/* ++ * snapshot target ++ */ ++static int ++snapshot_ready(void) ++{ ++ static int debuginfo = 0; ++ ++ if (debuginfo) ++ return 1; ++ ++ if (STRUCT_EXISTS("struct dm_snapshot")) { ++ debuginfo = 1; ++ return 1; ++ } else ++ fprintf(fp, "No such struct info: dm_snapshot"); ++ ++ return 0; ++} ++ ++static void ++snapshot_show_table(unsigned long target) ++{ ++ unsigned long snap, orig_dev, cow_dev; ++ unsigned long long chunk_size; ++ char orig_name[BUFSIZE], cow_name[BUFSIZE], type; ++ ++ /* Get the address of struct dm_snapshot */ ++ GET_VALUE(target, dm_target, private, snap); ++ ++ /* Get snapshot parameters of the dm_snapshot */ ++ GET_VALUE(snap, dm_snapshot, origin, orig_dev); ++ GET_STR(orig_dev, dm_dev, name, orig_name, BUFSIZE); ++ GET_VALUE(snap, dm_snapshot, cow, cow_dev); ++ GET_STR(cow_dev, dm_dev, name, cow_name, BUFSIZE); ++ GET_VALUE(snap, dm_snapshot, type, type); ++ GET_VALUE(snap, dm_snapshot, chunk_size, chunk_size); ++ ++ fprintf(fp, " orig:%s cow:%s type:%c chunk_size:%llu", ++ orig_name, cow_name, type, chunk_size); ++} ++ ++static void ++snapshot_show_status(unsigned long target) ++{ ++ int valid; ++ unsigned long snap; ++ ++ /* Get the address of struct dm_snapshot */ ++ GET_VALUE(target, dm_target, private, snap); ++ ++ /* Get snapshot parameters of the dm_snapshot */ ++ GET_VALUE(snap, dm_snapshot, valid, valid); ++ ++ fprintf(fp, " vaild:%d", valid); ++} ++ ++static void ++snapshot_show_queue(unsigned long target) ++{ ++ fprintf(fp, " No queue info"); ++} ++ ++static struct dminfo_target_analyzer snapshot_analyzer = { ++ .target_name = "snapshot", ++ .ready = snapshot_ready, ++ .show_table = snapshot_show_table, ++ .show_status = snapshot_show_status, ++ .show_queue = snapshot_show_queue ++}; ++ ++/* ++ * snapshot-origin target ++ */ ++static int ++origin_ready(void) ++{ ++ return 1; ++} ++ ++static void ++origin_show_table(unsigned long target) ++{ ++ unsigned long dm_dev; ++ char buf[BUFSIZE]; ++ ++ /* Get the name of the struct dm_dev */ ++ GET_VALUE(target, dm_target, private, dm_dev); ++ GET_STR(dm_dev, dm_dev, name, buf, BUFSIZE); ++ ++ fprintf(fp, " orig_dev:%s", buf); ++} ++ ++static void ++origin_show_status(unsigned long target) ++{ ++ /* snapshot-origin target has no status */ ++ fprintf(fp, " No status info"); ++} ++ ++static void ++origin_show_queue(unsigned long target) ++{ ++ /* snapshot-origin target has no queue */ ++ fprintf(fp, " No queue info"); ++} ++ ++static struct dminfo_target_analyzer snapshot_origin_analyzer = { ++ .target_name = "snapshot-origin", ++ .ready = origin_ready, ++ .show_table = origin_show_table, ++ .show_status = origin_show_status, ++ .show_queue = origin_show_queue ++}; ++ ++/* ++ * Core part of dminfo ++ */ ++#define DMINFO_LIST 0 ++#define DMINFO_DEPS 1 ++#define DMINFO_TABLE 2 ++#define DMINFO_STATUS 3 ++#define DMINFO_QUEUE 4 ++ ++static int ++dm_core_ready(void) ++{ ++ static int debuginfo = 0; ++ ++ if (debuginfo) ++ return 1; ++ ++ if (STRUCT_EXISTS("struct hash_cell")) { ++ debuginfo = 1; ++ return 1; ++ } else ++ fprintf(fp, "No such struct info: hash_cell\n"); ++ ++ return 0; ++} ++ ++/* Display dependency information of the 'table' */ ++static void ++dminfo_show_deps(unsigned long table) ++{ ++ int major, minor, count; ++ unsigned long head, next, last, dev, bdev; ++ char buf[BUFSIZE]; ++ ++ /* head = dm_table.devices */ ++ GET_ADDR(table, dm_table, devices, head); ++ ++ fprintf(fp, " %-3s %-3s %-16s %-5s %s\n", ++ "MAJ", "MIN", "GENDISK", "COUNT", "DEVNAME"); ++ ++ list_for_each (next, head, last) { ++ /* Get dependency information. (next == struct *dm_dev) */ ++ GET_VALUE(next, dm_dev, count, count); ++ GET_VALUE(next, dm_dev, bdev, bdev); ++ GET_VALUE(bdev, block_device, bd_disk, dev); ++ GET_VALUE(dev, gendisk, major, major); ++ GET_VALUE(dev, gendisk, first_minor, minor); ++ GET_STR(dev, gendisk, disk_name, buf, BUFSIZE); ++ ++ fprintf(fp, " %-3d %-3d %-16lx %-5d %s\n", ++ major, minor, dev, count, buf); ++ } ++} ++ ++/* ++ * Display target specific information in the 'table', if the target ++ * analyzer is registered and available. ++ */ ++static void ++dminfo_show_details(unsigned long table, unsigned int num_targets, int info_type) ++{ ++ unsigned int i; ++ unsigned long head, target_size, target, target_type; ++ struct dminfo_target_analyzer *ta; ++ char buf[BUFSIZE]; ++ ++ /* ++ * head = dm_table.targets. ++ * This is the head of struct dm_target array. ++ */ ++ GET_VALUE(table, dm_table, targets, head); ++ target_size = STRUCT_SIZE("struct dm_target"); ++ ++ fprintf(fp, " %-16s %-11s %s\n", ++ "TARGET", "TARGET_TYPE", "PRIVATE_DATA"); ++ ++ for (i = 0; i < num_targets; i++, fprintf(fp, "\n")) { ++ target = head + target_size * i; /* Get next target */ ++ ++ /* Get target information */ ++ GET_VALUE(target, dm_target, type, target_type); ++ GET_PTR_STR(target_type, target_type, name, buf, BUFSIZE); ++ ++ fprintf(fp, " %-16lx %-11s", target, buf); ++ ++ if (!(ta = find_target_analyzer(buf)) || !ta->ready ++ || !ta->ready()) ++ continue; ++ ++ switch (info_type) { ++ case DMINFO_TABLE: ++ if (ta->show_table) ++ ta->show_table(target); ++ break; ++ case DMINFO_STATUS: ++ if (ta->show_status) ++ ta->show_status(target); ++ break; ++ case DMINFO_QUEUE: ++ if (ta->show_queue) ++ ta->show_queue(target); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ if (i != num_targets) ++ fprintf(fp, " ERROR: targets are less than num_targets:%d", ++ num_targets); ++} ++ ++/* ++ * Display lists (and detail information if specified) of existing ++ * dm devices. ++ */ ++static void ++dminfo_show_list(int additional_info) ++{ ++ int i, major, minor, array_len; ++ unsigned int num_targets; ++ unsigned long _name_buckets, head, next, last, md, dev, table; ++ char buf[BUFSIZE]; ++ ++ _name_buckets = symbol_value("_name_buckets"); ++ array_len = get_array_length("_name_buckets", NULL, 0); ++ ++ if (additional_info == DMINFO_LIST) ++ fprintf(fp, "%-3s %-3s %-16s %-16s %-7s %s\n", ++ "MAJ", "MIN", "MAP_DEV", "DM_TABLE", ++ "TARGETS", "MAPNAME"); ++ ++ for (i = 0; i < array_len; i++) { ++ /* head = _name_buckets[i] */ ++ head = _name_buckets + (i * SIZE(list_head)); ++ ++ list_for_each (next, head, last) { /* next == hash_cell */ ++ /* Get device and table information */ ++ GET_PTR_STR(next, hash_cell, name, buf, BUFSIZE); ++ GET_VALUE(next, hash_cell, md, md); ++ GET_VALUE(md, mapped_device, disk, dev); ++ GET_VALUE(dev, gendisk, major, major); ++ GET_VALUE(dev, gendisk, first_minor, minor); ++ GET_VALUE(md, mapped_device, map, table); ++ GET_VALUE(table, dm_table, num_targets, num_targets); ++ ++ if (additional_info != DMINFO_LIST) ++ fprintf(fp, "%-3s %-3s %-16s %-16s %-7s %s\n", ++ "MAJ", "MIN", "MAP_DEV", "DM_TABLE", ++ "TARGETS", "MAPNAME"); ++ ++ fprintf(fp, "%-3d %-3d %-16lx %-16lx %-7d %s\n", ++ major, minor, md, table, num_targets, buf); ++ ++ switch(additional_info) { ++ case DMINFO_DEPS: ++ dminfo_show_deps(table); ++ break; ++ case DMINFO_TABLE: ++ case DMINFO_STATUS: ++ case DMINFO_QUEUE: ++ dminfo_show_details(table, num_targets, ++ additional_info); ++ break; ++ default: ++ break; ++ } ++ ++ if (additional_info != DMINFO_LIST) ++ fprintf(fp, "\n"); ++ } ++ } ++} ++ ++/* ++ * Display the original bio information for the 'bio'. ++ * If the 'bio' is for dm devices, the original bio information is pointed ++ * by bio.bi_private as struct target_io. ++ */ ++static void ++dminfo_show_bio(unsigned long bio) ++{ ++ int major, minor; ++ unsigned long target_io, dm_io, dm_bio, md, dev; ++ char buf[BUFSIZE]; ++ ++ /* Get original bio and device information */ ++ GET_VALUE(bio, bio, bi_private, target_io); ++ GET_VALUE(target_io, target_io, io, dm_io); ++ GET_VALUE(dm_io, dm_io, bio, dm_bio); ++ GET_VALUE(dm_io, dm_io, md, md); ++ GET_VALUE(md, mapped_device, disk, dev); ++ GET_VALUE(dev, gendisk, major, major); ++ GET_VALUE(dev, gendisk, first_minor, minor); ++ GET_STR(dev, gendisk, disk_name, buf, BUFSIZE); ++ ++ fprintf(fp, "%-16s %-3s %-3s %-16s %s\n", ++ "DM_BIO_ADDRESS", "MAJ", "MIN", "MAP_DEV", "DEVNAME"); ++ fprintf(fp, "%-16lx %-3d %-3d %-16lx %s\n", ++ dm_bio, major, minor, md, buf); ++} ++ ++static void ++cmd_dminfo(void) ++{ ++ int c, additional_info = DMINFO_LIST; ++ unsigned long bio; ++ ++ if (!dm_core_ready()) ++ return; ++ ++ /* Parse command line option */ ++ while ((c = getopt(argcnt, args, "b:dlqst")) != EOF) { ++ switch(c) ++ { ++ case 'b': ++ bio = stol(optarg, FAULT_ON_ERROR, NULL); ++ dminfo_show_bio(bio); ++ return; ++ case 'd': ++ additional_info = DMINFO_DEPS; ++ break; ++ case 'l': ++ additional_info = DMINFO_LIST; ++ break; ++ case 'q': ++ additional_info = DMINFO_QUEUE; ++ break; ++ case 's': ++ additional_info = DMINFO_STATUS; ++ break; ++ case 't': ++ additional_info = DMINFO_TABLE; ++ break; ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ dminfo_show_list(additional_info); ++} ++ ++/* ++ * dminfo help ++ */ ++static char *help_dminfo[] = { ++ "dminfo", /* command name */ ++ "device mapper (dm) information", /* short description */ ++ "[-b bio | -d | -l | -q | -s | -t]", /* argument synopsis */ ++ " This command displays information about device-mapper mapped ", ++ " devices (dm devices).", ++ " If no argument is entered, displays lists of existing dm devices.", ++ " It's same as -l option.", ++ "", ++ " -b bio displays the information of the dm device which the bio", ++ " is submitted in. If the bio isn't for dm devices,", ++ " results will be error.", ++ " -d displays dependency information for existing dm devices.", ++ " -l displays lists of existing dm devices.", ++ " -q displays queued I/O information for each target of", ++ " existing dm devices.", ++ " -s displays status information for each target of existing", ++ " dm devices.", ++ " -t displays table information for each target of existing", ++ " dm devices.", ++ "", ++ "EXAMPLE", ++ " Display lists of dm devices. \"MAP_DEV\" is the address of the", ++ " struct mapped_device. \"DM_TABLE\" is the address of the struct", ++ " dm_table. \"TARGETS\" is the number of targets which are in", ++ " the struct dm_table.", ++ "", ++ " %s> dminfo", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 8 c4866c80 c4866280 1 vg0-snap0", ++ " 253 6 f6a04a80 f6a04580 1 vg0-lv0-real", ++ " 253 0 c4840380 c4841880 1 mp0", ++ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd", ++ " 253 7 c4866a80 c4866380 1 vg0-snap0-cow", ++ " 253 4 d441e280 c919ed80 1 dummy1", ++ " 253 3 f5dc4280 cba81d80 1 dummy0", ++ " 253 2 f7c53180 c4866180 1 vg0-lv0", ++ " 253 1 f746d280 f746cd80 1 mp0p1", ++ "", ++ " Display the dm device information which the bio is submitted in.", ++ " The bio (ceacee80) is a clone of the bio (ceacee00) which is", ++ " submitted in the dm-3 (dummy0). And the bio (ceacee00) is a clone", ++ " of the bio (ceaced80) which is submitted in the dm-4 (dummy1), too.", ++ " The bio (ceaced80) is the original bio.", ++ "", ++ " %s> dminfo -b ceacee80", ++ " DM_BIO_ADDRESS MAJ MIN MAP_DEV DEVNAME", ++ " ceacee00 253 3 f5dc4280 dm-3", ++ " crash> dminfo -b ceacee00", ++ " DM_BIO_ADDRESS MAJ MIN MAP_DEV DEVNAME", ++ " ceaced80 253 4 d441e280 dm-4", ++ " crash> dminfo -b ceaced80", ++ " dminfo: invalid kernel virtual address: 64 type: \"GET_VALUE: dm_io.bio\"", ++ "", ++ " Display dependency information for each target.", ++ " The vg0-snap0 depends on thd dm-6 (vg0-lv0-real) and the dm-7", ++ " (vg0-snap0-cow)", ++ "", ++ " %s> dminfo -d", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 8 c4866c80 c4866280 1 vg0-snap0", ++ " MAJ MIN GENDISK COUNT DEVNAME", ++ " 253 7 c4866980 1 dm-7", ++ " 253 6 f6a04280 1 dm-6", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 6 f6a04a80 f6a04580 1 vg0-lv0-real", ++ " MAJ MIN GENDISK COUNT DEVNAME", ++ " 8 0 f7f24c80 1 sda", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 7 c4866a80 c4866380 1 vg0-snap0-cow", ++ " MAJ MIN GENDISK COUNT DEVNAME", ++ " 8 0 f7f24c80 1 sda", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 2 f7c53180 c4866180 1 vg0-lv0", ++ " MAJ MIN GENDISK COUNT DEVNAME", ++ " 253 6 f6a04280 1 dm-6", ++ "", ++ " Display queued I/O information for each target.", ++ " The information is displayed under the \"PRIVATE_DATA\" column.", ++ "", ++ " %s> dminfo -q", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8961080 mirror (reads) (writes) (quiesced) (recovered)", ++ "", ++ " --------------------------------------------------------------", ++ " \"reads/writes\" are members of the struct mirror_set, and", ++ " \"quiesced/recovered\" are members of the struct region_hash.", ++ " If the list is empty, the member is bracketed by \"()\".", ++ " --------------------------------------------------------------", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 0 c4840380 c4841880 1 mp0", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8802080 multipath queue_size:0", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 1 f746d280 f746cd80 1 mp0p1", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8821080 linear No queue info", ++ "", ++ " Display status information for each target.", ++ " The information is displayed under the \"PRIVATE_DATA\" column.", ++ "", ++ " %s> dminfo -s", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 0 c4840380 c4841880 1 mp0", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8802080 multipath queue_if_no_path:0 hwh:none nr_pgs:1", ++ " PG PG_STATUS NR_PATHS PATHS", ++ " 1 active 2 8:16(A,0) 8:32(A,0)", ++ "", ++ " --------------------------------------------------------------", ++ " Format of \"PATHS\": :(,)", ++ " Status: A:active, F:faulty", ++ " Fail_count: the value of the struct pgpath.fail_count", ++ " --------------------------------------------------------------", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8961080 mirror in_sync:1 dev:8:16(A,0),8:32(A,0)", ++ "", ++ " --------------------------------------------------------------", ++ " Format of \"dev\": :(,)", ++ " Status: A:active, D:degraded", ++ " Error_count: the value of the struct mirror.error_count", ++ " --------------------------------------------------------------", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 1 f746d280 f746cd80 1 mp0p1", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8821080 linear No status info", ++ "", ++ " Display table information for each target.", ++ " The information is displayed under the \"PRIVATE_DATA\" column.", ++ "", ++ " %s> dminfo -t", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 8 c4866c80 c4866280 1 vg0-snap0", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f89b4080 snapshot orig:253:6 cow:253:7 type:P chunk_size:16", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 6 f6a04a80 f6a04580 1 vg0-lv0-real", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f890f080 linear begin:0 len:204800 dev:8:5 offset:384", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 0 c4840380 c4841880 1 mp0", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8802080 multipath queue_if_no_path:0 hwh:none nr_pgs:1", ++ " PG PATH_SELECTOR NR_PATHS PATHS", ++ " 1 round-robin 2 8:16 8:32", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 5 f7c50c80 c488e480 1 via_cbeheddbdd", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8961080 mirror log:core dev:8:16(0),8:32(0)", ++ "", ++ " --------------------------------------------------------------", ++ " Format of \"dev\": :()", ++ " Offset: the value of the struct mirror.offset", ++ " --------------------------------------------------------------", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 7 c4866a80 c4866380 1 vg0-snap0-cow", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f899d080 linear begin:0 len:8192 dev:8:5 offset:205184", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 2 f7c53180 c4866180 1 vg0-lv0", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8bbc080 snapshot-origin orig_dev:253:6", ++ "", ++ " MAJ MIN MAP_DEV DM_TABLE TARGETS MAPNAME", ++ " 253 1 f746d280 f746cd80 1 mp0p1", ++ " TARGET TARGET_TYPE PRIVATE_DATA", ++ " f8821080 linear begin:0 len:2040192 dev:253:0 offset:63", ++ NULL ++}; ++ ++/* ++ * Registering command extension ++ */ ++ ++static struct command_table_entry command_table[] = { ++ {"dminfo", cmd_dminfo, help_dminfo, 0}, ++ {NULL, NULL, NULL, 0}, ++}; ++ ++int _init(void) ++{ ++ register_extension(command_table); ++ ++ dminfo_register_target_analyzer(&zero_analyzer); ++ dminfo_register_target_analyzer(&error_analyzer); ++ dminfo_register_target_analyzer(&linear_analyzer); ++ dminfo_register_target_analyzer(&mirror_analyzer); ++ dminfo_register_target_analyzer(&multipath_analyzer); ++ dminfo_register_target_analyzer(&crypt_analyzer); ++ dminfo_register_target_analyzer(&stripe_analyzer); ++ dminfo_register_target_analyzer(&snapshot_analyzer); ++ dminfo_register_target_analyzer(&snapshot_origin_analyzer); ++ ++ return 0; ++} ++ ++int _fini(void) ++{ ++ return 0; ++} +--- crash/extensions/echo.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/echo.c 2007-11-09 16:57:28.000000000 -0500 +@@ -0,0 +1,114 @@ ++/* echo.c - simple example of a crash extension ++ * ++ * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. ++ * Copyright (C) 2002, 2003, 2004, 2005 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005 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" /* From the crash source top-level directory */ ++ ++int _init(void); ++int _fini(void); ++ ++void cmd_echo(void); /* Declare the commands and their help data. */ ++char *help_echo[]; ++ ++static struct command_table_entry command_table[] = { ++ { "echo", cmd_echo, help_echo, 0 }, /* One or more commands, */ ++ { NULL } /* terminated by NULL, */ ++}; ++ ++ ++int ++_init(void) /* Register the command set. */ ++{ ++ register_extension(command_table); ++ return 1; ++} ++ ++/* ++ * The _fini() function is called if the shared object is unloaded. ++ * If desired, perform any cleanups here. ++ */ ++int ++_fini(void) ++{ ++ return 1; ++} ++ ++ ++/* ++ * Arguments are passed to the command functions in the global args[argcnt] ++ * array. See getopt(3) for info on dash arguments. Check out defs.h and ++ * other crash commands for usage of the myriad of utility routines available ++ * to accomplish what your task. ++ */ ++void ++cmd_echo(void) ++{ ++ int c; ++ ++ while ((c = getopt(argcnt, args, "")) != EOF) { ++ switch(c) ++ { ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ while (args[optind]) ++ fprintf(fp, "%s ", args[optind++]); ++ ++ fprintf(fp, "\n"); ++} ++ ++/* ++ * The optional help data is simply an array of strings in a defined format. ++ * For example, the "help echo" command will use the help_echo[] string ++ * array below to create a help page that looks like this: ++ * ++ * NAME ++ * echo - echoes back its arguments ++ * ++ * SYNOPSIS ++ * echo arg ... ++ * ++ * DESCRIPTION ++ * This command simply echoes back its arguments. ++ * ++ * EXAMPLE ++ * Echo back all command arguments: ++ * ++ * crash> echo hello, world ++ * hello, world ++ * ++ */ ++ ++char *help_echo[] = { ++ "echo", /* command name */ ++ "echoes back its arguments", /* short description */ ++ "arg ...", /* argument synopsis, or " " if none */ ++ ++ " This command simply echoes back its arguments.", ++ "\nEXAMPLE", ++ " Echo back all command arguments:\n", ++ " crash> echo hello, world", ++ " hello, world", ++ NULL ++}; ++ ++ +--- crash/extensions/sial.mk.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/sial.mk 2007-10-30 11:13:30.000000000 -0400 +@@ -0,0 +1,17 @@ ++# ++ifeq ($(TARGET), PPC64) ++ TARGET_FLAGS = -D$(TARGET) -m64 ++else ++ TARGET_FLAGS = -D$(TARGET) ++endif ++ ++all: sial.so ++ ++lib-sial: ++ cd libsial && make ++ ++sial.so: ../defs.h sial.c lib-sial ++ gcc -g -I.. -Ilibsial -I../gdb-6.1/bfd -I../gdb-6.1/include -I../gdb-6.1/gdb -I../gdb-6.1/gdb/config -nostartfiles -shared -rdynamic -o sial.so sial.c -fPIC $(TARGET_FLAGS) -Llibsial -lsial ++ ++clean: ++ cd libsial && make clean +--- crash/extensions/libsial/sial_define.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_define.c 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,519 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include ++#include "sial.h" ++/* ++ This set of functions handle #define for simple constant or macros. ++ We read from the current parser input strem untill end of line. ++ ++ The big thing is that we need to do some parsing to get the deinf names ++ and parameters. Also at the time of the macro instanciation, we need to parse ++ the parameters again. That leads to a more complex package... ++*/ ++ ++#define MAXP 20 ++typedef struct mac_s { ++ ++ char *name; /* this macro name */ ++ int np; /* number of parameters */ ++ int issub; /* subs have to be threated differently */ ++ char **p; /* parameters names */ ++ char *buf; /* text for the macro itself */ ++ struct mac_s *next; /* next on the list */ ++ srcpos_t pos; ++ ++} mac_t; ++ ++typedef struct { ++ mac_t *m; ++ char **subs; ++} smac_t; ++ ++static mac_t* macs=0; ++ ++/* we have to add a space at the end of the value ++ Again, this is to prevent a EOF on the parsing stream */ ++def_t* ++sial_add_def(def_t*d, char*name, char*val) ++{ ++def_t*nd=sial_alloc(sizeof(def_t)); ++char *buf=sial_alloc(strlen(val)+2); ++ ++ strcpy(buf, val); ++ strcat(buf, " "); ++ sial_free(val); ++ nd->name=name; ++ nd->val=buf; ++ nd->next=d; ++ return nd; ++} ++ ++/* search for a macro is the current list */ ++mac_t * ++sial_getmac(char *name, int takeof) ++{ ++mac_t *m; ++mac_t *prev=0; ++mac_t *sial_getcurmac(void); ++ ++ if(takeof || !(m=sial_getcurmac())) m=macs; ++ ++ for(; m; m=m->next) { ++ ++ if( !strcmp(m->name, name) ) { ++ ++ if(takeof) { ++ ++ if(!prev) macs=m->next; ++ else prev->next=m->next; ++ ++ } ++ return m; ++ } ++ prev=m; ++ } ++ return 0; ++} ++ ++node_t* ++sial_macexists(node_t*var) ++{ ++char *name=NODE_NAME(var); ++int val; ++ ++ if(sial_getmac(name, 0)) val=1; ++ else val=0; ++ return sial_makenum(B_UL, val); ++} ++static void ++sial_freemac(mac_t*m) ++{ ++int i; ++ ++ for(i=0;inp;i++) sial_free(m->p[i]); ++ if(m->np) sial_free(m->p); ++ sial_free(m); ++} ++ ++/* ++ These are called at 2 different points. ++ One call at the very begining. One call for each file. ++*/ ++void* sial_curmac(void) { return macs; } ++ ++void ++sial_flushmacs(void *vtag) ++{ ++mac_t *m, *next; ++mac_t *tag=(mac_t *)vtag; ++ ++ for(m=macs; m!=tag; m=next) { ++ ++ next=m->next; ++ sial_freemac(m); ++ } ++ macs=m; ++} ++ ++/* this function is called to register a new macro. ++ The text associated w/ the macro is still on the parser stream. ++ Untill eol. ++*/ ++void ++sial_newmac(char *mname, char *buf, int np, char **p, int silent) ++{ ++char *p2; ++mac_t *m; ++ ++ { ++ char *p=buf+strlen(buf)-1; ++ ++ /* eliminate trailing blanks */ ++ while(*p && (*p==' ' || *p=='\t')) p--; ++ *(p+1)='\0'; ++ ++ /* eliminate leading blanks */ ++ p=buf; ++ while(*p && (*p==' ' || *p=='\t')) p++; ++ ++ /* copy and append a space. This is to prevent unloading of the ++ macro before the sial_chkvarmac() call as been performed */ ++ p2=sial_alloc(strlen(p)+2); ++ strcpy(p2, p); ++ sial_free(buf); ++ p2[strlen(p2)+1]='\0'; ++ p2[strlen(p2)]=' '; ++ buf=p2; ++ } ++ ++ if((m=sial_getmac(mname, 1)) && strcmp(m->buf, buf)) { ++ ++ /* when processing the compile options, be silent. */ ++ if(!silent) { ++ ++ sial_warning("Macro redefinition '%s' with different value_t\n" ++ "value_t=[%s]\n" ++ "Previous value_t at %s:%d=[%s]\n" ++ , mname, buf, m->pos.file, m->pos.line, m->buf); ++ } ++ ++ } ++ m=(mac_t*)sial_alloc(sizeof(mac_t)); ++ m->name=sial_strdup(mname); ++ m->np=np; ++ m->p=p; ++ m->buf=buf; ++ m->next=macs; ++ m->issub=0; ++ sial_setpos(&m->pos); ++ macs=m; ++} ++ ++/* this function is called by the enum declaration function and ++ when a enum type is extracted from the image to push a set ++ of define's onto the stack, that correspond to each identifier ++ in the enum. ++*/ ++void ++sial_pushenums(enum_t *et) ++{ ++ while(et) { ++ ++ char *buf=sial_alloc(40); ++ ++ sprintf(buf, "%d", et->value); ++ sial_newmac(et->name, buf, 0, 0, 0); ++ et=et->next; ++ } ++} ++ ++static void ++sial_skipcomment(void) ++{ ++int c; ++ ++ while((c=sial_input())) { ++ ++ if(c=='*') { ++ ++ int c2; ++ ++ if((c2=sial_input())=='/') return; ++ sial_unput(c2); ++ } ++ } ++} ++ ++static void ++sial_skipstr(void) ++{ ++int c; ++ ++ while((c=sial_input())) { ++ ++ if(c=='\\') sial_input(); ++ else if(c=='"') return; ++ } ++} ++ ++ ++/* skip over strings and comment to a specific chracter */ ++static void ++sial_skipto(int x) ++{ ++int c; ++ ++ while((c=sial_input())) { ++ ++ if(c==x) return; ++ ++ switch(c) { ++ ++ case '\\': ++ sial_input(); ++ break; ++ ++ case '"': ++ sial_skipstr(); ++ break; ++ ++ case '/': { ++ ++ int c2; ++ ++ if((c2=sial_input())=='*') { ++ ++ sial_skipcomment(); ++ ++ } else sial_unput(c2); ++ } ++ break; ++ ++ case '(': ++ ++ sial_skipto(')'); ++ break; ++ ++ case ')': ++ sial_error("Missing parameters to macro"); ++ break; ++ } ++ ++ } ++ ++ sial_error("Expected '%c'", x); ++} ++ ++ ++/* ++ This function gets called when the buffer for a macro as been fully ++ parsed. We need to take the associated parameter substitution macros ++ of of the stack and deallocate associated data. ++*/ ++static void ++sial_popmac(void *vsm) ++{ ++smac_t *sm=(smac_t *)vsm; ++int i; ++ ++ for(i=0;im->np;i++) { ++ ++ mac_t *m=sial_getmac(sm->m->p[i], 1); ++ ++ if(!m) sial_error("Oops macro pop!"); ++ sial_free(m->buf); ++ sial_free(m->name); ++ sial_free(m); ++ } ++ sial_free(sm->subs); ++ sial_free(sm); ++} ++ ++/* ++ ++ need to get the actual parameters from the parser stream. ++ This can be simple variable or complex multiple line expressions ++ with strings and commants imbedded in them... ++ ++*/ ++static int ++sial_pushmac(mac_t *m) ++{ ++int i; ++char **subs=sial_alloc(sizeof(char*)*m->np); ++smac_t *sm; ++int siallex(void); ++ ++ /* the next token should be a '(' */ ++ if(siallex() != '(') { ++ ++ sial_error("Expected '(' after '%s'", m->name); ++ ++ } ++ ++ /* get the parameters */ ++ for(i=0;inp;i++) { ++ ++ char *p=sial_cursorp(); ++ int nc; ++ ++ if(inp-1) sial_skipto(','); ++ else sial_skipto(')'); ++ ++ nc=sial_cursorp()-p-1; ++ subs[i]=sial_alloc(nc+2); ++ strncpy(subs[i], p, nc); ++ subs[i][nc]=' '; ++ subs[i][nc+1]='\0'; ++ } ++ ++ /* take care of the macro() case. ex: IS_R10000()i.e. no parms */ ++ if(!m->np) ++ sial_skipto(')'); ++ ++ sm=sial_alloc(sizeof(smac_t)); ++ ++ sm->m=m; ++ sm->subs=subs; ++ ++ /* we push the associated buffer on the stream */ ++ sial_pushbuf(m->buf, 0, sial_popmac, sm, 0); ++ ++ /* we push the subs onto the macro stack */ ++ for(i=0;inp;i++) { ++ ++ mac_t *pm=sial_alloc(sizeof(mac_t)); ++ ++ pm->name=sial_alloc(strlen(m->p[i])+1); ++ strcpy(pm->name, m->p[i]); ++ pm->np=0; ++ pm->p=0; ++ pm->buf=subs[i]; ++ pm->next=macs; ++ pm->issub=1; ++ macs=pm; ++ } ++ return 1; ++ ++} ++ ++ ++/* ++ This one is called from the lexer to check if a 'var' is to be substituted for ++ a macro ++*/ ++int ++sial_chkmacvar(char *mname) ++{ ++mac_t *m; ++ ++ if((m=sial_getmac(mname, 0))) { ++ ++ ++ /* simple constant ? */ ++ if(!m->p) { ++ ++ sial_pushbuf(m->buf, 0, 0, 0, m->issub ? m->next : 0); ++ ++ } else { ++ return sial_pushmac(m); ++ } ++ return 1; ++ ++ } ++ return 0; ++ ++} ++ ++/* ++ Skip an unsupported preprocessor directive. ++*/ ++void ++sial_skip_directive(void) ++{ ++ sial_free(sial_getline()); ++} ++ ++void ++sial_undefine(void) ++{ ++int c; ++int i=0; ++char mname[MAX_SYMNAMELEN+1]; ++mac_t *m; ++ ++ /* skip all white spaces */ ++ while((c=sial_input()) == ' ' || c == '\t') if(c=='\n' || !c) { ++ ++ sial_error("Macro name expected"); ++ } ++ ++ mname[i++]=c; ++ ++ /* get the constant or macro name */ ++ while((c=sial_input()) != ' ' && c != '\t') { ++ ++ if(c=='\n' || !c) break; ++ if(i==MAX_SYMNAMELEN) break; ++ mname[i++]=c; ++ } ++ mname[i]='\0'; ++ if((m=sial_getmac(mname, 1))) sial_freemac(m); ++ else sial_addneg(mname); ++} ++ ++/* ++ This one is called from the lexer after #define as been detected ++*/ ++void ++sial_define(void) ++{ ++int c; ++int i=0; ++char mname[MAX_SYMNAMELEN+1]; ++ ++ /* skip all white spaces */ ++ while((c=sial_input()) == ' ' || c == '\t') if(c=='\n' || !c) goto serror; ++ ++ mname[i++]=c; ++ ++ /* get the constant or macro name */ ++ while((c=sial_input()) != ' ' && c != '\t' && c != '(') { ++ ++ if(c=='\n' || !c) break; ++ ++ if(i==MAX_SYMNAMELEN) break; ++ ++ mname[i++]=c; ++ } ++ mname[i]='\0'; ++ ++ /* does this macro have paraneters */ ++ /* If so, '(' will be right after name of macro. No spaces. */ ++ if(c=='(') { ++ ++ int np, nc, done; ++ char **pnames; ++ char curname[MAX_SYMNAMELEN+1]; ++ ++ np=nc=done=0; ++ pnames=(char **)sial_alloc(sizeof(char*)*MAXP); ++ ++ while(!done) { ++ ++ c=sial_input(); ++ ++ switch(c) { ++ case '\n': case 0: ++ goto serror; ++ ++ /* continuation */ ++ case '\\': ++ if(sial_input()!='\n') goto serror; ++ break; ++ ++ case ',': ++ if(!nc) goto serror; ++last: ++ curname[nc]='\0'; ++ pnames[np]=sial_alloc(strlen(curname)+1); ++ strcpy(pnames[np], curname); ++ nc=0; ++ np++; ++ break; ++ ++ case ')': ++ done=1; ++ if(nc) goto last; ++ break; ++ ++ case ' ': ++ case '\t': ++ break; ++ ++ default: ++ curname[nc++]=c; ++ break; ++ } ++ } ++ sial_newmac(mname, sial_getline(), np, pnames, 0); ++ return; ++ ++ } else if(c == '\n') { ++ ++ /* if nothing speciied then set to "1" */ ++ sial_newmac(mname, sial_strdup("1"), 0, 0, 0); ++ ++ } else { ++ ++ sial_newmac(mname, sial_getline(), 0, 0, 0); ++ } ++ ++ return; ++ ++serror: ++ ++ sial_error("Syntax error on macro definition"); ++} +--- crash/extensions/libsial/sialpp.l.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sialpp.l 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,85 @@ ++%{ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++ ++#define YY_NO_UNPUT ++%} ++ ++%{ ++#include ++ ++#ifdef linux ++#define YY_INPUT(buf,result,max_size) \ ++{ \ ++ int c = sial_input(); \ ++ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ ++} ++#endif ++ ++#include "sial.h" ++#include "sialpp.tab.h" ++#if linux ++#define yylval sialpplval ++#endif ++ ++#define retok(t) return(t) ++int nomacs=0; ++extern int sial_chkmacvar(char *); ++extern node_t *sial_newchar(void); ++%} ++ ++ABC [a-zA-Z_] ++ABCN [a-zA-Z0-9_] ++N [0-9] ++X [0-9a-fA-F] ++ ++%% ++ ++[ \t\n]+ { ; } ++ ++"defined" { retok(P_DEFINED); } ++"&&" { retok(P_BAND); } ++"||" { retok(P_BOR); } ++"<" { retok(P_LT); } ++"<=" { retok(P_LE); } ++"==" { retok(P_EQ); } ++">=" { retok(P_GE); } ++">" { retok(P_GT); } ++"!=" { retok(P_NE); } ++"|" { retok(P_OR); } ++"!" { retok(P_NOT); } ++"^" { retok(P_XOR); } ++">>" { retok(P_SHR); } ++"<<" { retok(P_SHL); } ++"+" { retok(P_ADD); } ++"-" { retok(P_SUB); } ++"/" { retok(P_DIV); } ++"%" { retok(P_MOD); } ++"*" { retok(P_MUL); } ++ ++(("0x"+){X}+[lL]*|{N}+[lL]*) { yylval.n = sial_newnum(yytext); retok(P_NUMBER); } ++ ++{ABC}{ABCN}* { ++ if(strlen(yytext) > MAX_SYMNAMELEN) { ++ ++ sial_error("Symbol name too long"); ++ } ++ if(nomacs || !sial_chkmacvar(yytext)) { ++ ++ yylval.n = sial_newvnode(yytext); ++ retok(P_VAR); ++ } ++ } ++ ++\'.\' { yylval.n = sial_makenum(B_SC, yytext[1]); retok(P_NUMBER); } ++\'\\.\' { yylval.n = sial_makenum(B_SC, sial_getseq(yytext[2])); retok(P_NUMBER); } ++ ++ ++. { retok(yylval.i = yytext[0]); } ++ ++%% ++#undef input ++#undef unput ++#define input() sial_input() ++#define unput(c) sial_unput(c) +--- crash/extensions/libsial/sial_node.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_node.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,69 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++/* ++ These function are use to allocate a new node. ++ It's a layer between the type specific functions and the parser. ++*/ ++#include "sial.h" ++#include ++ ++/* ++ Allocate a new node structure ++*/ ++node_t* ++sial_newnode() ++{ ++node_t*n; ++ ++ n = (node_t*) sial_calloc(sizeof(node_t)); ++ TAG(n); ++ return n; ++} ++ ++void ++sial_free_siblings(node_t*ni) ++{ ++ while(ni) { ++ ++ node_t*next=ni->next; ++ ++ NODE_FREE(ni); ++ ++ ni=next; ++ } ++} ++ ++/* ++ This function is called du ring compile time ++ to exevaluate constant expression, like sizeof() and ++ array sizes and enum constant. ++*/ ++value_t * ++sial_exenode(node_t*n) ++{ ++value_t *v; ++int *exval; ++jmp_buf exitjmp; ++void *sa; ++srcpos_t p; ++ ++ sial_curpos(&n->pos, &p); ++ sa=sial_setexcept(); ++ ++ if(!setjmp(exitjmp)) { ++ ++ sial_pushjmp(J_EXIT, &exitjmp, &exval); ++ v=NODE_EXE(n); ++ sial_rmexcept(sa); ++ sial_popjmp(J_EXIT); ++ ++ } else { ++ ++ sial_rmexcept(sa); ++ return 0; ++ ++ } ++ sial_curpos(&p, 0); ++ return v; ++} +--- crash/extensions/libsial/README.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/README 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,1024 @@ ++ ++ SIAL : Simple Image Access Language ++ =================================== ++ ++ Sial is a C interpreter that permits easy access to the symbol ++and type information stored in a executable image like a coredump or live ++memory interfaces (e.g. /dev/kmem, /dev/mem). It support a FULL C ++syntax and the same variable and function scope and type. Symbols and ++type information in the image become standard variables and types in ++the sial scripts's context. ++ ++ This README focuses on the differences between sial and a C compiler ++so, for C syntax information, please refer to a C reference manual. I ++also explain the mechanisms of the API that allow sial to be inserted ++into any debugging tool that deals with objects and can furnish symbol ++and type information to sial through the API. The more specific lcrash ++sial implementation is described and a howto on creating commands is ++also given here. ++ ++Preprocessor commands ++--------------------- ++ ++ All preprocessor commands I knew of are supported. #define, ++ #undef, #ifdef, #if, #ifndef, #else, #elif, #endif and #include. ++ ++ This one is ignored: #ident #pragma ++ ++ Sial has a builtin secondary parser for preprocessor expression ++ evaluation. ++ ++Symbols ++------- ++ ++ The symbols from the system image and their associated value ++ are available from within sial. Since, most of the time, no ++ type information is associated with the symbols, a reference to ++ a symbol return the actual address of the symbol within the ++ system image. So you might say that sial adds a level of ++ reference for symbol information. Let's say there is a (int) ++ symbol called "nproc" that contains the number of processes ++ currently running on the system. To get the value of nproc from ++ sial one would have to write something like this: ++ ++ void ++ showprocs() ++ { ++ int i; ++ int np; ++ ++ np=*(int*)nproc; ++ ++ for(i=0;ip_next) ++ ++ do something... ++ } ++ } ++ ++Variable Initialization ++-------------------- ++ ++ Variable assignment at the time of declaration is supported. ++ Also, the function __init() will be executed, if it is defined, ++ right after the macro file as been compiled. ++ ++ Using an uinitialized variable will generate a run time error. ++ ++Variable types ++-------------- ++ ++ All types made available from the API can be used. ++ These are the types already defined in the executable image. ++ ++ Floating point types are not supported at this time. I have no ++ plan to support it. ++ ++ Declaration of arrays is not supported. To access a array from ++ the image, use a pointer. ++ ++ Unions and structures type declarations are supported and can be ++ used to create additional types that become available within ++ the same macro file. ++ ++ Typedef are supported. ++ ++ Function pointers are not supported (use 'string' type instead, ++ see "Operators" below) ++ ++ Sial defines a 'string' types to handle ansi C strings within ++ the interpreter. This string type also support some of the ++ operator (+, ==, =, !=, etc... see below) ++ ++Variable Scope ++-------------- ++ ++ All symbols available in the system image become global ++ variable in the sial context. ++ ++ Variable declared within sial can be given one of 3 different ++ scopes like in normal C. ++ ++ GLOBAL: A variable that is declared outside a ++ function that is not marked as static. this variable if ++ available to all macros that have been load into the ++ interpreter. ++ ++ Ex: file1: ++ ++ int global; int func() { } ++ ++ file2: ++ ++ func2() ++ { ++ int i; ++ ++ i=global; ++ } ++ ++ NB: since sial currently validates variable existence only at ++ run time there is no need to declare a 'global' as an 'extern' ++ variable. At run time, if none of the currently loaded macros ++ define a global variable called 'global' then 'i=global' will ++ fail with a 'unknown variable' error. ++ ++ FILE: A Variable that is declared outside any ++ functions that is tagged with the static keyword. This ++ variables is available to all functions defined in the ++ same macro file. ++ ++ Ex: ++ ++ file1: ++ ++ static int maxproc=100; ++ sraruc int array; ++ ++ __init() ++ { ++ int i; ++ ++ for(i=0;i<10;i++) array[i]=-1; ++ ++ } ++ ++ void func1() ++ { ++ int i; ++ ++ for(i=0;i, <=, >=. ++ ++ examples: ++ ++ s = "hello" + "World"; ++ ++ if("hello" == "world" ) { ... } ++ ++ The 'in' operator is supported on dynamic arrays. ++ ++ if(i in array) { ... } ++ str=i in array ? "yes" : "no"; ++ ++ Function callbacks ++ ------------------ ++ ++ Function calls through function pointers is not possible ++ currently. Instead, use a 'string' type to achieve the same ++ result. When sial is about to perform a call, it will look at ++ the type of the variable used to name the function. If the type ++ is 'string' it will use the value that string and call that ++ function instead. ++ ++ func0(int i, int j) ++ { ++ printf("i=%d j=%d\n", i,j); ++ } ++ func1(string func) ++ { ++ func(1,2); ++ } ++ main() ++ { ++ func1("func0"); ++ } ++ ++ In the above example, func1() ends up calling func0() not ++ func. This can be used as a callback mechanism, specially ++ useful for creating generating function that call s linked list ++ of objects and calls a variable function for each object. Like ++ a function that walks tasks or procs. ++ ++ The sizeof() operator is evaluated at execution time. So you ++ can supply a type, a variable, or any valid expression and the ++ appropriate size will be returned. The expression *will* be ++ executed, so be careful. ++ ++Statements ++---------- ++ ++ All C statements except 'goto' are supported. ++ ++ The 'for(i in array)' is supported on dynamic arrays. ++ ++Dynamic arrays ++------------------------------- ++ ++ When indexing through a non pointer variable you end up ++ creating a dynamic array. ++ ++ Example: ++ ++ int func() ++ { ++ char *cp, c; ++ int array, i; ++ ++ cp=(char *)symbol; ++ c=cp[10]; ++ ++ array[0]="one string"; ++ array[12]="second string"; ++ ++ for(i in array) { ++ ++ printf("array[%d]=%s\n", i, array[i]); ++ } ++ ++ } ++ ++ In the 'c=cp[10]' statement, sial goes to the system image to ++ get one 'char' at the address symbol+10. ++ ++ In the second case, 'array' is not a pointer, it's a 'int'. So ++ sial threats all indexing through it as dynamic. ++ ++ Additionally, sial supports multi levels of dynamic index, ++ which makes possible to create random trees of indexed values: ++ ++ int func() ++ { ++ int array, i, j; ++ ++ array[10]="array10"; ++ array[10][3]="array10,3"; ++ array[20]="array20"; ++ array[20][99]="array20,99"; ++ ++ for(i in array) { ++ ++ printf("array[%d]=%s\n", i, array[i]); ++ ++ for(j in array[i]) { ++ ++ printf("array[%d][%d]=%s\n", i, j, array[i][j]); ++ ++ } ++ ++ } ++ } ++ ++ I think it is a good thing to have, since system image access ++ and analysis require frequent lists search. So for example, ++ with dynamic arrays, one can walk the proc list taking note of ++ the proc*, then walking a user thread list taking note of the ++ thread* and gathering some metrics for each of these threads. ++ In order to get to these metrics at some later point in the ++ execution, something like this could be used: ++ ++ func() ++ { ++ proc *p; ++ ++ for(p in procs) { ++ ++ thread *t; ++ ++ for(t in procs[p]) { ++ ++ int rss, size; ++ ++ /* we use index 0 for rss and 1 for size */ ++ printf("proc %p, thread %p, rss:size = %d:%d\n" ++ , p, t, procs[p][t][0], procs[p][t][1]); ++ } ++ } ++ } ++ ++ Arrays are always passed by reference. On creation the ++ reference count is set to one. So this array will exist ++ untill the variable it's assigned to dies. ++ ++ Arrays can be created by dso's. See the DSo section below for more ++ information and examples of this. ++ ++ ++Sial API ++-------- ++ ++ Sial can be integrated into any tool that needs to access ++ symbol and type information from some object. Currently it is ++ integrated in lcrash and icrash (tools that access Linux and ++ Irix kernel images respectively), but it should be possible to ++ use it, for example, in dbx or gdb. The API gives a simple ++ interface through which the host application sends symbol and ++ type (including member) information and gives access to the ++ image itself so that sial can read random blocks of data from ++ the image. ++ ++ >> sial_builtin(bt *bt) ++ ++ Install some set of builtin function. See below ++ (builtin api). ++ ++ ++ >> sial_chkfname(char *fname, int silent); ++ ++ Will check for the exsistance of a function in sial. ++ Typically used to check xtra entry points before the ++ application registers a new command (see sial_setcallback). ++ ++ >> sial_open(): ++ ++ The first function that should be called is sial_open(). ++ sial_open() will return a value of 1 if everything is ok or 0 ++ in case of some problem. This call initializes internal date ++ for the sial package. ++ ++ >> sial_setapi(apiops* ops, int nbytes): ++ ++ This function will setup the callbacks that sial will use ++ to get information from the application. ++ ++ See 'callback interface' below. ++ ++ >> sial_load(char *name); ++ ++ To have sial load and compile a macro or a set of macro ++ use sial_load(). Parameter name gives the name of the ++ file to compile. If name points to a directory instead, ++ then all the files in this directory will be load. So ++ an application would call sial_load() when it first ++ starts up specifying some well known files or ++ directories to load. For example $HOME/.xxx and ++ /etc/xxx would be loaded, ~/.xxx containing user ++ defined macros, and /etc/xxx containing system macros. ++ ++ >> sial_unload(char *funcname) ++ ++ To unload the a macro file use this function. ++ "funcname" is the name of any global function in the ++ file you want to unload. ++ ++ >> void sial_setcallback(void (*scb)(char *)); ++ ++ To be called prior to any load calls. ++ After each loads, sial will call this function ++ back with the name of each functions compiled. ++ Typicly, the application will then perform checks ++ and potencially install a new command for this ++ function. ++ ++ ex: ++ void ++ reg_callback(char *name) ++ { ++ char fname[MAX_SYMNAMELEN+sizeof("_usage")+1]; ++ _command_t cmds[2]; ++ ++ snprintf(fname, sizeof(fname), "%s_help", name); ++ if(!sial_chkfname(fname, 0)) return; ++ snprintf(fname, sizeof(fname), "%s_usage", name); ++ if(!sial_chkfname(fname, 0)) return; ++ ++ cmds[0].cmd=strdup(name); ++ cmds[0].real_cmd=0; ++ cmds[0].cmdfunc=run_callback; ++ cmds[0].cmdparse=parse_callback; ++ cmds[0].cmdusage=usage_callback; ++ cmds[0].cmdhelp=help_callback; ++ cmds[1].cmd=0; ++ unregister_cmd(cmds[0].cmd); ++ (void)register_cmds(cmds); ++ } ++ ++ >> sial_setipath(char *path) ++ ++ When sial processes a #include directive it will use ++ the specified path as a search path. ++ The usual PATH format is supported ex: ++ "/etc/include:/usr/include". ++ ++ >> sial_setmpath(char *path) ++ ++ When sial_load() is called with a relative path name or ++ just the name of a file, it will use a search PATH to ++ locate it. The path parameter to sial_set,path() sets ++ this path. The usual PATH format is supported ex: ++ "/etc/xxx:/usr/lib/xxx". ++ ++ >> sial_setofile(FILE *ofile) ++ ++ All output of sial commands will be send to file ofile. ++ ++ >> sial_cmd(char *cmd, char **argv, int nargs) ++ ++ This is the way to execute a sial command that as been ++ loaded. 'cmd' is the name of function to call. 'argv' ++ are the argument to this function. 'nargs' is the ++ number of argument in array 'argv'. ++ ++ Sial_cmd() will process argv and make the corresponding ++ values available to the function by creating global ++ variables that the function can test and use. ++ ++ >> sial_showallhelp() ++ ++ This command will send a complete list of the commands ++ long with the usage and help for each one of them. This ++ function should be called when the user request ++ something like 'help all'. ++ ++ >> sial_showhelp(char *func) ++ ++ This will display the help information for a particular ++ function loaded in sial. ++ ++The callback interface ++---------------------- ++ ++ Everytime sial needs some piece of information, it will call ++ the application back for it. THe sial_apiset() function is used ++ to install this callback interface into sial. Here is the list ++ of callback functions: ++ ++ typedef unsigned long long ull; ++ ++ Sial_apiset() passes this structure to sial: ++ ++ typedef struct { ++ ++ int (*getmem)(ull, void *, int); ++ int (*putmem)(ull, void *, int); ++ int (*member)(char *, ull, type * , member *); ++ int (*getctype)(int ctype, char * , type*); ++ char* (*getrtype)(ull, type *); ++ int (*alignment)(ull); ++ int (*getval)(char *, ull *); ++ enum_t* (*getenum)(char *name); ++ def_t* (*getdefs)(); ++ uint8_t (*get_uint8)(void*); ++ uint16_t (*get_uint16)(void*); ++ uint32_t (*get_uint32)(void*); ++ uint64_t (*get_uint64)(void*); ++ } apiops; ++ ++ ++ The apiops* struct defines the following member and function pointers: ++ ++ -getmem(ull addr, void *buffer, int nbytes) ++ ++ Read nbytes from image at virtual address addr (32 or ++ 64 bit) to buffer. ++ ++ -putmem(ull addr, void *buffer, int nbytes) ++ ++ Write nbytes from buffer to image at virtual address ++ addr (32 or 64 bit). ++ ++ -member(char *name, ull pidx, type *tm, member *m); ++ ++ Get information on a structure member called name. ++ Pidx is a unique type index for the parent structure. ++ The getctype() function should fill in this index in ++ it's type*. The dwarf model uses unique indexes (die ++ offsets) that can be used here. 'tm' will hold ++ information on the type of the member. 'm' will hold ++ information on the member specific stuff (bit sizes, ++ bit offset etc.). ++ ++ Use the sial_member_...() functions to setup m. ++ Use the sial_type_...() functions to setup t. ++ ++ -getctype(int ctype, char *name, type *tout) ++ ++ Get type information for a complex type. Ctype ++ specifies that name is a type of type struct/union or ++ enum. tout contain the returned type information. ++ ++ -getrtype(ull idx, type *t) ++ ++ Gets the type string linked to a typedef. For example, ++ the gettdeftype() would return ++ "unsigned long long". This enables sial to drill down a ++ typedef (a typedef can be build from a typedef ++ itself) in order to perform proper type validation for ++ assignment or function parameters or return values. ++ ++ -getval(char *sname, ull *value) ++ ++ Returns the value of symbol "sname" from the image. The ++ value is returned in 'value'. On any image this is ++ address of the symbol within the image itself, not the ++ value of the symbol itself. See explanation of this ++ above. ++ ++ -getenum(char *name); ++ ++ Return a list of enum values. ++ Sial will make these available as symbol for the duration ++ of the compile. ++ ++ -getdefs() ++ ++ Return a list of #defines to be active througout the ++ sial session. ++ ++ -get_uint8/16/32/64() ++ ++ Return converted unsigned integers. As parameters are passed pointers ++ to unsigned int values in dump representation. The return values are ++ the corresponding unsigned int values in the representation of the ++ host architecture, where sial is running. ++ ++The builtin API ++--------------- ++ ++ Sometime it is necessary to create a C function that will ++ handle some piece of the work, that a macro cannot do. Sial's ++ builtin function are implemented this way. Generic function ++ like 'printf' or 'getstr' can get some parameter input from the ++ macros and do something (printf) or they get some information, ++ map it to a sial value and return it to a macro (getstr). ++ ++ ++ Sial can load new functiosn from DSOs. If the extension of ++ a file name is ".so" then sial opens it and gets a list ++ of function specs from it. Unload of that file will ++ uninstall these functions. ++ ++ The API between the dso and sial is quite simple at this time. ++ It has not been exercised as must as it would need to, so it ++ might get more flexible and thus complex in the future. ++ ++ Here are two examples of simple extensions. ++ ++ This is an example of a simple extension. An equivalent ++ os the "hello world" C program, but this one gets 2 parameters ++ , one int and one string and returns the received int. ++ ++ #include "sial_api.h" ++ ++ value * ++ helloworld(value *vi, value *vs) ++ { ++ int i=sial_getval(vi); ++ char *s=(char*)sial_getval(vs); ++ ++ sial_msg("Hello to the world![%d] s=[%s]\n", i, s); ++ return sial_makebtype(1); ++ } ++ ++ BT_SPEC_TABLE = { ++ { "int hello(int i, string s)", helloworld}, ++ { 0, 0} ++ }; ++ ++ static char *buf; ++ ++ BT_INIDSO_FUNC() ++ { ++ sial_msg("Hello world being initialized\n"); ++ buf=sial_alloc(1000); ++ return 1; ++ } ++ ++ BT_ENDDSO_FUNC() ++ { ++ sial_msg("Hello world being shutdown\n"); ++ sial_free(buf); ++ } ++ ++ The BT_SPEC_TABLE is scanned. It's a simple table ++ with 2 entries per functions and terminated with ++ a NULL prototype. ++ ++ The DSO initializer function is called. ++ If it returns 0 then installtion is terminates. ++ If it returns 1 we proceed forward. ++ ++ The prototype is compiled and a syntax error ++ will send the error message to the application ++ output file (stdout usually). ++ ++ When the prototype as compiled with no errors ++ the function is installed and ready to be used from ++ sial macros. ++ ++ Type checking is performed by sial at ++ execution time on both, the function parameters ++ andthe function return. ++ ++ DSO's can also receive, create and manipulate dynamic arrays. ++ Here is an example of this: ++ ++ #include "sial_api.h" ++ ++ #ifdef ARRAY_STATIC ++ static value *v; ++ #endif ++ ++ value * ++ mkarray(value* vi) ++ { ++ int i=sial_getval(vi); ++ #ifndef ARRAY_STATIC ++ value *v=sial_makebtype(0); ++ #endif ++ ++ sial_msg("Received value [%d]\n", i); ++ /* build an array indexed w/ int w/ 2 string values */ ++ sial_addvalarray(v, sial_makebtype(0) ++ , sial_makestr("Value of index 0")); ++ sial_addvalarray(v, sial_makebtype(2) ++ , sial_makestr("Value of index 2")); ++ #if ARRAY_STATIC ++ /* ++ For a static array use : ++ Then the array will persist until you Free it. ++ */ ++ sial_refarray(v, 1); ++ #endif ++ return v; ++ } ++ ++ value * ++ showstrarray(value* va) ++ { ++ value *v1=sial_strindex(va, "foo"); ++ value *v2=sial_strindex(va, "goo"); ++ ++ printf("array[1]=%d\n", sial_getval(v1)); ++ printf("array[2]=%d\n", sial_getval(v2)); ++ sial_addvalarray(va, sial_makestr("gaa"), sial_makebtype(3)); ++ sial_addvalarray(va, sial_makestr("doo"), sial_makebtype(4)); ++ sial_freeval(v1); ++ sial_freeval(v2); ++ return sial_makebtype(0); ++ } ++ ++ value * ++ showintarray(value* va) ++ { ++ value *v1=sial_intindex(va, 1); ++ value *v2=sial_intindex(va, 2); ++ ++ printf("array[1]=%d\n", sial_getval(v1)); ++ printf("array[2]=%d\n", sial_getval(v2)); ++ sial_freeval(v1); ++ sial_freeval(v2); ++ return sial_makebtype(0); ++ } ++ ++ BT_SPEC_TABLE = { ++ { "int mkarray(int i)", mkarray}, ++ { "void showintarray(int i)",showintarray}, ++ { "void showstrarray(int i)",showstrarray}, ++ { 0, 0} ++ }; ++ ++ static char *buf; ++ ++ BT_INIDSO_FUNC() ++ { ++ sial_msg("mkarray initialized\n"); ++ #ifdef ARRAY_STATIC ++ /* we will need a static value to attach the ++ array too */ ++ v=sial_makebtype(0); ++ #endif ++ return 1; ++ } ++ ++ BT_ENDDSO_FUNC() ++ { ++ sial_msg("mkarray being shutdown\n"); ++ #ifdef ARRAY_STATIC ++ sial_freeval(v); ++ /* freing the value decrements the reference ++ count by one. So, if none of the calling ++ macros copied the value to a static ++ sial variable, it will free the array */ ++ #endif ++ } ++ ++Macro Construction ++------------------ ++ ++ When sial as been integrated into an application and a basic ++ set of builtin command as been created, it is time to start ++ creating the macro themselves. Some basic rules and conventions ++ apply to macro construction that make the coding and ++ documenting steps of macro definition easy. ++ ++ I will use the function foo as an example. Function foo is ++ defined in file /usr/tmp/sial/foo. Function foo is a user ++ callable function, meaning that it can be executed by the ++ sial_cmd() function. The command input section of the ++ application can thus call sial_cmd("foo", char *argv, int ++ nargs) to execute the foo macro. ++ ++ ------------ file foo ------------- ++ ++ foo_opt(){ return "ab:c"; } ++ ++ foo_usage(){ return "[-a] [-b barg] [-c] addr [addr [addr...]]"; } ++ ++ foo_help(){ return "This is an example function"; } ++ ++ static int ++ doproc(proc_t *p) ++ { ++ printf("p=0x%p\n", p); ++ } ++ ++ int foo() ++ { ++ int all, i; ++ string barg; ++ ++ if(exists(aflag)) all=1; ++ else all=0; ++ ++ if(exists("bflag")) bval=barg; ++ ++ for(i in argv) { ++ ++ proc_t *p; ++ ++ p=(proc_t*)atoi(argv[i], 16); ++ ++ doproc(p); ++ ++ } ++ } ++ ++ ------------ end of file foo -------------- ++ ++ The application calls sial_load() to load foo. Sial calls ++ back the application with the names of all fucntions declared ++ in that file. The aplication can then register commands for ++ the user to type according to this list of functions. ++ In this case 'foo'. ++ ++ The application then uses sial_cmd() to run a specific ++ command 'foo'. ++ ++ Before executing the command, sial checks if a foo_opt() ++ function exists and if so, calls it. This function returns the ++ proper getopt() argument specification string. If this function ++ does not exists then all arguments are passed down to the foo() ++ function directly. ++ ++ If the arguments supplied by the user do not follow the proper ++ syntax then the function foo_usage() will be called, if it ++ exists. If the foo_usage() function does not exists, a generic ++ error message is generated by sial. ++ ++ If the command 'help foo' is issued, the application should be ++ calling sial_exefunc("help_foo", 0) whish will return a VALUE_S ++ for the help for foo. Or whatever foo_help() returns. ++ ++ Each option, their associated value and addition arguments are ++ made available to the foo funtion by creating the following ++ global variables before the actual call. ++ ++ Each option, if specified, will trigger the existence of flag ++ variable. In the foo() case, this means that variables aflag, ++ bflag and cflag can possibly exist. The function ++ exists("variable name") can then be used to test for this ++ option presence. ++ ++ If an option has an associated value (getopt's ':' is specified ++ on the string foo_opt() returns) this value is made available ++ as a string type variable called Xarg, where X is the option ++ letter. In the case of foo() variable 'string barg' would exist ++ if the -b option was supplied by the user. ++ ++ The rest of the arguments supplied by the user are made ++ available in an array of 'string' called argv. argv[0] is ++ set to the name of the function 'foo' and argc is a global ++ that defines how many argv their are. ++ ++Builtin functions ++================= ++ ++ Here is a description of the current set of builtin functions. ++ ++ unsigned long long ++ atoi(string value [, int base]) ++ ++ Convert a string value to a long long. Base is the base ++ that should be used to process the string e.g. 8, 10 or ++ 16. If not specified, then the standard numeric format ++ will be scnanned for ex: ++ ++ 0x[0-9a-fA-F]+ : hexadecimal ++ 0[0-7]+ : octal ++ [1-9]+[0-9]* : decimal ++ ++ This function is used when converting command line ++ arguments to pointers. ++ ++ Example: ++ ++ void ++ mycommand() ++ { ++ int i; ++ ++ for(i=1;i> mycommand 0xa80000004ab14578 ++ ++ int exists(string name) ++ ++ Checks for the existance of a variable. Returns 1 if ++ the variables does exist and 0 otherwise. This function ++ is mostly used to test if some options were specified ++ on when the macro was executed from command line. ++ ++ It can also be used to test for image variable. ++ ++ example: ++ ++ void ++ mycommand() ++ { ++ if(exists("aflag")) { ++ ++ // user has specified -a option ++ } ++ } ++ ++ void exit() ++ ++ Terminate macro excution now. ++ ++ int getchar() ++ ++ Get a single character from tty. ++ ++ string gets() ++ ++ Get a line of input from tty. ++ ++ string getstr(void *) ++ ++ Gets a null terminated string from the image at the ++ address specified. Sial will read a series of 16 byte ++ values from the image untill it find the \0 character. ++ Up to 4000 bytes will be read this way. ++ ++ string getnstr(void *, int n) ++ ++ Gets n characters from the image at the specified ++ address and returns the corresponding string. ++ ++ string itoa(unsigned long long) ++ ++ Convert a unsigned long long to a decimal string. ++ ++ void printf(char *fmt, ...); ++ ++ Send a formatted message to the screen or output file. ++ For proper allignment of output on 32 and 64 bit systems ++ one can use the %> sequence along with the %p format. ++ ++ On a 32 bit system %p will print a 8 character hexadecimal ++ value and on a 64 bit system it will print a 16 character ++ value. So, to get proper alignment on both type of systems ++ use the %> format which will print nothing on a 64 bit system ++ but will print 8 times the following character on a 32 bit ++ system. ++ ++ example: ++ ++ struct proc *p; ++ ++ printf("Proc %> uid pid\n"); ++ printf("0x%p %8d %8d\n" ++ , p, p->p_uid,p_p_pid); ++ ++ int sial_depend(string file) ++ ++ Loads a macro or directory of macros called ++ 'file'. Contrary to sial_load() it will not ++ give any error messages. Returns 1 on success ++ 0 otherwise. ++ ++ int sial_load(string file) ++ ++ Loads and compiles a sial macro file. ++ returns 1 if successful or 0 otherwise. ++ ++ void sial_unload(string file) ++ ++ Unload's a sial macro file ++ ++ string sprintf(string format, ...) ++ ++ Creates a string from the result of a sprintf. ++ Example: ++ ++ void ++ mycommand() ++ { ++ ++ string msg; ++ ++ msg=sprintf("i=%d\n", i); ++ } ++ The result will be truncated to maxbytes if it would be ++ longer. ++ ++ int strlen(string s) ++ ++ Return the length of string s. ++ ++ string substr(string s, int start, int len) ++ ++ Creates a string from the substring starting a charcater ++ 'start' of 's' for 'len' characters. ++ example: ++ ++ s=substr("this is the original", 6, 2); ++ ++ So 's' will become "is". ++ ++ ++ -------------------------------------------------------- ++ ++Questions/Comments ++Luc Chouinard, lucchouina@yahoo.com +--- crash/extensions/libsial/sial_op.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_op.c 2007-11-09 15:46:25.000000000 -0500 +@@ -0,0 +1,904 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include "sial.h" ++#include "sial.tab.h" ++#include ++#include ++ ++#define MAXPARMS 10 ++ ++typedef struct { ++ ++ int op; /* operator */ ++ int np; /* number of operands */ ++ node_t*parms[MAXPARMS]; /* operands */ ++ ++ srcpos_t pos; ++ ++} oper; ++ ++#define P1 (o->parms[0]) ++#define P2 (o->parms[1]) ++#define P3 (o->parms[2]) ++#define P4 (o->parms[3]) ++ ++#define V1 (v1?v1:(v1=NODE_EXE(P1))) ++#define V2 (v2?v2:(v2=NODE_EXE(P2))) ++#define V3 (v3?v3:(v3=NODE_EXE(P3))) ++#define V4 (v4?v4:(v4=NODE_EXE(P4))) ++ ++#define L1 (unival(V1)) ++#define L2 (unival(V2)) ++#define L3 (unival(V3)) ++#define L4 (unival(V4)) ++ ++#define S1 ((V1)->v.data) ++#define S2 ((V2)->v.data) ++#define S3 ((V3)->v.data) ++#define S4 ((V4)->v.data) ++ ++void sial_do_deref(int n, value_t *v, value_t *ref); ++ul ++sial_bool(value_t *v) ++{ ++ switch(v->type.type) { ++ ++ case V_BASE: ++ switch(v->type.size) { ++ case 1: return !(!(v->v.uc)); ++ case 2: return !(!(v->v.us)); ++ case 4: return !(!(v->v.ul)); ++ case 8: return !(!(v->v.ull)); ++ default: sial_error("Oops sial_bool()[%d]", v->type.size); break; ++ } ++ case V_STRING : return !(!(*((char*)(v->v.data)))); ++ case V_REF: return sial_defbsize()==8?(!(!(v->v.ull))):(!(!(v->v.ul))); ++ default : ++ ++ sial_error("Invalid operand for boolean expression"); ++ return 0; ++ } ++} ++ ++static int cops[]={BAND,BOR,NOT,LT,LE,EQ,GE,GT,NE,CEXPR}; ++#define NCOPS (sizeof(cops)/sizeof(cops[0])) ++ ++static int ++is_cond(int op) ++{ ++int i; ++ ++ for(i=0;itype)) { ++ ++ case 1: v1->v.uc=rl; break; ++ case 2: v1->v.us=rl; break; ++ case 4: v1->v.ul=rl; break; ++ case 8: v1->v.ull=rl; break; ++ ++ } ++ /* the result of an assignment cannot be a lvalue_t */ ++ v1->set=0; ++} ++ ++#define anyop(t) (V1->type.type==t || (o->np>1 && V2->type.type==t)) ++ ++typedef struct { ++ node_t*index; ++ node_t*var; ++ srcpos_t pos; ++} index_t ; ++ ++static value_t * ++sial_exeindex(index_t *i) ++{ ++value_t *var; ++value_t *vi=NODE_EXE(i->index); ++value_t *v; ++srcpos_t p; ++ ++ sial_curpos(&i->pos, &p); ++ ++ /* we need to make believe it's been initiazed */ ++ sial_setini(i->var); ++ var=NODE_EXE(i->var); ++ ++ /* check the type of the variable */ ++ /* if it's a pointer then index through the image */ ++ if(var->type.type==V_REF) { ++ ++ int size; ++ int n=sial_getval(vi); ++ value_t *ref; ++ ++ /* if this is an array and we're not at the rightmost index */ ++ if(var->type.idxlst && var->type.idxlst[1]) { ++ ++ int i, size=var->type.size; ++ ++ v=sial_cloneval(var); ++ ++ v->type.idxlst[0]=0; ++ for(i=1; var->type.idxlst[i]; i++) { ++ ++ size *= var->type.idxlst[i]; ++ v->type.idxlst[i]=var->type.idxlst[i+1]; ++ } ++ ++ if(sial_defbsize()==4) { ++ ++ v->v.ul+=size*n; ++ v->mem=v->v.ul; ++ ++ } else { ++ ++ v->v.ull+=size*n; ++ v->mem=v->v.ull; ++ } ++ ++ ++ } else { ++ ++ v=sial_newval(); ++ ref=sial_cloneval(var); ++ ++ if(var->type.ref==1) size=var->type.size; ++ else size=sial_defbsize(); ++ ++ if(sial_defbsize()==4) { ++ ++ ref->v.ul+=size*n; ++ ref->mem=ref->v.ul; ++ ++ } else { ++ ++ ref->v.ull+=size*n; ++ ref->mem=ref->v.ull; ++ } ++ sial_do_deref(1, v, ref); ++ sial_freeval(ref); ++ } ++ ++ } else { ++ ++ v=sial_newval(); ++ ++ /* use dynamic indexing aka awk indexing */ ++ sial_valindex(var, vi, v); ++ } ++ ++ /* discard expression results */ ++ sial_freeval(var); ++ sial_freeval(vi); ++ sial_curpos(&p, 0); ++ ++ return v; ++} ++ ++void ++sial_freeindex(index_t *i) ++{ ++ NODE_FREE(i->index); ++ NODE_FREE(i->var); ++ sial_free(i); ++} ++ ++node_t* ++sial_newindex(node_t*var, node_t*idx) ++{ ++index_t *i=sial_alloc(sizeof(index_t )); ++node_t*n=sial_newnode(); ++ ++ i->index=idx; ++ i->var=var; ++ n->exe=(xfct_t)sial_exeindex; ++ n->free=(ffct_t)sial_freeindex; ++ n->data=i; ++ sial_setpos(&i->pos); ++ return n; ++} ++ ++typedef struct { ++ node_t*fname; ++ node_t*parms; ++ srcpos_t pos; ++ void *file; ++} call; ++ ++static value_t * ++sial_execall(call *c) ++{ ++value_t *rv; ++srcpos_t p; ++ ++ sial_curpos(&c->pos, &p); ++ rv=sial_docall(c->fname, c->parms, c->file); ++ sial_curpos(&p, 0); ++ return rv; ++} ++ ++void ++sial_freecall(call *c) ++{ ++ NODE_FREE(c->fname); ++ sial_free_siblings(c->parms); ++ sial_free(c); ++} ++ ++node_t* ++sial_newcall(node_t* fname, node_t* parms) ++{ ++node_t*n=sial_newnode(); ++call *c=sial_alloc(sizeof(call)); ++ ++ c->fname=fname; ++ c->file=sial_getcurfile(); ++ c->parms=parms; ++ n->exe=(xfct_t)sial_execall; ++ n->free=(ffct_t)sial_freecall; ++ n->data=c; ++ sial_setpos(&c->pos); ++ return n; ++} ++ ++typedef struct { ++ node_t*expr; ++ srcpos_t pos; ++} adrof; ++ ++static value_t * ++sial_exeadrof(adrof *a) ++{ ++value_t *rv, *v=NODE_EXE(a->expr); ++ ++#if 0 ++ /* we can only do this op on something that came from system image ++ Must not allow creation of references to local variable */ ++ if(!v->mem) { ++ ++ sial_freeval(v); ++ sial_rerror(&a->pos, "Invalid operand to '&' operator"); ++ ++ } ++#endif ++ /* create the reference */ ++ rv=sial_newval(); ++ sial_duptype(&rv->type, &v->type); ++ sial_pushref(&rv->type, 1); ++ ++ /* remmember position in image */ ++ if(sial_defbsize()==8) rv->v.ull=v->mem; ++ else rv->v.ul=v->mem; ++ rv->mem=0; ++ ++ sial_freeval(v); ++ ++ return rv; ++} ++ ++void ++sial_freeadrof(adrof *a) ++{ ++ NODE_FREE(a->expr); ++ sial_free(a); ++} ++ ++node_t* ++sial_newadrof(node_t* expr) ++{ ++node_t*n=sial_newnode(); ++adrof *a=sial_alloc(sizeof(adrof)); ++ ++ a->expr=expr; ++ n->exe=(xfct_t)sial_exeadrof; ++ n->free=(ffct_t)sial_freeadrof; ++ n->data=a; ++ sial_setpos(&a->pos); ++ return n; ++} ++ ++static int ++sial_reftobase(value_t *v) ++{ ++int idx= v->type.idx; ++ ++ if(v->type.type==V_REF) { ++ ++ if(sial_defbsize()==4) ++ v->type.idx=B_UL; ++ else ++ v->type.idx=B_ULL; ++ } ++ return idx; ++} ++ ++static value_t* ++sial_docomp(int op, value_t *v1, value_t *v2) ++{ ++ ++ /* if one parameter is string then both must be */ ++ if(v1->type.type == V_STRING || v2->type.type == V_STRING) { ++ ++ if(v1->type.type != V_STRING || v2->type.type != V_STRING) { ++ ++ sial_error("Invalid condition arguments"); ++ } ++ else { ++ ++ switch(op) { ++ ++ case EQ: { /* expr == expr */ ++ ++ return sial_makebtype(!strcmp(v1->v.data, v2->v.data)); ++ ++ } ++ case GT: case GE: { /* expr > expr */ ++ ++ return sial_makebtype(strcmp(v1->v.data, v2->v.data) > 0); ++ ++ } ++ case LE: case LT: { /* expr <= expr */ ++ ++ return sial_makebtype(strcmp(v1->v.data, v2->v.data) < 0); ++ ++ } ++ case NE: { /* expr != expr */ ++ ++ return sial_makebtype(strcmp(v1->v.data, v2->v.data)); ++ ++ } ++ default: { ++ ++ sial_error("Oops conditional unknown 1"); ++ ++ } ++ } ++ } ++ ++ } ++ else { ++ ++ int idx1, idx2; ++ value_t *v=sial_newval(); ++ ++ /* make sure pointers are forced to proper basetype ++ before calling sial_baseop()*/ ++ idx1=sial_reftobase(v1); ++ idx2=sial_reftobase(v2); ++ ++ ++ switch(op) { ++ ++ case EQ: ++ case GT: ++ case GE: ++ case LE: ++ case LT: ++ case NE: ++ sial_baseop(op, v1, v2, v); ++ break; ++ default: { ++ ++ sial_error("Oops conditional unknown 2"); ++ ++ } ++ } ++ v1->type.idx=idx1; ++ v2->type.idx=idx2; ++ return v; ++ } ++ return 0; ++} ++ ++static value_t * ++sial_exeop(oper *o) ++{ ++value_t *v=0, *v1=0, *v2=0, *v3=0, *v4=0; ++int top; ++srcpos_t p; ++ ++ sial_curpos(&o->pos, &p); ++ ++ /* if ME (op on myself) operator, translate to normal operator ++ we will re-assign onto self when done */ ++ ++ top=getop(o->op); ++ ++ if(top == ASSIGN) { ++ ++ goto doop; ++ ++ } else if(top == IN) { ++ ++ /* the val in array[] test is valid for anything but struct/union */ ++ v=sial_makebtype((ull)sial_lookuparray(P1,P2)); ++ ++ } ++ else if(is_cond(top)) { ++ ++ /* the operands are eithr BASE (integer) or REF (pointer) */ ++ /* all conditional operators accept a mixture of pointers and integer */ ++ /* set the return as a basetype even if bool */ ++ ++ switch(top) { ++ ++ case CEXPR: { /* conditional expression expr ? : stmt : stmt */ ++ ++ if(sial_bool(V1)) { ++ ++ v=sial_cloneval(V2); ++ ++ } else { ++ ++ v=sial_cloneval(V3); ++ ++ } ++ ++ } ++ break; ++ case BOR: { /* a || b */ ++ ++ v=sial_makebtype((ull)(sial_bool(V1) || sial_bool(V2))); ++ ++ } ++ break; ++ case BAND: { /* a && b */ ++ ++ v=sial_makebtype((ull)(sial_bool(V1) && sial_bool(V2))); ++ ++ } ++ break; ++ case NOT: { /* ! expr */ ++ ++ v=sial_makebtype((ull)(! sial_bool(V1))); ++ ++ } ++ break; ++ default: { ++ ++ v=sial_docomp(top, V1, V2); ++ ++ } ++ } ++ ++ } else if(anyop(V_STRING)) { ++ ++ if(top == ADD) ++ { ++ char *buf; ++ ++ if(V1->type.type != V_STRING || V2->type.type != V_STRING) { ++ ++ sial_rerror(&P1->pos, "String concatenation needs two strings!"); ++ ++ } ++ buf=sial_alloc(strlen(S1)+strlen(S2)+1); ++ strcpy(buf, S1); ++ strcat(buf, S2); ++ v=sial_makestr(buf); ++ sial_free(buf); ++ } ++ else { ++ ++ sial_rerror(&P1->pos, "Invalid string operator"); ++ ++ } ++ } ++ /* arithmetic operator */ ++ else if(anyop(V_REF)) { ++ ++ int size; ++ value_t *vt; ++ ++ /* make sure we have the base type second */ ++ if(V1->type.type != V_REF) { vt=V1; v1=V2; v2=vt; } ++ ++ ++ if(V1->type.type == V_BASE) { ++inval: ++ sial_error("Invalid operand on pointer operation"); ++ } ++ ++ /* get the size of whas we reference */ ++ size=V1->type.size; ++ ++ switch(top) { ++ case ADD: { /* expr + expr */ ++ /* adding two pointers ? */ ++ if(V2->type.type == V_REF) goto inval; ++ ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) + L2 * size); ++ } ++ break; ++ case SUB: { /* expr - expr */ ++ /* different results if mixed types. ++ if both are pointers then result is a V_BASE */ ++ if(V2->type.type == V_REF) ++ v=sial_makebtype(L1 - L2); ++ ++ else { ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) - L2 * size); ++ } ++ } ++ break; ++ case PREDECR: { /* pre is easy */ ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) - size); ++ sial_setval(v1, v); ++ } ++ break; ++ case PREINCR: { ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) + size); ++ sial_setval(v1, v); ++ } ++ break; ++ case POSTINCR: { ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) + size); ++ sial_setval(v1, v); ++ sial_transfer(v, v1, unival(v1)); ++ } ++ break; ++ case POSTDECR: { ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) - size); ++ sial_setval(v1, v); ++ sial_transfer(v, v1, unival(v1)); ++ } ++ break; ++ default: ++ sial_error("Invalid operation on pointer [%d]",top); ++ } ++ } ++ else { ++ ++ /* both operands are V_BASE */ ++ switch(top) { ++ ++ /* for mod and div, we check for divide by zero */ ++ case MOD: case DIV: ++ if(!L2) { ++ sial_rerror(&P1->pos, "Mod by zero"); ++ } ++ case ADD: case SUB: case MUL: case XOR: ++ case OR: case AND: case SHL: case SHR: ++ { ++ sial_baseop(top, V1, V2, v=sial_newval()); ++ } ++ break; ++ case UMINUS: { ++ ++ value_t *v0=sial_newval(); ++ sial_defbtype(v0, (ull)0); ++ /* keep original type of v1 */ ++ v=sial_newval(); ++ sial_duptype(&v0->type, &V1->type); ++ sial_duptype(&v->type, &V1->type); ++ sial_baseop(SUB, v0, V1, v); ++ sial_freeval(v0); ++ /* must make result signed */ ++ sial_mkvsigned(v); ++ } ++ break; ++ case FLIP: { ++ ++ value_t *v0=sial_newval(); ++ sial_defbtype(v0, (ull)0xffffffffffffffffll); ++ /* keep original type of v1 */ ++ sial_duptype(&v0->type, &V1->type); ++ sial_baseop(XOR, v0, V1, v=sial_newval()); ++ sial_freeval(v0); ++ } ++ break; ++ case PREDECR: { /* pre is easy */ ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) - 1); ++ sial_setval(v1, v); ++ } ++ break; ++ case PREINCR: { ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) + 1); ++ sial_setval(v1, v); ++ } ++ break; ++ case POSTINCR: { ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) + 1); ++ sial_setval(v1, v); ++ sial_transfer(v, v1, unival(v1)); ++ } ++ break; ++ case POSTDECR: { ++ V1; ++ sial_transfer(v=sial_newval(), v1, ++ unival(v1) - 1); ++ sial_setval(v1, v); ++ sial_transfer(v, v1, unival(v1)); ++ } ++ break; ++ default: sial_rerror(&P1->pos, "Oops ops ! [%d]", top); ++ } ++ } ++doop: ++ /* need to assign the value_t back to P1 */ ++ if(top != o->op || top==ASSIGN) { ++ ++ /* in the case the Lvalue_t is a variable , bypass execution and set ini */ ++ if(P1->exe == sial_exevar) { ++ ++ char *name=NODE_NAME(P1); ++ var_t*va=sial_getvarbyname(name, 0, 0); ++ value_t *vp; ++ ++ sial_free(name); ++ ++ if(top != o->op) vp=v; ++ else vp=V2; ++ ++ sial_chkandconvert(va->v, vp); ++ ++ sial_freeval(v); ++ v=sial_cloneval(va->v); ++ va->ini=1; ++ ++ } else { ++ ++ if(!(V1->set)) { ++ ++ sial_rerror(&P1->pos, "Not Lvalue_t on assignment"); ++ ++ } ++ else { ++ ++ /* if it's a Me-op then v is already set */ ++ V1; ++ if(top != o->op) { ++ sial_setval(v1, v); ++ } else { ++ sial_setval(v1, V2); ++ v=sial_cloneval(V2); ++ } ++ ++ } ++ } ++ /* the result of a assignment if not an Lvalue_t */ ++ v->set=0; ++ } ++ sial_freeval(v1); ++ sial_freeval(v2); ++ sial_freeval(v3); ++ sial_freeval(v4); ++ sial_setpos(&p); ++ return v; ++} ++ ++void ++sial_freeop(oper *o) ++{ ++int i; ++ ++ for(i=0;inp;i++) NODE_FREE(o->parms[i]); ++ sial_free(o); ++} ++ ++node_t* ++sial_newop(int op, int nargs, ...) ++{ ++va_list ap; ++node_t*n=sial_newnode(); ++oper *o=sial_alloc(sizeof(oper)); ++int i; ++ ++ o->op=op; ++ o->np=nargs; ++ ++ sial_setpos(&o->pos); ++ ++ va_start(ap, nargs); ++ ++ for(i=0 ; iparms[i]=va_arg(ap, node_t*))) break;; ++ } ++ ++ n->exe=(xfct_t)sial_exeop; ++ n->free=(ffct_t)sial_freeop; ++ n->data=o; ++ ++ va_end(ap); ++ return n; ++} ++ ++/* mult is a special case since the parse always return a PTR token ++ for the '*' signed. The PTR token value_t is the number of '* found. ++*/ ++node_t* ++sial_newmult(node_t*n1, node_t*n2, int n) ++{ ++ if(n>1) { ++ ++ sial_error("Syntax error"); ++ } ++ return sial_newop(MUL, 2, n1, n2); ++} ++/* ++ This function is called when we want to set a value_t in live memory ++ using a pointer to it. ++*/ ++static void ++sial_setderef(value_t *v1, value_t *v2) ++{ ++ void *sial_adrval(value_t *); ++ sial_transval(v2->type.size, v1->type.size, v2, sial_issigned(v2->type.typattr)); ++ API_PUTMEM(v1->mem, sial_adrval(v2), v2->type.size); ++} ++ ++/* ++ Do a de-referencing from a pointer (ref) and put the result in v. ++*/ ++typedef struct { ++ int lev; ++ node_t*n; ++} ptrto; ++ ++void ++sial_do_deref(int n, value_t *v, value_t *ref) ++{ ++ull madr, new_madr; ++ ++ if(n > ref->type.ref) { ++ ++ sial_error("Too many levels of dereference"); ++ ++ }else { ++ ++ ++ if(sial_defbsize()==4) madr=(ull)ref->v.ul; ++ else madr=ref->v.ull; ++ ++ /* copy the target type to the returned value_t's type_t*/ ++ sial_duptype(&v->type, &ref->type); ++ ++ /* do a number of deferences according to PTR value_t */ ++ while(n--) { ++ ++ sial_popref(&v->type, 1); ++ ++ if(!v->type.ref) { ++ ++ /* make sure the pointer is pointing into the vmcore */ ++ if(is_ctype(v->type.type)) { ++ ++ v->v.data=sial_alloc(v->type.size); ++ sial_getmem(madr, v->v.data, v->type.size); ++ ++ } else { ++ ++ /* get the data from the system image */ ++ switch(TYPE_SIZE(&v->type)) { ++ ++ case 1: sial_getmem(madr, &v->v.uc, 1); ++ break; ++ case 2: sial_getmem(madr, &v->v.us, 2); ++ break; ++ case 4: sial_getmem(madr, &v->v.ul, 4); ++ break; ++ case 8: sial_getmem(madr, &v->v.ull, 8); ++ break; ++ ++ } ++ } ++ } ++ else { ++ ++ /* get the pointer at this address */ ++ if(sial_defbsize()==4) { ++ ++ sial_getmem(madr, &v->v.ul, 4); ++ new_madr=v->v.ul; ++ ++ } else { ++ ++ sial_getmem(madr, &v->v.ull, 8); ++ new_madr=v->v.ull; ++ } ++ } ++ ++ /* remember this address. For the '&' operator */ ++ v->mem=madr; ++ madr=new_madr; ++ } ++ } ++ ++ /* we can always assign to a reference */ ++ v->set=1; ++ v->setval=v; ++ v->setfct=sial_setderef; ++} ++ ++static value_t * ++sial_exepto(ptrto *pto) ++{ ++value_t *v=sial_newval(); ++int n=pto->lev; ++value_t *ref=NODE_EXE(pto->n); ++ ++ sial_do_deref(n, v, ref); ++ sial_freeval(ref); ++ return v; ++} ++ ++static void ++sial_freepto(ptrto *pto) ++{ ++ NODE_FREE(pto->n); ++ sial_free(pto); ++} ++ ++ ++/* same thing for the ptrto operator */ ++node_t* ++sial_newptrto(int lev, node_t*n) ++{ ++ptrto *pto=sial_alloc(sizeof(ptrto)); ++node_t*nn=sial_newnode(); ++ ++ pto->lev=lev; ++ pto->n=n; ++ nn->exe=(xfct_t)sial_exepto; ++ nn->free=(ffct_t)sial_freepto; ++ nn->data=pto; ++ return nn; ++} +--- crash/extensions/libsial/sial_func.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_func.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,1191 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sial.h" ++ ++/* ++ The next few functions manege the files and associated functions. ++*/ ++struct fdata; ++ ++typedef struct fctype_t { ++ int idx; ++ struct fctype_t*next; ++ ++} fctype_t; ++ ++typedef struct func { ++ ++ char *name; /* name of the function */ ++ var_t*varlist; /* parameters information */ ++ var_t*rvar; /* return value_t information */ ++ node_t*body; /* execution node for body */ ++ int local; /* load i.e. static ? */ ++ srcpos_t pos; /* source position of function declaration */ ++ struct fdata *file; /* back pointer to corresponding file */ ++ struct func *next; /* linked list */ ++ ++} func; ++ ++typedef struct fdata { ++ ++ char *fname; /* name of the file */ ++ int isdso; /* is this from a loadable module ? ++ `globs' becomes the handle */ ++ time_t time; /* load time */ ++ var_t*fsvs; /* associated list of static variables */ ++ var_t*fgvs; /* associated list of global variables */ ++ void *globs; /* handle for these globals */ ++ func *funcs; /* chained list of functions */ ++ fctype_t *ctypes; /* ctypes declared by this function */ ++ struct fdata *next; /* chained list of files */ ++ ++} fdata; ++ ++static fdata *fall=0; ++void sialparse(void); ++static func * sial_getfbyname(char *name, fdata *thisfd); ++value_t * sial_execmcfunc(func *f, value_t **vp); ++ ++ull ++sial_getval(value_t*v) ++{ ++ull ret=0; ++ ++ if(!v) return 0; ++ ++ /* need to cast properly here */ ++ if(v->type.type==V_BASE || v->type.type==V_REF) { ++ ++ if(v->type.type==V_REF || !sial_issigned(v->type.typattr)) { ++ ++ switch(TYPE_SIZE(&v->type)) { ++ case 1: ret= (ull) v->v.uc; break; ++ case 2: ret= (ull) v->v.us; break; ++ case 4: ret= (ull) v->v.ul; break; ++ case 8: ret= (ull) v->v.ull; break; ++ default: sial_error("Oops getval base"); ++ } ++ ++ } else { ++ ++ switch(TYPE_SIZE(&v->type)) { ++ case 1: ret= (ull) v->v.sc; break; ++ case 2: ret= (ull) v->v.ss; break; ++ case 4: ret= (ull) v->v.sl; break; ++ case 8: ret= (ull) v->v.sll; break; ++ default: sial_error("Oops getval base"); ++ } ++ } ++ } ++ /* in the case of a struct/union we pass a pointer to it */ ++ else ret = (unsigned long)v->v.data; ++ return ret; ++} ++ ++static int ++sial_dohelp(char *fname) ++{ ++char buf[MAX_SYMNAMELEN+1]; ++char *hstr; ++ ++ sprintf(buf, "%s_help", fname); ++ ++ if(sial_chkfname(buf, 0)) { ++ ++ char buf2[MAX_SYMNAMELEN+1]; ++ char *ustr; ++ ++ sprintf(buf2, "%s_usage", fname); ++ ustr=(char*)(unsigned long)sial_exefunc(buf2, 0); ++ sial_msg("COMMAND: %s %s\n\n", fname , ustr?ustr:""); ++ hstr=(char*)(unsigned long)sial_exefunc(buf, 0); ++ sial_format(1, hstr); ++ sial_format(0, "\n"); ++ sial_msg("\n"); ++ return 1; ++ } ++ return 0; ++} ++ ++void ++sial_showallhelp() ++{ ++fdata *filep; ++ ++ for(filep=fall; filep; filep=filep->next) { ++ ++ func *funcp; ++ ++ for(funcp=filep->funcs;funcp;funcp=funcp->next) { ++ ++ (void)sial_dohelp(funcp->name); ++ ++ } ++ } ++} ++ ++int ++sial_showhelp(char *fname) ++{ ++ return sial_dohelp(fname); ++} ++ ++void* ++sial_getcurfile() { return fall; } ++ ++int ++sial_isnew(void *p) ++{ ++fdata *fd=(fdata *)p; ++struct stat stats; ++ ++ if(!stat(fd->fname, &stats)) { ++ ++ if(stats.st_mtime > fd->time) { ++ ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++void * ++sial_findfile(char *name, int unlink) ++{ ++fdata *fd; ++fdata *last=0; ++ ++ for(fd=fall; fd; last=fd, fd=fd->next) { ++ ++ if(!strcmp(fd->fname, name)) { ++ ++ /* remove from the list ?*/ ++ if(unlink) { ++ ++ if(!last) fall=fd->next; ++ else last->next=fd->next; ++ ++ } ++ return fd; ++ } ++ ++ } ++ return 0; ++} ++ ++void ++sial_freefunc(func *fn) ++{ ++ sial_free(fn->name); ++ NODE_FREE(fn->body); ++ if(fn->varlist) sial_freesvs(fn->varlist); ++ sial_freevar(fn->rvar); ++ sial_free(fn); ++} ++ ++static void ++sial_unloadso(fdata *fd) ++{ ++typedef int (*fp_t)(void); ++fp_t fp; ++func *f; ++ ++ if((fp=(fp_t)dlsym(fd->globs, BT_ENDDSO_SYM))) { ++ ++ fp(); ++ } ++ for(f=fd->funcs; f; ) { ++ ++ func *n=f->next; ++ sial_rmbuiltin(f->varlist); ++ sial_freevar(f->varlist); ++ sial_free(f); ++ f=n; ++ } ++ ++ dlclose(fd->globs); ++ ++ if(fall==fd) fall=fd->next; ++ else { ++ ++ fdata *last=fall; ++ ++ while(last->next) { ++ ++ if(last->next==fd) { ++ ++ last->next=fd->next; ++ break; ++ } ++ last=last->next; ++ } ++ } ++ ++ /* free the associated static and global variables */ ++ if(fd->fsvs) sial_freesvs(fd->fsvs); ++ if(fd->fgvs) sial_freesvs(fd->fgvs); ++ sial_free(fd->fname); ++ sial_free(fd); ++} ++ ++static void (*cb)(char *, int)=0; ++void sial_setcallback(void (*scb)(char *, int)) ++{ cb=scb; } ++static void ++sial_docallback( fdata *fd, int load) ++{ ++func *f; ++ ++ if(!cb) return; ++ ++ for(f=fd->funcs; f; f=f->next) { ++ ++ cb(f->name, load); ++ } ++} ++ ++void ++sial_freefile(fdata *fd) ++{ ++ if(fd) { ++ ++ func *fct, *nxt; ++ fctype_t *ct, *nct; ++ ++ if(fd->isdso) { ++ ++ sial_unloadso(fd); ++ return; ++ } ++ ++ /* free the associated static and global variables */ ++ if(fd->fsvs) sial_freesvs(fd->fsvs); ++ if(fd->fgvs) sial_freesvs(fd->fgvs); ++ ++ /* free all function nodes */ ++ // let debugger know ... ++ sial_docallback(fd, 0); ++ for(fct=fd->funcs; fct; fct=nxt) { ++ ++ nxt=fct->next; ++ sial_freefunc(fct); ++ } ++ ++ for(ct=fd->ctypes; ct; ct=nct) { ++ ++ nct=ct->next; ++ sial_free(ct); ++ } ++ sial_free(fd->fname); ++ if(fd->globs) sial_rm_globals(fd->globs); ++ sial_free(fd); ++ } ++ else sial_warning("Oops freefile!"); ++} ++ ++int ++sial_deletefile(char *name) ++{ ++fdata *fd=sial_findfile(name, 0); ++ ++ if(fd) { ++ ++ sial_freefile(fd); ++ (void)sial_findfile(name, 1); ++ return 1; ++ ++ } ++ return 0; ++} ++ ++static int parsing=0; ++static jmp_buf parjmp; ++ ++void ++sial_parseback(void) ++{ ++ if(parsing) { ++ ++ parsing=0; ++ longjmp(parjmp, 1); ++ } ++} ++ ++/* link in a new set of static file variables */ ++int ++sial_file_decl(var_t*svs) ++{ ++ sial_validate_vars(svs); ++ ++ if(!fall->fsvs) ++ fall->fsvs=(void*)sial_newvlist(); ++ ++ if(!fall->fgvs) ++ fall->fgvs=(void*)sial_newvlist(); ++ ++ (void)sial_addnewsvs(fall->fgvs, fall->fsvs, svs); ++ ++ return 1; ++} ++ ++typedef struct sigaction sact; ++static int sigs[]={SIGSEGV, SIGILL, SIGTRAP, SIGINT, SIGPIPE}; ++#define S_NSIG (sizeof(sigs)/sizeof(sigs[0])) ++ ++void ++sial_except_handler(int sig) ++{ ++static int i=0; ++ if(sig != SIGPIPE && sig != SIGINT) sial_error("Exception caught!"); ++ sial_dojmp(J_EXIT, &i); ++} ++ ++void * ++sial_setexcept() ++{ ++int i; ++sact *osa=sial_alloc(S_NSIG*sizeof(sact)); ++#if linux ++sact na; ++ ++ memset(&na, 0, sizeof(na)); ++ na.sa_handler=sial_except_handler; ++ na.sa_flags=SA_NODEFER; ++ ++#else ++sact na={ SA_NODEFER+SA_SIGINFO, sial_except_handler, 0, 0 }; ++#endif ++ ++ ++ for(i=0;ifname="__expr__"; ++ fd->next=fall; ++ fall=fd; ++ ++ sial_pushbuf(exp2, "stdin", 0, 0, 0); ++ parsing=1; ++ if(!setjmp(parjmp)) { ++ ++ sial_rsteofoneol(); ++ sial_settakeproto(1); ++ sialparse(); ++ sial_settakeproto(0); ++ ++ /* remove longjump for parsing */ ++ parsing=0; ++ ++ if(!fall->fgvs) { ++ ++ sial_error("Invalid function declaration."); ++ ++ } ++ ++ ret=fall->fgvs->next; ++ ++ } else { ++ ++ sial_popallin(); ++ ret=0; ++ ++ } ++ sial_free(exp2); ++ /* only free the top of the fgvs list to keep 'ret' */ ++ if(fall->fgvs) sial_freevar(fall->fgvs); ++ if(fall->fsvs) sial_freesvs(fall->fsvs); ++ fall=fd->next; ++ sial_free(fd); ++ return ret; ++} ++ ++/* ++ Load a dso file. ++ We are looking for the btinit() and btshutdown() functions. ++ ++ btinit() will should initialized the package and call sial_builtin() ++ to install the sial functions. ++ ++ btshutdown(), if it exists, will be called when an unload of the ++ file is requested. The dso should deallocate memory etc... at that ++ time. ++*/ ++static int ++sial_loadso(char *fname, int silent) ++{ ++void *h; ++ ++ if((h=dlopen(fname, RTLD_LAZY))) { ++ ++ typedef int (*fp_t)(void); ++ fp_t fp; ++ ++ if((fp=(fp_t)dlsym(h, BT_INIDSO_SYM))) { ++ ++ btspec_t *sp; ++ ++ if(fp()) { ++ ++ if((sp=(btspec_t *)dlsym(h, BT_SPEC_SYM))) { ++ ++ int i; ++ fdata *fd=sial_calloc(sizeof(fdata)); ++ func **ff=&fd->funcs; ++ ++ fd->fname=fname; ++ fd->isdso=1; ++ fd->globs=h; ++ ++ for(i=0;sp[i].proto;i++) { ++ ++ var_t*v; ++ ++ if((v=sial_builtin(sp[i].proto, sp[i].fp))) { ++ ++ func *f=sial_alloc(sizeof(func)); ++ ++ f->varlist=v; ++ f->next=*ff; ++ *ff=f; ++ } ++ } ++ fd->next=fall; ++ fall=fd; ++ return 1; ++ ++ } else if(!silent) { ++ ++ sial_msg("Missing '%s' table in dso [%s]", BT_SPEC_SYM, fname); ++ ++ } ++ ++ } else if(!silent) { ++ ++ sial_msg("Could not initialize dso [%s]", fname); ++ ++ } ++ ++ } else if(!silent) { ++ ++ sial_msg("Missing '%s' function in dso [%s]", BT_INIDSO_SYM, fname); ++ } ++ dlclose(h); ++ } ++ else if(!silent) sial_msg(dlerror()); ++ sial_free(fname); ++ return 0; ++} ++ ++void ++sial_addfunc_ctype(int idx) ++{ ++fctype_t *fct=sial_alloc(sizeof(fctype_t)); ++ ++ fct->idx=idx; ++ fct->next=fall->ctypes; ++ fall->ctypes=fct; ++} ++ ++int ++sial_newfile(char *name, int silent) ++{ ++fdata *fd; ++fdata *oldf; ++char *fname=sial_strdup(name); ++void *mtag; ++ ++ /* check if this is a dso type file */ ++ if(!strcmp(fname+strlen(fname)-3, ".so")) { ++ ++ if(sial_findfile(name,0)) { ++ ++ if(!silent) ++ sial_msg("Warning: dso must be unloaded before reload\n"); ++ return 0; ++ } ++ return sial_loadso(fname, silent); ++ ++ } ++ ++ fd=sial_calloc(sizeof(fdata)); ++ oldf=sial_findfile(name,1); ++ ++ /* push this file onto the parser stack */ ++ if(!sial_pushfile(fname)) { ++ ++ sial_free(fname); ++ if(!silent && errno != EISDIR) sial_msg("File %s : %s\n", name, strerror(errno)); ++ return 0; ++ } ++ ++ /* we also need to remove the globals for this file ++ before starting the parsing */ ++ if(oldf && oldf->globs) { ++ ++ sial_rm_globals(oldf->globs); ++ oldf->globs=0; ++ ++ } ++ ++ needvar=instruct=0; ++ ++ fd->fname=fname; ++ ++ /* put it on the list */ ++ fd->next=fall; ++ fall=fd; ++ ++ /* we tag the current ctype list so we know later what to clean up */ ++ sial_tagst(); ++ ++ /* we also tag the macro stack so we can erase out defines and ++ keep the compiler and api ones. */ ++ mtag=sial_curmac(); ++ ++ parsing=1; ++ if(!setjmp(parjmp)) { ++ ++ func *fct; ++ int ret=1; ++ ++ /* parse it */ ++ sial_rsteofoneol(); ++ ++ sialparse(); ++ ++ /* remove longjump for parsing */ ++ parsing=0; ++ ++ /* before adding the globals we need to push all the static ++ variables for this file since the initialization expressions ++ might use them (e.g. sizeof('a static var')). Eh, as long as ++ we keep the interpreter handling a superset of the 'standard' C ++ I don't have a problem with it. Do you ? */ ++ ++ { ++ int lev; ++ ++ lev=sial_addsvs(S_STAT, fd->fsvs); ++ ++ /* ok to add the resulting globals now */ ++ fall->globs=sial_add_globals(fall->fgvs); ++ ++ sial_setsvlev(lev); ++ } ++ ++ /* ok to free olf version */ ++ if(oldf) sial_freefile(oldf); ++ ++ sial_flushtdefs(); ++ sial_flushmacs(mtag); ++ ++ /* we proceed with the callback */ ++ sial_docallback(fd, 1); ++ ++ fd->time=time(0); ++ ++ /* compilation was ok , check for a __init() function to execute */ ++ if((fct=sial_getfbyname("__init", fd))) { ++ ++ int *exval; ++ jmp_buf exitjmp; ++ sact *sa; ++ ++ sa=sial_setexcept(); ++ ++ if(!setjmp(exitjmp)) { ++ ++ sial_pushjmp(J_EXIT, &exitjmp, &exval); ++ sial_freeval(sial_execmcfunc(fct, 0)); ++ sial_rmexcept(sa); ++ sial_popjmp(J_EXIT); ++ ++ } ++ else { ++ ++ sial_rmexcept(sa); ++ ret=0; ++ } ++ ++ } ++ return ret; ++ } ++ else { ++ ++ /* remove all streams from the stack */ ++ sial_popallin(); ++ ++ /* error, free this partial one and reinstall old one */ ++ if(oldf) { ++ /* we zap the top pointer (it's fd) */ ++ oldf->next=fall->next; ++ fall=oldf; ++ oldf->globs=sial_add_globals(oldf->fgvs); ++ } ++ else { ++ ++ fall=fall->next; ++ } ++ ++ /* and free fd */ ++ sial_freefile(fd); ++ } ++ sial_flushtdefs(); ++ sial_flushmacs(mtag); ++ return 0; ++} ++ ++/* scan the current list of functions for the one named name */ ++static func * ++sial_getfbyname(char *name, fdata *thisfd) ++{ ++fdata *fd; ++ ++ /* check localy first */ ++ if(thisfd) { ++ ++ for(fd=fall; fd; fd=fd->next) { ++ ++ func *f; ++ ++ if(fd->isdso) continue; ++ ++ /* skip non-local function */ ++ if(thisfd != fd) continue; ++ ++ for(f=fd->funcs; f; f=f->next) { ++ ++ if(!strcmp(f->name, name)) return f; ++ } ++ } ++ } ++ ++ /* check global function */ ++ for(fd=fall; fd; fd=fd->next) { ++ ++ func *f; ++ ++ if(fd->isdso) continue; ++ ++ for(f=fd->funcs; f; f=f->next) { ++ ++ /* skip static functions not local */ ++ if(f->local) continue; ++ ++ if(!strcmp(f->name, name)) return f; ++ } ++ } ++ return 0; ++} ++ ++/* external boolean to check if a function exists */ ++int sial_funcexists(char *name) ++{ ++ return !(!(sial_getfbyname(name, 0))); ++} ++ ++/* ++ This combined set of functions enables the aplication to ++ get alist of currently defined commands that have a help. ++*/ ++static fdata *nxtfdata=0; ++static func *nxtfunc; ++void ++sial_rstscan(void) ++{ ++ nxtfdata=0; ++} ++char * ++sial_getnxtfct(void) ++{ ++ if(!nxtfdata) { ++ ++ if(!fall) return 0; ++ nxtfdata=fall; ++ nxtfunc=nxtfdata->funcs;; ++ } ++ ++ while(nxtfdata) { ++ ++ if(!nxtfdata->isdso) for(; nxtfunc; nxtfunc=nxtfunc->next) { ++ ++ int l=strlen(nxtfunc->name); ++ ++ if(l > 5) { ++ ++ if(!strcmp(nxtfunc->name+l-5, "_help")) { ++ ++ char buf[MAX_SYMNAMELEN+1]; ++ func *ret; ++ ++ strncpy(buf, nxtfunc->name, l-5); ++ buf[l-5]='\0'; ++ ++ /* make sure we do have the function */ ++ if((ret=sial_getfbyname(buf, 0))) { ++ ++ nxtfunc=nxtfunc->next; ++ return ret->name; ++ } ++ } ++ } ++ } ++ nxtfdata=nxtfdata->next; ++ if(nxtfdata) nxtfunc=nxtfdata->funcs; ++ } ++ sial_rstscan(); ++ return 0; ++} ++ ++/* ++ This is the entry point for the error handling ++*/ ++void ++sial_exevi(char *fname, int line) ++{ ++char buf[200]; ++char *ed=getenv("EDITOR"); ++ ++ if(!ed) ed="vi"; ++ snprintf(buf, sizeof(buf), "%s +%d %s", ed, line, fname); ++ system(buf); ++ sial_load(fname); ++} ++ ++/* ++ This funciton is called to start a vi session on a function ++ (file=0) or a file (file=1); ++*/ ++void ++sial_vi(char *fname, int file) ++{ ++int line, freeit=0; ++char *filename; ++ ++ if(file) { ++ ++ filename=sial_filempath(fname); ++ ++ if(!filename) { ++ ++ sial_msg("File not found : %s\n", fname); ++ return; ++ ++ } ++ ++ line=1; ++ freeit=1; ++ ++ ++ } else { ++ ++ func *f=sial_getfbyname(fname, 0); ++ ++ if(!f) { ++ ++ sial_msg("Function not found : %s\n", fname); ++ return; ++ ++ } else { ++ ++ filename=f->pos.file; ++ line=f->pos.line; ++ ++ } ++ } ++ ++ sial_exevi(filename, line); ++ ++ if(freeit) sial_free(filename); ++ ++} ++ ++char * ++sial_getfile(char *fname) ++{ ++func *f; ++ ++ if((f=sial_getfbyname(fname, 0))) return f->file->fname; ++ return 0; ++} ++ ++static void ++sial_insertfunc(func *f) ++{ ++ f->next=fall->funcs; ++ fall->funcs=f; ++} ++ ++value_t * ++sial_execmcfunc(func *f, value_t **vp) ++{ ++value_t *retval; ++jmp_buf env; ++var_t*parm=0; ++int i=0; ++char *ocurp, *curp; ++ ++ /* set the current path */ ++ { ++ char *p; ++ ++ curp=sial_strdup(f->file->fname); ++ if((p=strrchr(curp, '/'))) *p='\0'; ++ ocurp=sial_curp(curp); ++ } ++ ++ ++ if(!(setjmp(env))) { ++ ++ /* push a return level */ ++ sial_pushjmp(J_RETURN, &env, &retval); ++ ++ /* Now it's ok to add any static vars for this file */ ++ sial_addsvs(S_FILE, f->file->fsvs); ++ ++ /* we need to create brand new variables with ++ the name of the declared arguments */ ++ if(f->varlist) { ++ ++ for(i=0, parm=f->varlist->next; ++ vp && (parm != f->varlist) && vp[i]; ++ parm=parm->next, i++) { ++ ++ var_t*var=sial_newvar(parm->name); ++ ++ var->v=sial_cloneval(parm->v); ++ sial_chkandconvert(var->v, vp[i]); ++ sial_add_auto(var); ++ sial_freeval(vp[i]); ++ ++ } ++ } ++ if(vp && vp[i]) { ++ ++ sial_warning("Too many parameters to function call"); ++ ++ } else if(parm != f->varlist) { ++ ++ sial_warning("Not enough parameters for function call"); ++ } ++ ++ /* we execute the buddy of the function */ ++ retval=NODE_EXE(f->body); ++ ++ sial_freeval(retval); ++ ++ retval=0; ++ ++ sial_popjmp(J_RETURN); ++ } ++ ++ /* make sure non void function do return something */ ++ if(!retval) { ++ ++ if(!sial_isvoid(f->rvar->v->type.typattr)) ++ ++ sial_rwarning(&f->pos, "Non void function should return a value."); ++ ++ } else { ++ ++ /* type checking here ... */ ++ } ++ ++ sial_curp(ocurp); ++ sial_free(curp); ++ ++ return retval; ++} ++ ++/* this is the externalized function that the API users call to execute ++ a function */ ++ull ++sial_exefunc(char *fname, value_t **vp) ++{ ++func *f; ++ull ret; ++ ++ if(!sial_chkfname(fname, 0)) ++ sial_warning("Unknown function called: %s\n", fname); ++ ++ /* builtin vs cmc ...*/ ++ if((f=sial_getfbyname(fname, 0))) ret=sial_getval(sial_execmcfunc(f, vp)); ++ else ret=sial_getval(sial_exebfunc(fname, vp)); ++ /* sial_freeval(v); */ ++ return ret; ++} ++ ++value_t * ++sial_exefunc_common(char *fname, node_t*parms, fdata *fd) ++{ ++int i; ++node_t*args; ++value_t *vp[BT_MAXARGS+1]; ++func *f; ++ ++ /* We most execute before pushing the S_FILE vars so the the ++ local variable for the caller can still be accessed */ ++ for(i=0,args=parms; args; args=args->next) { ++ ++ if(i==BT_MAXARGS) { ++ ++ sial_error("Max number of parameters exceeded [%d]", BT_MAXARGS); ++ } ++ vp[i++]=NODE_EXE(args); ++ ++ } ++ ++ /* null out the rest */ ++ for(;i<=BT_MAXARGS;i++) vp[i]=0; ++ ++ /* builtin vs cmc ...*/ ++ if((f=sial_getfbyname(fname, fd))) return sial_execmcfunc(f, vp); ++ else return sial_exebfunc(fname, vp); ++} ++ ++ ++/* this function is called by the sial_exeop() through a CALL op. */ ++value_t * ++sial_docall(node_t*name, node_t*parms, void *arg) ++{ ++fdata *fd = arg; ++char *sname=sial_vartofunc(name); ++value_t *v=0; ++ ++ if(sial_chkfname(sname, fd)) { ++ ++ v=sial_exefunc_common(sname, parms, fd); ++ ++ } ++ else sial_rerror(&name->pos, "Unknown function being called:[%s]", sname, fd); ++ /* sial_vartofunc() allocates the name */ ++ /* we don't free this item if mem debug has been set */ ++ if(!sial_ismemdebug()) sial_free(sname); ++ return v; ++ ++} ++ ++int ++sial_newfunc(var_t*fvar, node_t* body) ++{ ++var_t*v=fvar->next; ++ ++ if(v == fvar) { ++ ++ sial_freevar(v); ++ NODE_FREE(body); ++ sial_error("Syntax error in function declaration"); ++ ++ }else{ ++ ++ func *fn, *fi ; ++ ++ sial_freevar(fvar); ++ ++ /* we do the func insertion first so that if we have a problem ++ we can jump our of the parser using the sial_parback() function ++ which will deallocate the stuff */ ++ ++ fn=sial_alloc(sizeof(func)); ++ if(sial_isstatic(v->v->type.typattr)) fn->local=1; ++ fn->rvar=v; ++ fn->varlist=v->dv->fargs; ++ ++ /* check for func(void) */ ++ if(fn->varlist && fn->varlist->next != fn->varlist) { ++ ++ var_t*v=fn->varlist->next; ++ ++ if(v->v->type.type != V_REF && sial_isvoid(v->v->type.typattr)) { ++ ++ /* cut the chain here */ ++ if(v->next != fn->varlist) { ++ ++ sial_error("function parameter cannot have 'void' type"); ++ } ++ sial_freesvs(fn->varlist); ++ fn->varlist=0; ++ } ++ } ++ ++ v->dv->fargs=0; ++ fn->name=sial_strdup(v->name); ++ fn->local=sial_isstatic(v->v->type.typattr)?1:0; ++ fn->body=body; ++ fn->file=fall; ++ ++ /* the position of the function is the position of the var_t*/ ++ memcpy(&fn->pos, &v->dv->pos, sizeof(srcpos_t)); ++ ++ /* emit a warning for variables in the main statement group that ++ shadow ont of the parameters */ ++ if(fn->varlist) { ++ ++ var_t*v; ++ ++ for(v=fn->varlist->next; v!=fn->varlist; v=v->next) { ++ ++ var_t*vs; ++ ++ if((vs=sial_inlist(v->name, sial_getsgrp_avs(body))) || ++ (vs=sial_inlist(v->name, sial_getsgrp_svs(body)))) { ++ ++ sial_rwarning(&vs->dv->pos, "variable '%s' shadow's a function parameter" ++ , v->name); ++ ++ } ++ } ++ } ++ ++ if((fi=sial_getfbyname(fn->name, fall))) { ++ ++ /* check for local conflicts */ ++ if(fi->file == fn->file) { ++ ++ sial_insertfunc(fn); ++ sial_rerror(&fn->pos, "Function '%s' redefinition, first defined in file '%s' line %d" ++ , fn->name, fi->pos.file, fi->pos.line); ++ ++ /* check for global conflicts */ ++ } else if(!fn->local) { ++ ++ sial_insertfunc(fn); ++ sial_rerror(&fn->pos, "Function '%s' already defined in file %s, line %d" ++ , fn->name, fi->pos.file, fi->pos.line); ++ ++ } /* else... it's a static that shadows a global somewhere else. So it's ok */ ++ ++ } ++ ++ /* Searching is all done, so insert it */ ++ sial_insertfunc(fn); ++ ++ /* check out the storage class. Only 'static' is supported */ ++ if(!sial_isjuststatic(v->v->type.typattr)) { ++ ++ sial_error("Only 'static' storage class is valid for a function"); ++ } ++ } ++ return 1; ++} ++ ++/* check for the existance of a function in the list */ ++int ++sial_chkfname(char *fname, void *vfd) ++{ ++fdata *fd=(fdata *)vfd; ++ ++ /* check script functions */ ++ if(!sial_getfbyname(fname, fd)) { ++ ++ /* check builtin list */ ++ if(sial_chkbuiltin(fname)) return 1; ++ return 0; ++ ++ } ++ return 1; ++} ++ ++/* ++ ++ Thsi is the interface function with the command interpreter. ++ It needs to be able to execute a function giving a name and ++ passing some random parameters to it. ++ ++ A return of 0 means "no such function". ++*/ ++int ++sial_runcmd(char *fname, var_t*args) ++{ ++ if(sial_chkfname(fname, 0)) { ++ ++ value_t *val; ++ int *exval; ++ jmp_buf exitjmp; ++ void *vp; ++ ull ret; ++ sact *sa; ++ ++ /* make sure arguments are available in the global vars */ ++ vp=sial_add_globals(args); ++ ++ /* we set the exception handler too... */ ++ sa=sial_setexcept(); ++ ++ if(!setjmp(exitjmp)) { ++ ++ sial_pushjmp(J_EXIT, &exitjmp, &exval); ++ ++ /* we need to create a var with that name */ ++ val=sial_exefunc_common(fname, 0, 0); ++ ++ sial_popjmp(J_EXIT); ++ ++ if(val) { ++ ++ ret=unival(val); ++ sial_freeval(val); ++ } ++ else ret=0; ++ } ++ else { ++ ++ ret=*exval; ++ } ++ ++ /* remove exception handlers and restore previous handlers */ ++ sial_rmexcept(sa); ++ ++ /* remove args from global vars */ ++ sial_rm_globals(vp); ++ return ret; ++ } ++ return 0; ++} ++ +--- crash/extensions/libsial/sial_alloc.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_alloc.c 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,430 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++ ++#define MEMDEBUG 1 ++/* ++*/ ++#include "sial.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef __GNUC__ ++# if __LP64__ ++# define NKPC 16 ++# else ++# define NKPC 4 ++# endif ++#else ++// must be the SGI Mips compiler. ++# if (_MIPS_SZLONG == 64) ++# define NKPC 16 ++# else ++# define NKPC 4 ++# endif ++#endif ++#define PAGESIZE (NKPC*1024) ++ ++ ++/* ++ Jump defines ++*/ ++#define MAXJMPS (S_MAXDEEP*3) ++int njmps=0; ++ ++typedef struct blklist { ++ ++ struct blklist *next; /* root based doubly chained */ ++ struct blklist *prev; ++ int size; /* size of the allocation */ ++ int istmp; /* was flaged as temp ? */ ++ int level; /* coresponding level */ ++ void *caller; /* __return_address of caller */ ++ void *freer; /* __return_address of freer */ ++ ++} blist; ++ ++#define SIZEBL (((sizeof(blist)+8)/8)*8) ++ ++void pbl(void *p) ++{ ++blist *bl=(blist*)(((char*)p)-SIZEBL); ++ sial_msg("struct blklist *%p {", bl); ++ sial_msg(" next=%p", bl->next); ++ sial_msg(" prev=%p", bl->prev); ++ sial_msg(" size=%d", bl->size); ++ sial_msg(" istmp=%d", bl->istmp); ++ sial_msg(" level=%d", bl->level); ++ sial_msg(" caller=%p", bl->caller); ++ sial_msg(" freer=%p", bl->freer); ++} ++ ++static blist temp={ &temp, &temp, 0, 0, 0, 0, 0 }; ++ ++value_t* ++sial_findsym(value_t *vadr) ++{ ++ char *addr=sial_getptr(vadr, char); ++ char *p = API_FINDSYM(addr); ++ ++ if(p) { ++ return sial_setstrval(sial_newval(), p); ++ } else { ++ return sial_setstrval(sial_newval(),""); ++ } ++} ++ ++value_t* ++sial_showaddr(value_t *vadr) ++{ ++void *addr=sial_getptr(vadr, void); ++blist *bl; ++int n=0; ++ ++ for(bl=temp.next; bl != &temp; bl=bl->next) { ++ ++ if(bl->caller==addr) { ++ ++ if(!(n%8)) sial_msg("\n"); ++ sial_msg("0x%08x ", ((char *)bl) + SIZEBL); ++ n++; ++ } ++ } ++ return sial_makebtype(0); ++} ++ ++static int memdebug=0; ++ ++/* these two functions must *not* receive any values */ ++value_t* sial_memdebugon() { memdebug=1; return sial_makebtype(0); } ++value_t* sial_memdebugoff() { memdebug=0; return sial_makebtype(0); } ++int sial_ismemdebug() { return memdebug; } ++ ++value_t* ++sial_showtemp() ++{ ++blist *bl; ++int i, totsiz, totbl; ++static int ncallers=0; ++static void *callers[1000]; ++static int count[1000]; ++static int sizes[1000]; ++static int dir=0; ++ ++ if(!dir) { ++ ++ memset(callers, 0, sizeof(void*)*1000); ++ memset(count, 0, sizeof(int)*1000); ++ memset(sizes, 0, sizeof(int)*1000); ++ ncallers=0; ++ } ++ ++ if(dir==1) dir=0; ++ else dir=1; ++ ++ for(bl=temp.next; bl != &temp; bl=bl->next) { ++ ++ int i; ++ ++ for(i=0;icaller) { ++ if(dir) { count[i]++; sizes[i]+=bl->size; } ++ else { count[i]--; sizes[i]-=bl->size; } ++ break; ++ } ++ ++ if(i==ncallers) { ++ callers[ncallers]=bl->caller; ++ count[ncallers]=1; ++ sizes[ncallers]=bl->size; ++ ncallers++; ++ } ++ ++ } ++ totbl=totsiz=0; ++ for(i=0;icaller=retaddr; ++} ++ ++#define PAGEMASK 0xfffffffffffff000ll ++#define MAGIC 0xdeadbabe ++void * ++sial_alloc(int size) ++{ ++char *m; ++blist *bl; ++ ++#ifdef MEMDEBUG ++unsigned long p, pp; ++int npages; ++#endif ++ ++ size=size+SIZEBL; ++ ++#if MEMDEBUG ++ ++ if(memdebug) { ++ ++ npages=((size+PAGESIZE+4)/PAGESIZE)+2; ++ p=(unsigned long)malloc(npages*PAGESIZE); ++ p=(p+PAGESIZE)&PAGEMASK; ++ pp=p+((npages-2)*PAGESIZE); ++ p=pp-size; ++ p = p ^ (p & 0x0fll); ++ *((int*)(p-4))=MAGIC; ++ mprotect((void*)pp, PAGESIZE, PROT_READ); ++ m=(char*)p; ++ ++ } else { ++ ++ m=malloc(size); ++ } ++ ++#else ++ ++ m=malloc(size); ++ ++#endif ++ ++ ++ bl=(blist*)m; ++ bl->size=size; ++ bl->level=njmps; ++ bl->prev=bl->next=bl; ++ bl->istmp=0; ++ TAG(m+SIZEBL); ++ return m+SIZEBL; ++} ++ ++void ++sial_maketemp(void *p) ++{ ++blist *bl; ++ ++ if(!p) return; ++ ++ bl=(blist*)(((char*)p)-SIZEBL); ++ bl->prev=&temp; ++ bl->next=temp.next; ++ bl->istmp=1; ++ temp.next->prev=bl; ++ temp.next=bl; ++} ++ ++void * ++sial_calloc(int size) ++{ ++char *p=sial_alloc(size); ++ ++ TAG(p); ++ memset(p, 0, size); ++ return p; ++} ++ ++static void ++sial_free_bl(blist *bl, void *ra) ++{ ++ bl->freer=ra; ++ bl->prev->next=bl->next; ++ bl->next->prev=bl->prev; ++ ++#ifdef MEMDEBUG ++ ++ if(memdebug) { ++ ++ /* help out dbx/gdb when they're watching the allocated area ++ by writing over it */ ++ { ++ int i, ni=bl->size/sizeof(void*); ++ char *p=(char*)bl; ++ unsigned long up; ++ ++ for(i=0;inext; ++ sial_free_bl(bl, __return_address); ++ bl=next; ++ } ++} ++ ++int ++sial_istemp(void *p) ++{ ++ return ((blist*)(((char*)p)-SIZEBL))->istmp; ++} ++ ++char * ++sial_strdup(char *s) ++{ ++char *ns=sial_alloc(strlen(s)+1); ++ ++ strcpy(ns, s); ++ TAG(ns); ++ return ns; ++} ++ ++void * ++sial_dupblock(void *p) ++{ ++void *p2; ++int size=((blist*)(((char*)p)-SIZEBL))->size-SIZEBL; ++ ++ if(!p) return 0; ++ ++ p2=sial_alloc(size); ++ memcpy(p2, p, size); ++ return p2; ++} ++ ++/* cheap realloc. we drop the original ++ This function is only used ones in configmon(1) code */ ++void * ++sial_realloc(void *p, int size) ++{ ++int cursize=((blist*)(((char*)p)-SIZEBL))->size-SIZEBL; ++void *p2; ++ ++ p2=sial_calloc(size); ++ memcpy(p2, p, cursize 1) { ++ ++ jmp_buf *env; ++ ++ while(njmps && jmps[--njmps].type!=type); ++ if(jmps[njmps].val) *(jmps[njmps].val)=val; ++ env=jmps[njmps].env; ++ ++ /* reset the variable level too... */ ++ sial_setsvlev(jmps[njmps].svlev); ++ ++ longjmp(*env, 1); ++ /* NOT REACHED */ ++ ++ } else sial_parseback(); /* we use the same code for initializing ++ static and automatic variables. In the case of statiuc variables ++ is the initizer expression throws an error then there's no J_EXIT ++ jump context and njmps is null. It's treated as a parsing error */ ++} ++ ++void ++sial_popjmp(int type) ++{ ++ if(!njmps) { ++ ++ sial_error("Pop underflow!"); ++ } ++ njmps--; ++ if(jmps[njmps].type != type) { ++ ++ sial_error("Wrong pop! %d vs %d", jmps[njmps].type, type); ++ } ++ sial_setsvlev(jmps[njmps].svlev); ++} ++ +--- crash/extensions/libsial/sial_print.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_print.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,398 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include ++#include ++#include "sial.h" ++/* ++ This set of function are used to print value_ts. ++*/ ++ ++/* utility that returns a string of '*' for a reference */ ++static ++char *sial_getref(int lev) ++{ ++static char *ptrs="*******"; ++ ++ return ptrs+strlen(ptrs)-lev; ++} ++ ++static char * ++sial_getidx(type_t *t, char*buf, int len) ++{ ++int pos=0; ++ ++ buf[0]='\0'; ++ if(t->idxlst) { ++ ++ int i; ++ ++ for(i=0; t->idxlst[i] && pos < len; i++) { ++ ++ pos += snprintf(buf+pos, len-pos, "[%d]", t->idxlst[i]); ++ } ++ } ++ return buf; ++} ++ ++#define INDENT 4 /* print indent at beginning of new line */ ++#define SPACER 16 /* space for type string */ ++#define NAMESPACE 16 /* space used for member/var names */ ++#define NBUNDLE 4 /* when printing arrays print this much before \n */ ++ ++static void ++sial_indent(int level, int indent) ++{ ++ if(!indent) return; ++ sial_msg("%*s", level*INDENT, ""); ++} ++ ++static void sial_ptype2(type_t*t, value_t*v, int level, int indent, char *name, int ref, int justv); ++ ++/* ++ Print a struct/union type or value ++*/ ++static void ++sial_print_ctype(type_t *t, value_t *v, int level, int indent, char *name, int ref, int justv) ++{ ++stinfo_t *st=sial_getstbyindex(t->idx, t->type); ++stmember_t *m; ++char buf[100]; ++ ++ if(!st) sial_error("Oops sial_print_ctype!"); ++ ++ if(!st->all) { ++ ++ sial_fillst(st); ++ if(!st->all) sial_error("Reference to a incomplete type"); ++ } ++ ++ sial_indent(level, indent); ++ ++ if(!justv) { ++ snprintf(buf, sizeof(buf)-1, "%s %s", sial_ctypename(t->type), st->name?st->name:""); ++ sial_msg("%-*s ", SPACER, buf); ++ ++ /* is this is a pointer, bail out */ ++ } ++ if(ref) return; ++ ++ if(v && !justv) sial_msg(" = "); ++ ++ sial_msg("{\n"); ++ ++ for(m=st->stm; m; m=m->next) { ++ ++ value_t *vm=0; ++ ++ sial_indent(level+1, 1); ++ if(v) { ++ vm=sial_newval(); ++ sial_duptype(&vm->type, &m->type); ++ sial_exememlocal(v, m, vm); ++ sial_ptype2(&vm->type, vm, level+1, 0, m->m.name, 0, 0); ++ ++ } else sial_ptype2(&m->type, vm, level+1, 0, m->m.name, 0, 0); ++ sial_msg(";\n"); ++ if(vm) sial_freeval(vm); ++ } ++ ++ sial_indent(level, 1); ++ sial_msg("}"); ++ if(name) sial_msg(" %s", name); ++ ++} ++ ++static void ++sial_prbval(value_t *v) ++{ ++ if(sial_issigned(v->type.typattr)) sial_msg("%8lld", sial_getval(v)); ++ else sial_msg("%8llu", sial_getval(v)); ++} ++ ++static int ++sial_prtstr(value_t *v, int justv) ++{ ++value_t *vs; ++char *s, *p; ++ ++ if(sial_defbsize()==8) v->v.ull=v->mem; ++ else v->v.ul=v->mem; ++ vs=sial_getstr(v); ++ s=sial_getptr(vs, char); ++ for(p=s; *p; p++) if(!isprint(*p)) return 0; ++ if(p==s) { sial_freeval(vs); return 0; } ++ if(!justv) sial_msg("= "); ++ sial_msg("\"%s\"", s); ++ sial_freeval(vs); ++ return 1; ++} ++ ++static void ++sial_prtarray(type_t*t, ull mem, int level, int idx) ++{ ++int i; ++int j, size=1; ++ ++ for(j=idx+1; t->idxlst[j]; j++) size *= t->idxlst[j]; ++ size *= t->type==V_REF ? sial_defbsize() : t->size; ++ ++ /* start printing */ ++ sial_msg("{"); ++ sial_msg("\n"); ++ sial_indent(level+1, 1); ++ ++ for(i=0; iidxlst[idx]; i++, mem += size) { ++ ++ if(t->idxlst[idx+1]) { ++ ++ sial_msg("[%d] = ", i); ++ sial_prtarray(t, mem, level+1, idx+1); ++ ++ } else { ++ ++ /* time to deref and print final type */ ++ value_t *v=sial_newval(), *vr=sial_newval(); ++ int *pi=t->idxlst; ++ ++ t->idxlst=0; ++ ++ sial_duptype(&vr->type, t); ++ sial_pushref(&vr->type, 1); ++ if(sial_defbsize()==8) vr->v.ull=mem; ++ else vr->v.ul=(ul)mem; ++ sial_do_deref(1, v, vr); ++ if(is_ctype(v->type.type) || !(i%NBUNDLE)) sial_msg("[%2d] ", i); ++ sial_ptype2(&v->type, v, level+1, 0, 0, 0, 1); ++ sial_msg(", "); ++ /* anything else then struct/unions, print in buddles */ ++ if(!is_ctype(v->type.type) && !((i+1)%NBUNDLE)) { ++ ++ sial_msg("\n"); ++ sial_indent(level+1, 1); ++ } ++ sial_freeval(v); ++ sial_freeval(vr); ++ t->idxlst=pi; ++ } ++ } ++ sial_msg("\n"); ++ sial_indent(level, 1); ++ sial_msg("}"); ++} ++ ++/* ++ Print a type. ++ Typical output of the 'whatis' command. ++*/ ++static ++void sial_ptype2(type_t*t, value_t*v, int level, int indent, char *name, int ref, int justv) ++{ ++int type=t->type; ++ ++ sial_indent(level, indent); ++ switch(type) { ++ ++ case V_STRUCT: case V_UNION: ++ ++ /* make sure we have all the member info */ ++ sial_print_ctype(t, v, level, 0, name, ref, justv); ++ break; ++ ++ ++ case V_TYPEDEF: ++ /* no typedef should get here */ ++ sial_warning("Typedef in print!"); ++ break; ++ ++ case V_ENUM: ++ /* no enum should get here */ ++ sial_warning("ENUM in print!"); ++ break; ++ ++ case V_REF: ++ { ++ int refi=t->ref, ref=refi; ++ ++ /* decrement ref if this was declared as a array */ ++ if(t->idxlst) ref--; ++ ++ /* print the referenced type */ ++ sial_popref(t, t->ref); ++ sial_ptype2(t, 0, level, 0, 0, 1, justv); ++ sial_pushref(t, refi); ++ ++ if(!justv) { ++ ++ char buf[100], buf2[100]; ++ int pos=0, len=sizeof(buf); ++ ++ buf[0]='\0'; ++ if(t->fct) buf[pos++]='('; ++ if(pos < len) ++ pos += snprintf(buf+pos, len-pos, "%s%s", sial_getref(ref), name?name:""); ++ if(pos < len) ++ pos += snprintf(buf+pos, len-pos, "%s", sial_getidx(t, buf2, sizeof(buf2))); ++ if(pos < len && t->fct) ++ pos += snprintf(buf+pos, len-pos, "%s", ")()"); ++ ++ sial_msg("%*s ", NAMESPACE, buf); ++ } ++ ++ /* arrays are ref with boundaries... */ ++ if(t->idxlst && v) { ++ ++ if(t->idxlst[1] || t->rtype!=V_BASE || t->size!=1 || !sial_prtstr(v, justv)) ++ { ++ if(!justv) sial_msg("= "); ++ sial_popref(t, 1); ++ sial_prtarray(t, v->mem, level, 0); ++ sial_pushref(t, 1); ++ } ++ ++ } else if(v) { ++ ++ if(!justv) sial_msg("= "); ++ if(!sial_getval(v)) sial_msg("(nil)"); ++ else { ++ if(sial_defbsize()==8) sial_msg("0x%016llx", sial_getval(v)); ++ else sial_msg("0x%08x", sial_getval(v)); ++ } ++ if(t->ref==1 && t->rtype==V_BASE && t->size==1) { ++ ++ (void)sial_prtstr(v, justv); ++ } ++ } ++ } ++ break; ++ ++ case V_BASE: ++ { ++ if(sial_isenum(t->typattr)) { ++ ++ stinfo_t *st=sial_getstbyindex(t->rtype, V_ENUM); ++ if(!justv) { ++ char buf[200]; ++ snprintf(buf, sizeof(buf), "enum %s", st->name?st->name:""); ++ sial_msg("%-*s ", SPACER, buf); ++ sial_msg("%*s ", NAMESPACE, (name&&v)?name:""); ++ } ++ if(v) { ++ ++ enum_t *e=st->enums; ++ ++ sial_msg("= "); ++ sial_prbval(v); ++ while(e) { ++ ++ if(e->value==sial_getval(v)) { ++ sial_msg(" [%s]", e->name); ++ break; ++ } ++ e=e->next; ++ } ++ if(!e) sial_msg(" [???]"); ++ ++ }else{ ++ ++ enum_t *e=st->enums; ++ int count=0; ++ ++ sial_msg(" {"); ++ while(e) { ++ ++ if(!(count%4)) { ++ sial_msg("\n"); ++ sial_indent(level+1, 1); ++ } ++ count ++; ++ sial_msg("%s = %d, ", e->name, e->value); ++ e=e->next; ++ ++ } ++ sial_msg("\n"); ++ sial_indent(level, 1); ++ sial_msg("%-*s ", SPACER, "}"); ++ if(ref) return; ++ sial_msg("%*s ", NAMESPACE, name?name:""); ++ } ++ ++ } else { ++ ++ if(!justv) { ++ sial_msg("%-*s " , SPACER , sial_getbtypename(t->typattr)); ++ if(ref) return; ++ sial_msg("%s%*s ", sial_getref(t->ref), NAMESPACE, name?name:""); ++ } ++ if(v) { ++ ++ if(!justv) sial_msg("= "); ++ sial_prbval(v); ++ } ++ } ++ } ++ break; ++ case V_STRING: ++ if(!justv) { ++ sial_msg("%-*s " , SPACER , "string"); ++ sial_msg("%*s ", NAMESPACE, name?name:""); ++ } ++ if(v) { ++ ++ if(!justv) sial_msg("= "); ++ sial_msg("\"%s\"", v->v.data); ++ } ++ break; ++ } ++ if(indent) sial_msg("\n"); ++} ++ ++static value_t* ++sial_ptype(value_t*v) ++{ ++ sial_ptype2(&v->type, 0, 0, 1, 0, 0, 0); ++ sial_msg("\n"); ++ return 0; ++} ++ ++node_t* ++sial_newptype(var_t*v) ++{ ++node_t*n=sial_newnode(); ++ ++ n->data=v->next->v; ++ v->next->v=0; /* save value against freeing */ ++ sial_freevar(v->next); ++ sial_freevar(v); ++ n->exe=(xfct_t)sial_ptype; ++ n->free=(ffct_t)sial_freeval; ++ n->name=0; ++ sial_setpos(&n->pos); ++ return n; ++} ++ ++static value_t * ++sial_pval(node_t*n) ++{ ++value_t *v=NODE_EXE(n); ++char *name=NODE_NAME(n); ++ ++ sial_ptype2(&v->type, v, 0, 1, name, 0, 0); ++ sial_free(name); ++ sial_freeval(v); ++ return 0; ++} ++ ++node_t* ++sial_newpval(node_t*vn, int fmt) ++{ ++node_t*n=sial_newnode(); ++ ++ n->data=vn; ++ n->exe=(xfct_t)sial_pval; ++ n->free=(ffct_t)sial_freenode; ++ n->name=0; ++ sial_setpos(&n->pos); ++ return n; ++} +--- crash/extensions/libsial/README.sial.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/README.sial 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,97 @@ ++ ++ This file (README.sial) gives some information specific to the crash(1) ++ integration of sial. ++ ++ Please refer to the README file generic libsial informations. ++ ++ An example script can be found as ../scripts/sial.c ++ ++ PATHS and ENVIRONMENT ++ ===================== ++ ++ The default location to look for macros and include files are ++ /usr/share/sial/.sial and ~/..sial ++ ++ The default 'root' for #include location are thus: ++ ++ /usr/share/sial/crash/include and ~/.sial/include. ++ ++ There are two environment variables that control these locations. ++ ++ SIAL_IPATH : path to use for include files. ++ ex: setenv SIAL_IPATH /usr/include:$(ROOT)/usr/include:~/.lcrash/include ++ ++ SIAL_MPATH : path to use for finding macro files. ++ ex: setenv SIAL_MPATH /usr/tmp/macs:~/.sial ++ ++ #define's ++ ===================== ++ ++ The current independent #define's are: ++ ++ Name Value/format ++ ==== ===== ++ linux 1 ++ __linux 1 ++ __linux__ 1 ++ unix 1 ++ __unix 1 ++ __unix 1 ++ LINUX_RELEASE 0x%08x ++ LINUX_2_2_16 (LINUX_RELEASE==0x020210) ++ LINUX_2_2_17 (LINUX_RELEASE==0x020211) ++ LINUX_2_4_0 (LINUX_RELEASE==0x020400) ++ LINUX_2_2_X (((LINUX_RELEASE) & 0xffff00) == 0x020200) ++ LINUX_2_4_X (((LINUX_RELEASE) & 0xffff00) == 0x020400) ++ ++ For i386 images/cores only. ++ ++ Name Value ++ ==== ==== ++ i386 1 ++ __i386 1 ++ __i386__ 1 ++ ++ For ia64 images/cores only. ++ ++ Name Value ++ ==== ===== ++ ia64 1 ++ __ia64 1 ++ __ia64__ 1 ++ __LP64__ 1 ++ _LONGLONG 1 ++ __LONG_MAX__ 9223372036854775807L ++ ++ If you feel there should be more standard defined pushed ++ there, let me know. ++ ++ ++ Loading/Unloading ++ ===================== ++ ++ crash defines two new commands for loading and unloading sial ++ macros called "sload" and "sunload", respectively. ++ ++ Using "sload" should be enough, since sial automaticly "sunload's" ++ the previous copy after successfully compiling the new one. ++ ++ DSO's must be unload'ed before you can reload them. ++ ++ Editing ++ ===================== ++ ++ To facilitate macro editing, crash makes a "edit" command available. ++ edit will get you directly in the macro file that ++ defines function "funcname" at the line where "funcname" starts. ++ ++ edit -f /somedir/somefile, will start editing a new/old file. ++ ++ edit -l will get you position on the "l"ast compile or runtime error. ++ ++ Macro integration into the crash framework ++ ============================================= ++ ++ Refer to the README file on writing a user level command. ++ Also look at the 'sial.c' example in the scripts directory ++ +--- crash/extensions/libsial/sial_stat.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_stat.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,435 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include "sial.h" ++#include "sial.tab.h" ++#include ++#include ++ ++#define MAXPARMS 10 ++ ++typedef struct stat { ++ ++ int stype; ++ int np; ++ struct stat *next; ++ srcpos_t pos; ++ node_t*n; ++ node_t*parms[MAXPARMS]; ++ var_t*svs; /* if statement block then these are the auto and static ++ wars for it */ ++ var_t*avs; ++ ++} stat; ++ ++#define SETVS value_t *v1=0,*v2=0,*v3=0,*v4=0 ++#define FV1 sial_freeval(v1),v1=0 ++#define FV2 sial_freeval(v2),v2=0 ++#define FV3 sial_freeval(v3),v3=0 ++#define FV4 sial_freeval(v4),v4=0 ++#define UNSETVS FV1,FV2,FV3,FV4 ++ ++#define P1 (s->parms[0]) ++#define P2 (s->parms[1]) ++#define P3 (s->parms[2]) ++#define P4 (s->parms[3]) ++ ++#define V1 (v1?v1:(v1=NODE_EXE(P1))) ++#define V2 (v2?v2:(v2=NODE_EXE(P2))) ++#define V3 (v3?v3:(v3=NODE_EXE(P3))) ++#define V4 (v4?v4:(v4=NODE_EXE(P4))) ++ ++#define L1 (unival(V1)) ++#define L2 (unival(V2)) ++#define L3 (unival(V3)) ++#define L4 (unival(V4)) ++ ++#define S1 (V1->v.data) ++#define S2 (V2->v.data) ++#define S3 (V3->v.data) ++#define S4 (V4->v.data) ++ ++/* this is used to execute staement lists e.g. i=1,j=3; */ ++static value_t* ++sial_exeplist(node_t*n) ++{ ++value_t *val=0; ++ ++ if(n) { ++ ++ do { ++ ++ if(val) sial_freeval(val), val=0; ++ val=NODE_EXE(n); ++ n=n->next; ++ ++ } while(n); ++ } ++ return val; ++} ++ ++static int ++sial_dofor(stat *s) ++{ ++jmp_buf brkenv; ++jmp_buf cntenv; ++SETVS; ++ ++ if(!setjmp(brkenv)) { ++ ++ sial_pushjmp(J_BREAK, &brkenv, 0); ++ ++ v1=sial_exeplist(P1); ++ FV1; ++ ++ while(!P2 || sial_bool(V2)) { ++ ++ FV2; ++ ++ if(!setjmp(cntenv)) { ++ ++ sial_pushjmp(J_CONTINUE, &cntenv, 0); ++ V4; ++ FV4; ++ sial_popjmp(J_CONTINUE); ++ ++ } ++ ++ UNSETVS; /* make sure we re-execute everything each time */ ++ v3=sial_exeplist(P3); ++ FV3; ++ } ++ sial_popjmp(J_BREAK); ++ ++ } ++ UNSETVS; ++ return 1; ++} ++ ++static int ++sial_dowhile(stat *s) ++{ ++jmp_buf brkenv; ++jmp_buf cntenv; ++SETVS; ++ ++ if(!setjmp(brkenv)) { ++ ++ sial_pushjmp(J_BREAK, &brkenv, 0); ++ ++ while(sial_bool(V1)) { ++ ++ FV1; ++ ++ if(!setjmp(cntenv)) { ++ ++ sial_pushjmp(J_CONTINUE, &cntenv, 0); ++ V2; ++ FV2; ++ sial_popjmp(J_CONTINUE); ++ ++ } ++ ++ UNSETVS; /* make sure we re-execute everything each time */ ++ } ++ FV1; ++ sial_popjmp(J_BREAK); ++ ++ } ++ ++ return 1; ++} ++ ++static int ++sial_dodo(stat *s) ++{ ++jmp_buf brkenv; ++jmp_buf cntenv; ++SETVS; ++ ++ if(!setjmp(brkenv)) { ++ ++ sial_pushjmp(J_BREAK, &brkenv, 0); ++ ++ do { ++ ++ FV2; ++ if(!setjmp(cntenv)) { ++ ++ sial_pushjmp(J_CONTINUE, &cntenv, 0); ++ V1; ++ FV1; ++ sial_popjmp(J_CONTINUE); ++ ++ } ++ ++ UNSETVS; /* make sure we re-execute everything each time */ ++ ++ } while (sial_bool(V2)); ++ FV2; ++ ++ sial_popjmp(J_BREAK); ++ ++ } ++ ++ UNSETVS; ++ return 1; ++} ++ ++static int ++sial_doif(stat *s) ++{ ++SETVS; ++ul b; ++ ++ b=sial_bool(V1); ++ FV1; ++ ++ if(s->np==3) { ++ ++ if (b) ++ V2; ++ else ++ V3; ++ ++ } else { ++ ++ if (b) ++ V2; ++ ++ } ++ ++ UNSETVS; ++ return 1; ++} ++ ++static int ++sial_doswitch(stat *s) ++{ ++jmp_buf brkenv; ++ull cval; ++SETVS; ++ ++ if(!setjmp(brkenv)) { ++ ++ sial_pushjmp(J_BREAK, &brkenv, 0); ++ cval=unival(V1); ++ FV1; ++ sial_docase(cval, P2->data); ++ sial_popjmp(J_BREAK); ++ ++ } ++ ++ UNSETVS; ++ return 1; ++} ++ ++static void ++sial_exein(stat *s) ++{ ++jmp_buf cntenv; ++SETVS; ++ ++ if(!setjmp(cntenv)) { ++ ++ sial_pushjmp(J_CONTINUE, &cntenv, 0); ++ V3; ++ sial_popjmp(J_CONTINUE); ++ ++ } ++ UNSETVS; ++} ++ ++static int ++sial_doin(stat *s) ++{ ++jmp_buf brkenv; ++ if(!setjmp(brkenv)) { ++ ++ sial_pushjmp(J_BREAK, &brkenv, 0); ++ sial_walkarray(P1, P2, (void (*)(void *))sial_exein, s); ++ sial_popjmp(J_BREAK); ++ } ++ return 1; ++} ++ ++/* this is where all of the flow control takes place */ ++ ++static value_t* ++sial_exestat(stat *s) ++{ ++srcpos_t p; ++value_t *val=0; ++ ++ do { ++ ++ /* dump the val while looping */ ++ if(val) sial_freeval(val); ++ val=0; ++ ++ sial_curpos(&s->pos, &p); ++ ++ ++ switch(s->stype) { ++ ++ case FOR : sial_dofor(s); break; ++ case WHILE: sial_dowhile(s); break; ++ case IN: sial_doin(s); break; ++ case IF: sial_doif(s); break; ++ case DO: sial_dodo(s); break; ++ case SWITCH: sial_doswitch(s); break; ++ case DOBLK: ++ { ++ int lev; ++ ++ /* add any static variables to the current context */ ++ lev=sial_addsvs(S_STAT, s->svs); ++ sial_addsvs(S_AUTO, sial_dupvlist(s->avs)); ++ ++ /* with the block statics inserted exeute the inside stmts */ ++ if(s->next) val=sial_exestat(s->next); ++ ++ /* remove any static variables to the current context */ ++ if(s->svs) sial_setsvlev(lev); ++ ++ sial_curpos(&p, 0); ++ ++ return val; ++ } ++ ++ case BREAK: sial_dojmp(J_BREAK, 0); break; ++ case CONTINUE: sial_dojmp(J_CONTINUE, 0); break; ++ case RETURN: { ++ ++ ++ if(s->parms[0]) { ++ ++ val=(s->parms[0]->exe)(s->parms[0]->data); ++ } ++ else val=sial_newval(); ++ ++ sial_curpos(&p, 0); ++ sial_dojmp(J_RETURN, val); ++ } ++ break; ++ case PATTERN: ++ ++ val=sial_exeplist(s->parms[0]); ++ ++ } ++ ++ sial_curpos(&p, 0); ++ ++ } while((s=s->next)); ++ ++ /* we most return a type val no mather what it is */ ++ /* that's just the way it is...Somethings will never change...*/ ++ if(!val) val=sial_newval(); ++ ++ return val; ++} ++ ++void ++sial_freestat(stat *s) ++{ ++int i; ++ ++ if(s->next) sial_freenode(s->next->n); ++ ++ for(i=0;inp && s->parms[i];i++) { ++ ++ NODE_FREE(s->parms[i]); ++ ++ } ++ sial_free(s); ++} ++ ++void ++sial_freestat_static(stat *s) ++{ ++ ++ if(s->next) sial_freenode(s->next->n); ++ ++ /* free associated static var list */ ++ sial_freesvs(s->svs); ++ sial_freesvs(s->avs); ++ sial_free(s); ++} ++ ++var_t*sial_getsgrp_avs(node_t*n) { return ((stat *)n->data)->avs; } ++var_t*sial_getsgrp_svs(node_t*n) { return ((stat *)n->data)->svs; } ++ ++/* add a set of static variable to a statement */ ++node_t* ++sial_stat_decl(node_t*n, var_t*svs) ++{ ++node_t*nn; ++stat *s; ++ ++ sial_validate_vars(svs); ++ ++ nn=sial_newnode(); ++ s=sial_alloc(sizeof(stat)); ++ ++ /* add statics and autos to this statement */ ++ s->svs=sial_newvlist(); ++ s->avs=sial_newvlist(); ++ sial_addnewsvs(s->avs, s->svs, svs); ++ ++ if(n) s->next=(stat*)(n->data); ++ else s->next=0; ++ s->stype=DOBLK; ++ s->n=nn; ++ nn->exe=(xfct_t)sial_exestat; ++ nn->free=(ffct_t)sial_freestat_static; ++ nn->data=s; ++ sial_setpos(&s->pos); ++ ++ return nn; ++} ++ ++node_t* ++sial_newstat(int type, int nargs, ...) ++{ ++va_list ap; ++node_t*n=sial_newnode(); ++stat *s=sial_alloc(sizeof(stat)); ++int i; ++ ++ s->stype=type; ++ ++ va_start(ap, nargs); ++ ++ for(i=0;iparms[i]=va_arg(ap, node_t*); ++ } ++ ++ s->np=i; ++ s->n=n; ++ s->next=0; ++ n->exe=(xfct_t)sial_exestat; ++ n->free=(ffct_t)sial_freestat; ++ n->data=s; ++ ++ sial_setpos(&s->pos); ++ ++ va_end(ap); ++ return n; ++} ++ ++node_t* ++sial_addstat(node_t*list, node_t*s) ++{ ++ if(!s && list) return list; ++ if(s && !list) return s; ++ else { ++ stat *sp=(stat*)(list->data); ++ ++ while(sp->next) sp=sp->next; ++ sp->next=(stat*)(s->data); ++ return list; ++ ++ } ++} ++ +--- crash/extensions/libsial/sial_num.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_num.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,233 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include ++#include "sial.h" ++ ++typedef struct { ++ int type; ++ ull val; ++} num; ++ ++/* ++ Numeric constants. ++*/ ++ ++static value_t* ++sial_exenum(num *n) ++{ ++value_t *v=sial_newval(); ++ ++ v->type.type=V_BASE; ++ v->type.idx=n->type; ++ if(n->type==B_SLL) { ++ ++ll: ++ v->v.sll=n->val; ++ v->type.size=8; ++ ++ }else if(n->type==B_SC) { ++ ++ v->v.sc=n->val; ++ v->type.size=1; ++ ++ } else { ++ ++ if(sial_defbsize()==4) { ++ ++ v->v.sl=n->val; ++ v->type.size=4; ++ ++ } else { ++ ++ v->type.idx=B_SLL; ++ goto ll; ++ } ++ } ++ v->type.typattr=sial_idxtoattr(v->type.idx); ++ v->set=0; ++ return v; ++} ++ ++void ++sial_freenumnode(num *n) ++{ ++ sial_free(n); ++} ++ ++node_t* ++sial_makenum(int type, ull val) ++{ ++node_t*n=sial_newnode(); ++num *nu=sial_alloc(sizeof(num)); ++ ++ TAG(nu); ++ ++ nu->type=type; ++ nu->val=val; ++ n->exe=(xfct_t)sial_exenum; ++ n->free=(ffct_t)sial_freenumnode; ++ n->data=nu; ++ ++ sial_setpos(&n->pos); ++ return n; ++} ++ ++/* ++ Execution of the sizeof() operator. ++ This sould be done at compile time, but I have not setup ++ a 'type only' execution path for the nodes. ++ Runtime is good enough to cover mos cases. ++*/ ++#define SN_TYPE 1 ++#define SN_EXPR 2 ++ ++typedef struct { ++ int type; ++ void *p; ++ srcpos_t pos; ++} snode_t; ++ ++static value_t * ++sial_exesnode(snode_t*sn) ++{ ++srcpos_t pos; ++type_t*t; ++value_t *v=sial_newval(); ++value_t *v2=0; ++int size; ++ ++ sial_curpos(&sn->pos, &pos); ++ if(sn->type == SN_TYPE) { ++ ++ t=(type_t*)(sn->p); ++ ++ } else { ++ ++ sial_setinsizeof(1); ++ v2=NODE_EXE((node_t*)(sn->p)); ++ t=&v2->type; ++ sial_setinsizeof(0); ++ } ++ ++ switch(t->type) { ++ ++ case V_REF: ++ ++ if(t->idxlst) { ++ ++ int i; ++ for(size=t->size,i=0;t->idxlst[i];i++) size *= t->idxlst[i]; ++ ++ } else size=sial_defbsize(); ++ ++ break; ++ case V_STRUCT: case V_UNION: ++ ++ if(sial_ispartial(t)) { ++ ++ sial_error("Invalid type specified"); ++ } ++ size=t->size; ++ ++ break; ++ case V_BASE: case V_STRING: ++ size=t->size; ++ break; ++ ++ default: size=0; ++ } ++ ++ sial_defbtype(v, (ull)size); ++ ++ sial_curpos(&pos, 0); ++ ++ if(v2) sial_freeval(v2); ++ ++ return v; ++ ++} ++ ++static void ++sial_freesnode(snode_t*sn) ++{ ++ if(sn->type == SN_TYPE) sial_free(sn->p); ++ else NODE_FREE(sn->p); ++ sial_free(sn); ++} ++ ++node_t* ++sial_sizeof(void *p, int type) ++{ ++node_t*n=sial_newnode(); ++snode_t*sn=sial_alloc(sizeof(snode_t)); ++ ++ n->exe=(xfct_t)sial_exesnode; ++ n->free=(ffct_t)sial_freesnode; ++ n->data=sn; ++ sn->type=type; ++ sn->p=p; ++ sial_setpos(&sn->pos); ++ return n; ++} ++ ++node_t* ++sial_newnum(char *buf) ++{ ++int type; ++unsigned long long val; ++ ++ type=B_SL; ++ ++ /* get the value_t of this constant. Could be hex, octal or dec. */ ++ if(buf[0]=='0') { ++ ++ if(buf[1]=='x') { ++ ++ if(!sscanf(buf, "%llx", &val)) goto error; ++ ++ } else { ++ ++ if(!sscanf(buf,"%llo", &val)) goto error; ++ } ++ ++ } else { ++ ++ if(!sscanf(buf,"%lld", &val)) goto error; ++ ++ } ++ ++ if(val & 0xffffffff00000000ll) type=B_SLL; ++ ++ /* threat the long and long long atributes */ ++ { ++ int l=strlen(buf); ++ ++ if(l>1) { ++ ++ if(buf[l-1]=='l' || buf[l-1]=='L') { ++ ++ if(l>2) { ++ ++ if(sial_defbsize()==8 || buf[l-2]=='l' || buf[l-2]=='L') { ++ ++ type=B_SLL; ++ ++ } ++ else type=B_SL; ++ ++ } ++ ++ } ++ } ++ } ++ { ++ node_t*n=sial_makenum(type, val); ++ TAG(n->data); ++ return n; ++ } ++error: ++ sial_error("Oops! NUMBER"); ++ return 0; ++} +--- crash/extensions/libsial/sial_str.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_str.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,185 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include ++#include "sial.h" ++ ++/* ++ Create a new string node from a string. ++*/ ++ ++value_t * ++sial_setstrval(value_t *val, char *buf) ++{ ++char *newbuf=sial_strdup(buf); ++ ++ val->v.data=(void*)newbuf; ++ val->type.type=V_STRING; ++ val->type.size=strlen(buf)+1; ++ val->set=0; ++ return val; ++} ++ ++value_t * ++sial_makestr(char *s) ++{ ++ return sial_setstrval(sial_newval(), s); ++} ++ ++static value_t* ++sial_exestr(char *buf) ++{ ++value_t *v=sial_newval(); ++ ++ sial_setstrval(v, buf); ++ return v; ++} ++ ++void ++sial_freestrnode(char *buf) ++{ ++ sial_free(buf); ++} ++ ++node_t* ++sial_allocstr(char *buf) ++{ ++node_t*n=sial_newnode(); ++ ++ n->exe=(xfct_t)sial_exestr; ++ n->free=(ffct_t)sial_freestrnode; ++ n->data=buf; ++ sial_setpos(&n->pos); ++ ++ return n; ++} ++ ++node_t* ++sial_strconcat(node_t*n1, node_t*n2) ++{ ++char *newbuf=sial_alloc(strlen(n1->data)+strlen(n2->data)+1); ++ ++ strcpy(newbuf, n1->data); ++ strcat(newbuf, n2->data); ++ sial_free(n1->data); ++ n1->data=newbuf; ++ sial_freenode(n2); ++ return n1; ++} ++ ++static int ++is_valid(int c, int base) ++{ ++ switch(base) ++ { ++ case 16: return (c>='0' && c<='9') || (toupper(c) >= 'A' && toupper(c) <= 'F'); ++ case 10: return (c>='0' && c<='9'); ++ case 8: return (c>='0' && c<='7'); ++ } ++ return 0; ++} ++ ++/* extract a number value_t from the input stream */ ++static int sial_getnum(int base) ++{ ++int val=0; ++ while(1) ++ { ++ char c=sial_input(), C; ++ ++ C=toupper(c); ++ if(is_valid(C, base)) { ++ ++ val=val*base; ++ val+=(C>='A')?10+('F'-C):'9'-C; ++ } ++ else ++ { ++ sial_unput(c); ++ break; ++ } ++ } ++ return val; ++} ++ ++int ++sial_getseq(int c) ++{ ++int i; ++static struct { ++ int code; ++ int value; ++} seqs[] = { ++ { 'n', '\n' }, ++ { 't', '\t' }, ++ { 'f', '\f' }, ++ { 'r', '\r' }, ++ { 'n', '\n' }, ++ { 'v', '\v' }, ++ { '\\', '\007' }, ++}; ++ for(i=0;i ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sial.h" ++ ++char *sialpp_create_buffer(void *, int); ++void sialpp_switch_to_buffer(void *); ++void sialpp_delete_buffer(void *); ++ ++typedef void fdone(void *data); ++extern void* sial_create_buffer(FILE *, int); ++typedef struct inbuf_s { ++ srcpos_t pos; /* current filename,line,col */ ++ int cursor; /* position of next input() character */ ++ int len; /* length of the buffer */ ++ char *buf; /* buffer */ ++ void *data; /* opaque data for callback */ ++ void *mac; /* for nested defines substitutions */ ++ fdone *donefunc; /* function to call when done with buffer */ ++ int space; ++ int eofonpop; /* terminate parsing at end of buffer ? */ ++#if linux ++ void* yybuf; ++#endif ++ ++} inbuf_t; ++ ++void sial_switch_to_buffer(void *); ++void sial_delete_buffer(void *); ++#define MAXIN 20 ++static inbuf_t inlist[MAXIN]; ++static inbuf_t *in=0; ++static int nin=0; ++static int eol=0, virgin=1; ++#if linux ++static int inpp=0; ++#endif ++ ++extern void *sial_getmac(char *, int); ++ ++/* this function is called by the macro macro functions to set ++ and test the current buffer level in order to take care of ++ imbedded macros w/ the same parameter names. ++ see sial_getmac(). ++*/ ++void *sial_getcurmac(void) ++{ ++ return in ? in->mac : 0 ; ++} ++ ++static void ++sial_pusherror(void) ++{ ++ sial_error("Too many level of input stream"); ++} ++ ++/* ++ Push a buffer onto the parser input stream. ++*/ ++void ++sial_pushbuf(char *buf, char *fname, void (*vf)(void *), void *d, void *m) ++{ ++fdone *f=(fdone*)vf; ++ ++ if(nin==MAXIN) sial_pusherror(); ++ ++ /* if we are pushing a macro then use upper level coordinates */ ++ if(fname) { ++ ++ inlist[nin].pos.line=1; ++ inlist[nin].pos.col=1; ++ inlist[nin].pos.file=fname; ++ ++ } else sial_setpos(&inlist[nin].pos); ++ ++ /* set it */ ++ if(nin) { ++ ++ sial_curpos(&inlist[nin].pos, &inlist[nin-1].pos); ++ ++ } else { ++ ++ sial_curpos(&inlist[nin].pos, 0); ++ ++ } ++ ++ inlist[nin].buf=buf; ++ inlist[nin].donefunc=f; ++ inlist[nin].space=0; ++ inlist[nin].data=d; ++ inlist[nin].mac=m; ++ inlist[nin].cursor=0; ++ inlist[nin].len=strlen(buf); ++ inlist[nin].eofonpop=0; ++#if linux ++ if(inpp) { ++ inlist[nin].yybuf=sialpp_create_buffer(0, inlist[nin].len); ++ sialpp_switch_to_buffer(inlist[nin].yybuf); ++ }else{ ++ inlist[nin].yybuf=sial_create_buffer(0, inlist[nin].len); ++ sial_switch_to_buffer(inlist[nin].yybuf); ++ } ++#endif ++ in=&inlist[nin]; ++ nin++; ++} ++ ++/* read the rest of the "#include" line from the parser input stream ++ open the corresponding file, push it's contain on the parser input ++ stream. ++*/ ++int ++sial_pushfile(char *name) ++{ ++struct stat s; ++char *fname; ++ ++ if(nin==MAXIN) sial_pusherror(); ++ ++ fname=sial_fileipath(name); ++ ++ if(fname) { ++ ++ if(!stat(fname, &s)) { ++ ++ char *buf=sial_alloc(s.st_size+1); ++ int fd; ++ ++ if((fd=open(fname, O_RDONLY))==-1) { ++ ++ sial_msg("%s: %s", fname, strerror(errno)); ++ ++ } ++ else { ++ ++ if(read(fd, buf, s.st_size) != s.st_size) { ++ ++ if(errno != EISDIR) ++ sial_msg("%s: read error : %s", fname, strerror(errno)); ++ ++ } ++ else { ++ ++ ++ buf[s.st_size]='\0'; ++ sial_pushbuf(buf, fname, sial_free, buf, 0); ++ close(fd); ++ return 1; ++ ++ } ++ close(fd); ++ } ++ ++ sial_free(buf); ++ ++ } ++ sial_free(fname); ++ } ++ return 0; ++ ++} ++ ++/* ++ Done with the current buffer. ++ Go back to previous on the stack. ++*/ ++static int ++sial_popin(void) ++{ ++ ++ if(eol || !nin) { ++ ++ if(!nin) in=0; ++ return 1; ++ ++ } else { ++ ++ nin--; ++ ++ /* call back */ ++ if(inlist[nin].donefunc) { ++ ++ inlist[nin].donefunc(inlist[nin].data); ++ } ++ if(inlist[nin].eofonpop) { ++ ++ eol=1; ++#if linux ++ inpp=0; ++#endif ++ } ++ if(!nin) in=0; ++ else { ++ ++ in=&inlist[nin-1]; ++ if(!eol) { ++#if linux ++ if(inpp) { ++ sialpp_switch_to_buffer(inlist[nin-1].yybuf); ++ sialpp_delete_buffer(inlist[nin].yybuf); ++ } else { ++ sial_switch_to_buffer(inlist[nin-1].yybuf); ++ sial_delete_buffer(inlist[nin].yybuf); ++ } ++#endif ++ } ++ sial_curpos(&in->pos, 0); ++ } ++ return 0; ++ } ++} ++ ++/* ++ With linux we need to use the wrap function ++ so that the flex buffer stuff is keaped in the game. ++*/ ++int ++sialwrap(void) ++{ ++ return sial_popin(); ++} ++ ++int ++sialppwrap(void) ++{ ++ if(eol) return 1; ++ return sial_popin(); ++} ++ ++void ++sial_popallin(void) ++{ ++ while(nin) { ++ eol=0; ++ sial_popin(); ++ } ++} ++ ++#define BLK_IFDEF 1 ++#define BLK_IFNDEF 2 ++#define BLK_IF 3 ++#define BLK_ELIF 4 ++#define BLK_ELSE 5 ++ ++typedef struct ifblk { ++ int type; /* type of block */ ++ int exprpos; /* curpor to start of corresponding expression */ ++ int bstart; /* curpor position at start of block */ ++ int dirlen; /* length of the directive name */ ++ int bend; /* cursor position at end of block */ ++ struct ifblk *next; ++} ifblk_t; ++ ++static int ++sial_isif(int pos) ++{ ++ if( ++ (in->len-pos>6 && !strncmp(in->buf+pos, "ifndef", 6)) ++ || (in->len-pos>5 && !strncmp(in->buf+pos, "ifdef", 5)) ++ || (in->len-pos>2 && !strncmp(in->buf+pos, "if", 2)) ++ ++ ) return 1; ++ ++ return 0; ++} ++ ++/* ++ Get directly to next block, skipping nested blocks. ++*/ ++static int ++sial_nxtblk(int pos, int lev) ++{ ++int virgin=0; ++ ++ while(1) { ++ ++ if(pos==in->len) { ++ ++ sial_error("Block without endif"); ++ } ++ ++ if(virgin && in->buf[pos]=='#') { ++ ++ pos++; ++ ++ /* nested if ? */ ++ if(in->buf[pos]=='i' && sial_isif(pos)) { ++ ++ while(1) { ++ pos=sial_nxtblk(pos, lev+1); ++ if(in->len-pos>5 && !strncmp(in->buf+pos, "endif", 5)) break; ++ } ++ ++ } else if(in->buf[pos]=='e') return pos; ++ ++ } else if(in->buf[pos]=='\n') { ++ ++ virgin=1; ++ ++ } else if(in->buf[pos] != ' ' && in->buf[pos] != '\t') { ++ ++ virgin=0; ++ } ++ pos++; ++ } ++} ++ ++static ifblk_t * ++sial_getblklst(void) ++{ ++ifblk_t *lst, *last; ++int doneelse=0, pos; ++ ++ lst=sial_alloc(sizeof(ifblk_t)); ++ ++ lst->bstart=in->cursor-1; ++ if(!strncmp(in->buf+in->cursor, "ifdef", 5)) { ++ ++ lst->type=BLK_IFDEF; ++ lst->exprpos=lst->bstart+6; ++ lst->dirlen=6; ++ ++ } else if(!strncmp(in->buf+in->cursor, "ifndef", 6)){ ++ ++ lst->type=BLK_IFNDEF; ++ lst->exprpos=lst->bstart+7; ++ lst->dirlen=7; ++ ++ } else { ++ ++ lst->type=BLK_IF; ++ lst->exprpos=lst->bstart+3; ++ lst->dirlen=3; ++ } ++ ++ last=lst; ++ pos=in->cursor; ++ ++ while(1) { ++ ++ ifblk_t *new=sial_alloc(sizeof(ifblk_t)); ++ ++ pos=sial_nxtblk(pos, 0); ++ ++ last->bend=pos-2; ++ new->bstart=pos-1; ++ if(!strncmp(in->buf+pos, "elif", 4)) { ++ ++ if(doneelse) { ++ ++ sial_error("Additional block found after #else directive"); ++ } ++ new->type=BLK_ELIF; ++ new->exprpos=new->bstart+5; ++ new->dirlen=5; ++ ++ } else if(!strncmp(in->buf+pos, "else", 4)) { ++ ++ if(doneelse) { ++ ++ sial_error("#else already done"); ++ } ++ new->type=BLK_ELSE; ++ new->exprpos=new->bstart+5; ++ new->dirlen=5; ++ doneelse=1; ++ ++ } else if(!strncmp(in->buf+pos, "endif", 5)) { ++ ++ sial_free(new); ++ last->next=0; ++ break; ++ } ++ last->next=new; ++ last=new; ++ } ++ return lst; ++} ++ ++/* ++ Zap a complete block. ++ We put spaces everywhere but over the newline. ++ Hey, it works. It's good enough for me. ++*/ ++static void ++sial_zapblk(ifblk_t *blk) ++{ ++int i; ++ ++ for(i=blk->bstart;ibend;i++) { ++ ++ if(in->buf[i]!='\n') in->buf[i]=' '; ++ } ++} ++ ++int sial_eol(char c) { return (!c || c=='\n') ? 1 : 0; } ++ ++/* ++ This function is called by sial_input() when a #if[def] is found. ++ We gather all blocks of the if/then/else into a list. ++ Parsing and execution of the expression is done only when needed. ++*/ ++void sial_rsteofoneol(void) ++{ ++ eol=0; ++ virgin=1; ++#if linux ++ inpp=0; ++#endif ++} ++ ++void ++sial_zapif(void) ++{ ++ifblk_t *lst=sial_getblklst(); ++ifblk_t *last=lst; ++int b=0; ++ ++ /* we scan the entire list untill a condition is true or we ++ reach #else or we reach the end */ ++ while(lst) { ++ ++ switch(lst->type) { ++ ++ case BLK_IFDEF: ++ case BLK_IFNDEF: ++ { ++ char mname[MAX_SYMNAMELEN+1], c; ++ int i=0, j=lst->bstart+lst->dirlen; ++ int v; ++ ++ /* get the macro name and see if it exists */ ++ /* skip all white spaces */ ++ while((c=in->buf[j]) == ' ' || c == '\t') if(c=='\n' || !c) { ++ ++ sial_error("Macro name not found!"); ++ ++ } else j++; ++ ++ /* get the constant or macro name */ ++ while((c=in->buf[j]) != ' ' && c != '\t' && c != '(') { ++ ++ if(c=='\n' || !c) break; ++ ++ if(i==MAX_SYMNAMELEN) break; ++ ++ mname[i++]=c; ++ j++; ++ } ++ mname[i]='\0'; ++ lst->dirlen += (j-lst->bstart-lst->dirlen); ++ if(sial_getmac(mname,0)) v=1; ++ else v=0; ++ b=lst->type==BLK_IFDEF?v:!v; ++ ++ } ++ break; ++ ++ case BLK_IF: case BLK_ELIF: ++ { ++ node_t*n; ++ void sialpprestart(int); ++ void sialppparse(void); ++ char *expr=sial_getline(); ++ int len=lst->dirlen; ++ ++#if linux ++ sialpprestart(0); ++ inpp=1; ++#endif ++ lst->dirlen += (in->cursor-lst->exprpos-1); ++ sial_pushbuf(expr, 0, sial_free, expr, 0); ++ in->eofonpop=1; ++ in->cursor += len; ++ sialppparse(); ++ ++ sial_rsteofoneol(); ++ eol=0; ++ ++ /* get the resulting node_t*/ ++ n=sial_getppnode(); ++ ++ /* execute it */ ++ { ++ ++ int *exval; ++ jmp_buf exitjmp; ++ void *sa; ++ value_t *v; ++ ++ sa=sial_setexcept(); ++ ++ if(!setjmp(exitjmp)) { ++ ++ sial_pushjmp(J_EXIT, &exitjmp, &exval); ++ v=NODE_EXE(n); ++ sial_rmexcept(sa); ++ sial_popjmp(J_EXIT); ++ b=sial_bool(v); ++ sial_freeval(v); ++ ++ } else { ++ ++ sial_rmexcept(sa); ++ sial_parseback(); ++ } ++ } ++ } ++ break; ++ ++ case BLK_ELSE: ++ { ++ ++ b=1; ++ ++ } ++ break; ++ } ++ ++ last=lst; ++ if(b) break; ++ ++ /* count new lines */ ++ { ++ while(in->cursor < lst->bend+1) { ++ ++ if(sial_eol(in->buf[in->cursor])) ++ sial_line(1); ++ in->cursor++; ++ } ++ ++ } ++ lst=lst->next; ++ } ++ ++ if(lst) { ++ ++ /* remove the # directive itself */ ++ memset(in->buf+lst->bstart, ' ', lst->dirlen); ++ ++ /* zap all remaining blocks */ ++ while((lst=lst->next)) { sial_zapblk(lst); last=lst; } ++ } ++ ++ /* most remove the #endif */ ++ memset(in->buf+last->bend+1, ' ', 6); ++} ++ ++static int rawinput=0; ++void sial_rawinput(int on) { rawinput=on; } ++ ++/* ++ Get the next character from the input stream tack. ++*/ ++int ++sial_input(void) ++{ ++register char c; ++ ++redo: ++ ++ if(!in || eol) { ++ ++ return 0; ++ } ++ ++ if(in->cursor==in->len) { ++ ++#if linux ++ return (-1); ++#else ++ sial_popin(); ++ goto redo; ++#endif ++ } ++ ++ c=in->buf[in->cursor++]; ++ if(!rawinput) { ++ if(c=='\\') { ++ ++ if(in->cursor==in->len) return c; ++ else if(in->buf[in->cursor]=='\n') { ++ ++ sial_line(1); ++ in->cursor++; ++ goto redo; ++ } ++ ++ } else if(c=='/') { ++ ++ if(in->cursor==in->len) return c; ++ else if(in->buf[in->cursor]=='/') { ++ ++ /* C++ stype comment. Eat it. */ ++ in->cursor++; ++ while(in->cursorlen) { ++ ++ c=in->buf[in->cursor++]; ++ if(c=='\n') { ++ /* leave the newline in there */ ++ in->cursor--; ++ break; ++ } ++ } ++ goto redo; ++ ++ }else if(in->buf[in->cursor]=='*') { ++ ++ /* C style comment, eat it */ ++ in->cursor++; ++ while(in->cursorlen) { ++ ++ c=in->buf[in->cursor++]; ++ if(c=='*' && (in->cursorlen)) { ++ ++ if(in->buf[in->cursor]=='/') { ++ ++ in->cursor++; ++ break; ++ ++ } ++ ++ } else if(c=='/' && (in->cursorlen)) { ++ ++ if(in->buf[in->cursor]=='*') { ++ ++ sial_warning("Nested comment"); ++ ++ } ++ ++ } ++ if(c=='\n') sial_line(1); ++ } ++ goto redo; ++ } ++ ++ }else if(virgin && c=='#') { ++ ++ char *p=in->buf+in->cursor; ++ char *end=in->buf+in->len; ++ int c=0; ++ ++ /* skip white spaces '# define ... ' */ ++ while(p<(end-4) && (*p==' ' || *p=='\t')) { p++; c++; } ++ ++ /* this must be a preprocessor command */ ++ /* we trigger on the if, ifdef only. #define, #undef, #include are ++ handled by the lexer */ ++ ++ if(!strncmp(p, "if", 2)) { ++ ++ in->cursor += c; ++ sial_zapif(); ++ /* zapif sets the cursor correctly */ ++ goto redo; ++ } ++ } ++ } ++ ++ if(c=='\n') { ++ ++ virgin=1; ++ sial_line(1); ++ ++ }else if(c != ' ' && c != '\t') { ++ ++ virgin=0; ++ ++ } ++ else if(!rawinput){ ++ ++ register char c2=c; ++ ++ /* return one white space for a group of them */ ++ while((in->cursor < in->len) ++ && in->buf[in->cursor]==c2) in->cursor++; ++ ++ } ++ ++ return c; ++} ++ ++char * ++sial_cursorp() ++{ ++ if(!in) return 0; ++ return in->buf+in->cursor; ++} ++ ++void ++sial_unput(char c) ++{ ++ ++ if(!c) return; ++ if(!nin) return; ++ if(!in->cursor) { ++ ++ sial_error("Fatal unput error"); ++ ++ } ++ in->buf[--in->cursor]=c; ++ if(c=='\n') { ++ ++ sial_line(-1); ++ } ++} ++ ++/* ++ Get a single line from the parser stream. ++*/ ++char * ++sial_getline() ++{ ++char *buf2=0; ++ ++ /* use the current input stream for that */ ++ if(in) { ++ ++ /* save the cursor */ ++ int n=0, c; ++ char *buf=sial_alloc(in->len-in->cursor+1); ++ ++ while(!sial_eol(c=sial_input())) ++ buf[n++]=c; ++ buf[n]='\0'; ++ buf2=sial_alloc(n+2); ++ strcpy(buf2,buf); ++ buf2[n]=' '; ++ buf2[n+1]='\0'; ++ sial_free(buf); ++ /* leave the newline there */ ++ sial_unput(c); ++ } ++ return buf2; ++} ++ ++ ++/* read a complete line from the input stream */ ++void ++sial_include(void) ++{ ++char name[MAX_SYMNAMELEN+1]; ++int n=0; ++int c; ++int found=0; ++ ++ while((c=sial_input())) { ++ ++ if(c=='"') { ++ ++ if(!found) found++; ++ else break; ++ continue; ++ } ++ ++ if(c=='<') { ++ ++ found++; ++ continue; ++ ++ } ++ if(c=='>') break; ++ if(sial_eol(c)) { ++ ++ sial_error("Unexpected EOL on #include"); ++ } ++ if(found) { ++ ++ if(n==MAX_SYMNAMELEN) { ++ ++ sial_error("Filename too long"); ++ } ++ name[n++]=c; ++ ++ } ++ } ++ name[n]='\0'; ++ ++ /* flush the rest of the line */ ++ while((c=sial_input())) { ++ ++ if(sial_eol(c)) break; ++ } ++ sial_unput(c); ++ if(sial_fileipath(name)) { ++ ++ sial_pushfile(name); ++ ++ } else { ++ ++ sial_msg("Include file not found: '%s' [include path is '%s']", name, sial_getipath()); ++ } ++} +--- crash/extensions/libsial/mkbaseop.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/mkbaseop.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,169 @@ ++/* ++ * Copyright 2000 Silicon Graphics, Inc. All rights reserved. ++ */ ++ ++#include ++#include ++#include "sial.h" ++#include "sial.tab.h" ++/* ++ This utility generates a operator function table for the base type. ++ Each combinaison of operand type and operation needs a dedicated ++ function. We use a table defined here in to generate an indirect table ++ that if indexed (from within sial_op.c) using : ++ ++ value_t * (func)(value_t *v1, value_t *v2) = table[type1][type2][op]; ++*/ ++static struct opss { ++ char *str; ++ char *acro; ++ int code; ++} opstbl[] = { ++ { "+", "ADD", ADD }, ++ { "-", "SUB", SUB }, ++ { "/", "DIV", DIV }, ++ { "*", "MUL", MUL }, ++ { "^", "XOR", XOR }, ++ { "%", "MOD", MOD }, ++ { "|", "OR", OR }, ++ { "&", "AND", AND }, ++ { "<<", "SHL", SHL }, ++ { ">>", "SHR", SHR }, ++ { "==", "EQ", EQ }, /* most be first bool */ ++ { ">", "GT", GT }, ++ { "<", "LT", LT }, ++ { ">=", "GE", GE }, ++ { "<=", "LE", LE }, ++ { "!=", "NE", NE }, ++}; ++ ++static char *typtbl[] = { "sc", "uc", "ss", "us", "sl", "ul", "sll", "ull" }; ++ ++#define NOPS (sizeof(opstbl)/sizeof(opstbl[0])) ++#define NTYPS (sizeof(typtbl)/sizeof(typtbl[0])) ++ ++int ++main() ++{ ++int i,j,k; ++ ++ printf("\ ++#include \"sial.h\"\n\ ++#include \"sial.tab.h\"\n\ ++/**************************************************************\n\ ++ This file is generated by a program.\n\ ++ Check out and modify libsial/mkbaseop.c instead !\n\ ++***************************************************************/\n"); ++ ++ ++ /* create all the functions for all combinaison */ ++ for(i=0;iv.%s = v1->v.%s %s v2->v.%s;\n" ++" ret->type.type=%s->type.type;\n" ++" ret->type.idx=%s->type.idx;\n" ++" ret->type.size=%s->type.size;\n" ++"}\n", ++ opstbl[k].acro, ++ typtbl[i], ++ typtbl[j], ++ j>=i?typtbl[j]:typtbl[i], ++ typtbl[i], ++ opstbl[k].str, ++ typtbl[j], ++ j>=i?"v2":"v1", ++ j>=i?"v2":"v1", ++ j>=i?"v2":"v1"); ++ ++ } else { ++ ++ printf("" ++"static void \n" ++"op_%s_%s_%s(value_t *v1,value_t *v2,value_t *ret)\n" ++"{\n" ++" ret->v.%s = ( v1->v.%s %s v2->v.%s );\n" ++" ret->type.type=V_BASE;\n" ++" ret->type.idx=B_UL;\n" ++" ret->type.size=4;\n" ++"}\n", ++ opstbl[k].acro, ++ typtbl[i], ++ typtbl[j], ++ "ul", ++ typtbl[i], ++ opstbl[k].str, ++ typtbl[j]); ++ } ++ ++ } ++ ++ } ++ ++ } ++ ++ /* create the array from within which the runtime functions ++ will indexed to get a function pointer */ ++ ++ printf("void (*opfuncs[%d][%d][%d])()={\n", NTYPS, NTYPS, NOPS); ++ ++ for(i=0;itype.idx][v2->type.idx][i])(v1,v2,ret);\n\ ++}\n", NOPS, NOPS); ++ exit(0); ++} +--- crash/extensions/libsial/sial_type.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_type.c 2008-10-28 14:53:26.000000000 -0400 +@@ -0,0 +1,1169 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include "sial.h" ++#include "sial.tab.h" ++#include ++#include ++/* ++ This file contains functions that deals with type and type ++ casting operators. ++*/ ++#define B_SIZE_MASK 0x0007f0 ++#define B_SIGN_MASK 0x00f000 ++#define B_STOR_MASK 0x1f0000 ++#define B_CHAR 0x000010 ++#define B_SHORT 0x000020 ++#define B_INT 0x000040 ++#define B_LONG 0x000080 ++#define B_LONGLONG 0x000100 ++#define B_FLOAT 0x000200 ++#define B_CONST 0x000400 ++#define B_SIGNED 0x001000 ++#define B_UNSIGNED 0x002000 ++#define B_STATIC 0x010000 ++#define B_REGISTER 0x020000 ++#define B_VOLATILE 0x040000 ++#define B_TYPEDEF 0x080000 ++#define B_EXTERN 0x100000 ++#define B_VOID 0x800000 ++#define B_USPEC 0x000001 /* user specified sign */ ++#define B_ENUM 0x000002 /* btype is from a enum */ ++ ++#define is_size(i) ((i)&B_SIZE_MASK) ++#define is_sign(i) ((i)&B_SIGN_MASK) ++#define is_stor(i) ((i)&B_STOR_MASK) ++#define issigned(v) (v->type.typattr & B_SIGNED) ++#define vsize(v) (is_size(v->type.typattr)) ++ ++static struct { ++ int btype; ++ int key; ++ char *name; ++} blut[] = { ++ { B_VOID, VOID , "void"}, ++ { B_TYPEDEF, TDEF , "tdef"}, ++ { B_EXTERN, EXTERN , "extern"}, ++ { B_STATIC, STATIC , "static"}, ++ { B_VOLATILE, VOLATILE , "volatile"}, ++ { B_CONST, CONST , "const"}, ++ { B_REGISTER, REGISTER , "register"}, ++ { B_UNSIGNED, UNSIGNED , "unsigned"}, ++ { B_SIGNED, SIGNED , "signed"}, ++ { B_CHAR, CHAR, "char" }, ++ { B_SHORT, SHORT , "short"}, ++ { B_INT, INT , "int"}, ++ { B_LONG, LONG , "long"}, ++ { B_LONGLONG, DOUBLE , "long long"}, ++ { B_FLOAT, FLOAT , "float"}, ++}; ++ ++type_t * ++sial_newtype() ++{ ++ return sial_calloc(sizeof(type_t)); ++} ++ ++void ++sial_freetype(type_t* t) ++{ ++ if(t->idxlst) sial_free(t->idxlst); ++ sial_free(t); ++} ++ ++/* this function is called by the parser to merge the ++ storage information (being hold in a basetype) into the real type_t*/ ++type_t* ++sial_addstorage(type_t*t1, type_t*t2) ++{ ++ t1->typattr |= is_stor(t2->typattr); ++ sial_freetype(t2); ++ return t1; ++} ++ ++char * ++sial_ctypename(int type) ++{ ++ switch(type) { ++ ++ case V_TYPEDEF: return "typedef"; ++ case V_STRUCT: return "struct"; ++ case V_UNION: return "union"; ++ case V_ENUM: return "enum"; ++ default: return "???"; ++ } ++} ++ ++int sial_isstatic(int atr) { return atr & B_STATIC; } ++int sial_isenum(int atr) { return atr & B_ENUM; } ++int sial_isconst(int atr) { return atr & B_CONST; } ++int sial_issigned(int atr) { return atr & B_SIGNED; } ++int sial_istdef(int atr) { return atr & B_TYPEDEF; } ++int sial_isxtern(int atr) { return atr & B_EXTERN; } ++int sial_isvoid(int atr) { return atr & B_VOID; } ++int sial_isstor(int atr) { return is_stor(atr); } ++int sial_is_struct(int ctype) { return ctype==V_STRUCT; } ++int sial_is_enum(int ctype) { return ctype==V_ENUM; } ++int sial_is_union(int ctype) { return ctype==V_UNION; } ++int sial_is_typedef(int ctype) { return ctype==V_TYPEDEF; } ++ ++/* type seting */ ++int sial_type_gettype(type_t*t) { return t->type; } ++void sial_type_settype(type_t*t, int type) { t->type=type; } ++void sial_type_setsize(type_t*t, int size) { t->size=size; } ++int sial_type_getsize(type_t*t) { return t->size; } ++void sial_type_setidx(type_t*t, ull idx) { t->idx=idx; } ++ull sial_type_getidx(type_t*t) { return t->idx; } ++void sial_type_setidxlst(type_t*t, int* idxlst) { t->idxlst=idxlst; } ++void sial_type_setref(type_t*t, int ref, int type) { t->ref=ref; t->rtype=type; } ++void sial_type_setfct(type_t*t, int val) { t->fct=val; } ++void sial_type_mkunion(type_t*t) { t->type=V_UNION; } ++void sial_type_mkenum(type_t*t) { t->type=V_ENUM; } ++void sial_type_mkstruct(type_t*t) { t->type=V_STRUCT; } ++void sial_type_mktypedef(type_t*t) { t->type=V_TYPEDEF; } ++ ++static int defbtype=B_LONG|B_SIGNED; ++static int defbidx=B_SL; ++static int defbsize=4; ++static int defbsign=B_SIGNED; ++int sial_defbsize() { return defbsize; } ++ ++char * ++sial_getbtypename(int typattr) ++{ ++int i; ++char *name=sial_alloc(200); ++ ++ name[0]='\0'; ++ for(i=0;itype.type==V_REF) { ++ ++ return TYPE_SIZE(&v->type)==4 ? (ull)(v->v.ul) : v->v.ull; ++ ++ } else switch(v->type.idx) { ++ ++ case B_SC: return (ull)(v->v.sc); ++ case B_UC: return (ull)(v->v.uc); ++ case B_SS: return (ull)(v->v.ss); ++ case B_US: return (ull)(v->v.us); ++ case B_SL: return (ull)(v->v.sl); ++ case B_UL: return (ull)(v->v.ul); ++ case B_SLL: return (ull)(v->v.sll); ++ case B_ULL: return (ull)(v->v.ull); ++ default: sial_error("Oops univ()[%d]", TYPE_SIZE(&v->type)); break; ++ } ++ return 0; ++} ++ ++void ++sial_duptype(type_t*t, type_t*ts) ++{ ++ memmove(t, ts, sizeof(type_t)); ++ if(ts->idxlst) { ++ ++ t->idxlst=sial_calloc(sizeof(int)*(MAXIDX+1)); ++ memmove(t->idxlst, ts->idxlst, sizeof(int)*(MAXIDX+1)); ++ } ++} ++ ++#define asarray(v) (v->arr!=v->arr->next) ++ ++/* ++ Duplicate a value_t. ++ On duplication we do verification of the value_ts involved. ++ this is to make it possible to pass array to subfunctions ++ and to override specific value_ts that also have arrays attached ++ to them. ++*/ ++void ++sial_dupval(value_t *v, value_t *vs) ++{ ++int isvoid=(v->type.typattr & B_VOID); ++ ++ /* if both have an attached array ... fail */ ++ if(asarray(v) && asarray(vs)) { ++ ++ sial_error("Can't override array"); ++ ++ } ++ /* when we are attaching a new array to the destination value_t ++ we need to add the destination reference count to the source */ ++ if(asarray(v)) { ++ ++ array_t*a=v->arr; ++ ++ /* preserve the array accross the freedata and memmove */ ++ v->arr=0; ++ sial_freedata(v); ++ ++ /* copy the new value_t over it */ ++ memmove(v, vs, sizeof(value_t)); ++ ++ /* and restore the array_t*/ ++ v->arr=a; ++ ++ } else { ++ ++ sial_refarray(vs, 1); ++ sial_freedata(v); ++ memmove(v, vs, sizeof(value_t)); ++ } ++ ++ sial_duptype(&v->type, &vs->type); ++ sial_dupdata(v, vs); ++ ++ /* conserve the void atribute across asignements */ ++ v->type.typattr |= isvoid; ++} ++ ++/* ++ clone a value_t. ++*/ ++value_t * ++sial_cloneval(value_t *v) ++{ ++value_t *nv=sial_alloc(sizeof(value_t)); ++ ++ memmove(nv, v, sizeof(value_t)); ++ sial_refarray(v, 1); ++ sial_dupdata(nv, v); ++ return nv; ++} ++ ++static signed long long ++twoscomp(ull val, int nbits) ++{ ++ return val | (0xffffffffffffffffll << nbits); ++ // XXX return (val-1)^0xffffffffll; ++} ++ ++/* ++ Get a bit field value_t from system image or live memory. ++ We do all operations with a ull untill the end. ++ Then we check for the basetype size and sign and convert ++ apropriatly. ++*/ ++void ++get_bit_value(ull val, int nbits, int boff, int size, value_t *v) ++{ ++ ull mask; ++ int dosign=0; ++ int vnbits=size*8; ++ ++ /* first get the value_t */ ++ if (nbits >= 32) { ++ int upper_bits = nbits - 32; ++ mask = ((1 << upper_bits) - 1); ++ mask = (mask << 32) | 0xffffffff; ++ } ++ else { ++ mask = ((1 << nbits) - 1); ++ } ++ val = val >> boff; ++ val &= mask; ++ ++ if(issigned(v)) { ++ ++ /* get the sign bit */ ++ if(val >> (nbits-1)) dosign=1; ++ ++ } ++ switch(vsize(v)) { ++ ++ case B_CHAR: { ++ if(dosign) { ++ v->v.sc=(signed char)twoscomp(val, nbits); ++ } ++ else { ++ v->v.uc=val; ++ } ++ } ++ break; ++ case B_SHORT: { ++ if(dosign) { ++ v->v.ss=(signed short)twoscomp(val, nbits); ++ } ++ else { ++ v->v.us=val; ++ } ++ } ++ break; ++ case B_LONG: ++ ++ if(sial_defbsize()==8) goto ll; ++ ++ case B_INT: { ++ if(dosign) { ++ v->v.sl=(signed long)twoscomp(val, nbits); ++ } ++ else { ++ v->v.ul=val; ++ } ++ } ++ break; ++ case B_LONGLONG: { ++ll: ++ if(dosign) { ++ v->v.sll=(signed long long)twoscomp(val, nbits); ++ } ++ else { ++ v->v.ull=val; ++ } ++ } ++ break; ++ default: ++ sial_error("Oops get_bit_value_t..."); ++ break; ++ } ++ ++} ++/* ++ Set a bit field value_t. dvalue_t is the destination value_t as read ++ from either the system image of live memory. ++ */ ++ull ++set_bit_value_t(ull dvalue, ull value, int nbits, int boff) ++{ ++ ull mask; ++ ++ if (nbits >= 32) { ++ int upper_bits = nbits - 32; ++ mask = ((1 << upper_bits) - 1); ++ mask = (mask << 32) | 0xffffffff; ++ } ++ else { ++ mask = ((1 << nbits) - 1); ++ } ++ /* strip out the current value_t */ ++ dvalue &= ~(mask << boff); ++ ++ /* put in the new one */ ++ dvalue |= (value << boff); ++ return dvalue; ++} ++ ++/* this function is called when we have determined the systems ++ default int size (64 bit vs 32 bits) */ ++void ++sial_setdefbtype(int size, int sign) ++{ ++int idx=B_INT; ++ ++ switch(size) { ++ ++ case 1: defbtype=B_CHAR; idx=B_UC; break; ++ case 2: defbtype=B_SHORT;idx=B_US; break; ++ case 4: defbtype=B_INT; idx=B_UL; break; ++ case 8: defbtype=B_LONGLONG; idx=B_ULL; break; ++ ++ } ++ if(sign) defbsign = B_SIGNED; ++ else defbsign = B_UNSIGNED; ++ defbtype |= defbsign; ++ defbsize=size; ++ defbidx=idx; ++} ++ ++static int ++getbtype(int token) ++{ ++int i; ++ ++ for(i=0;itype.type=V_BASE; ++ v->setfct=sial_setfct; ++ v->type.idx=idx; ++ v->mem=0; ++ switch(idx) { ++ ++ case B_UC: case B_SC: ++ v->type.size=1; ++ v->v.uc=i; ++ break; ++ case B_US: case B_SS: ++ v->type.size=2; ++ v->v.us=i; ++ break; ++ case B_UL: case B_SL: ++ v->type.size=4; ++ v->v.ul=i; ++ break; ++ case B_ULL: case B_SLL: ++ v->type.size=8; ++ v->v.ull=i; ++ break; ++ default: sial_error("Oops defbtypesize!"); break; ++ } ++ return v; ++} ++ ++value_t * ++sial_defbtype(value_t *v, ull i) ++{ ++ v->type.typattr=defbtype; ++ return sial_defbtypesize(v, i, defbidx); ++} ++ ++value_t * ++sial_makebtype(ull i) ++{ ++value_t *v=sial_calloc(sizeof(value_t)); ++ ++ sial_defbtype(v, i); ++ sial_setarray(&v->arr); ++ TAG(v); ++ return v; ++} ++ ++value_t * ++sial_newval() ++{ ++value_t *v=sial_makebtype(0); ++ ++ return v; ++} ++ ++/* take the current basetypes and generate a uniq index */ ++static void ++settypidx(type_t*t) ++{ ++int v1, v2, v3, size; ++ ++ if(t->typattr & B_CHAR) { ++ size=1; ++ v1=B_SC; v2=B_UC; ++ v3=(defbsign==B_SIGNED?B_SC:B_UC); ++ } else if(t->typattr & B_SHORT) { ++ size=2; ++ v1=B_SS; v2=B_US; v3=B_SS; ++ } else if(t->typattr & B_LONG) { ++ if(sial_defbsize()==4) { ++ size=4; ++ v1=B_SL; v2=B_UL; v3=B_SL; ++ } else goto ll; ++ } else if(t->typattr & B_INT) { ++go: ++ size=4; ++ v1=B_SL; v2=B_UL; v3=B_SL; ++ } else if(t->typattr & B_LONGLONG) { ++ll: ++ size=8; ++ v1=B_SLL; v2=B_ULL; v3=B_SLL; ++ } ++ else goto go; ++ ++ if(t->typattr & B_SIGNED) t->idx=v1; ++ else if(t->typattr & B_UNSIGNED) t->idx=v2; ++ else t->idx=v3; ++ t->size=size; ++} ++ ++/* take the current basetypes and generate a uniq index */ ++int ++sial_idxtoattr(int idx) ++{ ++int i; ++static struct { ++ ++ int idx; ++ int attr; ++ ++} atoidx[] = { ++ ++ {B_SC, B_SIGNED | B_CHAR}, ++ {B_UC, B_UNSIGNED| B_CHAR}, ++ {B_SS, B_SIGNED | B_SHORT}, ++ {B_US, B_UNSIGNED| B_SHORT}, ++ {B_SL, B_SIGNED | B_LONG}, ++ {B_UL, B_UNSIGNED| B_LONG}, ++ {B_SLL, B_SIGNED | B_LONGLONG}, ++ {B_ULL, B_UNSIGNED| B_LONGLONG}, ++}; ++ ++ for(i=0; i < sizeof(atoidx)/sizeof(atoidx[0]); i++) { ++ ++ if(atoidx[i].idx==idx) return atoidx[i].attr; ++ } ++ sial_error("Oops sial_idxtoattr!"); ++ return 0; ++} ++ ++void ++sial_mkvsigned(value_t*v) ++{ ++ v->type.typattr &= ~B_SIGN_MASK; ++ v->type.typattr |= B_SIGNED; ++ settypidx(&v->type); ++} ++ ++/* if there's no sign set the default */ ++void ++sial_chksign(type_t*t) ++{ ++ if(sial_isvoid(t->typattr)) return; ++ if(!is_sign(t->typattr)) { ++ ++ /* char is compile time dependant */ ++ if(t->idx==B_SC || t->idx==B_UC) t->typattr |= defbsign; ++ /* all other sizes are signed by default */ ++ else t->typattr |= B_SIGNED; ++ } ++ settypidx(t); ++} ++ ++/* if ther's no size specification, make it an INT */ ++void ++sial_chksize(type_t*t) ++{ ++ if(!sial_isvoid(t->typattr) && !is_size(t->typattr)) sial_addbtype(t, INT); ++} ++ ++/* create a new base type element */ ++type_t* ++sial_newbtype(int token) ++{ ++int btype; ++type_t*t=sial_newtype(); ++ ++ if(!token) btype=defbtype; ++ else { ++ ++ btype=getbtype(token); ++ if(is_sign(btype)) btype |= B_USPEC; ++ } ++ t->type=V_BASE; ++ t->typattr=btype; ++ settypidx(t); ++ TAG(t); ++ return t; ++} ++ ++/* set the default sign on a type if user did'nt specify one and not int */ ++#define set_base_sign(a) if(!(base & (B_USPEC|B_INT))) base = (base ^ is_sign(base)) | a ++ ++/* ++ char short int long longlong ++char XXX XXX XXX XXX XXX ++short XXX XXX OOO XXX XXX ++int XXX OOO XXX OOO OOO ++long XXX XXX OOO OOO XXX ++longlong XXX XXX OOO XXX XXX ++ ++ the parser let's you specify any of the B_ type. It's here that we ++ have to check things out ++ ++*/ ++type_t* ++sial_addbtype(type_t*t, int newtok) ++{ ++int btype=getbtype(newtok); ++int base=t->typattr; ++ ++ /* size specification. Check for 'long long' any other ++ combinaison of size is invalid as is 'long long long' */ ++ if(is_size(btype)) { ++ ++ int ibase=base; ++ ++ switch(btype) { ++ ++ case B_LONG: { ++ ++ ++ if(!(base & (B_CHAR|B_SHORT))) { ++ ++ set_base_sign(B_UNSIGNED); ++ ++ if(base & B_LONG || sial_defbsize()==8) { ++ ++ ibase &= ~B_LONGLONG; ++ base |= B_LONGLONG; ++ base &= ~B_LONG; ++ ++ } else { ++ ++ base |= B_LONG; ++ } ++ } ++ break; ++ } ++ case B_INT: { ++ ++ /* ++ * This is a bit of a hack to circumvent the ++ * problem that "long int" or "long long int" ++ * is a valid statement in C. ++ */ ++ if(!(base & (B_INT|B_CHAR|B_LONG|B_LONGLONG))) { ++ ++ set_base_sign(B_SIGNED); ++ base |= B_INT; ++ } ++ if (base & (B_LONG|B_LONGLONG)) ++ ibase = 0; ++ break; ++ } ++ case B_SHORT: { ++ ++ if(!(base & (B_SHORT|B_CHAR|B_LONG|B_LONGLONG))) { ++ ++ base |= B_SHORT; ++ set_base_sign(B_UNSIGNED); ++ } ++ ++ } ++ case B_CHAR: { ++ ++ if(!(base & (B_CHAR|B_SHORT|B_INT|B_LONG|B_LONGLONG))) { ++ ++ base |= B_CHAR; ++ set_base_sign(defbsign); ++ } ++ ++ } ++ } ++ ++ if(ibase == base) { ++ ++ sial_warning("Invalid combinaison of sizes"); ++ ++ } ++ ++ } else if(is_sign(btype)) { ++ ++ if(base & B_USPEC) { ++ ++ if(is_sign(btype) == is_sign(base)) ++ ++ sial_warning("duplicate type specifier"); ++ ++ else ++ ++ sial_error("invalid combination of type specifiers"); ++ } ++ /* always keep last found signed specification */ ++ base ^= is_sign(base); ++ base |= btype; ++ base |= B_USPEC; ++ ++ } else if(is_stor(btype)) { ++ ++ if(is_stor(base)) { ++ ++ sial_warning("Suplemental storage class ignore"); ++ ++ } ++ else base |= btype; ++ } ++ t->typattr=base; ++ settypidx(t); ++ return t; ++} ++ ++/* this function gets called back from the API when the user need to parse ++ a type declaration. Like when a typedef dwarf returns a type string */ ++ ++void ++sial_pushref(type_t*t, int ref) ++{ ++ if(t->type==V_REF) { ++ ++ t->ref += ref; ++ ++ } else { ++ ++ t->ref=ref; ++ ++ if(ref) { ++ ++ t->rtype=t->type; ++ t->type=V_REF; ++ } ++ } ++} ++void ++sial_popref(type_t*t, int ref) ++{ ++ ++ if(!t->ref) return; ++ ++ t->ref-=ref; ++ ++ if(!t->ref) { ++ ++ t->type=t->rtype; ++ } ++} ++ ++typedef struct { ++ int battr; ++ char *str; ++} bstr; ++static bstr btypstr[] = { ++ {CHAR, "char"}, ++ {SHORT, "short"}, ++ {INT, "int"}, ++ {LONG, "long"}, ++ {DOUBLE, "double"}, ++ {SIGNED, "signed"}, ++ {UNSIGNED, "unsigned"}, ++ {STATIC, "static"}, ++ {REGISTER, "register"}, ++ {VOLATILE, "volatile"}, ++ {VOID, "void"}, ++}; ++int ++sial_parsetype(char *str, type_t*t, int ref) ++{ ++char *p; ++char *tok, *pend; ++int ctype=0, i, first, found; ++type_t*bt=0; ++ ++ /* if it's a simple unamed ctype return 0 */ ++ if(!strcmp(str, "struct")) { t->type=V_STRUCT; return 0; } ++ if(!strcmp(str, "enum")) { t->type=V_ENUM; return 0; } ++ if(!strcmp(str, "union")) { t->type=V_UNION; return 0; } ++ ++ p=sial_strdup(str); ++ ++ /* get he level of reference */ ++ for(pend=p+strlen(p)-1; pend>=p; pend--) { ++ ++ if(*pend==' ' || *pend == '\t') continue; ++ if(*pend == '*' ) ref ++; ++ else break; ++ ++ } ++ *++pend='\0'; ++ ++again: ++ tok=strtok(p," "); ++ if(!strcmp(tok, "struct")) { ++ ++ ctype=V_STRUCT; ++ ++ } else if(!strcmp(tok, "union")) { ++ ++ ctype=V_UNION; ++ ++ } else if(!strcmp(tok, "enum")) { ++ sial_free(p); ++ p=(char*)sial_alloc(strlen("unsigned int") + 1); ++ /* force enum type into unigned int type for now */ ++ strcpy(p, "unsigned int"); ++ goto again; ++ ++ } ++ if(ctype) { ++ ++ char *name=strtok(NULL, " \t"); ++ bt=sial_getctype(ctype, name, 1); ++ ++ /* we accept unknow struct reference if it's a ref to it */ ++ /* the user will probably cast it to something else anyway... */ ++ if(!bt) { ++ ++ if(ref) { ++ ++ bt=(type_t*)sial_getvoidstruct(ctype); ++ ++ } else { ++ ++ sial_error("Unknown Struct/Union/Enum %s", name); ++ ++ } ++ } ++ ++ sial_duptype(t, bt); ++ sial_freetype(bt); ++ sial_pushref(t, ref); ++ sial_free(p); ++ return 1; ++ } ++ ++ /* this must be a basetype_t*/ ++ first=1; ++ do { ++ found=0; ++ for(i=0;inext->v->type); ++ sial_freesvs(v); ++ return type; ++} ++ ++typedef struct cast { ++ ++ type_t*t; ++ node_t*n; ++ srcpos_t pos; ++ ++} cast; ++ ++/* make sure we do the proper casting */ ++void ++sial_transval(int s1, int s2, value_t *v, int issigned) ++{ ++vu_t u; ++ ++ if(s1==s2) return; ++ ++ if(issigned) { ++ ++ switch(s1) { ++ case 1: ++ switch(s2) { ++ case 2: ++ u.us=v->v.sc; ++ break; ++ case 4: ++ u.ul=v->v.sc; ++ break; ++ case 8: ++ u.ull=v->v.sc; ++ break; ++ } ++ break; ++ case 2: ++ switch(s2) { ++ case 1: ++ u.uc=v->v.ss; ++ break; ++ case 4: ++ u.ul=v->v.ss; ++ break; ++ case 8: ++ u.ull=v->v.ss; ++ break; ++ } ++ break; ++ case 4: ++ switch(s2) { ++ case 2: ++ u.us=v->v.sl; ++ break; ++ case 1: ++ u.uc=v->v.sl; ++ break; ++ case 8: ++ u.ull=v->v.sl; ++ break; ++ } ++ break; ++ case 8: ++ switch(s2) { ++ case 2: ++ u.us=v->v.sll; ++ break; ++ case 4: ++ u.ul=v->v.sll; ++ break; ++ case 1: ++ u.uc=v->v.sll; ++ break; ++ } ++ break; ++ } ++ ++ } else { ++ ++ switch(s1) { ++ case 1: ++ switch(s2) { ++ case 2: ++ u.us=v->v.uc; ++ break; ++ case 4: ++ u.ul=v->v.uc; ++ break; ++ case 8: ++ u.ull=v->v.uc; ++ break; ++ } ++ break; ++ case 2: ++ switch(s2) { ++ case 1: ++ u.uc=v->v.us; ++ break; ++ case 4: ++ u.ul=v->v.us; ++ break; ++ case 8: ++ u.ull=v->v.us; ++ break; ++ } ++ break; ++ case 4: ++ switch(s2) { ++ case 2: ++ u.us=v->v.ul; ++ break; ++ case 1: ++ u.uc=v->v.ul; ++ break; ++ case 8: ++ u.ull=v->v.ul; ++ break; ++ } ++ break; ++ case 8: ++ switch(s2) { ++ case 2: ++ u.us=v->v.ull; ++ break; ++ case 4: ++ u.ul=v->v.ull; ++ break; ++ case 1: ++ u.uc=v->v.ull; ++ break; ++ } ++ break; ++ } ++ } ++ memmove(&v->v, &u, sizeof(u)); ++ if(v->type.type!=V_REF) v->type.size=s2; ++} ++ ++value_t * ++sial_execast(cast *c) ++{ ++/* we execute the expression node_t*/ ++value_t *v=NODE_EXE(c->n); ++ ++ /* ... and validate the type cast */ ++ if(v->type.type != V_REF && v->type.type != V_BASE) { ++ ++ sial_rerror(&c->pos, "Invalid typecast"); ++ ++ } ++ else { ++ ++ int vsize=TYPE_SIZE(&v->type); ++ int issigned=sial_issigned(v->type.typattr); ++ ++ /* Now, just copy the cast type over the current type_t*/ ++ sial_duptype(&v->type, c->t); ++ ++ /* Take into account the size of the two objects */ ++ sial_transval(vsize, TYPE_SIZE(c->t), v, issigned); ++ } ++ return v; ++} ++ ++void ++sial_freecast(cast *c) ++{ ++ NODE_FREE(c->n); ++ sial_freetype(c->t); ++ sial_free(c); ++} ++ ++node_t* ++sial_typecast(type_t*type, node_t*expr) ++{ ++ if(type->type==V_STRING) { ++ ++ sial_error("Cannot cast to a 'string'"); ++ return 0; ++ ++ } else { ++ ++ node_t*n=sial_newnode(); ++ cast *c=sial_alloc(sizeof(cast)); ++ ++ c->t=type; ++ c->n=expr; ++ n->exe=(xfct_t)sial_execast; ++ n->free=(ffct_t)sial_freecast; ++ n->data=c; ++ sial_setpos(&c->pos); ++ return n; ++ } ++} ++ ++/* ++ Validate type conversions on function calls and assignments. ++*/ ++void ++sial_chkandconvert(value_t *vto, value_t *vfrm) ++{ ++type_t*tto=&vto->type; ++type_t*tfrm=&vfrm->type; ++ ++ if(tto->type == tfrm->type) { ++ ++ if(tto->type == V_BASE) { ++ ++ int attr=tto->typattr; ++ int idx=tto->idx; ++ ++ sial_transval(tfrm->size, tto->size, vfrm, sial_issigned(vfrm->type.typattr)); ++ sial_dupval(vto, vfrm); ++ tto->typattr=attr; ++ tto->idx=idx; ++ return; ++ ++ } else if(tto->type == V_REF) { ++ ++ if(sial_isvoid(tto->typattr) || sial_isvoid(tfrm->typattr)) goto dupit; ++ ++ if(tto->ref == tfrm->ref && tto->rtype == tfrm->rtype) { ++ ++ if(is_ctype(tto->rtype)) { ++ ++ if(tto->idx == tfrm->idx || sial_samectypename(tto->rtype, tto->idx, tfrm->idx)) ++ goto dupit; ++ ++ } else if(tto->size == tfrm->size) { ++ ++ int attr=tto->typattr; ++ sial_dupval(vto, vfrm); ++ tto->typattr=attr; ++ return; ++ } ++ } ++ } ++ /* Allow assignments between enums of the same type */ ++ else if(is_ctype(tto->type) || tto->type == V_ENUM) { ++ ++ /* same structure type_t*/ ++ if(tto->idx == tfrm->idx || sial_samectypename(tto->type, tto->idx, tfrm->idx)) ++ goto dupit; ++ } ++ else if(tto->type == V_STRING) goto dupit; ++ ++ } ++ else if((tto->type == V_ENUM && tfrm->type == V_BASE) || ++ (tto->type == V_BASE && tfrm->type == V_ENUM)) { ++ /* convert type from or to enum */ ++ int attr=tto->typattr; ++ int idx=tto->idx; ++ ++ sial_transval(tfrm->size, tto->size, vfrm, sial_issigned(vfrm->type.typattr)); ++ sial_dupval(vto, vfrm); ++ tto->typattr=attr; ++ tto->idx=idx; ++ return; ++ } ++ // support NULL assignment to pointer ++ else if(tto->type == V_REF && tfrm->type == V_BASE && !sial_getval(vfrm)) return; ++ sial_error("Invalid type conversion"); ++ ++dupit: ++ sial_dupval(vto, vfrm); ++} ++ +--- crash/extensions/libsial/sial_api.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_api.h 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,267 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++ ++/* minor and major version number */ ++#define S_MAJOR 3 ++#define S_MINOR 0 ++ ++#define MAX_SYMNAMELEN 100 ++#define MAXIDX 20 ++ ++/* abi values */ ++#define ABI_MIPS 1 ++#define ABI_INTEL_X86 2 ++#define ABI_INTEL_IA 3 ++#define ABI_S390 4 ++#define ABI_S390X 5 ++#define ABI_PPC64 6 ++ ++/* types of variables */ ++#define V_BASE 1 ++#define V_STRING 2 ++#define V_REF 3 ++#define V_ENUM 4 ++#define V_UNION 5 ++#define V_STRUCT 6 ++#define V_TYPEDEF 7 ++#define V_ARRAY 8 ++ ++#define ENUM_S struct enum_s ++#define DEF_S struct def_s ++#define MEMBER_S struct member_s ++#define TYPE_S struct type_s ++#define VALUE_S struct value_s ++#define ARRAY_S struct array_s ++#define NODE_S struct node_s ++#define IDX_S struct idx_s ++#define VAR_S struct var_s ++ ++ENUM_S; ++DEF_S; ++MEMBER_S; ++TYPE_S; ++VALUE_S; ++ARRAY_S; ++NODE_S; ++IDX_S; ++VAR_S; ++ ++#if linux ++#include ++typedef uint64_t ull; ++typedef uint32_t ul; ++#else ++typedef unsigned long long ull; ++typedef unsigned long ul; ++#endif ++ ++/* THe API function calls numbers */ ++typedef struct { ++ ++ int (*getmem)(ull, void *, int); /* write to system image */ ++ int (*putmem)(ull, void *, int); /* read from system image */ ++ char* (*member)(char *, ull, TYPE_S * /* get type and positional information ... */ ++ , MEMBER_S *, ull *lidx); /* ... about the member of a structure */ ++ int (*getctype)(int ctype, char * /* get struct/union type information */ ++ , TYPE_S*); ++ char* (*getrtype)(ull, TYPE_S *); /* get complex type information */ ++ int (*alignment)(ull); /* get alignment value for a type */ ++ int (*getval)(char *, ull *); /* get the value of a system variable */ ++ ENUM_S* (*getenum)(char *name); /* get the list of symbols for an enum type */ ++ DEF_S* (*getdefs)(void); /* get the list of compiler pre-defined macros */ ++ uint8_t (*get_uint8)(void*); ++ uint16_t (*get_uint16)(void*); ++ uint32_t (*get_uint32)(void*); ++ uint64_t (*get_uint64)(void*); ++ char* (*findsym)(char*); ++} apiops; ++ ++/* ++ Builtin API defines.... ++*/ ++/* call this function to install a new builtin ++ ++ proto is the function prototype ex: ++ struct proc* mybuiltin(int flag, char *str); ++ ++ "mybuiltin" will be the sial name for the function. ++ "fp" is the pointer to the builtin function code. ++ ++*/ ++typedef VALUE_S* bf_t(VALUE_S*, ...); ++typedef struct btspec { ++ char *proto; ++ bf_t *fp; ++} btspec_t; ++ ++/* dso entry points */ ++#define BT_SPEC_TABLE btspec_t bttlb[] ++#define BT_SPEC_SYM "bttlb" ++#define BT_INIDSO_FUNC int btinit ++#define BT_INIDSO_SYM "btinit" ++#define BT_ENDDSO_FUNC void btend ++#define BT_ENDDSO_SYM "btend" ++ ++/* maximum number of parameters that can be passed to a builtin */ ++#define BT_MAXARGS 20 ++ ++extern apiops *sial_ops; ++#define API_GETMEM(i, p, n) ((sial_ops->getmem)((i), (p), (n))) ++#define API_PUTMEM(i, p, n) ((sial_ops->putmem)((i), (p), (n))) ++#define API_MEMBER(n, i, tm, m, l) ((sial_ops->member)((n), (i), (tm), (m), (l))) ++#define API_GETCTYPE(i, n, t) ((sial_ops->getctype)((i), (n), (t))) ++#define API_GETRTYPE(i, t) ((sial_ops->getrtype)((i), (t))) ++#define API_ALIGNMENT(i) ((sial_ops->alignment)((i))) ++#define API_GETVAL(n, v) ((sial_ops->getval)((n), (v))) ++#define API_GETENUM(n) ((sial_ops->getenum)(n)) ++#define API_GETDEFS() ((sial_ops->getdefs)()) ++#define API_GET_UINT8(ptr) ((sial_ops->get_uint8)(ptr)) ++#define API_GET_UINT16(ptr) ((sial_ops->get_uint16)(ptr)) ++#define API_GET_UINT32(ptr) ((sial_ops->get_uint32)(ptr)) ++#define API_GET_UINT64(ptr) ((sial_ops->get_uint64)(ptr)) ++#define API_FINDSYM(p) ((sial_ops->findsym)(p)) ++ ++#if linux ++# if __LP64__ ++# define sial_getptr(v, t) ((t*)sial_getval(v)) ++# else ++# define sial_getptr(v, t) ((t*)(ul)sial_getval(v)) ++# endif ++#else ++# if (_MIPS_SZLONG == 64) ++# define sial_getptr(v, t) ((t*)sial_getval(v)) ++# else ++# define sial_getptr(v, t) ((t*)(ul)sial_getval(v)) ++# endif ++#endif ++ ++/* startup function */ ++int sial_open(void); /* initialize a session with sial */ ++void sial_apiset(apiops *, int, int, int);/* define the API for a connection */ ++void sial_setofile(void *); /* sial should output messages to this file */ ++void *sial_getofile(void); /* where is sial currently outputing */ ++void sial_setmpath(char *p); /* set the search path for sial scripts */ ++void sial_setipath(char *p); /* set the search path for sial include files */ ++VAR_S *sial_builtin(char *proto, bf_t);/* install a builtin function */ ++int sial_cmd(char *name, char **argv, int argc); /* execute a command w/ args */ ++ ++/* load/unload of script files and directories */ ++ull sial_load(char *); /* load/parse a file */ ++ull sial_unload(char *); /* load/parse a file */ ++void sial_loadall(void); /* load all files found in set path */ ++ ++/* variables associated functions */ ++VAR_S *sial_newvar(char *); /* create a new static/auto variable */ ++void *sial_add_globals(VAR_S*); /* add a set of variable to the globals context */ ++VAR_S *sial_newvlist(void); /* create a root for a list of variables */ ++ ++int sial_tryexe(char *, char**, int);/* try to execute a function */ ++int sial_parsetype(char*, TYPE_S *, int);/* parse a typedef line */ ++ull sial_exefunc(char *, VALUE_S **);/* to execute a function defined in sial */ ++ ++/* help related function */ ++void sial_showallhelp(void); /* display help info for all commands */ ++int sial_showhelp(char *); /* display help info for a single command */ ++ ++/* allocation related function */ ++void *sial_alloc(int); /* allocate some memory */ ++void *sial_calloc(int); /* allocate some 0 filed memory */ ++void sial_free(void*); /* free it */ ++char *sial_strdup(char*); /* equivalent of strdup() returns sial_free'able char */ ++void *sial_dupblock(void *p); /* duplicate the contain of a block of allocated memory */ ++void *sial_realloc(void *p, int size); /* reallocate a block */ ++void sial_maketemp(void *p); /* put a block on the temp list */ ++void sial_freetemp(void); /* free the temp list */ ++VALUE_S *sial_makebtype(ull); /* create a default base type value (int) */ ++ ++/* handle values */ ++VALUE_S *sial_newval(void); /* get a new placeholder for a value */ ++void sial_freeval(VALUE_S *); /* free a value* and associated structs */ ++VALUE_S *sial_makestr(char *); /* create a string value */ ++ull sial_getval(VALUE_S*); /* transform a random value to a ull */ ++VALUE_S *sial_cloneval(VALUE_S *); /* make a clone of a value */ ++ ++/* array related */ ++/* add a new array element to a value */ ++void sial_addvalarray(VALUE_S*v, VALUE_S*idx, VALUE_S*val); ++/* return the value associated with a int index */ ++VALUE_S *sial_intindex(VALUE_S *, int); ++/* return the value associated with a 'string' index */ ++VALUE_S *sial_strindex(VALUE_S *, char *); ++/* set the value of an array element */ ++void sial_setarrbval(ARRAY_S*, int); ++/* get the array element coresponding to index */ ++ARRAY_S *sial_getarrval(ARRAY_S**, VALUE_S*); ++/* get the initiale array for a variable */ ++ARRAY_S *sial_addarrelem(ARRAY_S**, VALUE_S*, VALUE_S*); ++ ++/* type manipulation */ ++int sial_is_struct(int); ++int sial_is_enum(int); ++int sial_is_union(int); ++int sial_is_typedef(int); ++int sial_type_gettype(TYPE_S*t); ++int sial_chkfname(char *fname, void *vfd); ++int sial_loadunload(int load, char *name, int silent); ++ ++void sial_type_settype(TYPE_S*t, int type); ++void sial_setcallback(void (*scb)(char *, int)); ++void sial_vilast(void); ++void sial_vi(char *fname, int file); ++void sial_type_setsize(TYPE_S*t, int size); ++int sial_type_getsize(TYPE_S*t); ++void sial_type_setidx(TYPE_S*t, ull idx); ++ull sial_type_getidx(TYPE_S*t); ++void sial_type_setidxlst(TYPE_S*t, int *idxlst); ++void sial_type_setref(TYPE_S*t, int ref, int type); ++void sial_type_setfct(TYPE_S*t, int val); ++void sial_type_mkunion(TYPE_S*t); ++void sial_type_mkenum(TYPE_S*t); ++void sial_type_mkstruct(TYPE_S*t); ++void sial_type_mktypedef(TYPE_S*t); ++TYPE_S*sial_newtype(void); ++void sial_freetype(TYPE_S*t); ++TYPE_S*sial_getctype(int ctype_t, char *name, int silent); ++void sial_type_free(TYPE_S* t); ++void sial_pushref(TYPE_S*t, int ref); ++void sial_duptype(TYPE_S*to, TYPE_S*from); ++int sial_defbsize(void); ++TYPE_S*sial_newbtype(int token); ++void sial_setdbg(unsigned int lvl); ++unsigned int sial_getdbg(void); ++void sial_setname(char *name); ++char *sial_getname(void); ++void sial_setclass(char *class); ++char **sial_getclass(void); ++ ++/* struct member functions */ ++void sial_member_soffset(MEMBER_S*m, int offset); ++void sial_member_ssize(MEMBER_S*m, int size); ++void sial_member_sfbit(MEMBER_S*m, int fbit); ++void sial_member_snbits(MEMBER_S*m, int nbits); ++void sial_member_sname(MEMBER_S*m, char *name); ++ ++/* enums */ ++ENUM_S* sial_add_enum(ENUM_S* e, char* name, int val); ++/* defines */ ++DEF_S* sial_add_def(DEF_S* d, char *name, char *val); ++ ++/* error handling */ ++/* display error w/ file/line coordinates */ ++/* does not return */ ++void sial_error(char *, ...); ++/* display warning w/ file/line coordinates */ ++void sial_warning(char *, ...); ++/* display a message and continue */ ++void sial_msg(char *, ...); ++/* display a debug message */ ++#define DBG_TYPE 0x00000001 ++#define DBG_STRUCT 0x00000002 ++#define DBG_NAME 0x10000000 // ++#define DBG_ALL 0x0fffffff ++void sial_dbg(int class, int level, char *, ...); ++void sial_dbg_named(int class, char *name, int level, char *, ...); ++ ++/* parsers debug flags */ ++extern int sialdebug, sialppdebug; +--- crash/extensions/libsial/sial_var.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_var.c 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,1320 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include ++#include ++#include "sial.h" ++ ++/* ++ Get an existing variable from the current set. ++*/ ++ ++/* variable lists for the different scopes */ ++typedef struct { ++ int type; ++ var_t*svs; ++} svlist; ++ ++typedef struct glo { ++ struct glo *next; ++ var_t*vv; ++} glo; ++ ++/* ++ Free indexes specifications. ++*/ ++void ++sial_freeidx(idx_t *idx) ++{ ++int i; ++ ++ for(i=0;inidx;i++) { ++ ++ if(idx->idxs[i]) NODE_FREE(idx->idxs[i]); ++ } ++ sial_free(idx); ++} ++ ++/* ++ Free a variable declaration structure. ++*/ ++void ++sial_freedvar(dvar_t*dv) ++{ ++ if(!dv) return; ++ if(--dv->refcount) return; ++ if(dv->name) sial_free(dv->name); ++ if(dv->idx) sial_freeidx(dv->idx); ++ if(dv->init) NODE_FREE(dv->init); ++ if(dv->fargs) sial_freesvs(dv->fargs); ++ sial_free(dv); ++} ++ ++void ++sial_setarray(array_t**arpp) ++{ ++array_t*arp=*arpp; ++ ++ if(!arp) { ++ ++ arp=sial_calloc(sizeof(array_t)); ++ TAG(arp); ++ arp->next=arp->prev=arp; ++ arp->ref=1; ++ *arpp=arp; ++ } ++} ++ ++/* ++ this is the main variable declaration function. ++ We support the global scope attribute that make the declared ++ variable accessible to all function from all scripts. ++ ++ By default the scope of a variable either the statement block ++ where it was declared (or first used): ++ { ++ int var; ++ ... ++ } ++ Then it's scope is the block itself. ++ ++ Or the file, if it was declared outside of a function. ++ ++ Storage is by default 'automatic' and can be made permanent ++ by using the 'static' keywork in the declaration. ++ 'Volatile' and 'register' storage classes are supported but ++ have no effect. ++*/ ++var_t* ++sial_vardecl(dvar_t*dv, type_t*t) ++{ ++var_t*vlist=sial_newvlist(); ++var_t*var; ++ ++ /* type *and* dv can have ref counts. First comes from typedef parsing ++ second comes from the declaration itself */ ++ dv->ref += t->ref; ++ ++ /* add one level of ref for arrays */ ++ if(dv->idx) dv->ref++; ++ ++ /* reset ref level for tests below */ ++ sial_popref(t, t->ref); ++ ++ TAG(vlist); ++ ++ if(!t->type) { ++ ++ int sto=sial_isstor(t->typattr); ++ ++ sial_freetype(t); ++ t=sial_newbtype(0); ++ t->typattr |= sto; ++ } ++ else if(t->type==V_BASE && !dv->ref) { ++ ++ sial_chksign(t); ++ sial_chksize(t); ++ } ++ ++ /* is this a new typedef declaration ? */ ++ /* typedef is considered just like any other storage class */ ++ if(sial_istdef(t->typattr)) { ++ ++ sial_tdef_decl(dv, t); ++ return 0; ++ } ++ ++ while(dv) { ++ ++ /* disalow var names that match against already defined vars */ ++ if(dv->name[0]) { ++ type_t *t=sial_getctype(V_TYPEDEF, dv->name, 1); ++ if(t) { ++ ++ sial_freetype(t); ++ sial_warning("Variable '%s' already defined as typedef.\n"); ++ } ++ } ++ ++ /* ++ some sanity checks here that apply to both var and struct ++ declarations ++ */ ++ if(is_ctype(t->type) && !dv->ref) { ++ ++ if(dv->name[0]) { ++ ++ if(!instruct) { ++ ++ if(!sial_isxtern(t->typattr)) { ++ ++ sial_freesvs(vlist); ++ sial_error("struct/union instances not supported, please use pointers"); ++ } ++ ++ } else if(sial_ispartial(t)) { ++ ++ sial_freesvs(vlist); ++ sial_error("Reference to incomplete type"); ++ } ++ } ++ } ++ if(dv->nbits) { ++ ++ if(t->type != V_BASE) { ++ ++ sial_freesvs(vlist); ++ sial_error("Bit fields can only be of integer type"); ++ ++ } ++ if(dv->idx) { ++ ++ sial_freesvs(vlist); ++ sial_error("An array of bits ? Come on..."); ++ } ++ } ++ ++ var=sial_newvar(dv->name); ++ ++ t->fct=dv->fct; ++ sial_duptype(&var->v->type, t); ++ sial_pushref(&var->v->type, dv->ref); ++ ++ var->dv=dv; ++ ++ TAG(var); ++ ++ if(t->type == V_STRING) { ++ ++ sial_setstrval(var->v, ""); ++ ++ } ++ ++ sial_setpos(&dv->pos); ++ ++ sial_enqueue(vlist, var); ++ ++ dv=dv->next; ++ } ++ sial_free(t); ++ TAG(vlist); ++ return vlist; ++} ++ ++dvar_t* ++sial_newdvar(node_t*v) ++{ ++dvar_t*dv; ++ ++ dv=sial_alloc(sizeof(dvar_t)); ++ memset(dv, 0, sizeof(dvar_t)); ++ if(v) { ++ dv->name=NODE_NAME(v); ++ NODE_FREE(v); ++ ++ } else { ++ ++ dv->name=sial_alloc(1); ++ dv->name[0]='\0'; ++ } ++ dv->refcount=1; ++ sial_setpos(&dv->pos); ++ return dv; ++} ++ ++dvar_t* ++sial_dvarini(dvar_t*dv, node_t*init) ++{ ++ dv->init=init; ++ return dv; ++} ++ ++dvar_t* ++sial_dvarptr(int ref, dvar_t*dv) ++{ ++ dv->ref+=ref; ++ return dv; ++} ++ ++dvar_t* ++sial_dvaridx(dvar_t*dv, node_t*n) ++{ ++ if(!dv->idx) { ++ ++ dv->idx=sial_alloc(sizeof(idx_t)); ++ dv->idx->nidx=0; ++ } ++ dv->idx->idxs[dv->idx->nidx++]=n; ++ return dv; ++} ++ ++dvar_t* ++sial_dvarfld(dvar_t*dv, node_t*n) ++{ ++ ++ if(n) { ++ ++ value_t *va=sial_exenode(n); ++ ++ /* get the value_t for the bits */ ++ if(!va) dv->nbits=0; ++ else { ++ dv->nbits=unival(va); ++ sial_freeval(va); ++ } ++ NODE_FREE(n); ++ ++ } else dv->nbits=0; ++ ++ dv->bitfield=1; ++ return dv; ++} ++ ++dvar_t* ++sial_dvarfct(dvar_t*dv, var_t*fargs) ++{ ++ dv->fct=1; ++ dv->fargs=fargs; ++ return dv; ++} ++ ++dvar_t* ++sial_linkdvar(dvar_t*dvl, dvar_t*dv) ++{ ++dvar_t*v; ++ ++ /* need to keep declaration order for variable initialization */ ++ if(dv) { ++ ++ for(v=dvl; v->next; v=v->next); ++ dv->next=0; ++ v->next=dv; ++ } ++ return dvl; ++} ++ ++idx_t * ++sial_newidx(node_t*n) ++{ ++idx_t *idx; ++ ++ if(!instruct) { ++ ++ sial_error("Array supported only in struct/union declarations"); ++ } ++ idx=sial_alloc(sizeof(idx_t)); ++ idx->nidx=1; ++ idx->idxs[0]=n; ++ return idx; ++} ++ ++idx_t * ++sial_addidx(idx_t *idx, node_t*n) ++{ ++ if(idx->nidx==MAXIDX) { ++ ++ sial_error("Maximum number of dimension is %d", MAXIDX); ++ } ++ idx->idxs[idx->nidx++]=n; ++ return idx; ++} ++ ++static svlist svs[S_MAXDEEP]; ++static glo *globs=0; ++int svlev=0; ++ ++void ++sial_refarray(value_t *v, int inc) ++{ ++array_t*ap, *na; ++ ++ if(!v->arr) return; ++ v->arr->ref+=inc; ++ if(v->arr->ref == 0) { ++ ++ /* free all array element. */ ++ for(ap=v->arr->next; ap!=v->arr; ap=na) { ++ ++ na=ap->next; ++ sial_freeval(ap->idx); ++ sial_freeval(ap->val); ++ sial_free(ap); ++ } ++ sial_free(v->arr); ++ v->arr=0; ++ ++ } else { ++ ++ /* do the same to all sub arrays */ ++ for(ap=v->arr->next; ap!=v->arr; ap=na) { ++ ++ na=ap->next; ++ sial_refarray(ap->val, inc); ++ } ++ } ++ ++} ++ ++void ++sial_freedata(value_t *v) ++{ ++ ++ if(is_ctype(v->type.type) || v->type.type == V_STRING) { ++ ++ if(v->v.data) sial_free(v->v.data); ++ v->v.data=0; ++ ++ } ++ sial_refarray(v, -1); ++} ++ ++void ++sial_dupdata(value_t *v, value_t *vs) ++{ ++ ++ if(is_ctype(vs->type.type) || vs->type.type == V_STRING) { ++ ++ v->v.data=sial_alloc(vs->type.size); ++ memmove(v->v.data, vs->v.data, vs->type.size); ++ } ++} ++ ++void ++sial_freeval(value_t *v) ++{ ++ if(!v) return; ++ sial_freedata(v); ++ sial_free(v); ++} ++ ++ ++void ++sial_freevar(var_t*v) ++{ ++ ++ if(v->name) sial_free(v->name); ++ sial_freeval(v->v); ++ sial_freedvar(v->dv); ++ sial_free(v); ++} ++ ++void ++sial_enqueue(var_t*vl, var_t*v) ++{ ++ v->prev=vl->prev; ++ v->next=vl; ++ vl->prev->next=v; ++ vl->prev=v; ++} ++ ++void ++sial_dequeue(var_t*v) ++{ ++ v->prev->next=v->next; ++ v->next->prev=v->prev; ++ v->next=v->prev=v; ++} ++ ++/* ++ This function is called to validate variable declaration. ++ No array decalration for variables (this can only be checked in ++ sial_stat_decl() and sial_file_decl() usingthe idx field ofthe var struct. ++ Same comment for nbits. Only in struct declarations. ++*/ ++void ++sial_validate_vars(var_t*svs) ++{ ++var_t*v, *next; ++ ++ if(!svs) return; ++ ++ for(v=svs->next; v!=svs; v=next) { ++ ++ next=v->next; ++ ++ /* just remove extern variables */ ++ if(sial_isxtern(v->v->type.typattr)) { ++ ++ sial_dequeue(v); ++ sial_freevar(v); ++ ++ } else { ++ ++ if(v->dv->idx) { ++ ++ sial_freesvs(svs); ++ sial_error("Array instanciations not supported."); ++ ++ } ++ if(v->dv->nbits) { ++ ++ sial_freesvs(svs); ++ sial_error("Syntax error. Bit field unexpected."); ++ } ++ } ++ } ++} ++ ++var_t* ++sial_inlist(char *name, var_t*vl) ++{ ++var_t*vp; ++ ++ if(vl) { ++ ++ for(vp=vl->next; vp!=vl; vp=vp->next) { ++ ++ if(!strcmp(name, vp->name)) { ++ ++ return vp; ++ ++ } ++ ++ } ++ } ++ return 0; ++} ++ ++static var_t*apiglobs; ++ ++void ++sial_setapiglobs(void) ++{ ++ apiglobs=sial_newvlist(); ++ sial_add_globals(apiglobs); ++} ++ ++static var_t* ++sial_inglobs(char *name) ++{ ++var_t*vp; ++glo *g; ++ ++ for(g=globs; g; g=g->next) { ++ ++ if((vp=sial_inlist(name, g->vv))) return vp; ++ } ++ return 0; ++} ++ ++ ++void ++sial_chkglobsforvardups(var_t*vl) ++{ ++var_t*v; ++ ++ if(!vl) return; ++ ++ for(v=vl->next; v != vl; v=v->next) { ++ ++ var_t*vg; ++ ++ if(v->name[0] && (vg=sial_inglobs(v->name))) { ++ ++ /* if this is a prototype declaration then skip it */ ++ if(v->dv && v->dv->fct) continue; ++ ++ sial_rerror(&v->dv->pos, "Duplicate declaration of variable '%s', defined at %s:%d" ++ , v->name, vg->dv->pos.file, vg->dv->pos.line); ++ } ++ } ++} ++ ++/* ++ This function scans a list of variable and looks for those that have not been initialized yet. ++ Globals, statics and autos all get initialized through here. ++*/ ++static void ++sial_inivars(var_t*sv) ++{ ++var_t*v; ++ ++ if(!sv) return; ++ ++ for(v=sv->next; v!=sv; v=v->next) { ++ ++ /* check if we need to initialize it */ ++ if(!v->ini && v->dv && v->dv->init) { ++ ++ value_t *val; ++ srcpos_t pos; ++ ++ sial_curpos(&v->dv->pos, &pos); ++ ++ if((val=sial_exenode(v->dv->init))) { ++ ++ sial_chkandconvert(v->v, val); ++ sial_freeval(val); ++ v->ini=1; ++ ++ } else { ++ ++ sial_rwarning(&v->dv->pos, "Error initializing '%s'", v->name); ++ } ++ sial_curpos(&pos, 0); ++ } ++ } ++} ++ ++/* return the last set of globals */ ++var_t* ++sial_getcurgvar() ++{ ++ if(!globs) return 0; ++ return globs->vv; ++} ++ ++void * ++sial_add_globals(var_t*vv) ++{ ++glo *ng=sial_alloc(sizeof(glo)); ++ ++ sial_inivars(vv); ++ ng->vv=vv; ++ ng->next=globs; ++ sial_chkglobsforvardups(vv); ++ globs=ng; ++ return ng; ++} ++ ++void ++sial_rm_globals(void *vg) ++{ ++glo *g=(glo*)vg; ++ ++ if(globs) { ++ ++ if(globs==g) globs=g->next; ++ else { ++ ++ glo *gp; ++ ++ for(gp=globs; gp; gp=gp->next) { ++ ++ if(gp->next==g) { ++ ++ gp->next=g->next; ++ ++ } ++ ++ } ++ } ++ sial_free(g); ++ } ++} ++ ++ ++ ++/* ++ This is where we implement the variable scoping. ++*/ ++var_t* ++sial_getvarbyname(char *name, int silent, int local) ++{ ++var_t*vp; ++int i, aidx=0; ++ull apiv; ++ ++ for(i=svlev-1; i>=0; i--) { ++ ++ if((vp=sial_inlist(name, svs[i].svs))) { ++ ++ return vp; ++ } ++ if(svs[i].type==S_AUTO && !aidx) aidx=i; ++ ++ /* when we get to the function we're finished */ ++ if(svs[i].type==S_FILE) break; ++ } ++ ++ /* have'nt found any variable named like this one */ ++ /* first check the globals */ ++ if(!(vp=sial_inglobs(name))) { ++ ++ int off=0; ++ ++ /* check the API for a corresponding symbol */ ++ /* Jump over possible leading "IMG_" prefix */ ++ if(!strncmp(name, "IMG_", 4)) off=4; ++ if(!local && API_GETVAL(name+off, &apiv)) { ++ ++ vp=sial_newvar(name); ++ vp->ini=1; ++ ++ sial_defbtype(vp->v, apiv); ++ vp->v->mem=apiv; ++ ++ /* put this on the global list */ ++ sial_enqueue(apiglobs, vp); ++ } ++ else { ++ ++ if(silent) return 0; ++ sial_error("Unknown variable [%s]", name); ++ } ++ } ++ return vp; ++} ++ ++value_t * ++sial_exists(value_t *vname) ++{ ++char *name=sial_getptr(vname, char); ++ ++ return sial_defbtype(sial_newval(), (sial_getvarbyname(name, 1, 0) || sial_funcexists(name))); ++} ++ ++/* get a new empty vlist */ ++var_t* ++sial_newvlist() ++{ ++var_t*p=sial_newvar(""); ++ TAG(p); ++ TAG(p->name); ++ return p; ++} ++ ++/* this is called when we duplicate a list of automatic variables */ ++var_t* ++sial_dupvlist(var_t*vl) ++{ ++var_t*nv=(var_t*)sial_newvlist(); /* new root */ ++var_t*vp; ++ ++ for(vp=vl->next; vp !=vl; vp=vp->next) { ++ ++ var_t*v=sial_newvar(vp->name); /* new var_t*/ ++ ++ v->dv=vp->dv; ++ v->dv->refcount++; ++ v->ini=vp->ini; ++ sial_dupval(v->v, vp->v); ++ ++ /* we start with a new array for automatic variable */ ++ sial_refarray(v->v, -1); ++ v->v->arr=0; ++ sial_setarray(&v->v->arr); ++ ++ /* can't check ctypes for initialisation */ ++ if(is_ctype(v->v->type.type)) v->ini=1; ++ sial_enqueue(nv, v); ++ ++ } ++ return nv; ++} ++ ++void ++sial_addtolist(var_t*vl, var_t*v) ++{ ++ if(!v->name[0] || !sial_inlist(v->name, vl)) { ++ ++ sial_enqueue(vl, v); ++ ++ } else { ++ ++ /* if this is a prototype declaration then skip it */ ++ if(v->dv && v->dv->fct) return; ++ ++ sial_error("Duplicate declaration of variable %s", v->name); ++ } ++} ++ ++static void ++sial_chkforvardups(var_t*vl) ++{ ++var_t*v; ++ ++ if(!vl) return; ++ ++ for(v=vl->next; v!=vl; v=v->next) { ++ ++ var_t*v2=v->next; ++ ++ for(v2=v->next; v2!=vl; v2=v2->next) { ++ ++ if(v2->name[0] && !strcmp(v->name, v2->name)) { ++ ++ sial_rerror(&v2->dv->pos, "Duplicate declaration of variable '%s'", v->name); ++ ++ } ++ } ++ } ++} ++ ++static int takeproto=0; ++void sial_settakeproto(int v) { takeproto=v; } ++ ++ ++/* ++ This function scans a new list of declared variables ++ searching for static variables. ++*/ ++void ++sial_addnewsvs(var_t*avl, var_t*svl, var_t*nvl) ++{ ++var_t*v; ++ ++ if(nvl) { ++ ++ for(v=nvl->next; v!=nvl; ) { ++ ++ var_t*next; ++ ++ /* save next before sial_enqueue() trashes it ... */ ++ next=v->next; ++ ++ /* if this is a external variable or prototype function declaration ++ skip it */ ++ if((!takeproto && v->dv->fct && !v->dv->ref) || sial_isxtern(v->v->type.typattr)) { ++ ++ v=next; ++ continue; ++ } ++ ++ if(sial_isstatic(v->v->type.typattr)) { ++ ++ sial_addtolist(svl, v); ++ ++ } else { ++ ++ sial_addtolist(avl, v); ++ } ++ /* with each new variables check for duplicate declarations */ ++ sial_chkforvardups(avl); ++ sial_chkforvardups(svl); ++ ++ v=next; ++ } ++ /* discard nvl's root */ ++ sial_freevar(nvl); ++ } ++} ++ ++int ++sial_addsvs(int type, var_t*sv) ++{ ++int curlev=svlev; ++ ++ if(svlev==S_MAXDEEP) { ++ ++ sial_error("Svars stack overflow"); ++ ++ } else { ++ ++ svs[svlev].type=type; ++ svs[svlev].svs=sv; ++ svlev++; ++ ++ /* perform automatic initializations */ ++ sial_inivars(sv); ++ ++ /* if S_FILE then we are entering a function so start a newset of ++ stack variables */ ++ if(type == S_FILE ) { ++ ++ (void)sial_addsvs(S_AUTO, (var_t*)sial_newvlist()); ++ ++ } ++ } ++ return curlev; ++} ++ ++void ++sial_add_statics(var_t*var) ++{ ++int i; ++ ++ for(i=svlev-1;i>=0;i--) { ++ ++ if(svs[i].type==S_FILE ) { ++ ++ if(svs[i].svs) ++ sial_enqueue(svs[i].svs, var); ++ else ++ svs[i].svs=var; ++ return; ++ ++ } ++ } ++ sial_rwarning(&var->dv->pos, "No static context for var %s.", var->name); ++} ++ ++void sial_freesvs(var_t*v) ++{ ++var_t*vp; ++ ++ for(vp=v->next; vp != v; ) { ++ ++ var_t*vn=vp->next; ++ ++ sial_freevar(vp); ++ ++ vp=vn; ++ } ++ sial_freevar(v); ++} ++ ++int ++sial_getsvlev() { return svlev; } ++ ++/* reset the current level of execution and free up any automatic ++ variables. */ ++void ++sial_setsvlev(int newlev) ++{ ++int lev; ++ ++ for(lev=svlev-1; lev>=newlev; lev--) { ++ ++ if(svs[lev].type==S_AUTO) { ++ ++ sial_freesvs(svs[lev].svs); ++ ++ } ++ ++ } ++ svlev=newlev; ++} ++ ++/* ++ called by the 'var in array' bool expression. ++*/ ++int ++sial_lookuparray(node_t*vnode, node_t*arrnode) ++{ ++value_t *varr=NODE_EXE(arrnode); ++array_t*ap, *apr=varr->arr; ++value_t *val; ++int b=0; ++ ++ val=NODE_EXE(vnode); ++ ++ if(apr) { ++ ++ for(ap=apr->next; ap != apr; ap=ap->next) { ++ ++ if(VAL_TYPE(ap->idx) == VAL_TYPE(val)) { ++ ++ switch(VAL_TYPE(val)) { ++ case V_STRING: b=(!strcmp(ap->idx->v.data, val->v.data)); break; ++ case V_BASE: b=(unival(ap->idx)==unival(val)); break; ++ case V_REF: ++ if(sial_defbsize()==4) ++ b=(ap->idx->v.ul==val->v.ul); ++ else ++ b=(ap->idx->v.ull==val->v.ull); ++ break; ++ default: ++ sial_rerror(&vnode->pos, "Invalid indexing type %d", VAL_TYPE(val)); ++ } ++ if(b) break; ++ } ++ ++ } ++ } ++ sial_freeval(val); ++ sial_freeval(varr); ++ return b; ++} ++ ++/* ++ The actual for(i in array) core... ++*/ ++void ++sial_walkarray(node_t*varnode, node_t*arrnode, void (*cb)(void *), void *data) ++{ ++value_t *v; ++value_t *av; ++array_t*ap, *apr; ++ ++ sial_setini(varnode); ++ v=NODE_EXE(varnode); ++ ++ av=NODE_EXE(arrnode); ++ ++ if(av->arr) { ++ ++ for(apr=av->arr, ap=apr->next; ap != apr; ap=ap->next) { ++ ++ /* we set the value_t of the variable */ ++ sial_setval(v,ap->idx); ++ ++ (cb)(data); ++ ++ } ++ } ++ sial_freeval(v); ++ sial_freeval(av); ++} ++ ++/* scan the current array for a specific index and return value_t ++ XXX should use some hashing tables here for speed and scalability */ ++array_t* ++sial_getarrval(array_t**app, value_t *idx) ++{ ++array_t*ap, *apr; ++ ++ /* sial_setarray(app); AAA comment out */ ++ apr=*app; ++ ++ for(ap=apr->next; ap != apr; ap=ap->next) { ++ ++ if(ap->idx->type.type == idx->type.type) { ++ ++ int b=0; ++ ++ switch(idx->type.type) { ++ case V_STRING: b=(!strcmp(ap->idx->v.data, idx->v.data)); ++ break; ++ case V_BASE: b=(unival(ap->idx)==unival(idx)); ++ break; ++ case V_REF: ++ if(sial_defbsize()==4) ++ b=(ap->idx->v.ul==idx->v.ul); ++ else ++ b=(ap->idx->v.ull==idx->v.ull); ++ break; ++ default: ++ sial_error("Invalid index type %d", idx->type.type); ++ } ++ ++ if(b) { ++ ++ return ap; ++ ++ } ++ } ++ } ++ ++ /* we have not found this index, create one */ ++ ap=(array_t*)sial_calloc(sizeof(array_t)); ++ ap->idx=sial_makebtype(0); ++ sial_dupval(ap->idx, idx); ++ ++ /* just give it a int value_t of 0 for now */ ++ ap->val=sial_makebtype(0); ++ ++ /* we must set the same refenrence number as the ++ upper level array_t*/ ++ ap->val->arr->ref=apr->ref; ++ ++ /* link it in */ ++ ap->prev=apr->prev; ++ ap->next=apr; ++ apr->prev->next=ap; ++ apr->prev=ap; ++ ap->ref=0; ++ return ap; ++} ++ ++value_t * ++sial_intindex(value_t *a, int idx) ++{ ++value_t *v=sial_makebtype(idx); ++array_t*ap=sial_getarrval(&a->arr, v); ++ ++ sial_dupval(v, ap->val); ++ return v; ++} ++ ++value_t * ++sial_strindex(value_t *a, char *idx) ++{ ++value_t *v=sial_makestr(idx); ++array_t*ap=sial_getarrval(&a->arr, v); ++ ++ sial_dupval(v, ap->val); ++ return v; ++} ++ ++ ++void ++sial_setarrbval(array_t*a, int val) ++{ ++ sial_defbtype(a->val, (ull)val); ++} ++ ++array_t* ++sial_addarrelem(array_t**arr, value_t *idx, value_t *val) ++{ ++array_t*na; ++ ++ na=sial_getarrval(arr, idx); ++ ++ /* copy new val over */ ++ sial_freeval(na->val); ++ na->val=val; ++ ++ return na; ++} ++ ++/* insert a variable at the end of the list */ ++static void ++sial_varinsert(var_t*v) ++{ ++int i; ++ ++ for(i=svlev-1;i>=0;i--) { ++ ++ if(svs[i].type==S_AUTO) { ++ ++ sial_enqueue(svs[i].svs, v); ++ break; ++ } ++ } ++} ++ ++/* Dupicate and add a set of variables. Used to setup a function execution. ++ The new veriables are the actual parameters of the function so we mark them ++ As being initialized. ++*/ ++void ++sial_add_auto(var_t*nv) ++{ ++ nv->ini=1; ++ sial_varinsert(nv); ++} ++ ++void ++sial_valindex(value_t *var, value_t *idx, value_t *ret) ++{ ++ if(is_ctype(idx->type.type)) { ++ ++ sial_error("Invalid indexing type"); ++ ++ } else { ++ ++ array_t*a; ++ ++ a=sial_getarrval(&var->arr, idx); ++ ++ /* this is the first level of indexing through a variable */ ++ sial_dupval(ret, a->val); ++ ret->set=1; ++ ret->setval=a->val; ++ } ++} ++ ++void ++sial_addvalarray(value_t*v, value_t*idx, value_t*val) ++{ ++ sial_addarrelem(&v->arr, idx, val); ++ sial_freeval(idx); ++} ++ ++static void ++prtval(value_t*v) ++{ ++value_t*fmt=sial_makestr("%?"); ++ ++ sial_printf(fmt, v, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); ++ sial_freeval(fmt); ++} ++ ++static void ++prlevel(char *name, value_t*root, int level) ++{ ++ARRAY_S *arr; ++ ++ for(arr=root->arr->next; arr != root->arr; arr=arr->next) { ++ ++ printf("%*s%s[", level*3, "", name); ++ prtval(arr->idx); ++ printf("]="); ++ prtval(arr->val); ++ printf("\n"); ++ prlevel(name, arr->val, level+1); ++ } ++} ++ ++/* sial_prarr builtin */ ++value_t* ++sial_prarr(value_t*vname, value_t*root) ++{ ++char *name=sial_getptr(vname, char); ++ printf("%s=", name); ++ prtval(root); ++ printf("\n"); ++ prlevel(name, root, 1); ++ return sial_makebtype(0); ++} ++ ++var_t* ++sial_newvar(char *name) ++{ ++var_t*v=sial_calloc(sizeof(var_t)); ++char *myname=sial_alloc(strlen(name)+1); ++ ++ TAG(myname); ++ strcpy(myname,name); ++ v->name=myname; ++ v->v=sial_makebtype(0); ++ v->v->setval=v->v; ++ v->next=v->prev=v; ++ return v; ++} ++ ++ ++typedef struct { ++ node_t*n; ++ char name[1]; ++} vnode_t ; ++ ++static int insizeof=0; ++void sial_setinsizeof(int v) { insizeof=v;} ++ ++value_t * ++sial_exevar(void *arg) ++{ ++vnode_t *vn = arg; ++value_t *nv; ++var_t*curv; ++srcpos_t pos; ++ ++ sial_curpos(&vn->n->pos, &pos); ++ ++ if(!(curv=sial_getvarbyname(vn->name, 0, 0))) { ++ ++ sial_error("Oops! Var ref1.[%s]", vn->name); ++ ++ } ++ if(!curv->ini && !insizeof) { ++ ++ sial_error("Variable [%s] used before being initialized", curv->name); ++ ++ } ++ ++ nv=sial_newval(); ++ sial_dupval(nv,curv->v); ++ nv->set=1; ++ nv->setval=curv->v; ++ nv->setfct=sial_setfct; ++ ++ sial_curpos(&pos, 0); ++ ++ return nv; ++} ++ ++/* make sure a variable is flaged as being inited */ ++void ++sial_setini(node_t*n) ++{ ++ if((void*)n->exe == (void*)sial_exevar) { ++ ++ var_t*v=sial_getvarbyname(((vnode_t*)(n->data))->name, 0, 0); ++ v->ini=1; ++ } ++} ++ ++ ++/* get the name of a function through a variable */ ++char * ++sial_vartofunc(node_t*name) ++{ ++char *vname=NODE_NAME(name); ++value_t *val; ++ ++ /* if the nore is a general expression, then vname is 0 */ ++ if(!vname) { ++ ++ val=sial_exenode(name); ++ ++ } else { ++ ++ var_t*v; ++ ++ v=sial_getvarbyname(vname, 1, 1); ++ if(!v) return vname; ++ val=v->v; ++ } ++ ++ switch(val->type.type) ++ { ++ case V_STRING: ++ { ++ char *p=sial_alloc(val->type.size+1); ++ /* return the value_t of that string variable */ ++ strcpy(p, val->v.data); ++ sial_free(vname); ++ return p; ++ } ++ default: ++ /* return the name of the variable itself */ ++ sial_error("Invalid type for function pointer, expected 'string'."); ++ return vname; ++ } ++} ++ ++char * ++sial_namevar(vnode_t*vn) ++{ ++char *p; ++ ++ p=sial_strdup(vn->name); ++ TAG(p); ++ return p; ++} ++ ++static void ++sial_freevnode(vnode_t*vn) ++{ ++ sial_free(vn); ++} ++ ++/* ++ create or return existing variable node. ++*/ ++node_t* ++sial_newvnode(char *name) ++{ ++node_t*n=sial_newnode(); ++vnode_t*vn=sial_alloc(sizeof(vnode_t)+strlen(name)+1); ++ ++ TAG(vn); ++ ++ strcpy(vn->name, name); ++ n->exe=(xfct_t)sial_exevar; ++ n->free=(ffct_t)sial_freevnode; ++ n->name=(nfct_t)sial_namevar; ++ n->data=vn; ++ vn->n=n; ++ ++ sial_setpos(&n->pos); ++ ++ return n; ++} ++ ++#define TO (*to) ++#define FRM (*frm) ++ ++void ++sial_cparrelems(array_t**to, array_t**frm) ++{ ++array_t*ap; ++ ++ if(FRM) { ++ ++ sial_setarray(to); ++ for(ap=FRM->next; ap!=FRM; ap=ap->next) { ++ ++ array_t*na=sial_calloc(sizeof(array_t)); ++ ++ /* copy value_ts */ ++ sial_dupval(na->idx, ap->idx); ++ sial_dupval(na->val, ap->val); ++ ++ /* link it in */ ++ na->prev=TO->prev; ++ na->next=TO; ++ TO->prev->next=na; ++ TO->prev=na; ++ na->ref=1; ++ ++ /* copy that branch */ ++ sial_cparrelems(&na->val->arr, &ap->val->arr); ++ } ++ } ++} ++ +--- crash/extensions/libsial/sial-lsed.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial-lsed 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,32 @@ ++s/yyback/sialback/g ++s/yybgin/sialbgin/g ++s/yycrank/sialcrank/g ++s/yyerror/sialerror/g ++s/yyestate/sialestate/g ++s/yyextra/sialextra/g ++s/yyfnd/sialfnd/g ++s/yyin/sialin/g ++s/yyinput/sialinput/g ++s/yyleng/sialleng/g ++s/yylex/siallex/g ++s/yylineno/siallineno/g ++s/yylook/siallook/g ++s/yylsp/siallsp/g ++s/yylstate/siallstate/g ++s/yylval/siallval/g ++s/yymatch/sialmatch/g ++s/yymorfg/sialmorfg/g ++s/yyolsp/sialolsp/g ++s/yyout/sialout/g ++s/yyoutput/sialoutput/g ++s/yyprevious/sialprevious/g ++s/yysbuf/sialsbuf/g ++s/yysptr/sialsptr/g ++s/yysvec/sialsvec/g ++s/yytchar/sialtchar/g ++s/yytext/sialtext/g ++s/yytop/sialtop/g ++s/yyunput/sialunput/g ++s/yyvstop/sialvstop/g ++s/yywrap/sialwrap/g ++s/yydebug/sialdebug/g +--- crash/extensions/libsial/sial.y.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial.y 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,436 @@ ++%{ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include "sial.h" ++#include ++#include ++#include ++#include ++// to help resolve type name versus var name ambiguity... ++#define VARON needvar=1; ++#define VAROFF needvar=0; ++static int sial_toctype(int); ++int sialerror(char *); ++%} ++ ++%union { ++ node_t *n; ++ char *s; ++ int i; ++ type_t *t; ++ dvar_t *d; ++ var_t *v; ++} ++ ++%token STATIC DOBLK WHILE RETURN TDEF EXTERN VARARGS ++%token CHAR SHORT FLOAT DOUBLE VOID INT UNSIGNED LONG SIGNED VOLATILE REGISTER STRTYPE CONST ++%token BREAK CONTINUE DO FOR FUNC ++%token IF PATTERN BASETYPE ++%token STRUCT ENUM UNION ++%token SWITCH CASE DEFAULT ++%token ELSE CEXPR ++%token VAR NUMBER STRING ++%token TYPEDEF ++%token '(' ')' ',' ';' '{' '}' ++ ++%type termlist term opt_term opt_termlist ++%type stmt stmtlist expstmt stmtgroup ++%type var opt_var c_string ++%type for if while switch case caselist caseconstlist caseconst ++ ++%type dvar dvarlist dvarini ++ ++%type one_var_decl var_decl_list var_decl farglist decl_list ++ ++%type type ctype rctype btype_list tdef typecast ++%type storage_list string type_decl ++%type ctype_decl ++%type btype storage ctype_tok print ++ ++%right ASSIGN ADDME SUBME MULME DIVME MODME ANDME XORME ++%right ORME SHLME SHRME ++%right '?' ++%left IN ++%left BOR ++%left BAND ++%left OR ++%left XOR ++%left AND ++%left EQ NE ++%left GE GT LE LT ++%left SHL SHR ++%left ADD SUB ++%left MUL DIV MOD ++%left PRINT PRINTO PRINTD PRINTX TAKE_ARR ++%right ADROF PTRTO PTR UMINUS SIZEOF TYPECAST POSTINCR PREINCR POSTDECR PREDECR INCR DECR FLIP NOT ++%left ARRAY CALL INDIRECT DIRECT ++ ++%% ++ ++file: ++ /* empty */ ++ | fileobj ++ | file fileobj ++ ; ++ ++fileobj: ++ function ++ | var_decl ';' { sial_file_decl($1); } ++ | ctype_decl ';' { ; } ++ ; ++ ++function: ++ one_var_decl stmtgroup ++ { sial_newfunc($1, $2); } ++ ; ++ ++ ++for: ++ FOR '(' opt_termlist ';' opt_term ';' opt_termlist ')' expstmt ++ { $$ = sial_newstat(FOR, 4, $3, $5, $7, $9); } ++ | FOR '(' var IN term ')' expstmt ++ { $$ = sial_newstat(IN, 3, $3, $5, $7); } ++ ; ++ ++if: ++ IF '(' {VARON} term {VAROFF} ')' { $$ = $4; } ++ ; ++ ++switch : ++ SWITCH '(' {VARON} term {VAROFF} ')' '{' caselist '}' ++ ++ { $$ = sial_newstat(SWITCH, 2, $4, $8); } ++ ; ++ ++caselist: ++ case ++ | caselist case { $$ = sial_addcase($1, $2); } ++ ; ++ ++case : ++ caseconstlist stmtlist { $$ = sial_newcase($1, $2); } ++ ; ++ ++caseconst: ++ CASE term ':' { $$ = sial_caseval(0, $2); } ++ | DEFAULT ':' { $$ = sial_caseval(1, 0); } ++ ; ++ ++caseconstlist: ++ caseconst ++ | caseconstlist caseconst { $$ = sial_addcaseval($1, $2); } ++ ; ++ ++opt_term: ++ /* empty */ { $$ = 0; } ++ | term ++ ; ++ ++termlist: ++ term ++ | termlist ',' term { $$ = sial_sibling($1, $3); } ++ ; ++ ++opt_termlist: ++ /* empty */ { $$ = 0; } ++ | termlist ++ ; ++ ++stmt: ++ termlist ';' { $$ = sial_newstat(PATTERN, 1, $1); } ++ | while expstmt { $$ = sial_newstat(WHILE, 2, $1, $2); } ++ | switch ++ | for ++ | if expstmt ELSE expstmt { $$ = sial_newstat(IF, 3, $1, $2, $4); } ++ | if expstmt { $$ = sial_newstat(IF, 2, $1, $2); } ++ | DO expstmt WHILE '(' term ')' ';' ++ { $$ = sial_newstat(DO, 2, $2, $5); } ++ | RETURN term ';' { $$ = sial_newstat(RETURN, 1, $2); } ++ | RETURN ';' { $$ = sial_newstat(RETURN, 1, NULLNODE); } ++ | BREAK ';' { $$ = sial_newstat(BREAK, 0); } ++ | CONTINUE ';' { $$ = sial_newstat(CONTINUE, 0); } ++ | ';' { $$ = 0; } ++ ; ++ ++stmtlist: ++ /* empty */ { $$ = 0; } ++ | stmt ++ | stmtgroup ++ | stmtlist stmt { $$ = sial_addstat($1, $2); } ++ | stmtlist stmtgroup { $$ = sial_addstat($1, $2); } ++ ; ++ ++stmtgroup: ++ '{' decl_list stmtlist '}' { $$ = sial_stat_decl($3, $2); } ++ | '{' stmtlist '}' { $$ = sial_stat_decl($2, 0); } ++ ; ++ ++expstmt: ++ stmt ++ | stmtgroup ++ ; ++ ++term: ++ ++ term '?' term ':' term %prec '?' ++ { $$ = sial_newop(CEXPR, 3, $1, $3, $5); } ++ | term BOR term { $$ = sial_newop(BOR, 2, $1, $3); } ++ | term BAND term { $$ = sial_newop(BAND, 2, $1, $3); } ++ | NOT term { $$ = sial_newop(NOT, 1, $2); } ++ | term ASSIGN term { $$ = sial_newop(ASSIGN, 2, $1, $3); } ++ | term EQ term { $$ = sial_newop(EQ, 2, $1, $3); } ++ | term GE term { $$ = sial_newop(GE, 2, $1, $3); } ++ | term GT term { $$ = sial_newop(GT, 2, $1, $3); } ++ | term LE term { $$ = sial_newop(LE, 2, $1, $3); } ++ | term LT term { $$ = sial_newop(LT, 2, $1, $3); } ++ | term IN term { $$ = sial_newop(IN, 2, $1, $3); } ++ | term NE term { $$ = sial_newop(NE, 2, $1, $3); } ++ | '(' term ')' { $$ = $2; } ++ | term ANDME term { $$ = sial_newop(ANDME, 2, $1, $3); } ++ | PTR term %prec PTRTO { $$ = sial_newptrto($1, $2); } ++ | AND term %prec ADROF { $$ = sial_newadrof($2); } ++ | term OR term { $$ = sial_newop(OR, 2, $1, $3); } ++ | term ORME term { $$ = sial_newop(ORME, 2, $1, $3); } ++ | term XOR term { $$ = sial_newop(XOR, 2, $1, $3); } ++ | term XORME term { $$ = sial_newop(XORME, 2, $1, $3); } ++ | term SHR term { $$ = sial_newop(SHR, 2, $1, $3); } ++ | term SHRME term { $$ = sial_newop(SHRME, 2, $1, $3); } ++ | term SHL term { $$ = sial_newop(SHL, 2, $1, $3); } ++ | term SHLME term { $$ = sial_newop(SHLME, 2, $1, $3); } ++ | term ADDME term { $$ = sial_newop(ADDME, 2, $1, $3); } ++ | term SUBME term { $$ = sial_newop(SUBME, 2, $1, $3); } ++ | term MULME term { $$ = sial_newop(MULME, 2, $1, $3); } ++ | term DIV term { $$ = sial_newop(DIV, 2, $1, $3); } ++ | term DIVME term { $$ = sial_newop(DIVME, 2, $1, $3); } ++ | term MODME term { $$ = sial_newop(MODME, 2, $1, $3); } ++ | term MOD term { $$ = sial_newop(MOD, 2, $1, $3); } ++ | term SUB term { $$ = sial_newop(SUB, 2, $1, $3); } ++ | term ADD term { $$ = sial_newop(ADD, 2, $1, $3); } ++ | term PTR term %prec MUL { $$ = sial_newmult($1, $3, $2); } ++ | term AND term { $$ = sial_newop(AND, 2, $1, $3); } ++ | SUB term %prec UMINUS { $$ = sial_newop(UMINUS, 1, $2); } ++ | '~' term %prec FLIP { $$ = sial_newop(FLIP, 1, $2); } ++ | '+' term %prec UMINUS { $$ = $2; } ++ | term '(' ')' %prec CALL { $$ = sial_newcall($1, NULLNODE); } ++ | term '(' termlist ')' %prec CALL { $$ = sial_newcall($1, $3); } ++ | DECR term { $$ = sial_newop(PREDECR, 1, $2); } ++ | INCR term { $$ = sial_newop(PREINCR, 1, $2); } ++ | term DECR { $$ = sial_newop(POSTDECR, 1, $1); } ++ | term INCR { $$ = sial_newop(POSTINCR, 1, $1); } ++ | term INDIRECT var { $$ = sial_newmem(INDIRECT, $1, $3); } ++ | term INDIRECT tdef { $$ = sial_newmem(INDIRECT, $1, sial_tdeftovar($3)); } // resolve ambiguity ++ | term DIRECT var { $$ = sial_newmem(DIRECT, $1, $3); } ++ | term DIRECT tdef { $$ = sial_newmem(DIRECT, $1, sial_tdeftovar($3)); } // resolve ambiguity ++ | term '[' term ']' %prec ARRAY ++ { $$ = sial_newindex($1, $3); } ++ | NUMBER ++ | c_string ++ | typecast term %prec TYPECAST { $$ = sial_typecast($1, $2); } ++ | SIZEOF '(' var_decl ')' ++ { $$ = sial_sizeof(sial_newcast($3), 1); } ++ | SIZEOF term { $$ = sial_sizeof($2, 2); } ++ | print '(' var_decl ')' %prec SIZEOF ++ { $$ = sial_newptype($3); } ++ | print term %prec SIZEOF { $$ = sial_newpval($2, $1); } ++ | TAKE_ARR '(' term ',' term ')' { $$ = $3; /* sial_newtakearr($3, $5); */ } ++ | var ++ ; ++ ++print: ++ PRINT ++ | PRINTX ++ | PRINTO ++ | PRINTD ++ ; ++ ++typecast: ++ '(' var_decl ')' { $$ = sial_newcast($2); } ++ ; ++ ++var_decl_list: ++ var_decl ';' ++ | var_decl_list var_decl ';' { sial_addnewsvs($1, $1, $2); $$=$1; } ++ ; ++ ++decl_list: ++ ctype_decl ';' { $$ = 0; } ++ | var_decl ';' { $$ = $1; } ++ | decl_list var_decl ';' { $$=$1; if($1 && $2) sial_addnewsvs($1, $1, $2); } ++ | decl_list ctype_decl ';' { $$ = $1; } ++ ; ++ ++ ++var_decl: ++ type_decl dvarlist { needvar=0; $$ = sial_vardecl($2, $1); } ++ ; ++ ++one_var_decl: ++ type_decl dvar { needvar=0; $$ = sial_vardecl($2, $1); } ++ ; ++ ++type_decl: ++ type { $$=$1; needvar++; } ++ | storage_list { $$=$1; needvar++; } ++ | type storage_list { $$=sial_addstorage($1, $2); needvar++; } ++ | storage_list type { $$=sial_addstorage($2, $1); needvar++; } ++ | type_decl PTR { $$=$1; sial_pushref($1, $2);; needvar++; } ++ | type_decl storage_list { $$=sial_addstorage($1, $2); needvar++; } ++ ; ++ ++type: ++ ctype ++ | tdef ++ | btype_list ++ | string ++ | ctype_decl ++ ; ++ ++ctype_decl: ++ ctype_tok var '{' {sial_startctype(sial_toctype($1),$2);instruct++;} var_decl_list '}' ++ { instruct--; $$ = sial_ctype_decl(sial_toctype($1), $2, $5); } ++ | ctype_tok tdef '{' {sial_startctype(sial_toctype($1),lastv=sial_tdeftovar($2));instruct++;} var_decl_list '}' ++ { instruct--; $$ = sial_ctype_decl(sial_toctype($1), lastv, $5); } ++ | ctype_tok var '{' dvarlist '}' ++ { $$ = sial_enum_decl(sial_toctype($1), $2, $4); } ++ | ctype_tok tdef '{' dvarlist '}' ++ { $$ = sial_enum_decl(sial_toctype($1), sial_tdeftovar($2), $4); } ++ ; ++ ++ctype: ++ rctype { $$ = $1; } ++ | ctype_tok '{' {instruct++;} var_decl_list '}' ++ { instruct--; $$ = sial_ctype_decl(sial_toctype($1), 0, $4); } ++ | ctype_tok '{' dvarlist '}' ++ { $$ = sial_enum_decl(sial_toctype($1), 0, $3); } ++ ; ++ ++farglist: ++ /* empty */ { $$ = 0; } ++ | one_var_decl { $$ = $1; } ++ | farglist ',' one_var_decl { ++ if(!$1) sial_error("Syntax error"); ++ if($3) sial_addnewsvs($1, $1, $3); $$=$1; ++ } ++ | farglist ',' VARARGS { ++ if(!$1) sial_error("Syntax error"); ++ sial_addtolist($1, sial_newvar(S_VARARG)); $$=$1; ++ } ++ ; ++ ++ ++string: ++ STRTYPE { ++ type_t *t; ++ t=sial_newtype(); ++ t->type=V_STRING; ++ t->typattr=0; ++ $$ = t; ++ } ++ ; ++ ++rctype: ++ ctype_tok var { $$ = sial_newctype(sial_toctype($1), $2); } ++ | ctype_tok tdef { $$ = sial_newctype(sial_toctype($1), sial_tdeftovar($2)); } ++ ; ++ ++ctype_tok: ++ STRUCT ++ | ENUM ++ | UNION ++ ; ++ ++btype_list: ++ btype { $$ = sial_newbtype($1); } ++ | btype_list btype { $$ = sial_addbtype($1, $2); } ++ ; ++ ++c_string: ++ STRING { $$ = $1; } ++ | c_string STRING { $$ = sial_strconcat($1, $2); } ++ ; ++ ++btype: ++ LONG ++ | CHAR ++ | INT ++ | SHORT ++ | UNSIGNED ++ | SIGNED ++ | DOUBLE ++ | FLOAT ++ | VOID ++ ; ++ ++storage_list: ++ storage { $$ = sial_newbtype($1); } ++ | storage_list storage { sial_error("Only one storage class can be speficied"); } ++ ; ++ ++storage: ++ STATIC ++ | VOLATILE ++ | REGISTER ++ | TDEF ++ | EXTERN ++ | CONST ++ ; ++ ++dvarlist: ++ dvarini { $$ = $1; } ++ | dvarlist ',' dvarini { $$ = sial_linkdvar($1, $3); } ++ ; ++ ++dvarini: ++ dvar { $$ = $1; } ++ | dvar ASSIGN term { $$ = sial_dvarini($1, $3); } ++ ; ++ ++dvar: ++ opt_var { $$ = sial_newdvar($1); needvar=0; } ++ | ':' term { $$ = sial_dvarfld(sial_newdvar(0), $2); } ++ | dvar ':' term { $$ = sial_dvarfld($1, $3); } ++ | dvar '[' opt_term ']' { $$ = sial_dvaridx($1, $3); } ++ | PTR dvar { $$ = sial_dvarptr($1, $2); } ++ | dvar '(' ')' { $$ = sial_dvarfct($1, 0); } ++ | dvar '(' farglist ')' { $$ = sial_dvarfct($1, $3); } ++ | '(' dvar ')' { $$ = $2; } ++ ; ++ ++opt_var: ++ /* empty */ { $$ = 0; } ++ | var { $$ = $1; } ++ ; ++ ++var: ++ VAR { $$ = $1; } ++ ; ++ ++tdef: ++ TYPEDEF { $$ = $1; } ++ ; ++ ++while: ++ WHILE '(' {VARON} term {VAROFF} ')' { $$ = $4; } ++ ; ++ ++%% ++ ++static int ++sial_toctype(int tok) ++{ ++ switch(tok) { ++ case STRUCT: return V_STRUCT; ++ case ENUM: return V_ENUM; ++ case UNION: return V_UNION; ++ default: sial_error("Oops sial_toctype!"); return 0; ++ } ++} ++ ++/* ++ This file gets included into the yacc specs. ++ So the "sial.h" is already included ++*/ ++ ++int sialerror(char *p) { sial_error(p); return 0; } ++ +--- crash/extensions/libsial/sial_builtin.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_builtin.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,434 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include "sial.h" ++ ++/* information necessary for a builtin function */ ++typedef struct builtin { ++ ++ var_t*v; /* resulting variable declaration after parsing */ ++ bf_t *fp; /* pointer to actual function */ ++ char *proto; /* associated prototype_t*/ ++ struct builtin *next; /* to chain them */ ++ ++} builtin; ++ ++#define BT_EINVAL 1 /* Something is wrong and it's not ... */ ++value_t* ++sial_exit(int v) ++{ ++ /* Were we compiling ? */ ++ sial_parseback(); ++ ++ /* we were running... exit () */ ++ sial_dojmp(J_EXIT, &v); ++ ++ /* NOT REACHED */ ++ return 0; ++} ++ ++value_t* ++sial_bexit(value_t *vv) ++{ ++int v=sial_getval(vv); ++ ++ /* we're not going back to the he caller so free ++ the input value_t */ ++ sial_freeval(vv); ++ sial_exit(v); ++ /* NOT REACHED */ ++ return 0; ++} ++ ++#define MAXBYTES 4000 ++#define INCREMENT 16 ++value_t * ++sial_getstr(value_t *vmadr) ++{ ++ull madr=sial_getval(vmadr); ++char *buf=sial_alloc(MAXBYTES+1); ++char *p=buf; ++value_t *v; ++ ++ /* sial as already verified that this is a V_REF */ ++ /* since this is reading from a unkown size pool ++ we have to do an exponential reduction on the number of bytes ++ read ... */ ++ buf[0]=0; ++ while(1) { ++ ++ int i; ++ ++ if(!API_GETMEM(madr, p, INCREMENT)) break; ++ ++ /* have we found the '\0' yet ? */ ++ for(i=0;i= MAXBYTES) { ++ buf[MAXBYTES]='\0'; ++ break; ++ } ++ ++ } ++ v=sial_setstrval(sial_newval(), buf); ++ sial_free(buf); ++ return v; ++} ++ ++value_t * ++sial_substr(value_t *vp, value_t *vi, value_t *vj) ++{ ++char *p=sial_getptr(vp, char); ++ul i=sial_getval(vi); ++int l=strlen(p); ++int j=(vj?sial_getval(vj):(l-i+1)); ++char *s; ++value_t *v; ++ ++ if((i+j-1)>l || !i) { ++ ++ sial_error("Valid positions are [1..%d]\n", l); ++ ++ } ++ ++ s=sial_alloc(j+1); ++ strncpy(s, p+i-1, j); ++ s[j]='\0'; ++ v=sial_setstrval(sial_newval(), s); ++ sial_free(s); ++ return v; ++} ++ ++value_t * ++sial_getnstr(value_t* vmadr, value_t* vl) ++{ ++ull madr=sial_getval(vmadr); ++ul l=sial_getval(vl); ++char *buf=sial_alloc(l+1); ++value_t *v; ++ ++ if(!API_GETMEM(madr, buf, l)) buf[0]='\0'; ++ else buf[l]='\0'; ++ v=sial_setstrval(sial_newval(), buf); ++ sial_free(buf); ++ return v; ++} ++ ++value_t * ++sial_atoi(value_t *vs, value_t* vbase) ++{ ++char *s=sial_getptr(vs, char); ++int base=vbase ? sial_getval(vbase) : 0; ++ ++ strtoull(s, 0, (int) base); ++ return sial_defbtypesize(sial_newval(), strtoull(s, 0, base), B_ULL); ++} ++ ++value_t * ++sial_itoa(value_t* vi) ++{ ++ull i=sial_getval(vi); ++char p[40]; ++ ++ sprintf(p, "%llu", (unsigned long long)i); ++ return sial_setstrval(sial_newval(), p); ++} ++ ++value_t * ++sial_strlen(value_t *vs) ++{ ++char *s=sial_getptr(vs, char); ++ull l; ++ if(!s) l=0; ++ else l=strlen(s); ++ ++ return sial_defbtype(sial_newval(), l); ++} ++ ++value_t * ++sial_getchar(void) ++{ ++char c; ++struct termio tio, stio; ++int in=fileno(stdin); ++ ++ if(ioctl(in, TCGETA, &tio)) c=255; ++ else { ++ stio=tio; ++ tio.c_lflag &= ~(ICANON | ECHO); ++ tio.c_iflag &= ~(ICRNL | INLCR); ++ tio.c_cc[VMIN] = 1; ++ tio.c_cc[VTIME] = 0; ++ ioctl(in, TCSETA, &tio); ++ c=getc(stdin); ++ ioctl(in, TCSETA, &stio); ++ } ++ return sial_defbtype(sial_newval(), (ull)c); ++} ++ ++value_t * ++sial_gets(void) ++{ ++char p[1024]; ++ ++ if(!fgets(p, sizeof(p)-1, stdin)) p[0]='\0'; ++ else p[strlen(p)-1]='\0'; ++ return sial_setstrval(sial_newval(), p); ++} ++ ++static builtin *bfuncs=0; ++ ++/* ++ Check for the existance of a bt function ++*/ ++void * ++sial_chkbuiltin(char *name) ++{ ++builtin *bf; ++ ++ for(bf=bfuncs; bf; bf=bf->next) { ++ ++ if(!strcmp(name, bf->v->name)) { ++ ++ return bf; ++ } ++ } ++ return 0; ++} ++ ++/* ++ Remove a builtin. ++ This is done when we 'unload' a *.so file. ++*/ ++void ++sial_rmbuiltin(var_t*v) ++{ ++builtin *bf; ++builtin *last=0; ++ ++ for(bf=bfuncs; bf; bf=bf->next) { ++ ++ if(!strcmp(v->name, bf->v->name)) { ++ ++ if(!last) bfuncs=bf->next; ++ else { ++ ++ last->next=bf->next; ++ } ++ sial_free(bf->proto); ++ sial_free(bf); ++ } ++ last=bf; ++ } ++} ++ ++/* ++ Install a new builtin function. ++*/ ++var_t* ++sial_builtin(char *proto, bf_t* fp) ++{ ++var_t*v; ++ ++ /* parse the prototype_t*/ ++ if((v=sial_parsexpr(proto))) { ++ ++ builtin *bt; ++ int nargs=0; ++ ++ /* check name */ ++ if(!v->name || !v->name[0]) { ++ ++ sial_freevar(v); ++ sial_msg("Syntax error: no function name specified [%s]\n", proto); ++ return 0; ++ } ++ ++ /* check for function with same name */ ++ if(sial_chkfname(v->name, 0)) { ++ ++ sial_freevar(v); ++ sial_msg("Function already defined [%s]\n", proto); ++ return 0; ++ } ++ ++ if(v->dv->fargs) { ++ ++ var_t*vn=v->dv->fargs->next; ++ ++ while(vn!=v->dv->fargs) { ++ ++ nargs++; ++ vn=vn->next; ++ } ++ } ++ /* check number of args */ ++ if(nargs > BT_MAXARGS) { ++ ++ sial_freevar(v); ++ sial_msg("Too many parameters to function (max=%d) [%s]\n", BT_MAXARGS, proto); ++ return 0; ++ } ++ ++ ++ bt=sial_alloc(sizeof(builtin)); ++ bt->proto=sial_strdup(proto); ++ bt->fp=fp; ++ bt->v=v; ++ bt->next=0; ++ ++ /* install it */ ++ if(!bfuncs) bfuncs=bt; ++ else { ++ builtin *btp; ++ ++ for(btp=bfuncs; ; btp=btp->next) if(!btp->next) break; ++ btp->next=bt; ++ } ++ return v; ++ } ++ ++ sial_msg("Builtin [%s] not loaded.", proto); ++ ++ return 0; ++} ++ ++#define bcast(f) ((bf_t*)f) ++ ++static btspec_t sialbfuncs[] = { ++ { "unsigned long long atoi(string, ...)",bcast(sial_atoi)}, ++ { "int exists(string)", bcast(sial_exists)}, ++ { "void exit(int)", bcast(sial_bexit)}, ++ { "int getchar()", bcast(sial_getchar)}, ++ { "string gets()", bcast(sial_gets)}, ++ { "string getstr(char *)", bcast(sial_getstr)}, ++ { "string getnstr(char *, int)", bcast(sial_getnstr)}, ++ { "string itoa(int)", bcast(sial_itoa)}, ++ { "void printf(string, ...)", bcast(sial_printf)}, ++ { "void showtemp()", bcast(sial_showtemp)}, ++ { "void showaddr(char *)", bcast(sial_showaddr)}, ++ { "void memdebugon()", bcast(sial_memdebugon)}, ++ { "void memdebugoff()", bcast(sial_memdebugoff)}, ++ { "int sial_load(string)", bcast(sial_bload)}, ++ { "int sial_unload(string)", bcast(sial_bunload)}, ++ { "int depend(string)", bcast(sial_bdepend)}, ++ { "int strlen(string)", bcast(sial_strlen)}, ++ { "string sprintf(string, ...)", bcast(sial_sprintf)}, ++ { "string substr(string, int, ...)", bcast(sial_substr)}, ++ { "void prarr(string name, int i)", bcast(sial_prarr)}, ++ { "int member(void*, string name)", bcast(sial_ismember)}, ++ { "string findsym(string)", bcast(sial_findsym)}, ++}; ++ ++ ++/* ++ Install the sial builtins. ++*/ ++void ++sial_setbuiltins() ++{ ++int i; ++ ++ for(i=0;iv->dv->fargs) { ++ ++ var_t*vv=bf->v->dv->fargs->next; ++ ++ while(vv != bf->v->dv->fargs) { ++ ++ if(vv->name && !strcmp(vv->name, S_VARARG)) { ++ while(nargsv); ++ sial_chkandconvert(lvals[nargs], vals[nargs]); ++ } ++ nargs++; ++ vv=vv->next; ++ } ++ } ++ ++ /* check parameters number */ ++ if(iv->dv->pos, "Too few parameters to '%s'", bf->proto); ++ ++ } else if(i>nargs){ ++ ++ sial_rerror(&bf->v->dv->pos, "Too many parameters to '%s'", bf->proto); ++ ++ } ++ ++ if(vals) { ++ /* the actual call */ ++ v=(bf->fp) ( ++ lvals[0], lvals[1], ++ lvals[2], lvals[3], ++ lvals[4], lvals[5], ++ lvals[6], lvals[7], ++ lvals[8], lvals[9], ++ lvals[10], lvals[11], ++ lvals[12], lvals[13], ++ lvals[14], lvals[15], ++ lvals[16], lvals[17], ++ lvals[18], lvals[19] ++ ); ++ } else { ++ ++ v=(bf->fp) ((value_t*)0); ++ } ++ ++ while(i) { ++ ++ --i; ++ sial_freeval(vals[i]); ++ sial_freeval(lvals[i]); ++ } ++ ++ /* make a copy of the return value_t info */ ++ vr=sial_cloneval(bf->v->v); ++ sial_chkandconvert(vr, v); ++ sial_freeval(v); ++ ++ return vr; ++ } ++ ++ sial_error("Oops. sial_exebfunc()"); ++ return 0; ++} +--- crash/extensions/libsial/sial_case.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_case.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,125 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++/* ++ Set of functions to handle the case construct. ++*/ ++#include "sial.h" ++ ++void ++sial_freecaseval(caseval_t*cv) ++{ ++ sial_free(cv); ++} ++ ++node_t* ++sial_caseval(int isdef, node_t*val) ++{ ++caseval_t*cv=sial_alloc(sizeof(caseval_t)); ++node_t*n=sial_newnode(); ++value_t *v; ++ ++ cv->isdef=isdef; ++ if(val) { ++ ++ v=NODE_EXE(val); ++ cv->val=unival(v); ++ sial_freeval(v); ++ NODE_FREE(val); ++ ++ } else cv->val=0; ++ ++ sial_setpos(&cv->pos); ++ ++ cv->next=0; ++ n->data=cv; ++ return n; ++} ++ ++node_t* ++sial_addcaseval(node_t*n, node_t*n2) ++{ ++caseval_t*cv=(caseval_t*)n->data; ++caseval_t*ncv=(caseval_t*)n2->data; ++ ++ sial_free(n); ++ ncv->next=cv; ++ return n2; ++} ++ ++void ++sial_freecase(void *vcl) ++{ ++caselist_t*cl=(caselist_t*)vcl; ++ ++ NODE_FREE(cl->stmt); ++ sial_free(cl); ++} ++ ++node_t* ++sial_newcase(node_t*nc, node_t* n) ++{ ++caseval_t*cv=(caseval_t*)nc->data; ++caselist_t*cl=sial_alloc(sizeof(caselist_t)); ++node_t*nn=sial_newnode(); ++ ++ ++ nn->data=cl; ++ nn->free=(ffct_t)sial_freecase; ++ ++ cl->vals=cv; ++ sial_free(nc); ++ ++ cl->stmt=n; ++ cl->next=0; ++ ++ sial_setpos(&cl->pos); ++ ++ return nn; ++} ++ ++node_t* ++sial_addcase(node_t*n, node_t*n2) ++{ ++caselist_t*lcl; ++caselist_t*ncl=(caselist_t*)n2->data; ++caselist_t*cl=(caselist_t*)n->data; ++ ++ for(lcl=cl; lcl->next; lcl=lcl->next); ++ ++ /* we need to add case in the order they are listed */ ++ lcl->next=ncl; ++ sial_free(n2); ++ ncl->next=0; ++ ++ sial_setpos(&ncl->pos); ++ ++ return n; ++} ++ ++int ++sial_docase(ull val, caselist_t*cl) ++{ ++caselist_t*defclp=0, *clp; ++ ++ ++ for(clp=cl;clp;clp=clp->next) { ++ ++ caseval_t*cvp; ++ ++ for(cvp=clp->vals; cvp; cvp=cvp->next) { ++ ++ if(cvp->val==val) goto out; ++ else if(cvp->isdef) defclp=clp; ++ } ++ } ++out: ++ if(clp || (clp=defclp)) { ++ ++ for(;clp;clp=clp->next) { ++ ++ if(clp->stmt) NODE_EXE(clp->stmt); ++ } ++ } ++ return 1; ++} +--- crash/extensions/libsial/sial_member.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_member.c 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,321 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include ++#include "sial.h" ++#include "sial.tab.h" ++ ++/* these function are used to access and set members in structs */ ++ ++/* define a member access */ ++typedef struct mem { ++ char *name; /* member name */ ++ int dir; /* direct/indirect access */ ++ node_t*expr; /* expression node_t*/ ++ stmember_t*stm; /* associated member information */ ++ char *local; /* local memory or ... */ ++ ull mem; /* ... system memory access */ ++ srcpos_t p; ++} mem; ++ ++void * ++sial_adrval(value_t *v) ++{ ++ switch(v->type.size) { ++ ++ case 1: return &v->v.uc; ++ case 2: return &v->v.us; ++ case 4: return &v->v.ul; ++ case 8: return &v->v.ull; ++ } ++ sial_error("Oops sial_adrval"); ++ return 0; ++} ++ ++/* some API secondary entry points */ ++void sial_member_soffset(member_t*m, int offset) { m->offset=offset; } ++void sial_member_ssize(member_t*m, int size) { m->size=size; } ++void sial_member_sfbit(member_t*m, int fbit) { m->fbit=fbit; } ++void sial_member_snbits(member_t*m, int nbits) { m->nbits=nbits; } ++void sial_member_sname(member_t*m, char *name) { m->name=sial_strdup(name); } ++ ++ ++void ++sial_setmem(mem *m, value_t *v) ++{ ++stmember_t*stm=m->stm; ++ ++ /* check type compatibility. Ctypes should point to the same stinfo...*/ ++ if(stm->type.type != v->type.type ++ /* pointer most point to the same type of object */ ++ || (v->type.type==V_REF && v->type.rtype != stm->type.rtype) ++ /* ctypes should point to the same stinfo */ ++ || (is_ctype(v->type.type) && v->type.idx != stm->type.idx)) { ++ ++ sial_error("Incompatible types for assignment"); ++ ++ } ++ ++ if(stm->m.nbits) { ++ ++ ull dvalue_t=0; ++ ++ if(v->type.type!=V_BASE) { ++ ++ sial_error("Invalid assignment to bit field"); ++ ++ } ++ ++ /* do the bit gymnastic */ ++ /* we need to create a ull that contain the current ++ bit of teh destination */ ++ if(m->local) { ++ ++ memmove(m->local+stm->m.offset, ((char*)(&dvalue_t))+8-stm->m.size, stm->m.size); ++ dvalue_t=set_bit_value_t(dvalue_t, v->v.ull, stm->m.nbits, stm->m.fbit); ++ memmove(((char*)(&dvalue_t))+8-stm->m.size, m->local+stm->m.offset, stm->m.size); ++ ++ } ++ ++ if(m->mem) { ++ ++ API_GETMEM(m->mem+stm->m.offset, ((char*)(&dvalue_t))+8-stm->m.size, stm->m.size); ++ dvalue_t=set_bit_value_t(dvalue_t, v->v.ull, stm->m.nbits, stm->m.fbit); ++ API_PUTMEM(m->mem+stm->m.offset, ((char*)(&dvalue_t))+8-stm->m.size, stm->m.size); ++ ++ } ++ ++ ++ } else { ++ ++ /* move the data */ ++ if(is_ctype(v->type.type)) { ++ ++ if(m->local) { ++ ++ memmove(m->local+stm->m.offset, v->v.data, stm->m.size); ++ ++ } ++ if(m->mem) { ++ ++ API_PUTMEM(m->mem+stm->m.offset, v->v.data, stm->m.size); ++ } ++ ++ } else { ++ ++ sial_transval(v->type.size, stm->m.size, v, sial_issigned(v->type.typattr)); ++ ++ if(m->local) { ++ ++ memmove(m->local+stm->m.offset, sial_adrval(v), stm->m.size); ++ ++ } ++ ++ if(m->mem) { ++ ++ API_PUTMEM(m->mem+stm->m.offset, sial_adrval(v), stm->m.size); ++ } ++ } ++ } ++} ++ ++#define vdata(p, t) ((t*)(p->v.data)) ++ ++void ++sial_exememlocal(value_t *vp, stmember_t* stm, value_t *v) ++{ ++ /* expression should be a ctype_t*/ ++ if(!is_ctype(vp->type.type)) { ++ ++ sial_error("Invalid type for '.' expression"); ++ } ++ /* get that value_t from the application memory */ ++ if(is_ctype(stm->type.type) && !stm->type.idxlst) { ++ ++ void *data=sial_alloc(stm->m.size); ++ ++ memmove(data, vdata(vp, char)+stm->m.offset, stm->m.size); ++ if(vp->mem) v->mem=vp->mem+stm->m.offset; ++ v->v.data=data; ++ ++ } ++ /* bit field gymnastic */ ++ else if(stm->m.nbits) { ++ ++ ull value=0; ++ ++ memmove(vdata(vp, char)+stm->m.offset, ((char*)&value)+(sizeof(value)-stm->m.size), stm->m.size); ++ get_bit_value(value, stm->m.nbits, stm->m.fbit, stm->m.size, v); ++ ++ } ++ /* check if this is an array, if so then create a reference to it */ ++ else if(stm->type.idxlst) { ++ ++ ull mempos=vp->mem+stm->m.offset; ++ if(sial_defbsize()==8) v->v.ull=mempos; ++ else v->v.ul=mempos; ++ v->mem=mempos; ++ ++ } else { ++ ++ switch(TYPE_SIZE(&stm->type)) { ++ case 1: ++ memmove(&v->v.uc, vdata(vp, char)+stm->m.offset, 1); ++ break; ++ case 2: ++ memmove(&v->v.us, vdata(vp, char)+stm->m.offset, 2); ++ break; ++ case 4: ++ memmove(&v->v.ul, vdata(vp, char)+stm->m.offset, 4); ++ break; ++ case 8: ++ memmove(&v->v.ull, vdata(vp, char)+stm->m.offset, 8); ++ break; ++ default: ++ sial_error("Oops exemem2[%d]", TYPE_SIZE(&stm->type)); ++ break; ++ } ++ if(vp->mem) v->mem=vp->mem+stm->m.offset; ++ } ++} ++ ++value_t * ++sial_exemem(mem *m) ++{ ++value_t *v=sial_newval(); ++value_t *vp=NODE_EXE(m->expr); ++stmember_t*stm; ++srcpos_t p; ++ ++ sial_curpos(&m->p, &p); ++ ++ if(vp->type.type == V_REF) { ++ ++ if(vp->type.ref > 1) { ++ ++ sial_error("Too many levels of indirection for access to [%s]", m->name); ++ ++ } ++ } ++ ++ /* get the member information and attach it */ ++ stm=m->stm=(stmember_t*)sial_member(m->name, &vp->type); ++ if(!stm) { ++ ++ sial_freeval(v); ++ sial_freeval(vp); ++ sial_error("Invalid member name specified : %s", m->name); ++ ++ } ++ ++ /* get a copy of the type of thise member and put it in v */ ++ sial_duptype(&v->type, &stm->type); ++ ++ /* indirect i.e. (struct*)->member *most* be relative to the ++ system image. This is a restriction of this language */ ++ if(m->dir==INDIRECT) { ++ ++ ull mempos; ++ ++ if(vp->type.type != V_REF || !is_ctype(vp->type.rtype)) { ++ ++ sial_error("Invalid type for '->' expression"); ++ } ++ ++ m->local=0; ++ m->mem=sial_defbsize()==8?vp->v.ull:vp->v.ul; ++ mempos=m->mem+stm->m.offset; ++ ++ /* get that value_t from the system image */ ++ if(is_ctype(v->type.type) && !stm->type.idxlst) { ++ ++ v->v.data=sial_alloc(stm->m.size); ++ API_GETMEM(mempos, v->v.data, stm->m.size); ++ v->mem=mempos; ++ ++ } ++ /* bit field gymnastic */ ++ else if(stm->m.nbits) { ++ ++ ull value=0; ++ ++ API_GETMEM(m->mem+stm->m.offset, &value, stm->m.size); ++ get_bit_value(value, stm->m.nbits, stm->m.fbit, stm->m.size, v); ++ /* no mempos for bit fields ... */ ++ ++ } ++ /* check if this is an array, if so then create a reference to it */ ++ else if(stm->type.idxlst) { ++ ++ if(sial_defbsize()==8) v->v.ull=mempos; ++ else v->v.ul=mempos; ++ v->mem=mempos; ++ ++ } else { ++ ++ v->mem=mempos; ++ ++ switch(TYPE_SIZE(&stm->type)) { ++ case 1: ++ API_GETMEM(mempos, &v->v.uc, 1); ++ break; ++ case 2: ++ API_GETMEM(mempos, &v->v.us, 2); ++ break; ++ case 4: ++ API_GETMEM(mempos, &v->v.ul, 4); ++ break; ++ case 8: ++ API_GETMEM(mempos, &v->v.ull, 8); ++ break; ++ default: ++ sial_error("Oops exemem[%d]", TYPE_SIZE(&stm->type)); ++ break; ++ } ++ ++ } ++ } ++ /* direct i.e. (struct).member *most* be in referance to a local ++ structure. */ ++ else { ++ ++ m->mem=vp->mem; ++ m->local=vp->v.data; ++ ++ /* extract the value from a local copy */ ++ sial_exememlocal(vp, stm, v); ++ } ++ sial_curpos(&p, 0); ++ sial_freeval(vp); ++ v->setfct=(setfct_t)sial_setmem; ++ v->setval=(value_t*)m; ++ v->set=1; ++ return v; ++} ++ ++void ++sial_freemem(mem *m) ++{ ++ NODE_FREE(m->expr); ++ sial_free(m->name); ++ sial_free(m); ++} ++ ++node_t* ++sial_newmem(int dir, node_t*expr, node_t*name) ++{ ++char *nstr=NODE_NAME(name); ++node_t*n=sial_newnode(); ++mem *m=sial_alloc(sizeof(mem)); ++ ++ /* dicard nam node_t*/ ++ NODE_FREE(name); ++ m->name=nstr; ++ m->dir=dir; ++ m->expr=expr; ++ sial_setpos(&m->p); ++ n->data=m; ++ n->exe=(xfct_t)sial_exemem; ++ n->free=(ffct_t)sial_freemem; ++ return n; ++} +--- crash/extensions/libsial/sial.l.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial.l 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,206 @@ ++%{ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++%} ++ ++%{ ++#include ++ ++#ifdef linux ++#define YY_INPUT(buf,result,max_size) \ ++{ \ ++ int c = sial_input(); \ ++ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ ++} ++#endif ++ ++#define yylval siallval ++#include "sial.h" ++#define YY_NO_UNPUT ++#include "sial.tab.h" ++ ++#define retok(t) return(t) ++int needvar=0, instruct=0; ++node_t *lastv; ++static char *lastvar=0; ++char *sial_lastvar(void) { return lastvar; } ++extern void sial_skip_directive(void); ++extern void sial_define(void); ++extern void sial_include(void); ++extern void sial_undefine(void); ++extern char sial_newchar(void); ++extern int sial_chkmacvar(char *); ++%} ++ ++ABC [a-zA-Z_] ++ABCN [a-zA-Z0-9_] ++N [0-9] ++X [0-9a-fA-F] ++W [ \t\n] ++P #[ \t]* ++OP [(] ++CP [)] ++ ++%s var1 ++%s var2 ++%s var3 ++%s var4 ++%% ++ ++{W} { ; } ++ ++"..." { retok(VARARGS); } ++"&&" { retok(BAND); } ++"||" { retok(BOR); } ++"<" { retok(LT); } ++"<=" { retok(LE); } ++"==" { retok(EQ); } ++">=" { retok(GE); } ++">" { retok(GT); } ++"!=" { retok(NE); } ++ ++"&=" { retok(ANDME); } ++"|" { retok(OR); } ++"|=" { retok(ORME); } ++"!" { retok(NOT); } ++"^" { retok(XOR); } ++"&" { retok(AND); } ++"^=" { retok(XORME); } ++">>" { retok(SHR); } ++"<<=" { retok(SHLME); } ++">>=" { retok(SHRME); } ++"<<" { retok(SHL); } ++"++" { retok(INCR); } ++"+" { retok(ADD); } ++"--" { retok(DECR); } ++"-" { retok(SUB); } ++"+=" { retok(ADDME); } ++"-=" { retok(SUBME); } ++"*=" { retok(MULME); } ++"/=" { retok(DIVME); } ++"/" { retok(DIV); } ++"%=" { retok(MODME); } ++"%" { retok(MOD); } ++"=" { retok(ASSIGN); } ++"->" { retok(INDIRECT); } ++"." { retok(DIRECT); } ++"{" { needvar=0; retok('{'); } ++ ++\*+ { ++ yylval.i=strlen(yytext); ++ return PTR; ++ } ++ ++(("0x"+){X}+[lL]*|{N}+[lL]*) { yylval.n = sial_newnum(yytext); retok(NUMBER); } ++ ++{P}ident { sial_skip_directive(); } ++{P}pragma { sial_skip_directive(); } ++{P}define { ++ /* preprocessor command */ ++ /* either a simple constant or a macro */ ++ sial_define(); ++ } ++{P}include { ++ ++ /* file inclusion */ ++ sial_include(); ++ } ++{P}undef { ++ sial_undefine(); ++ } ++while { retok(WHILE); } ++for { retok(FOR); } ++do { retok(DO); } ++if { retok(IF); } ++else { retok(ELSE); } ++break { retok(BREAK); } ++continue { retok(CONTINUE); } ++in { retok(IN); } ++return { retok(RETURN); } ++ ++__char__ { retok(yylval.i=CHAR); } ++__short__ { retok(yylval.i=SHORT); } ++__int__ { retok(yylval.i=INT); } ++__float__ { retok(yylval.i=FLOAT); } ++__double__ { retok(yylval.i=DOUBLE); } ++__register__ { retok(yylval.i=REGISTER); } ++__volatile__ { retok(yylval.i=VOLATILE); } ++__void__ { retok(yylval.i=VOID); } ++__unsigned__ { retok(yylval.i=UNSIGNED); } ++__signed__ { retok(yylval.i=SIGNED); } ++__long__ { retok(yylval.i=LONG); } ++__const__ { retok(yylval.i=CONST); } ++__static__ { retok(yylval.i=STATIC); } ++__extern__ { retok(yylval.i=EXTERN); } ++ ++char { retok(yylval.i=CHAR); } ++short { retok(yylval.i=SHORT); } ++int { retok(yylval.i=INT); } ++float { retok(yylval.i=FLOAT); } ++double { retok(yylval.i=DOUBLE); } ++register { retok(yylval.i=REGISTER); } ++volatile { retok(yylval.i=VOLATILE); } ++void { retok(yylval.i=VOID); } ++unsigned { retok(yylval.i=UNSIGNED); } ++signed { retok(yylval.i=SIGNED); } ++long { retok(yylval.i=LONG); } ++const { retok(yylval.i=CONST); } ++static { retok(yylval.i=STATIC); } ++extern { retok(yylval.i=EXTERN); } ++ ++string { retok(yylval.i=STRTYPE); } ++__inline { ; } ++switch { retok(SWITCH); } ++case { retok(CASE); } ++default { retok(DEFAULT); } ++enum { retok(yylval.i=ENUM); } ++union { retok(yylval.i=UNION);} ++struct { retok(yylval.i=STRUCT); } ++typedef { retok(yylval.i=TDEF); } ++sizeof { retok(SIZEOF); } ++print { retok(PRINT); } ++printo { retok(PRINTO); } ++printd { retok(PRINTD); } ++printx { retok(PRINTX); } ++take_array { retok(TAKE_ARR); } ++ ++__var__ { BEGIN(var1); } ++{W}*{OP}{W}* { BEGIN(var2); } ++{ABC}{ABCN}* { BEGIN(var3); goto forcevar; } ++{W}*{CP}{W}* { BEGIN(INITIAL); } ++ ++ ++{ABC}{ABCN}* { ++ if((!needvar) && (yylval.t=sial_getctype(V_TYPEDEF, yytext, 1))) ++ { ++ /* hack to remember last tdef name */ ++ if(lastvar) sial_free(lastvar); ++ lastvar=sial_alloc(strlen(yytext)+1); ++ strcpy(lastvar, yytext); ++ needvar++; ++ retok(TYPEDEF); ++ } ++forcevar: ++ needvar=0; ++ if(strlen(yytext) > MAX_SYMNAMELEN) { ++ ++ sial_error("Symbol name too long"); ++ } ++ if(!sial_chkmacvar(yytext)) { ++ yylval.n = sial_newvnode(yytext); ++ retok(VAR); ++ } ++ } ++ ++\" { yylval.n = sial_newstr(); retok(STRING); } ++\'.\' { yylval.n = sial_makenum(B_SC, yytext[1]); retok(NUMBER); } ++\'\\.\' { yylval.n = sial_makenum(B_SC, sial_getseq(yytext[2])); retok(NUMBER); } ++ ++. { retok(yylval.i = yytext[0]); } ++ ++%% ++#undef input ++#undef unput ++#define input() sial_input() ++#define unput(c) sial_unput(c) +--- crash/extensions/libsial/Makefile.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/Makefile 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,94 @@ ++# ++# Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++# ++# ++# Makefile for LIBSIAL ++# ++ ++# Must be berkeley yacc. Bison will not cut it ++YACC = bison ++ ++LDIRT = lex.sial.c lex.sialpp.c sial.tab.c sial.tab.h sialpp.tab.c \ ++ sialpp.tab.h y.output mkbaseop baseops.c y.tab.c y.tab.h \ ++ libsial.so* *.output ++ ++LIBDIR = /usr/lib ++TARGETS = libsial.a ++ ++CFLAGS += -O3 -g -fPIC ++ifeq ($(TARGET), PPC64) ++ CFLAGS += -m64 ++endif ++ ++CFILES = sial_util.c sial_node.c sial_var.c sial_func.c sial_str.c \ ++ sial_op.c sial_num.c sial_stat.c sial_builtin.c sial_type.c \ ++ sial_case.c sial_api.c sial_member.c sial_alloc.c sial_define.c \ ++ sial_input.c sial_print.c ++ ++OFILES = $(CFILES:.c=.o) sialpp.tab.o sial.tab.o lex.sial.o lex.sialpp.o \ ++ baseops.o ++ ++HFILES = sial.h sial_api.h ++ ++LSOURCES = sial-lsed sialpp-lsed sial.l sialpp.l sial.y sialpp.y mkbaseop.c ++ ++all: default ++ ++showfiles: ++ @echo $(RELDIR)/$(CFILES) $(RELDIR)/$(HFILES) $(RELDIR)/$(LSOURCES) ++ ++exports: all ++ install $(TARGETS) $(ROOT)$(LIBDIR) ++ ++headers: ++ install -m 644 $(HFILES) $(ROOT)/usr/include ++ ++install: headers exports ++ (cd scripts ; $(MAKE) install ) ++ ++baseops.o: mkbaseop.c ++ $(CC) $(CFLAGS) -o mkbaseop mkbaseop.c ++ ./mkbaseop > baseops.c ++ $(CC) $(CFLAGS) -c baseops.c ++ ++mkbaseop.c sial_member.o sial_op.o sial_stat.o sial_type.o y.tab.o : sial.tab.h ++ ++lex.sial.o: lex.sial.c sial.tab.c sial.h ++ $(CC) $(CFLAGS) -c lex.sial.c ++ ++lex.sial.c: sial.l ++ flex -L -Psial -t sial.l > lex.sial.c ++ ++sial.tab.c: sial.tab.h ++ ++sialpp.tab.o: sialpp.tab.c ++ $(CC) $(CFLAGS) -c sialpp.tab.c ++ ++sial.tab.o: sial.tab.c ++ $(CC) $(CFLAGS) -c sial.tab.c ++ ++sial.tab.h : sial.y ++ $(YACC) -psial -v -t -d sial.y ++ ++lex.sialpp.o: lex.sialpp.c sialpp.tab.c sial.h ++ $(CC) $(CFLAGS) -c lex.sialpp.c ++ ++lex.sialpp.c: sialpp.l ++ flex -Psialpp -t sialpp.l > lex.sialpp.c ++ ++sialpp.tab.c: sialpp.tab.h sial.tab.h ++ ++sialpp.tab.h : sialpp.y sial.tab.h ++ $(YACC) -psialpp -v -t -d sialpp.y ++ ++default: $(TARGETS) ++ ++$(CFILES): $(HFILES) sial.tab.h ++ ++$(TARGETS): $(OFILES) ++ $(AR) ccurl $(TARGETS) $(OFILES) ++ ++clean: ++ -/bin/rm -f *.o $(TARGETS) $(LDIRT) ++ ++clobber: clean +--- crash/extensions/libsial/sialpp.y.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sialpp.y 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,88 @@ ++%{ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++/* ++ This is the grammar for the preprocessor expression evaluation. ++*/ ++#include "sial.h" ++#include "sial.tab.h" ++#include ++#include ++#include ++static node_t *last_term; ++%} ++ ++%union { ++ node_t *n; ++ int i; ++} ++ ++%token P_VAR P_NUMBER ++%token P_DEFINED ++ ++%type term ++ ++%right '?' ++%left P_BOR ++%left P_BAND ++%left P_OR ++%left P_XOR ++%left P_AND ++%left P_EQ P_NE ++%left P_GE P_GT P_LE P_LT ++%left P_SHL P_SHR ++%left P_ADD P_SUB ++%left P_MUL P_DIV P_MOD ++%right P_UMINUS P_FLIP P_NOT ++ ++%% ++ ++term: ++ ++ term '?' term ':' term %prec '?' ++ { $$ = sial_newop(CEXPR, 3, $1, $3, $5); last_term = $$; } ++ | term P_BOR term { $$ = sial_newop(BOR, 2, $1, $3); last_term = $$; } ++ | term P_BAND term { $$ = sial_newop(BAND, 2, $1, $3); last_term = $$; } ++ | P_NOT term { $$ = sial_newop(NOT, 1, $2); last_term = $$; } ++ | term P_EQ term { $$ = sial_newop(EQ, 2, $1, $3); last_term = $$; } ++ | term P_GE term { $$ = sial_newop(GE, 2, $1, $3); last_term = $$; } ++ | term P_GT term { $$ = sial_newop(GT, 2, $1, $3); last_term = $$; } ++ | term P_LE term { $$ = sial_newop(LE, 2, $1, $3); last_term = $$; } ++ | term P_LT term { $$ = sial_newop(LT, 2, $1, $3); last_term = $$; } ++ | term P_NE term { $$ = sial_newop(NE, 2, $1, $3); last_term = $$; } ++ | '(' term ')' { $$ = $2; last_term == $$; } ++ | term P_OR term { $$ = sial_newop(OR, 2, $1, $3); last_term = $$; } ++ | term P_XOR term { $$ = sial_newop(XOR, 2, $1, $3); last_term = $$; } ++ | term P_SHR term { $$ = sial_newop(SHR, 2, $1, $3); last_term = $$; } ++ | term P_SHL term { $$ = sial_newop(SHL, 2, $1, $3); last_term = $$; } ++ | term P_DIV term { $$ = sial_newop(DIV, 2, $1, $3); last_term = $$; } ++ | term P_MOD term { $$ = sial_newop(MOD, 2, $1, $3); last_term = $$; } ++ | term P_SUB term { $$ = sial_newop(SUB, 2, $1, $3); last_term = $$; } ++ | term P_ADD term { $$ = sial_newop(ADD, 2, $1, $3); last_term = $$; } ++ | term P_MUL term { $$ = sial_newop(MUL, 2, $1, $3); last_term = $$; } ++ | term '&' term %prec P_AND { $$ = sial_newop(AND, 2, $1, $3); last_term = $$; } ++ | P_SUB term %prec P_UMINUS { $$ = sial_newop(UMINUS, 1, $2); last_term = $$; } ++ | '~' term %prec P_FLIP { $$ = sial_newop(FLIP, 1, $2); last_term = $$; } ++ | '+' term %prec P_UMINUS { $$ = $2; last_term = $$; } ++ | P_DEFINED '(' {nomacs++;} P_VAR ')' ++ { nomacs=0; $$ = sial_macexists($4); last_term = $$; } ++ | P_NUMBER { last_term = $$; } ++ | P_VAR { $$ = sial_makenum(B_UL, 0); last_term = $$; } ++ ; ++ ++%% ++ ++node_t * ++sial_getppnode() ++{ ++ return last_term; ++} ++ ++int ++sialpperror(char *s) ++{ ++ sial_error(s); ++ return 1; ++} ++ +--- crash/extensions/libsial/sial_util.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_util.c 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,922 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include "sial.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static FILE *ofile=0; ++static int cols=25; ++static char *bold_on, *bold_off; ++ ++ ++static void ++sial_getwinsize(void) ++{ ++struct winsize w; ++ ++ if (ioctl (fileno(ofile), TIOCGWINSZ, &w) == 0) ++ { ++ cols=w.ws_col; ++ } ++ else /* use ENV */ ++ { ++ char *ewidth; ++ if ((ewidth = getenv ("COLUMNS"))) ++ cols = atoi (ewidth); ++ /* use what's in terminfo */ ++ if (cols <= 0) ++ cols = tigetnum ("co"); ++ } ++ if(cols <= 10) cols=10; ++ if(cols > 80) cols=80; ++} ++ ++void ++sial_setofile(void * f) ++{ ++int out; ++int ret; ++char *term; ++ ++ ofile=(FILE *)f; ++ ++ bold_on=""; ++ bold_off=""; ++ cols=80; ++ ++ out=fileno(ofile); ++ if(isatty(out)) ++ { ++ ++ if(!(term = getenv ("TERM"))) term="dumb"; ++ if(setupterm(term, out, &ret)!=ERR) ++ { ++ bold_on=tigetstr("bold"); ++ if(!bold_on) bold_on=""; ++ bold_off=tigetstr("sgr0"); ++ if(!bold_off) bold_off=""; ++ } ++ sial_getwinsize(); ++ } ++} ++ ++void * ++sial_getofile(void) ++{ ++ return ofile; ++} ++ ++/* ++ Output a line of text to the screen with line wrap ++ and escape sequence. ++*/ ++#define ESC '<' ++#define ESC2 '>' ++ ++static int ++sial_tabs(int tabs, char *t, int lf) ++{ ++int i; ++ ++ if(lf) fprintf(ofile, "\n"); ++ for(i=0;i cols) { ++ ++ char *p3=p+(cols-n-1); ++ ++ char c=*p3; ++ char c2=*(p3+1); ++ ++ *p3='-'; ++ *(p3+1)='\0'; ++ ++ fprintf(ofile, "%s", p); ++ *p3=c; ++ *(p3+1)=c2; ++ n=sial_tabs(tabs, t, 0); ++ ++ } else if(n + (p2-p) >= cols) { ++ ++ n=sial_tabs(tabs, t, 1); ++ ++ } else { ++ ++ fprintf(ofile, " "); ++ n++; ++ } ++ ++ } else if(*p=='\n') { ++ ++ n=sial_tabs(tabs, t, 1); ++ ++ } else { ++ ++ fprintf(ofile, "%c", *p); ++ n++; ++ } ++ } ++ ++} ++ ++void ++sial_msg(char *fmt, ...) ++{ ++va_list ap; ++ va_start(ap, fmt); ++ vfprintf(ofile, fmt, ap); ++ va_end(ap); ++} ++ ++void ++sial_freenode(node_t *n) ++{ ++ n->free(n->data); ++ sial_free(n); ++} ++ ++int lineno=1, lastline=1; ++int col=1; ++static char *filename=0; ++static char *lastfile=0; ++ ++void ++sial_setlastfile(char *fname, int line) ++{ ++ if(!fname) return; ++ if(lastfile) sial_free(lastfile); ++ lastfile=sial_strdup(fname); ++ lastline=line; ++} ++ ++void ++sial_rstpos(void) ++{ ++ lineno=1; ++ col=1; ++ /* do not free filename */ ++ filename=0; ++} ++ ++void ++sial_setpos(srcpos_t *p) ++{ ++ p->line=lineno; ++ p->col=col; ++ p->file=filename; ++} ++ ++/* set the current position */ ++void ++sial_curpos(srcpos_t *p, srcpos_t *s) ++{ ++ if(s) { ++ s->line=lineno; ++ s->col=col; ++ s->file=filename; ++ } ++ lineno=p->line; ++ col=p->col; ++ filename=p->file; ++} ++ ++int ++sial_line(int inc){ return lineno+=inc; } ++ ++int ++sial_col(int inc) { return col+=inc; } ++ ++char * ++sial_filename(void) { return filename; } ++ ++/* ++ This function scans a printf() fmt string and transaletes the %p ++ to %08x or %016x depending on the pointer size of the object image. ++ We also substiture %> for 8 spaces if the pointer size is 4 bytes, this ++ permits easy allignment of output on either 32 or 64 bit images. ++ ++ ex: ++ ++ Proc %> pid ppid ++ %p %3d %3d ++ ++ In this case the %> alligns the pid with it's corresponding value_t ++ in the next line of output. ++ ++ We also process the '?' format which will be set to match the ++ corresponding value_t type. ++ ++ Also, format versus argument type validation is performed. ++ ++*/ ++ ++/* ++ Printf formats have the form : ++ %3$-*3$.*4$lld ++ %20x ++ %08x ++ %-08.8f ++*/ ++/* these are the buildin blocks for a regex matching formats */ ++#define F_POSP "([0-9]+\\$)*" ++#define F_FLGS "([-'+ #0]*)" ++#define F_WARG "(\\*([0-9]+\\$)*){0,1}" ++#define F_WIDTH "([0-9]*)" ++#define F_PREC "((\\.(\\*([0-9]+\\$)*)*([0-9]*))*)" ++#define F_SIZE "([hlL]*)" ++#define F_FMT "([diouxXfeEgGcCsSpn?>]{1})" ++#define FMTREG F_POSP""F_FLGS""F_WARG""F_WIDTH""F_PREC""F_SIZE""F_FMT ++#define M_POSP 1 ++#define M_FLAGS 2 ++#define M_WIDTHARG 3 ++#define M_WIDTDIGITS 4 ++#define M_WIDTH 5 ++#define M_PRECARG 8 ++#define M_PRECDIGITS 9 ++#define M_PREC 10 ++#define M_SIZE 11 ++#define M_FMT 12 ++#define NMATCH 16 ++static int addit[]={M_FLAGS,M_WIDTHARG,M_WIDTH,M_PRECARG,M_PREC,M_SIZE}; ++ ++#define ptrto(idx) (matches[idx].rm_so==matches[idx].rm_eo?0:(pi+matches[idx].rm_so)) ++#define matchlen(idx) (matches[(idx)].rm_eo-matches[(idx)].rm_so) ++ ++void sial_error(char *fmt, ...); ++ ++static int ++chkforint(char *p, value_t **vals, int *curarg) ++{ ++int pos=-1; ++ ++ if(!p) return -1; ++ ++ /* a single star ? */ ++ if(isdigit(p[1])) { ++ ++ if(sscanf(p+1, "%d", &pos)!=1) { ++ ++ return pos; ++ } ++ pos--; ++ ++ } else { ++ ++ pos=*curarg; ++ *curarg=(*curarg)+1; ++ ++ } ++ ++ if(pos < BT_MAXARGS && vals[pos] && vals[pos]->type.type == V_BASE) return pos; ++ sial_error("Expected 'integer' type for arg%d", pos+1); ++ return -1; ++} ++ ++#define pushval(val, s, sig) ( \ ++ sig ? \ ++ ( \ ++ (s==8) ? \ ++ (val)->v.sll \ ++ : ( \ ++ (s==4) ? \ ++ (val)->v.sl \ ++ : ( \ ++ (s==2) ? \ ++ (val)->v.ss \ ++ :( \ ++ (s==1) ? \ ++ (val)->v.sc \ ++ :( \ ++ sial_error("Oops pushval"),1 \ ++ ) \ ++ ) \ ++ ) \ ++ ) \ ++ ) : ( \ ++ (s==8) ? \ ++ (val)->v.ull \ ++ : ( \ ++ (s==4) ? \ ++ (val)->v.ul \ ++ : ( \ ++ (s==2) ? \ ++ (val)->v.us \ ++ :( \ ++ (s==1) ? \ ++ (val)->v.uc \ ++ :( \ ++ sial_error("Oops pushval"),1 \ ++ ) \ ++ ) \ ++ ) \ ++ ) \ ++ ) \ ++ ) ++ ++ ++static char * ++add_fmt(int len, char *s, char *onefmt, int ppos, int wpos, int posarg, value_t **vals) ++{ ++int size=(vals[posarg]->type.type == V_REF ? sial_defbsize(): vals[posarg]->type.size); ++int sign=(vals[posarg]->type.type == V_REF ? 0 : sial_issigned(vals[posarg]->type.typattr)); ++ ++ if(vals[posarg]->type.type == V_STRING) { ++ ++ if(wpos>=0 && ppos<0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[wpos]) ++ , vals[posarg]->v.data); ++ else if(wpos<0 && ppos>=0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[ppos]) ++ , vals[posarg]->v.data); ++ else if(wpos>=0 && ppos>=0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[wpos]) ++ , (int)sial_getval(vals[ppos]) ++ , vals[posarg]->v.data); ++ else s+=snprintf(s, len, onefmt ++ , vals[posarg]->v.data); ++ ++ } else { ++#if defined(__s390x__) || defined(__s390__) ++ if(wpos>=0 && ppos<0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[wpos]) ++ , (unsigned long)pushval(vals[posarg], size, sign)); ++ else if(wpos<0 && ppos>=0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[ppos]) ++ , (unsigned long)pushval(vals[posarg], size, sign)); ++ else if(wpos>=0 && ppos>=0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[wpos]) ++ , (int)sial_getval(vals[ppos]) ++ , (unsigned long) pushval(vals[posarg], size, sign)); ++ else s+=snprintf(s, len, onefmt ++ , (unsigned long) pushval(vals[posarg], size, sign)); ++#else ++ if(wpos>=0 && ppos<0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[wpos]) ++ , pushval(vals[posarg], size, sign)); ++ else if(wpos<0 && ppos>=0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[ppos]) ++ , pushval(vals[posarg], size, sign)); ++ else if(wpos>=0 && ppos>=0) ++ s+=snprintf(s, len, onefmt ++ , (int)sial_getval(vals[wpos]) ++ , (int)sial_getval(vals[ppos]) ++ , pushval(vals[posarg], size, sign)); ++ else s+=snprintf(s, len, onefmt ++ , pushval(vals[posarg], size, sign)); ++#endif ++ } ++ return s; ++} ++ ++static char * ++sial_ptr(char *fmt, value_t **vals) ++{ ++ /* We need to ensure that we dont overflow our string buffer. Although its unlikely we will overflow it with ++ just numbers, strings will easliy overflow. So, lets check for strings and see how long they are. ++ */ ++int len=0; ++char *nfmt=NULL,*ni=NULL; ++char *onefmt=NULL, *onei=NULL; ++char *p=fmt; ++char last=' '; ++int curarg=0; ++#define NBYTES (len-(nfmt-ni)) ++ ++int i = 0; ++ ++ while(vals[i] != NULL) { ++ if(vals[i]->type.type == V_STRING) ++ len+=vals[i]->type.size; ++ i++; ++ } ++ /* We add a fudge factor of 100, which should cover all the number arguments */ ++ len+=strlen(fmt) + 100; ++ nfmt=sial_alloc(len); ++ ni=nfmt; ++ onefmt=sial_alloc(len); ++ onei=onefmt; ++ ++ ++ ++ while(*p) { ++ ++ if(*p=='%') { ++ ++ static regex_t preg; ++ static int done=0; ++ regmatch_t matches[NMATCH]; ++ ++ if(!done) { ++ ++ regcomp(&preg, FMTREG, REG_EXTENDED); ++ done=1; ++ } ++ ++ /* build a new format translation */ ++ onefmt=onei; ++ *onefmt++=*p++; ++ ++ /* if the returned pointer is (char*)-1 or NULL then something is wrong */ ++ if(!regexec(&preg, p, NMATCH, matches, 0)) { ++ ++ int i, n=matches[0].rm_eo-1; ++ int posarg, wpos, ppos; ++ char *pi=p; /* save p for ptrto() macro */ ++ ++ /* check that the width and precision field args point ++ to a int value_t. If they were used */ ++ wpos=chkforint(ptrto(M_WIDTHARG), vals, &curarg); ++ ppos=chkforint(ptrto(M_PRECARG), vals, &curarg); ++ ++ /* argument position was specfified ? */ ++ if(ptrto(M_POSP)) { ++ ++ /* we work from 0-n, printf works from 1-n */ ++ if(sscanf(ptrto(M_POSP), "%d", &posarg)==1) posarg--; ++ ++ if(posarg >= BT_MAXARGS || !vals[posarg]) { ++ sial_error("Invalid arg position specified [%d]", posarg+1); ++ } ++ ++ } else posarg=curarg++; ++ ++ /* jump over the format spec in the original */ ++ p+=n; ++#if 0 ++for(i=0;i=0 ){ ++ ++ *onefmt++='*'; ++ ++ } else goto def; ++ ++ break; ++ case M_PRECARG: ++ ++ if(ppos >=0 ){ ++ ++ *onefmt++='.'; ++ *onefmt++='*'; ++ ++ } else goto def; ++ ++ break; ++ case M_PREC: ++ if(ptrto(addit[i])) *onefmt++='.'; ++ goto def; ++ default: ++def: ++ if(ptrto(addit[i])) { ++ strcpy(onefmt, ptrto(addit[i])); ++ onefmt+=matchlen(addit[i]); ++ } ++ } ++ } ++ ++ if(*p=='p') { ++ ++ref: ++ /* if user overrides anything don't do nothing */ ++ if(ptrto(M_FLAGS)||ptrto(M_WIDTH)||ptrto(M_WIDTHARG)||ptrto(M_PREC)||ptrto(M_PRECARG)||ptrto(M_SIZE)) { ++ *onefmt++='p'; ++ ++ } else { ++ if(sial_defbsize()==8) { ++ ++ strcpy(onefmt, "016llx"); ++ onefmt+=6; ++ ++ } else { ++ ++ strcpy(onefmt, "08x"); ++ onefmt+=3; ++ } ++ } ++ *onefmt='\0'; ++ p++; ++ nfmt=add_fmt(NBYTES, nfmt, onei, ppos, wpos, posarg, vals); ++ ++ } else if(*p=='>') { ++ ++ nfmt--; ++ if(sial_defbsize()==8) { ++ ++ int i; ++ ++ for(i=0;i<8;i++) *nfmt++=last; ++ } ++ p++; ++ curarg--; ++ ++ } else if(*p=='?') { ++ ++ /* put the proper format for the user */ ++ if(!vals[posarg]) { ++ ++ sial_error("Expected additional argument %d\n", posarg+1); ++ ++ } else switch(vals[posarg]->type.type) { ++ ++ case V_BASE: case V_ENUM: ++ { ++ if(!ptrto(M_SIZE)) { ++ ++ if(vals[posarg]->type.size==8) { ++ ++ *onefmt++='l'; ++ *onefmt++='l'; ++ } ++ } ++ if(sial_issigned(vals[posarg]->type.typattr)) { ++ ++ *onefmt++='d'; ++ ++ }else{ ++ ++ *onefmt++='u'; ++ } ++ } ++ break; ++ case V_REF: ++ { ++ *p='p'; ++ goto ref; ++ } ++ case V_STRING: ++ { ++ *onefmt++='s'; ++ } ++ break; ++ } ++ p++; ++ *onefmt='\0'; ++ nfmt=add_fmt(NBYTES, nfmt, onei, ppos, wpos, posarg, vals); ++ ++ } else { ++ ++ /* check that format and value_t agree */ ++ /* can't do a lot more then check for strings vs anything_else */ ++ ++ if(!vals[posarg]) { ++ ++ sial_error("Expected additional argument %d\n", posarg+1); ++ ++ ++ } else if(*p=='s') { ++ ++ if(vals[posarg]->type.type != V_STRING) { ++ ++ sial_error("Expected type 'string' as arg%d", posarg+1); ++ } ++ ++ } else if(vals[posarg]->type.type == V_STRING) { ++ ++ sial_error("Incompatible type 'string' in arg%d", posarg+1); ++ ++ } ++ *onefmt++=*p++; ++ *onefmt='\0'; ++ nfmt=add_fmt(NBYTES, nfmt, onei, ppos, wpos, posarg, vals); ++ } ++ ++ } else { ++ ++ sial_warning("Malformed format specifier!"); ++ ++ } ++ ++ } else { ++ ++ last=*p; ++ if(nfmt-ni > len) sial_error("format tranlation overflow!"); ++ *nfmt++=*p++; ++ ++ } ++ } ++ sial_free(onei); ++ *nfmt='\0'; ++ return ni; ++} ++ ++value_t* sial_printf(value_t *vfmt, ...) ++{ ++char *fmt = sial_getptr(vfmt, char); ++va_list ap; ++value_t *vals[BT_MAXARGS]; ++int i; ++ ++ va_start(ap, vfmt); ++ for(i=0;i 9) ++ sial_msg("Invalid debug level value.\n"); ++ else ++ dbglvl=lvl; ++} ++char *sial_getname(void) ++{ ++ return dbg_name; ++} ++ ++void sial_setname(char *name) ++{ ++ if(dbg_name) sial_free(dbg_name); ++ dbg_name=sial_strdup(name); ++} ++ ++#define MAXCLASSES 10 ++static struct { ++ char *name; ++ int class; ++} classes [MAXCLASSES] = { ++ { "type", DBG_TYPE }, ++ { "struct", DBG_STRUCT }, ++ { 0 }, ++}; ++ ++char **sial_getclass(void) ++{ ++int i,j; ++static char *ptrs[MAXCLASSES+1]; ++ ++ for(i=j=0;classes[i].name;i++) { ++ if(clist&classes[i].class) ptrs[j++]=classes[i].name; ++ } ++ ptrs[i]=0; ++ return ptrs; ++} ++ ++void sial_setclass(char *cl) ++{ ++int i,j; ++ ++ for(i=0;classes[i].name;i++) { ++ if(!strcmp(classes[i].name,cl)) { ++ clist |= classes[i].class; ++ return; ++ } ++ } ++ sial_msg("Invalid class '%s' specified.\n", cl); ++} ++ ++static void ++sial_dbg_all(int class, char *name, int lvl, char *fmt, va_list ap) ++{ ++ if(lvl<=dbglvl && (clist & class) && (!dbg_name || !strcmp(name, dbg_name))) { ++ fprintf(ofile, "dbg(%d) : ", lvl); ++ vfprintf(ofile, fmt, ap); ++ } ++} ++ ++void ++sial_dbg(int class, int lvl, char *fmt, ...) ++{ ++va_list ap; ++ va_start(ap, fmt); ++ sial_dbg_all(class, 0, lvl, fmt, ap); ++ va_end(ap); ++} ++ ++void ++sial_dbg_named(int class, char *name, int lvl, char *fmt, ...) ++{ ++va_list ap; ++ va_start(ap, fmt); ++ sial_dbg_all(class, name, lvl, fmt, ap); ++ va_end(ap); ++} ++/******************************************************************/ ++ ++void ++sial_rerror(srcpos_t *p, char *fmt, ...) ++{ ++va_list ap; ++ ++ sial_setlastfile(p->file, p->line); ++ va_start(ap, fmt); ++ fprintf(ofile, "%s : line %d : Error: ", p->file, p->line); ++ vfprintf(ofile, fmt, ap); ++ fprintf(ofile, "\n"); ++ va_end(ap); ++ sial_exit(1); ++} ++ ++void ++sial_warning(char *fmt, ...) ++{ ++va_list ap; ++ ++ sial_setlastfile(filename, sial_line(0)); ++ va_start(ap, fmt); ++ fprintf(ofile, "%s : line %d : Warning: ", filename, lineno); ++ vfprintf(ofile, fmt, ap); ++ fprintf(ofile, "\n"); ++ va_end(ap); ++} ++ ++void ++sial_rwarning(srcpos_t *p, char *fmt, ...) ++{ ++va_list ap; ++ ++ sial_setlastfile(p->file, p->line); ++ va_start(ap, fmt); ++ fprintf(ofile, "%s : line %d : Warning: ", p->file, p->line); ++ vfprintf(ofile, fmt, ap); ++ fprintf(ofile, "\n"); ++ va_end(ap); ++} ++ ++void ++sial_vilast() ++{ ++ if(lastfile) { ++ ++ sial_exevi(lastfile, lastline); ++ ++ } else { ++ ++ sial_msg("No last error record available"); ++ } ++} ++ ++void ++sial_getcomment(void) ++{ ++ while(1) { ++ ++ unsigned char c; ++ ++ while((c=sial_input())!='*' && c!=255) ++ ++ if(c==255) goto bad; ++ ++ if((c=sial_input())=='/') return; ++ else if(c==255) { ++bad: ++ sial_error("Unterminated comment!"); ++ } ++ } ++} ++ ++/* on assignment this function is called to set the new value */ ++void ++sial_setfct(value_t *v1, value_t *v2) ++{ ++ /* duplicate type and data, safeguarding array info */ ++ sial_dupval(v1, v2); ++ ++ /* value_t v1 is still setable */ ++ v1->set=1; ++ v1->setval=v1; ++} ++ ++node_t * ++sial_sibling(node_t *n, node_t *m) ++{ ++node_t *p; ++ ++ if(m) { ++ ++ for(p=n;p->next;p=p->next); ++ p->next=m; ++ m->next=0; ++ } ++ return n; ++} ++ +--- crash/extensions/libsial/sialpp-lsed.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sialpp-lsed 2007-09-24 16:07:06.000000000 -0400 +@@ -0,0 +1,32 @@ ++s/yyback/sialppback/g ++s/yybgin/sialppbgin/g ++s/yycrank/sialppcrank/g ++s/yyerror/sialpperror/g ++s/yyestate/sialppestate/g ++s/yyextra/sialppextra/g ++s/yyfnd/sialppfnd/g ++s/yyin/sialppin/g ++s/yyinput/sialppinput/g ++s/yyleng/sialppleng/g ++s/yylex/sialpplex/g ++s/yylineno/sialpplineno/g ++s/yylook/sialpplook/g ++s/yylsp/sialpplsp/g ++s/yylstate/sialpplstate/g ++s/yylval/sialpplval/g ++s/yymatch/sialppmatch/g ++s/yymorfg/sialppmorfg/g ++s/yyolsp/sialppolsp/g ++s/yyout/sialppout/g ++s/yyoutput/sialppoutput/g ++s/yyprevious/sialppprevious/g ++s/yysbuf/sialppsbuf/g ++s/yysptr/sialppsptr/g ++s/yysvec/sialppsvec/g ++s/yytchar/sialpptchar/g ++s/yytext/sialpptext/g ++s/yytop/sialpptop/g ++s/yyunput/sialppunput/g ++s/yyvstop/sialppvstop/g ++s/yywrap/sialppwrap/g ++s/yydebug/sialdebug/g +--- crash/extensions/libsial/sial_api.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial_api.c 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,1516 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include "sial.h" ++#include "sial.tab.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* here we do some caching of the information. This can have a speedup effect ++ since it limits the number of accesses we do the dwarf (or whatever type) db that ++ drives the type and symbols information ++ */ ++ ++static stinfo_t slist={"root"}; ++ ++/* running key to new structures */ ++static ull nextidx=0, abitype=ABI_MIPS; ++#define LOCALTYPESBASE 0x8000000000000000ll ++static ull sial_nextidx(void) { return LOCALTYPESBASE+nextidx++; } ++ ++/* this set of function is used to cleanup tdefs after their use. ++ Trailing tdefs can be combersome. Trailing struct/union/enum get new idx ++ each time and are not a trouble */ ++static stinfo_t*tag=0; ++void ++sial_tagst(void) ++{ ++ tag=slist.next; ++} ++ ++void ++sial_flushtdefs(void) ++{ ++stinfo_t*st=slist.next; ++stinfo_t*last=&slist; ++ ++ while(st != tag) { ++ ++ stinfo_t*next=st->next; ++ ++ if(st->ctype.type==V_TYPEDEF && st->idx & LOCALTYPESBASE) { ++ ++ sial_free(st->name); ++ sial_free(st); ++ last->next=next; ++ ++ } else last=st; ++ ++ st=next; ++ ++ } ++ tag=0; ++} ++ ++static stinfo_t* ++sial_getst(char *name, int type) ++{ ++stinfo_t*tst; ++ ++ for(tst=slist.next; tst; tst=tst->next) { ++ ++ if(tst->ctype.type == type && tst->name && ! strcmp(tst->name, name)) { ++ ++ return tst; ++ } ++ } ++ return 0; ++} ++ ++#if 0 ++Not used yet. ++static void ++sial_rmst(stinfo_t*rst) ++{ ++stinfo_t*st=slist.next; ++stinfo_t*last=&slist; ++ ++ while(st) { ++ ++ if(st==rst) { ++ ++ last->next=st->next; ++ sial_free(st->name); ++ sial_free(st); ++ ++ return; ++ ++ } ++ ++ last=st; ++ st=st->next; ++ } ++} ++#endif ++ ++stinfo_t* ++sial_getstbyindex(ull idx, int type) ++{ ++stinfo_t*tst; ++ ++ for(tst=slist.next; tst; tst=tst->next) { ++ ++ if(tst->ctype.type == type && tst->idx == idx) { ++ ++ return tst; ++ } ++ } ++ return 0; ++} ++ ++static void ++sial_addst(stinfo_t*st) ++{ ++stinfo_t*tst; ++ ++ tst=slist.next; ++ slist.next=st; ++ st->next=tst; ++} ++ ++typedef struct neg_s { ++ struct neg_s *next; ++ char *name; ++} neg_t; ++ ++static neg_t *nlist=0; ++ ++void ++sial_addneg(char *name) ++{ ++neg_t *neg; ++ ++ neg=sial_alloc(sizeof *neg); ++ neg->name=sial_strdup(name); ++ neg->next=nlist; ++ nlist=neg; ++} ++ ++int ++sial_isneg(char *name) ++{ ++neg_t *neg; ++ ++ for(neg=nlist; neg; neg=neg->next) ++ if(!strcmp(neg->name, name)) return 1; ++ return 0; ++} ++ ++/* ++ This function is called by sial_vardecl() when the typedef storage class ++ as been specified. In which case we need to create new typedefs not variables. ++*/ ++void ++sial_tdef_decl(dvar_t*dv, type_t*t) ++{ ++ while(dv) { ++ ++ dvar_t*next; ++ ++ stinfo_t*st=sial_calloc(sizeof(stinfo_t)); ++ ++ if(dv->nbits) sial_error("No bits fields for typedefs"); ++ if(dv->idx) { ++ ++ /* we change a 'typedef type var[n];' into a 'typedef type_t*var;' */ ++ sial_freeidx(dv->idx); ++ dv->idx=0; ++ dv->ref++; ++ } ++#if 0 ++At this time we do not give any error messages or warnings. ++If a type is redefined within a single file that will means ++problem for the user put this is not a full blown C compiler. ++ ++ { ++ type_t*t=sial_newtype(); ++ ++ if(API_GETCTYPE(V_TYPEDEF, dv->name, t)) { ++ ++ sial_warning("Typedef %s already defined in image, redefinition ignored", ++ dv->name); ++ } ++ sial_freetype(t); ++ } ++#endif ++ t->typattr &= ~sial_istdef(t->typattr); ++ sial_duptype(&st->rtype, t); ++ sial_pushref(&st->rtype, dv->ref); ++ st->name=dv->name; ++ dv->name=0; ++ st->idx=sial_nextidx(); ++ st->ctype.type=V_TYPEDEF; ++ ++ sial_addst(st); ++ ++ next=dv->next; ++ dv->next=0; ++ sial_freedvar(dv); ++ dv=next; ++ } ++} ++ ++int ++sial_ispartial(type_t*t) ++{ ++stinfo_t*st=sial_getstbyindex(t->idx, t->type); ++ ++ if(!st) { ++ ++ sial_error("Oops sial_ispartial"); ++ } ++ return !st->all; ++} ++ ++char * ++sial_gettdefname(ull idx) ++{ ++stinfo_t*tst=sial_getstbyindex(idx, V_TYPEDEF); ++ ++ if(tst) return tst->name; ++ else return 0; ++} ++ ++static int init=0; ++static void ++sial_chkinit(void) ++{ ++ if(!init) { ++ ++ sial_error("Sial Package not initialized"); ++ ++ } ++} ++ ++void ++sial_getmem(ull kp, void *p, int n) ++{ ++ sial_chkinit(); ++ if(!API_GETMEM(kp, p, n)) { ++ ++ sial_error("Error on read from 0x%llx for %d", kp, n); ++ ++ } ++} ++ ++void ++sial_putmem(ull kp, char *p, int n) ++{ ++ sial_chkinit(); ++ if(!API_PUTMEM(kp, p,n)) { ++ ++ sial_error("Error on write at 0x%llx for %d", kp, n); ++ ++ } ++} ++ ++void ++sial_partialctype(int type, char *name) ++{ ++stinfo_t*st; ++ ++ /* check first if we have a partial of that type ++ already in progress (after a forward declaration) */ ++ if((st=sial_getst(name, type))) { ++ ++ /* if it's complete we need to start a new one */ ++ if(!st->all) return; ++ ++ } ++ st=sial_calloc(sizeof(stinfo_t)); ++ st->name=sial_strdup(name); ++ st->ctype.type=type; ++ st->all=0; ++ st->ctype.idx=st->idx=sial_nextidx(); ++ sial_addst(st); ++} ++ ++void ++sial_startctype_named(int type, char *name) ++{ ++stinfo_t*st; ++ ++ /* if no partial yet start one */ ++ if(!(st=sial_getst(name, type)) || st->all) ++ sial_partialctype(type, name); ++} ++ ++void ++sial_startctype(int type, node_t*namen) ++{ ++ sial_startctype_named(type, NODE_NAME(namen)); ++} ++ ++int ++sial_samectypename(int type, ull idx1, ull idx2) ++{ ++stinfo_t*st1, *st2; ++ ++ if((st1=sial_getstbyindex(idx1, type)) && ++ (st2=sial_getstbyindex(idx2, type))) { ++ ++ // check names ++ if(!strcmp(st1->name, st2->name)) return 1; ++ ++ // check all members and sizes in order ++ // unamed ctypes can end up here too... ++ if(st1->stm) { ++ stmember_t *m1=st1->stm, *m2=st2->stm; ++ while(m1 && m2) { ++ if(strcmp(m1->m.name, m2->m.name)) break; ++ if(m1->m.offset != m2->m.offset ) break; ++ if(m1->m.size != m2->m.size ) break; ++ m1=m1->next; ++ m2=m2->next; ++ } ++ if(!m1 && !m2) return 1; ++ } ++ else if(st1->enums) { ++ ++ enum_t *e1=st1->enums, *e2=st2->enums; ++ while(e1 && e2) { ++ if(strcmp(e1->name, e2->name)) break; ++ if(e1->value != e2->value ) break; ++ e1=e1->next; ++ e2=e2->next; ++ } ++ if(!e1 && !e2) return 1; ++ } ++ ++ } ++ return 0; ++} ++ ++#define VOIDIDX 0xbabebabell ++type_t* ++sial_getvoidstruct(int ctype) ++{ ++type_t*bt=sial_newtype(); ++ ++ bt->type=ctype; ++ bt->idx=VOIDIDX; ++ bt->size=0; ++ bt->ref=0; ++ return bt; ++} ++ ++void sial_fillst(stinfo_t *st); ++ ++/* Just in case this is an unnamed structure member then we need ++ to add it to the slist ourselves using the index. sial_getctype() would ++ not found it. ++*/ ++static void ++sial_memstinfo(stmember_t *stm, char *pname) ++{ ++int type=stm->type.ref?stm->type.rtype:stm->type.type; ++ ++ if(is_ctype(type)) { ++ ++ if(!sial_getstbyindex(stm->type.idx, type)) { ++ ++ stinfo_t*st=sial_calloc(sizeof(stinfo_t)); ++ ++ sial_duptype(&st->ctype, &stm->type); ++ st->ctype.type=type; ++ // dereference level is attached (wrongly) to type... ++ // zap it ++ st->ctype.ref=0; ++ st->idx=st->ctype.idx; ++ st->name=sial_strdup(pname); ++ sial_addst(st); ++ } ++ } ++} ++ ++void ++sial_fillst(stinfo_t *st) ++{ ++char *mname=0; ++ull idx=st->ctype.idx, lidx=0; ++stmember_t *stm=sial_calloc(sizeof(stmember_t)), **last=&st->stm; ++char *pname; ++ ++ sial_dbg_named(DBG_STRUCT, st->name, 2, "Fill St started [local=%d].\n", (idx & LOCALTYPESBASE) ? 1 : 0); ++ /* bail out if this is local type */ ++ if(idx & LOCALTYPESBASE) return; ++ ++ if(st->stm) sial_error("Oops sial_fillst!"); ++ ++ while((pname=API_MEMBER(mname, idx, &stm->type, &stm->m, &lidx))) { ++ ++ sial_dbg_named(DBG_STRUCT, st->name, 2, "member '%s'\n", pname); ++ sial_memstinfo(stm, pname); ++ stm->next=0; ++ *last=stm; ++ last=&stm->next; ++ mname=""; ++ stm=sial_calloc(sizeof(stmember_t)); ++ if(pname[0]) sial_free(pname); ++ } ++ st->all=1; ++ sial_free(stm); ++} ++ ++type_t* ++sial_getctype(int ctype, char *name, int silent) ++{ ++stinfo_t *st; ++type_t *t=sial_newtype(); ++ ++ sial_chkinit(); ++ sial_dbg_named(DBG_TYPE, name, 2, "getctype [%d] [%s] [s=%d]\n", ctype, name, silent); ++ if(!(st=sial_getst(name, ctype))) { ++ ++ sial_dbg_named(DBG_TYPE, name, 2, "getctype [%s] not found in cache\n", name); ++ if(silent && sial_isneg(name)) return 0; ++ ++ st=sial_calloc(sizeof(stinfo_t)); ++ if(!API_GETCTYPE(ctype, name, &st->ctype)) { ++ ++ sial_dbg_named(DBG_TYPE, name, 2, "[%s] not found in image\n", name); ++ sial_free(st); ++ sial_freetype(t); ++ // add any tdef to the neg list ++ if(ctype == V_TYPEDEF) sial_addneg(name); ++ if(silent) return 0; ++ /* we fill a partial structure for this one ++ assuming it will be defined later. This is to permit cross ++ referencing of structures, self referencing of structure, and ++ undefined structure (opaque structures) irix: see types.c : ++ __pasid_opaque ++ */ ++ sial_dbg_named(DBG_TYPE, name, 2, "[%s] creating partial type\n", name); ++ sial_partialctype(ctype, name); ++ return sial_getctype(ctype, name, silent); ++ } ++ sial_dbg_named(DBG_TYPE, name, 2, "getctype [%s] found in image\n", name); ++ st->name=sial_alloc(strlen(name)+1); ++ strcpy(st->name, name); ++ st->stm=0; ++ st->idx=st->ctype.idx; ++ st->all=1; ++ sial_addst(st); ++ /* ++ if this is a typedef then drill down to the real type ++ and make sure it is in the cache. That's what we return ++ ++ Bug cure: this would fail: ++ ++ struct sv { ++ int i; ++ }; ++ struct foo { ++ sv_t ms_sv; ++ }; ++ ++ Because the rtype index returned by API_GETRTYPE() is the die offset ++ in the image. If We already have redefine the real type locally the ++ call to sial_getctype() will not have a matching index later when we ++ don't find the index in the type cache. ++ ++ So we track the real index with ridx. This also ensures that ++ redefining a struct locally and using a typetef from the image will actualy ++ end up pointing to the local struct and not the image struct. ++ */ ++ if(ctype == V_TYPEDEF) { ++ ++ char *tname; ++ int itype; ++ ++ tname=API_GETRTYPE(st->idx, t); ++ ++ if(t->type==V_REF) itype=t->rtype; ++ else itype=t->type; ++ ++ /* if it's a named struct, enum or union then make sure we have it in the cache */ ++ if(is_ctype(itype) && tname && tname[0] && ++ (strcmp(tname,"struct ") != 0 ++ && strcmp(tname,"union ") != 0 ++ && strcmp(tname,"enum ") != 0)) { ++ ++ sial_freetype(t); ++ t=sial_getctype(itype, tname, silent); ++ ++ /* in IRIX we have a typedef struct __pasid_opaque* aspasid_t; ++ w/ no struct __pasid_opaque defined. The aspasid_t ends ++ up being used as a "named" void *. So we force a void * here */ ++ /* XXX: This should at least generate a warning */ ++ if(!t) { ++ sial_warning("voidstruct created (%s)\n", tname); ++ t=sial_getvoidstruct(itype); ++ } ++ } else if (is_ctype(itype) || itype == V_ENUM) { ++ ++ /* for unnamed structs, unions and enums create an entry */ ++ stinfo_t*st=sial_calloc(sizeof(stinfo_t)); ++ ++ sial_duptype(&st->ctype, t); ++ st->idx=t->idx; ++ st->name=sial_strdup(""); ++ sial_fillst(st); ++ sial_addst(st); ++ } ++ sial_duptype(&st->rtype, t); ++ ++ } else if(is_ctype(ctype)) { ++ ++ /* get all member info now ! */ ++ sial_fillst(st); ++ } ++ } ++ else sial_dbg_named(DBG_TYPE, name, 2, "getctype [%s] found in cache\n", name); ++ ++ if(ctype == V_ENUM || (ctype == V_TYPEDEF && st->rtype.type == V_ENUM)) { ++ st->enums=API_GETENUM(name); ++ sial_pushenums(st->enums); ++ } ++ if(ctype==V_TYPEDEF) sial_duptype(t, &st->rtype); ++ else sial_duptype(t, &st->ctype); ++ ++ return t; ++} ++ ++type_t* ++sial_newctype(int ctype, node_t*n) ++{ ++type_t*t; ++char *name; ++ ++ t=sial_getctype(ctype, name=NODE_NAME(n), 0); ++ NODE_FREE(n); ++ sial_free(name); ++ return t; ++} ++ ++/* ++ We don't use the type to point back to get the typedef name. ++ The type is now the real type not the type for the typedef. ++ So we keep a running sting of the last name variable name ++ the parser found and use that. ++ 5/23/00 ++*/ ++node_t* ++sial_tdeftovar(type_t*td) ++{ ++char *sial_lastvar(void); ++char *name=sial_lastvar(); ++ ++ sial_free(td); ++ return sial_newvnode(name); ++} ++ ++/* ++ Check to see if a cached member info is available ++*/ ++static stmember_t* ++sial_getm(char *name, type_t*tp, stinfo_t**sti) ++{ ++ull idx=tp->idx; ++stinfo_t*st; ++stmember_t*stm; ++ ++ for(st=slist.next; st; st=st->next) { ++ ++ if(st->idx == idx) { ++ ++ *sti=st; ++ ++ if(!st->stm) sial_fillst(st); ++ ++ for(stm=st->stm; stm; stm=stm->next) { ++ ++ ++ if(!strcmp(stm->m.name, name)) { ++ ++ return stm; ++ ++ } ++ } ++ } ++ } ++ return 0; ++} ++ ++value_t * ++sial_ismember(value_t*vp, value_t*vm) ++{ ++char *name=sial_getptr(vm, char); ++int ret=0; ++stinfo_t*st; ++ ++ if(sial_getm(name, &vp->type, &st)) ret=1; ++ ++ return sial_defbtype(sial_newval(), ret); ++} ++ ++/* XXX this entire stuff could very well be machine specific ... */ ++static int ++sial_getalign(type_t*t) ++{ ++ /* this is a custome type deal w/ it */ ++ if(t->type == V_BASE) { ++ ++ int n; ++ ++ /* Intel 386 ABI says that double values align on 4 bytes */ ++ if(abitype==ABI_INTEL_X86) n=((t->size>4)?4:t->size); ++ else n=t->size; ++ return n*8; ++ } ++ if(t->type == V_REF) { ++ /* ++ * This is an array but if there are additional references ++ * (>1) it is an array of pointers. In that case the pointer ++ * alignment has to be used. ++ */ ++ if(t->idxlst && t->ref == 1) { ++ int ret; ++ ++ sial_popref(t, 1); ++ ret=sial_getalign(t); ++ sial_pushref(t, 1); ++ return ret; ++ } ++ return sial_defbsize()*8; ++ } ++ /* alignment of a struct/union is on the largest of it's member or ++ largest allignment of sub structures */ ++ if(is_ctype(t->type)) { ++ ++ stinfo_t*st; ++ stmember_t*sm; ++ int maxallign=0; ++ ++ /* if this is a image type then let the api tell us */ ++ if(!(t->idx & LOCALTYPESBASE)) { ++ ++ return API_ALIGNMENT(t->idx)*8; ++ ++ } ++ ++ if(!(st=sial_getstbyindex(t->idx, t->type))) { ++ ++ sial_error("Oops sial_getalign"); ++ } ++ ++ for(sm=st->stm; sm; sm=sm->next) { ++ ++ int a=sial_getalign(&sm->type); ++ ++ if(a > maxallign) maxallign=a; ++ ++ } ++ ++ return maxallign; ++ ++ } ++ /* other types shoudl not be part of a ctype declaration ... */ ++ sial_error("Oops sial_getalign2!"); ++ return 0; ++} ++ ++static stinfo_t* ++sial_chkctype(int ctype, char *name) ++{ ++stinfo_t*sti; ++ ++ if(name) { ++ ++ /* we should already have a partial structure on the stack */ ++ sti=sial_getst(name, ctype); ++ ++#if 0 ++At this time I choose not to give any warning. ++Structure redefinition is a normal part of include files... ++ ++ /* We give a warning message for redefined types */ ++ { ++ type_t*t=sial_newtype(); ++ ++ if(API_GETCTYPE(ctype, name, t)) { ++ ++ sial_warning("%s %s redefinition", sial_ctypename(ctype), name); ++ } ++ sial_freetype(t); ++ } ++#endif ++ ++ if(sti->all) { ++ ++ sial_error("Oops sial_ctype_decl"); ++ } ++ ++ sial_free(name); ++ ++ } else { ++ ++ sti=sial_alloc(sizeof(stinfo_t)); ++ sti->name=0; ++ sti->idx=sial_nextidx(); ++ sial_addst(sti); ++ } ++ return sti; ++} ++ ++/* ++ This function is used to create new enum types. ++ The syntax for enum is: ++ enum ident { ++ ident [= int], ++ [ident [= int] ] ... ++ }; ++ So we check for an assign value and is it exists then ++ we reset the counter to it. ++ This is the way the mips compiler does it. Which migt be ++ the right way or not, although I fail to see why it's done ++ that way. ++ ++ So enum foo { ++ a, ++ b, ++ c=0, ++ d ++ }; ++ ++ Wil yield values : ++ ++ a=0 ++ b=1 ++ c=0 ++ c=1 ++*/ ++enum_t* ++sial_add_enum(enum_t*ep, char *name, int val) ++{ ++enum_t *epi, *nep=sial_alloc(sizeof(enum_t)); ++ ++ nep->name=name; ++ nep->value=val; ++ nep->next=0; ++ if(!ep) return nep; ++ epi=ep; ++ while(ep->next) ep=ep->next; ++ ep->next=nep; ++ return epi; ++} ++ ++type_t* ++sial_enum_decl(int ctype, node_t*n, dvar_t*dvl) ++{ ++dvar_t*dv=dvl, *next; ++int counter=0; ++stinfo_t*sti; ++enum_t *ep=0; ++char *name=n?NODE_NAME(n):0; ++type_t *t; ++ ++ if(n) sial_startctype(ctype, n); ++ sti=sial_chkctype(ctype, name); ++ ++ while(dv) { ++ ++ int val; ++ ++ /* evaluate an assignment ? */ ++ if(dv->init) { ++ ++ value_t *v=sial_exenode(dv->init); ++ ++ if(!v) { ++ ++ sial_rerror(&dv->pos, "Syntax error in enum expression"); ++ ++ } else if(v->type.type != V_BASE) { ++ ++ sial_rerror(&dv->pos, "Integer expression needed"); ++ } ++ ++ val=sial_getval(v); ++ counter=val+1; ++ sial_freeval(v); ++ ++ } else { ++ ++ val=counter++; ++ } ++ ++ ep=sial_add_enum(ep, dv->name, val); ++ ++ next=dv->next; ++ dv->next=0; ++ dv->name=0; ++ sial_freedvar(dv); ++ dv=next; ++ } ++ sti->enums=ep; ++ ++ /* now we push the values in the defines */ ++ sial_pushenums(sti->enums); ++ ++ /* we return a simple basetype_t*/ ++ /* after stahing the idx in rtype */ ++ t=sial_newbtype(INT); ++ t->rtype=sti->idx; ++ t->typattr |= sial_isenum(-1); ++ ++ return t; ++ ++} ++ ++/* ++ The next functions are used to produce a new type ++ and make it available throught the local cache. ++ This enables custom type definitions on top of the ++ ctypes defined in the object symbol tables. ++ ++ There is one function per suported architechture. ++ ++*/ ++/* macro for alignment to a log2 boundary */ ++#define Alignto(v, a) (((v) + (a) -1) & ~((a)-1)) ++/* ++ The algorith complies with the SysV mips ABI ++*/ ++type_t* ++sial_ctype_decl(int ctype, node_t*n, var_t*list) ++{ ++type_t*t; ++stinfo_t*sti; ++stmember_t **mpp; ++var_t*v; ++int bits_left, bit_alignment; ++int maxbytes, alignment, nextbit; ++char *name=n?NODE_NAME(n):0; ++ ++ if(list->next==list) { ++ ++ sial_error("Empty struct/union/enum declaration"); ++ } ++ ++ t=sial_newbtype(0); ++ sti=sial_chkctype(ctype, name); ++ t->type=sti->ctype.type=ctype; ++ t->idx=sti->ctype.idx=sti->idx; ++ sti->stm=0; ++ mpp=&sti->stm; ++ ++#if LDEBUG ++printf("\n%s %s\n", ctype==V_STRUCT?"Structure":"Union", name ? name : ""); ++#endif ++ ++ /* these are the running position in the structure/union */ ++ nextbit=0; /* next bit open for business */ ++ alignment=0; /* keeps track of the structure alignment ++ Mips ABI says align to bigest alignment of ++ all members of the struct/union. Also ++ unamed bit fields do not participate here. */ ++ maxbytes=0; /* tracking of the maximum member size for union */ ++ ++ for(v=list->next; v!=list; v=v->next) { ++ ++ stmember_t*stm=sial_calloc(sizeof(stmember_t)); ++ dvar_t*dv=v->dv; ++ int nbits; ++ ++ stm->m.name=sial_strdup(v->name); ++ sial_duptype(&stm->type, &v->v->type); ++ ++ /* if this member is a bit filed simply use that */ ++ if(dv->bitfield) { ++ ++ nbits=dv->nbits; ++ ++ /* aligment is the size of the declared base type size */ ++ bit_alignment=v->v->type.size*8; ++ ++ if(nbits > bit_alignment) { ++ ++ sial_error("Too many bits for specified type"); ++ } ++ ++ /* For unamed bit field align to smallest entity */ ++ /* except for 0 bit bit fields */ ++ if(!dv->name[0] && nbits) { ++ ++ bit_alignment=((nbits+7)/8)*8; ++ ++ } ++ ++ /* We compute the number of bits left in this entity */ ++ bits_left = bit_alignment - (nextbit%bit_alignment); ++ ++ /* 0 bits means, jump to next alignement unit anyway ++ if not already on such a boundary */ ++ if(!nbits && (bits_left != bit_alignment)) nbits=bits_left; ++ ++ /* Not enough space ? */ ++ if(nbits > bits_left) { ++ ++ /* jump to next start of entity */ ++ nextbit += bits_left; ++ ++ } ++ ++ /* update member information */ ++ stm->m.offset=(nextbit/bit_alignment)*v->v->type.size; ++ stm->m.fbit=nextbit % bit_alignment; ++ stm->m.nbits=nbits; ++ stm->m.size=v->v->type.size; ++#if LDEBUG ++ printf(" [%s] Bit member offset=%d, fbit=%d, nbits=%d\n", stm->m.name, stm->m.offset, stm->m.fbit, stm->m.nbits); ++#endif ++ /* an unamed bit field does not participate in the alignment value */ ++ if(!dv->name[0]) { ++ ++ bit_alignment=0; ++ ++ /* reset size so that it does not have affect in sial_getalign() */ ++ stm->type.size=1; ++ } ++ ++ } else { ++ ++ int nidx=1; ++ ++ if(dv->idx) { ++ ++ int i; ++ ++ /* flag it */ ++ stm->type.idxlst=sial_calloc(sizeof(int)*(dv->idx->nidx+1)); ++ ++ /* multiply all the [n][m][o]'s */ ++ for(i=0;iidx->nidx;i++) { ++ ++ value_t *vidx; ++ ull idxv; ++ ++ vidx=sial_exenode(dv->idx->idxs[i]); ++ if(!vidx) { ++ ++ sial_error("Error while evaluating array size"); ++ } ++ if(vidx->type.type != V_BASE) { ++ ++ sial_freeval(vidx); ++ sial_error("Invalid index type"); ++ ++ } ++ ++ idxv=sial_getval(vidx); ++ sial_freeval(vidx); ++ ++ stm->type.idxlst[i]=idxv; ++ ++ nidx *= idxv; ++ } ++ ++ ++ } ++ ++ /* the number of bits on which this item aligns itself */ ++ bit_alignment=sial_getalign(&stm->type); ++ ++ /* jump to this boundary */ ++ nextbit = Alignto(nextbit,bit_alignment); ++ ++ ++ if(stm->type.ref - (dv->idx?1:0)) { ++ ++ nbits=nidx*sial_defbsize()*8; ++ ++ } else { ++ ++ nbits=nidx*stm->type.size*8; ++ } ++ ++ if(abitype==ABI_INTEL_X86) { ++ ++ int pos=nextbit/8; ++ ++ pos = (pos & 0xfffffffc) + 3 - (pos & 0x2); ++ stm->m.offset=pos; ++ ++ } else { ++ ++ stm->m.offset=nextbit/8; ++ } ++ stm->m.nbits=0; ++ stm->m.size=nbits/8; ++#if LDEBUG ++printf(" [%s] Mmember offset=%d, size=%d size1=%d nidx=%d\n", stm->m.name, stm->m.offset, stm->m.size, stm->type.size, nidx); ++#endif ++ ++ } ++ ++ if(ctype==V_STRUCT) nextbit+=nbits; ++ /* Union members overlap */ ++ else nextbit=0; ++ ++ /* keep track of the maximum alignment */ ++ if(bit_alignment>alignment) alignment=bit_alignment; ++ ++ /* keep track of maximum size for unions */ ++ if(stm->m.size > maxbytes) maxbytes=stm->m.size; ++ ++ stm->next=0; ++ *mpp=stm; ++ mpp=&stm->next; ++ } ++ ++ /* pad the final structure according to it's most stricly aligned member */ ++ if(nextbit) nextbit = Alignto(nextbit, alignment); ++ else nextbit=Alignto(maxbytes*8, alignment); /* --> it's the case for a union */ ++ ++ t->size=sti->ctype.size=nextbit/8; ++ ++#if LDEBUG ++printf("Final size = %d\n", t->size); ++#endif ++ ++ sti->all=1; ++ sial_addfunc_ctype(sti->idx); ++ return t; ++} ++ ++/* ++ member access and caching. ++ If the member name is empty then the caller wants us ++ to populate the entire engregate. The apimember() should ++ support a getfirst() (member name == "") and getnext() ++ (member name != "") for this perpose. ++ */ ++stmember_t* ++sial_member(char *mname, type_t*tp) ++{ ++stinfo_t *sti; ++stmember_t *stm; ++ ++ if(!is_ctype(tp->type) && ! (tp->type==V_REF && is_ctype(tp->rtype))) { ++ ++ sial_error("Expression for member '%s' is not a struct/union", mname); ++ ++ ++ } ++ ++ if(tp->idx == VOIDIDX) { ++ ++ sial_error("Reference to member (%s) from unknown structure type", mname); ++ } ++ ++ if(!(stm=sial_getm(mname, tp, &sti))) { ++ ++ sial_error("Unknown member name [%s]", mname); ++ } ++ return stm; ++} ++ ++int ++sial_open() ++{ ++ sial_setofile(stdout); ++ /* push an empty level for parsing allocation */ ++ sial_pushjmp(0, 0, 0); ++ sial_setapiglobs(); ++ init=1; ++ sial_setbuiltins(); ++ return 1; ++} ++ ++/* here is a set of api function that do nothing */ ++static int apigetmem(ull iaddr, void *p, int nbytes) { return 1; } ++static int apiputmem(ull iaddr, void *p, int nbytes) { return 1; } ++static char* apimember(char *mname, ull pidx, type_t*tm, member_t *m, ull *lidx) { return 0; } ++static int apigetctype(int ctype, char *name, type_t*tout) { return 0; } ++static char * apigetrtype(ull idx, type_t*t) { return ""; } ++static int apialignment(ull idx) { return 0; } ++static int apigetval(char *name, ull *val) { return 0; } ++static enum_t* apigetenum(char *name) { return 0; } ++static def_t *apigetdefs(void) { return 0; } ++static char* apifindsym(char *p) { return 0; } ++ ++static apiops nullops= { ++ apigetmem, apiputmem, apimember, apigetctype, apigetrtype, apialignment, ++ apigetval, apigetenum, apigetdefs, 0, 0, 0, 0, apifindsym ++}; ++ ++apiops *sial_ops=&nullops;; ++ ++void ++sial_apiset(apiops *o, int abi, int nbpw, int sign) ++{ ++def_t *dt; ++ ++ sial_ops=o?o:&nullops; ++ sial_setdefbtype(nbpw, sign); ++ /* get the pre defines and push them. */ ++ dt=API_GETDEFS(); ++ while(dt) { ++ ++ sial_newmac(dt->name, dt->val, 0, 0, 1); ++ dt=dt->next; ++ } ++ /* add the sial define */ ++ sial_newmac(sial_strdup("sial"), sial_strdup("1"), 0, 0, 1); ++} ++ ++/* ++ Get and set path function. ++ ipath is include file search path. ++ mpath is macro search path ++*/ ++static char *mpath=""; ++static char *ipath=""; ++void sial_setmpath(char *p) { mpath=p; } ++void sial_setipath(char *p) { ipath=p; } ++char *sial_getmpath(void) { return mpath; } ++char *sial_getipath(void) { return ipath; } ++ ++static char *curp=0; ++char *sial_curp(char *p) { char *op=curp; p?(curp=p):(op=curp); return op; } ++ ++static char* ++sial_cattry(char *first, char *second) ++{ ++struct stat stats; ++char *buf=sial_alloc(strlen(first)+strlen(second)+2); ++ ++ strcpy(buf, first); ++ strcat(buf, "/"); ++ strcat(buf, second); ++ if(!stat(buf, &stats)) return buf; ++ sial_free(buf); ++ return 0; ++} ++ ++char * ++sial_filepath(char *fname, char *path) ++{ ++ struct stat buf; ++ /* valid file path, return immediatly */ ++ if(stat(fname,&buf) == 0) { ++ /* must return a free'able name */ ++ char *name=sial_strdup(fname); ++ TAG(name); ++ return name; ++ ++ } else if(fname[0]=='~') { ++ ++ if(strlen(fname)>1) { ++ ++ char *rname, *start; ++ struct passwd *pwd; ++ ++ if(fname[1]=='/') { ++ ++ /* current user name */ ++ pwd=getpwuid(getuid()); ++ ++ if(!pwd) { ++ sial_msg("Who are you : uid=%d \n?", getuid()); ++ return 0; ++ } ++ ++ start=fname+1; ++ ++ } else { ++ ++ char *p, s; ++ ++ for(p=fname+1;*p;p++) if(*p=='/') break; ++ s=*p; ++ *p='\0'; ++ ++ /* other user */ ++ pwd=getpwnam(fname+1); ++ if(!pwd) { ++ ++ sial_msg("Who is this : %s ?\n", fname+1); ++ return 0; ++ } ++ if(s) *p=s; ++ start=p; ++ } ++ rname=sial_alloc(strlen(start+1)+strlen(pwd->pw_dir)+2); ++ strcpy(rname, pwd->pw_dir); ++ strcat(rname, start); ++ return rname; ++ } ++ ++ } else { ++ ++ char *p=sial_strdup(path); ++ char *tok, *curp; ++ ++ /* we check if the file is found relatively to the current ++ position. I.e. the position of the running script */ ++ if((curp=sial_curp(0)) && (curp=sial_cattry(curp, fname))) { ++ ++ sial_free(p); ++ return curp; ++ } ++ ++ tok=strtok(p, ":"); ++ while(tok) { ++ ++ if((curp=sial_cattry(tok, fname))) { ++ ++ sial_free(p); ++ return curp; ++ } ++ tok=strtok(NULL, ":"); ++ ++ } ++ sial_free(p); ++ } ++ return 0; ++} ++ ++char* ++sial_filempath(char *fname) ++{ ++ return sial_filepath(fname, mpath); ++} ++ ++char * ++sial_fileipath(char *fname) ++{ ++ return sial_filepath(fname, ipath); ++} ++ ++/* load a file or a set of file */ ++int ++sial_loadunload(int load, char *name, int silent) ++{ ++DIR *dirp; ++int ret=1; ++char *fname=sial_filempath(name); ++ ++ if(!fname) { ++ ++ if(!silent) sial_msg("File not found : %s\n", name); ++ return 0; ++ } ++ ++ if((dirp=opendir(fname))) { ++ ++ struct dirent *dp; ++ char *buf; ++ ++ while ((dp = readdir(dirp)) != NULL) { ++ ++ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) ++ continue; ++ ++ buf=sial_alloc(strlen(fname)+dp->d_reclen+2); ++ sprintf(buf, "%s/%s", fname, dp->d_name); ++ if(load) { ++ ret &= sial_newfile(buf, silent); ++ }else{ ++ sial_deletefile(buf); ++ } ++ sial_free(buf); ++ } ++ closedir(dirp); ++ } ++ else { ++ ++ if(load) { ++ ret=sial_newfile(fname, silent); ++ }else{ ++ sial_deletefile(fname); ++ } ++ } ++ sial_free(fname); ++ return ret; ++} ++ ++/* ++ Load conditionaly. ++ If it's already load, return. ++*/ ++ull ++sial_depend(char *name) ++{ ++char *fname=sial_filempath(name); ++int ret=1 ; ++void *fp; ++ ++ if(!fname) ret=0; ++ else if(!(fp=sial_findfile(fname,0)) || sial_isnew(fp)) { ++ ++ ret=sial_loadunload(1, name, 1); ++ sial_free(fname); ++ } ++ return ret; ++} ++ ++value_t * ++sial_bdepend(value_t *vname) ++{ ++ return sial_makebtype(sial_depend(sial_getptr(vname, char))); ++} ++ ++ull ++sial_load(char *fname) ++{ ++ return sial_loadunload(1, fname, 0); ++} ++ ++value_t* ++sial_bload(value_t *vfname) ++{ ++char *fname=sial_getptr(vfname, char); ++value_t *v; ++ ++ v=sial_makebtype(sial_load(fname)); ++ return v; ++} ++ ++ull ++sial_unload(char *fname) ++{ ++ return sial_loadunload(0, fname, 0); ++} ++ ++value_t* ++sial_bunload(value_t *vfname) ++{ ++char *fname=sial_getptr(vfname, char); ++ ++ return sial_defbtype(sial_newval(), sial_unload(fname)); ++} ++ ++void ++sial_loadall() ++{ ++char *path=sial_strdup(sial_getmpath()); ++char *p, *pn; ++ ++ p=pn=path; ++ while(*pn) { ++ ++ if(*pn == ':') { ++ ++ *pn++='\0'; ++ sial_loadunload(1, p, 1); ++ p=pn; ++ ++ } else pn++; ++ } ++ if(p!=pn) sial_loadunload(1, p, 1); ++ /* sial_free(path); */ ++} ++ ++static void ++add_flag(var_t*flags, int c) ++{ ++char s[20]; ++var_t *v; ++ ++ sprintf(s, "%cflag", c); ++ v=sial_newvar(s); ++ sial_defbtype(v->v, (ull)0); ++ v->ini=1; ++ sial_enqueue(flags, v); ++} ++ ++int ++sial_cmd(char *fname, char **argv, int argc) ++{ ++value_t *idx, *val; ++ ++ sial_chkinit(); ++ ++ if(sial_chkfname(fname, 0)) { ++ ++ var_t*flags, *args, *narg; ++ char *opts, *newn=sial_alloc(strlen(fname)+sizeof("_usage")+1); ++ int c, i; ++ extern char *optarg; ++ extern int optind; ++ int dou; ++ char *f=sial_strdup("Xflag"); ++ ++ flags=(var_t*)sial_newvlist(); ++ ++ /* build a complete list of option variables */ ++ for(c='a';c<='z';c++) add_flag(flags, c); ++ for(c='A';c<='Z';c++) add_flag(flags, c); ++ ++ /* check if there is a getopt string associated with this command */ ++ /* there needs to be a fname_opt() and a fname_usage() function */ ++ sprintf(newn, "%s_opt", fname); ++ ++ if(sial_chkfname(newn, 0)) opts=(char*)(unsigned long)sial_exefunc(newn, 0); ++ else opts=""; ++ ++ sprintf(newn, "%s_usage", fname); ++ dou=sial_chkfname(newn, 0); ++ ++ /* build a set of variable from the given list of arguments */ ++ /* each options generate a conrresponding flag ex: -X sets Xflag to one ++ end the corresponding argument of a ":" option is in ex. Xarg ++ each additional arguments is keaped in the array args[] */ ++ ++ if(opts[0]) { ++ ++#ifdef linux ++ optind=0; ++#else ++ getoptreset(); ++#endif ++ while ((c = getopt(argc, argv, opts)) != -1) { ++ ++ var_t*flag, *opt; ++ char *a=sial_strdup("Xarg");; ++ ++ if(c==':') { ++ ++ sial_warning("Missing argument(s)"); ++ if(dou) sial_exefunc(newn, 0); ++ sial_free(a); ++ goto out; ++ ++ } else if(c=='?') { ++ ++ if(dou) { ++ ++ char *u=(char*)(unsigned long)sial_exefunc(newn, 0); ++ ++ if(u) sial_msg("usage: %s %s\n", fname, u); ++ } ++ sial_free(a); ++ goto out; ++ } ++ ++ ++ /* set the Xflag variable to 1 */ ++ f[0]=c; ++ flag=sial_inlist(f, flags); ++ sial_defbtype(flag->v, (ull)1); ++ flag->ini=1; ++ ++ /* create the Xarg variable */ ++ if(optarg && optarg[0]) { ++ ++ char *p=sial_alloc(strlen(optarg)+1); ++ ++ a[0]=c; ++ strcpy(p, optarg); ++ opt=(var_t*)sial_newvar(a); ++ sial_setstrval(opt->v, p); ++ opt->ini=1; ++ sial_enqueue(flags, opt); ++ } ++ sial_free(a); ++ } ++ sial_free(f); ++ } ++ else optind=1; ++ ++ /* put every other args into the argv[] array_t*/ ++ args=(var_t*)sial_newvar("argv"); ++ args->ini=1; ++ ++ /* create a argv[0] with the name of the command */ ++ { ++ ++ val=sial_makestr(fname); ++ idx=sial_makebtype(0); ++ ++ /* create the value's value */ ++ sial_addarrelem(&args->v->arr, idx, val); ++ sial_freeval(idx); ++ } ++ ++ for ( i=1; optind < argc; optind++, i++) { ++ ++ val=sial_makestr(argv[optind]); ++ idx=sial_makebtype(i); ++ ++ /* create the value's value */ ++ sial_addarrelem(&args->v->arr, idx, val); ++ sial_freeval(idx); ++ } ++ ++ narg=(var_t*)sial_newvar("argc"); ++ sial_defbtype(narg->v, i); ++ narg->ini=1; ++ ++ sial_enqueue(flags, narg); ++ ++ /* add the args variable to the flags queue */ ++ sial_enqueue(flags, args); ++ ++ /* now execute */ ++ sial_runcmd(fname, flags); ++ ++out: ++ /* free all arguments variables Xflag Xarg and argv[] */ ++ sial_freesvs(flags); ++ ++ sial_free(newn); ++ return 0; ++ } ++ return 1; ++} ++ +--- crash/extensions/libsial/sial.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/libsial/sial.h 2007-10-30 11:13:53.000000000 -0400 +@@ -0,0 +1,465 @@ ++/* ++ * Copyright 2001 Silicon Graphics, Inc. All rights reserved. ++ */ ++#include "sial_api.h" ++typedef unsigned long long caddr; ++ ++#define SRCPOS_S struct srcpos_s ++#define DVAR_S struct dvar_s ++#define CASELIST_S struct caselist_s ++#define CASEVAL_S struct caseval_s ++#define STMEMBER_S struct stmember_s ++#define STINFO_S struct stinfo_s ++ ++SRCPOS_S; ++DVAR_S; ++CASELIST_S; ++CASEVAL_S; ++STMEMBER_S; ++STINFO_S; ++ ++ ++/************* source position tracking ************/ ++typedef SRCPOS_S { ++ char *file; ++ int line; ++ int col; ++} srcpos_t; ++ ++/* member information */ ++typedef MEMBER_S { ++ ++ char *name; ++ int offset; /* offset from top of structure */ ++ int size; /* size in bytes of the member or of the bit array */ ++ int fbit; /* fist bit (-1) is not a bit field */ ++ int nbits; /* number of bits for this member */ ++ int value; /* for a enum member, the corresponding value_t */ ++ ++} member_t; ++ ++/* list to hold enum constant information */ ++typedef ENUM_S { ++ ++ struct enum_s *next; ++ char *name; ++ int value; ++ ++} enum_t; ++ ++/* list of macro symbols and there corresponding value_ts */ ++typedef DEF_S { ++ struct def_s * next; ++ char *name; ++ char *val; ++ ++} def_t; ++ ++/* type_t information past back and forth */ ++typedef TYPE_S { ++ int type; /* type_t of type_t */ ++ ull idx; /* index to basetype_t or ctype_t */ ++ int size; /* size of this item */ ++ /* ... next fields are use internally */ ++ int typattr; /* base type_t qualifiers */ ++ int ref; /* level of reference */ ++ int fct; /* 1 if function pointer */ ++ int *idxlst; /* points to list of indexes if array */ ++ ull rtype; /* type_t a reference refers too */ ++} type_t; ++ ++/* scope/storage of variables */ ++#define S_FILE 1 /* persistant file scope */ ++#define S_STAT 2 /* persistant statement scope */ ++#define S_AUTO 3 /* stack (default) */ ++#define S_GLOB 4 /* add to the global variables */ ++ ++typedef union vu_s { ++ unsigned char uc; ++ signed char sc; ++ unsigned short us; ++ signed short ss; ++ unsigned int ul; ++ signed int sl; ++ unsigned long long ull; ++ signed long long sll; ++ void *data; ++} vu_t; ++ ++/************* value_t **************/ ++typedef VALUE_S { ++ type_t type; ++ int set; /* is this is a Lvalue_t then set is 1 */ ++ VALUE_S *setval;/* value_t to set back to */ ++ void (*setfct)(struct value_s*, struct value_s*); ++ /* the function that will set the value */ ++ ARRAY_S *arr; /* array associated with value */ ++ vu_t v; ++ ull mem; ++} value_t; ++ ++/************** array linked lists *****************/ ++typedef ARRAY_S { ++ ++ ARRAY_S *next; /* to support a linked list of array elements */ ++ ARRAY_S *prev; ++ int ref; /* reference count on this array */ ++ VALUE_S *idx; /* arrays can be indexed using any type of variables */ ++ VALUE_S *val; /* arrays element values */ ++ ++} array_t; ++ ++/************* node_t *************/ ++typedef NODE_S { ++ VALUE_S* (*exe)(void*); /* execute it */ ++ void (*free)(void*); /* free it up */ ++ char* (*name)(void*); /* get a name */ ++ void *data; /* opaque data */ ++ NODE_S* next; ++ SRCPOS_S pos; ++} node_t; ++ ++typedef IDX_S { ++ ++ int nidx; ++ NODE_S *idxs[MAXIDX]; ++ ++} idx_t; ++ ++/*************** variable list ****************/ ++typedef VAR_S { ++ ++ char *name; ++ VAR_S *next; ++ VAR_S *prev; ++ VALUE_S *v; ++ int ini; ++ DVAR_S *dv; ++ ++} var_t; ++ ++/* V_BASE subtype */ ++#define B_SC 0 /* signed char */ ++#define B_UC 1 /* unsignec char */ ++#define B_SS 2 /* signed short */ ++#define B_US 3 /* unsigned short */ ++#define B_SL 4 /* signed long */ ++#define B_UL 5 /* unsigned long */ ++#define B_SLL 6 /* signed long long */ ++#define B_ULL 7 /* unsigned long long */ ++ ++#define is_ctype(t) ((t)==V_UNION || (t)==V_STRUCT) ++#define VAL_TYPE(v) (v->type.type) ++#define TYPE_SIZE(t) ((t)->type==V_REF?sial_defbsize():(t)->size) ++ ++/* type_ts of jumps */ ++#define J_CONTINUE 1 ++#define J_BREAK 2 ++#define J_RETURN 3 ++#define J_EXIT 4 ++ ++#define sial_setval(v, v2) if((v)->set) ((v)->setfct)((v)->setval, (v2)) ++ ++/************* case *************/ ++typedef CASEVAL_S { ++ ++ int isdef; ++ ull val; ++ CASEVAL_S *next; ++ SRCPOS_S pos; ++ ++} caseval_t; ++ ++typedef CASELIST_S { ++ ++ CASEVAL_S *vals; ++ NODE_S *stmt; ++ CASELIST_S *next; ++ SRCPOS_S pos; ++ ++} caselist_t; ++ ++/*************** struct member info ****************/ ++typedef STMEMBER_S { ++ ++ TYPE_S type; /* corresponding type_t */ ++ MEMBER_S m; /* member information */ ++ ++ STMEMBER_S *next; ++ ++} stmember_t; ++ ++typedef DVAR_S { ++ ++ char *name; ++ int refcount; ++ int ref; ++ int fct; ++ int bitfield; ++ int nbits; ++ IDX_S *idx; ++ NODE_S *init; ++ VAR_S *fargs; ++ SRCPOS_S pos; ++ DVAR_S *next; ++ ++} dvar_t; ++ ++typedef STINFO_S { ++ char *name; /* structure name */ ++ ull idx; /* key for search */ ++ int all; /* local : partial or complete declaration ? */ ++ TYPE_S ctype; /* associated type */ ++ TYPE_S rtype; /* real type_t when typedef */ ++ STMEMBER_S *stm; /* linked list of members */ ++ ENUM_S *enums; /* enums names and values */ ++ STINFO_S *next; /* next struct on the list */ ++ ++} stinfo_t; ++ ++stinfo_t *sial_getstbyindex(ull idx, int type_t); ++ ++typedef value_t* (*xfct_t)(void *); ++typedef char* (*nfct_t)(void *); ++typedef void (*ffct_t)(void *); ++typedef void (*setfct_t)(value_t*, value_t*); ++ ++#ifdef DEBUG ++#define NODE_EXE(n) (printf("(%s):[%d]\n",__FILE__, __LINE__), (n)->exe((n)->data)) */ ++#else ++#define NODE_EXE(n) ((n)->exe((n)->data)) ++#endif ++#define NODE_NAME(n) ((n)->name?((n)->name((n)->data)):0) ++#define NODE_FREE(n) (sial_freenode(n)) ++ ++#ifdef __GNUC__ ++#define __return_address (void*)(__builtin_return_address(0)) ++#else ++// must be the SGI Mips compiler. ++#endif ++#if 1 ++#define TAG(p) sial_caller(p, __return_address) ++#else ++#define TAG(p) ; ++#endif ++ ++node_t *sial_sibling(node_t*, node_t*); ++node_t *sial_newnode(void); ++node_t *sial_newvnode(char *); ++node_t *sial_newstr(void); ++node_t *sial_newnum(char *); ++node_t *sial_newop(int op, int nagrs, ...); ++node_t *sial_newptrto(int, node_t*); ++node_t *sial_newmult(node_t*, node_t*, int); ++node_t *sial_newstat(int op, int nargs, ...); ++node_t *sial_stat_decl(node_t*, var_t*); ++node_t *sial_addstat(node_t*, node_t*); ++node_t *sial_type_cast(type_t*, node_t*); ++node_t *sial_newmem(int, node_t*, node_t*); ++node_t *sial_newcall(node_t*, node_t*); ++node_t *sial_newindex(node_t*, node_t*); ++node_t *sial_newadrof(node_t*); ++node_t *sial_newcase(node_t*, node_t*); ++node_t *sial_addcase(node_t*, node_t*); ++node_t *sial_caseval(int, node_t*); ++node_t *sial_addcaseval(node_t*, node_t*); ++node_t *sial_sizeof(void *p, int type_t); ++node_t *sial_tdeftovar(type_t *td); ++node_t *sial_getppnode(void); ++node_t *sial_allocstr(char *buf); ++node_t *sial_makenum(int type_t, ull val); ++node_t *sial_macexists(node_t *var_t); ++node_t *sial_newptype(var_t *v); ++node_t *sial_newpval(node_t *vn, int fmt); ++node_t *sial_strconcat(node_t *, node_t *); ++node_t *sial_typecast(type_t*type, node_t*expr); ++ ++dvar_t *sial_newdvar(node_t *v); ++dvar_t *sial_linkdvar(dvar_t *dvl, dvar_t *dv); ++dvar_t *sial_dvarini(dvar_t *dv, node_t *init); ++dvar_t *sial_dvaridx(dvar_t *dv, node_t *n); ++dvar_t *sial_dvarfld(dvar_t *dv, node_t *n); ++dvar_t *sial_dvarptr(int ref, dvar_t *dv); ++dvar_t *sial_dvarfct(dvar_t *dv, var_t *fargs); ++ ++void sial_pushjmp(int type_t, void *env, void *val); ++void sial_popjmp(int type_t); ++void *sial_getcurfile(void); ++void sial_walkarray(node_t *varnode_t, node_t *arrnode_t, void(*cb)(void *), void *data); ++void get_bit_value(ull val, int nbits, int boff, int size, value_t *v); ++void sial_enqueue(var_t *vl, var_t *v); ++void sial_freenode(node_t *n); ++void sial_validate_vars(var_t *svs); ++void sial_freesvs(var_t *svs); ++void *sial_setexcept(void); ++void sial_tdef_decl(dvar_t *dv, type_t *t); ++void sial_refarray(value_t *v, int inc); ++void *sial_curmac(void); ++void sial_setfct(value_t *v1, value_t *v2); ++void sial_exevi(char *fname, int line); ++void sial_unput(char); ++void sial_dupval(value_t *v, value_t *vs); ++void sial_parseback(void); ++void sial_curpos(srcpos_t *p, srcpos_t *s); ++void sial_rmexcept(void *osa); ++void sial_chksign(type_t*t); ++void sial_chksize(type_t*t); ++void sial_setpos(srcpos_t *p); ++void sial_rerror(srcpos_t *p, char *fmt, ...); ++void sial_rwarning(srcpos_t *p, char *fmt, ...); ++void sial_chkandconvert(value_t *vto, value_t *vfrm); ++void sial_warning(char *fmt, ...); ++void sial_format(int tabs, char *str); ++void sial_freevar(var_t*v); ++void sial_rmbuiltin(var_t*v); ++void sial_rm_globals(void *vg); ++void sial_addnewsvs(var_t*avl, var_t*svl, var_t*nvl); ++void sial_dojmp(int type, void *val); ++void sial_pushbuf(char *buf, char *fname, void(*f)(void*), void *d, void *m); ++void sial_rsteofoneol(void); ++void sial_settakeproto(int v); ++void sial_popallin(void); ++void sial_tagst(void); ++void sial_flushtdefs(void); ++void sial_setsvlev(int newlev); ++void sial_flushmacs(void *tag); ++void sial_add_auto(var_t*nv); ++void *sial_chkbuiltin(char *name); ++void sial_freedata(value_t *v); ++void sial_dupdata(value_t *v, value_t *vs); ++void sial_setarray(array_t**arpp); ++void sial_rawinput(int on); ++void sial_setini(node_t*n); ++void sial_valindex(value_t *var, value_t *idx, value_t *ret); ++void sial_free_siblings(node_t*ni); ++void sial_mkvsigned(value_t*v); ++void sial_transval(int s1, int s2, value_t *v, int issigned); ++void sial_popref(type_t*t, int ref); ++void sial_getmem(ull kp, void *p, int n); ++void sial_baseop(int op, value_t *v1, value_t *v2, value_t *result); ++void sial_setinsizeof(int v); ++void sial_freeidx(idx_t *idx); ++void sial_freedvar(dvar_t*dv); ++void sial_pushenums(enum_t *et); ++void sial_addfunc_ctype(int idx); ++void sial_setapiglobs(void); ++void sial_setbuiltins(void); ++void sial_setdefbtype(int size, int sign); ++void get_bit_value(ull val, int nbits, int boff, int size, value_t *v); ++void *sial_findfile(char *name, int unlink); ++void sial_newmac(char *mname, char *buf, int np, char **p, int silent); ++void *sial_getcurfile(void); ++void *sial_getcurfile(void); ++void sial_startctype(int type, node_t*namen); ++void sial_addtolist(var_t*vl, var_t*v); ++void sial_arch_swapvals(void* vp, void *sp); ++void sial_fillst(stinfo_t *st); ++void sial_exememlocal(value_t *vp, stmember_t* stm, value_t *v); ++void sial_do_deref(int n, value_t *v, value_t *ref); ++void sial_addneg(char *name); ++ ++stmember_t*sial_member(char *mname, type_t*tp); ++ ++ull set_bit_value_t(ull dvalue_t, ull value_t, int nbits, int boff); ++ull unival(value_t *); ++ul sial_bool(value_t *); ++ ++value_t *sial_docall(node_t *, node_t *, void *); ++value_t *sial_docast(void); ++value_t *sial_newval(void); ++value_t *sial_exebfunc(char *, value_t **); ++value_t *sial_exevar(void *); ++value_t *sial_exenode(node_t *); ++value_t *sial_setstrval(value_t *, char *); ++value_t *sial_defbtype(value_t *, ull); ++value_t *sial_defbtypesize(value_t *, ull, int); ++value_t *sial_sprintf(value_t *, ...); ++value_t *sial_printf(value_t *, ...); ++value_t *sial_exists(value_t *vname); ++value_t *sial_exit(int v); ++value_t *sial_bload(value_t *name); ++value_t *sial_bdepend(value_t *name); ++value_t *sial_bunload(value_t *vfname); ++value_t *sial_showtemp(void); ++value_t *sial_showaddr(value_t *vadr); ++value_t *sial_findsym(value_t *vadr); ++value_t *sial_memdebugon(void); ++value_t *sial_memdebugoff(void); ++value_t *sial_ismember(value_t*vp, value_t*vm); ++ ++value_t *sial_prarr(value_t*name, value_t*root); ++value_t *sial_getstr(value_t*vm); ++ ++var_t *sial_vardecl(dvar_t *dv, type_t *t); ++var_t *sial_inlist(char *name, var_t *vl); ++var_t *sial_dupvlist(var_t *vl); ++var_t *sial_getcurgvar(void); ++var_t *sial_getvarbyname(char *name, int silent, int local); ++var_t *sial_getsgrp_avs(node_t *n); ++var_t *sial_getsgrp_svs(node_t *n); ++var_t *sial_parsexpr(char *); ++ ++int sial_file_decl(var_t *svs); ++int sial_newfunc(var_t *fvar, node_t* body); ++int sial_line(int inc); ++int sial_samectypename(int type_t, ull idx1, ull idx2); ++int sial_issigned(int attr); ++int sial_isstatic(int atr); ++int sial_isjuststatic(int attr); ++int sial_isconst(int atr); ++int sial_issigned(int atr); ++int sial_istdef(int atr); ++int sial_isxtern(int atr); ++int sial_isvoid(int atr); ++int sial_isstor(int atr); ++int sial_ispartial(type_t*t); ++int sial_input(void); ++int sial_addsvs(int type, var_t*sv); ++int sial_pushfile(char *name); ++int sial_chkfname(char *fname, void *fd); ++int sial_lookuparray(node_t*vnode, node_t*arrnode); ++int sial_runcmd(char *fname, var_t*args); ++int sial_getseq(int c); ++int sial_newfile(char *name, int silent); ++int sial_deletefile(char *name); ++int sial_getsvlev(void); ++int sial_idxtoattr(int idx); ++int sial_docase(ull val, caselist_t*cl); ++int siallex(void); ++int sialpplex(void); ++int sial_ismemdebug(void); ++int sial_isenum(int atr); ++int sial_funcexists(char *name); ++int sial_isnew(void* p); ++int sial_isneg(char *name); ++ ++char *sial_vartofunc(node_t *name); ++char *sial_gettdefname(ull idx); ++char *sial_ctypename(int type_t); ++char *sial_filempath(char *fname); ++char *sial_fileipath(char *fname); ++char *sial_getline(void); ++char *sial_cursorp(void); ++char *sial_getbtypename(int typattr); ++char *sial_filename(void); ++char *sial_curp(char *); ++ ++type_t *sial_newcast(var_t *v); ++type_t *sial_newctype(int ctype_t, node_t *n); ++type_t *sial_addbtype(type_t *t, int newtok); ++type_t *sial_ctype_decl(int ctype_t, node_t *n, var_t *list); ++type_t *sial_enum_decl(int ctype_t, node_t *n, dvar_t *dvl); ++type_t *sial_addstorage(type_t *t1, type_t *t2); ++type_t *sial_getvoidstruct(int ctype); ++ ++extern int lineno, needvar, instruct, nomacs; ++node_t *lastv; ++ ++#define NULLNODE ((node_t*)0) ++ ++/* configuration variables */ ++#define S_MAXSTRLEN 1024 /* lengh of a STRING variable value_t */ ++#define S_MAXDEEP 500 /* maximum stacking of calls */ ++#define S_MAXFILES 200 /* maximum number of macro files */ ++ ++#define S_VARARG "__VARARG" /* name of the special var for ... */ +--- crash/extensions/Makefile.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions/Makefile 2007-09-24 15:49:41.000000000 -0400 +@@ -0,0 +1,47 @@ ++# ++# Makefile for building crash shared object extensions ++# ++# Copyright (C) 2005, 2007 David Anderson ++# Copyright (C) 2005, 2007 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. ++# ++# To build the extension shared objects in this directory, run ++# "make extensions" from the top-level directory. ++# ++# To add a new extension object, simply copy your module's .c file ++# to this directory, and it will be built automatically using ++# the "standard" compile line. If that compile line does not ++# suffice, create a .mk file with the same prefix as the .c file, ++# and that makefile will be invoked. ++# ++ ++CONTRIB_SO := $(patsubst %.c,%.so,$(wildcard *.c)) ++ ++all: link_defs $(CONTRIB_SO) ++ ++link_defs: ++ @if [ ! -f defs.h ]; then \ ++ ln -s ../defs.h; fi ++ ++$(CONTRIB_SO): %.so: %.c ++ @if [ -f $*.mk ]; then \ ++ make -f $*.mk; \ ++ else \ ++ echo "gcc -nostartfiles -shared -rdynamic -o $@ $*.c -fPIC -D$(TARGET) $(TARGET_CFLAGS)"; \ ++ gcc -nostartfiles -shared -rdynamic -o $@ $*.c -fPIC -D$(TARGET) $(TARGET_CFLAGS); \ ++ fi ++ ++clean: ++ rm -f $(CONTRIB_SO) ++ @for MAKEFILE in `grep -sl "^clean:" *.mk`; \ ++ do make --no-print-directory -f $$MAKEFILE clean; \ ++ done +--- crash/lkcd_v7.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_v7.c 2005-11-10 15:25:45.000000000 -0500 +@@ -89,7 +89,11 @@ + ifd = 0; + + #ifdef LKCD_INDEX_FILE +- lkcd->memory_pages = (dh->dh_memory_size * (getpagesize()/lkcd->page_size)) * 2; ++ if (dh->dh_memory_end < 0x1000000000LL) { ++ lkcd->memory_pages = dh->dh_memory_end / lkcd->page_size + 1; ++ } else { ++ lkcd->memory_pages = (dh->dh_memory_size * (getpagesize()/lkcd->page_size)) * 2; ++ } + dump_index_size = (lkcd->memory_pages * sizeof(off_t)); + lkcd->page_offsets = 0; + strcpy(dumpfile_index_name, dumpfile); +--- crash/filesys.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/filesys.c 2009-02-04 15:01:56.000000000 -0500 +@@ -1,8 +1,8 @@ + /* filesys.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -17,8 +17,9 @@ + + #include "defs.h" + #include ++#include + +-static void show_mounts(ulong, int); ++static void show_mounts(ulong, int, struct task_context *); + static int find_booted_kernel(void); + static int find_booted_system_map(void); + static int verify_utsname(char *); +@@ -33,7 +34,7 @@ + static int open_file_reference(struct reference *); + static void memory_source_init(void); + static int get_pathname_component(ulong, ulong, int, char *, char *); +-static ulong *get_mount_list(int *); ++static ulong *get_mount_list(int *, struct task_context *); + char *inode_type(char *, char *); + static void match_proc_version(void); + static void get_live_memory_source(void); +@@ -43,6 +44,7 @@ + static int memory_driver_init(void); + static int create_memory_device(dev_t); + static void *radix_tree_lookup(ulong, ulong, int); ++static int match_file_string(char *, char *, char *); + + #define DENTRY_CACHE (20) + #define INODE_CACHE (20) +@@ -75,6 +77,7 @@ + #define DUMP_FULL_NAME 1 + #define DUMP_INODE_ONLY 2 + #define DUMP_DENTRY_ONLY 4 ++#define DUMP_EMPTY_FILE 8 + + /* + * Open the namelist, dumpfile and output devices. +@@ -99,6 +102,10 @@ + } + + if (pc->namelist) { ++ if (XEN_HYPER_MODE() && !pc->dumpfile) ++ error(FATAL, ++ "Xen hypervisor mode requires a dumpfile\n"); ++ + if (!pc->dumpfile && !get_proc_version()) + error(INFO, "/proc/version: %s\n", + strerror(errno)); +@@ -190,7 +197,15 @@ + if (!netdump_init(pc->dumpfile, fp)) + error(FATAL, "%s: initialization failed\n", + pc->dumpfile); +- } else if (pc->flags & NETDUMP) { ++ } else if (pc->flags & KDUMP) { ++ if (!kdump_init(pc->dumpfile, fp)) ++ error(FATAL, "%s: initialization failed\n", ++ pc->dumpfile); ++ } else if (pc->flags & XENDUMP) { ++ if (!xendump_init(pc->dumpfile, fp)) ++ error(FATAL, "%s: initialization failed\n", ++ pc->dumpfile); ++ } else if (pc->flags & DISKDUMP) { + if (!diskdump_init(pc->dumpfile, fp)) + error(FATAL, "%s: initialization failed\n", + pc->dumpfile); +@@ -217,10 +232,7 @@ + static void + match_proc_version(void) + { +- char command[BUFSIZE]; +- char buffer[BUFSIZE]; +- FILE *pipe; +- int found; ++ char buffer[BUFSIZE], *p1, *p2; + + if (pc->flags & KERNEL_DEBUG_QUERY) + return; +@@ -228,32 +240,37 @@ + if (!strlen(kt->proc_version)) + return; + +- sprintf(command, "/usr/bin/strings %s", pc->namelist); +- if ((pipe = popen(command, "r")) == NULL) { +- error(INFO, "%s: %s\n", pc->namelist, strerror(errno)); +- return; +- } +- +- found = FALSE; +- while (fgets(buffer, BUFSIZE-1, pipe)) { +- if (!strstr(buffer, "Linux version 2.")) +- continue; +- +- if (STREQ(buffer, kt->proc_version)) +- found = TRUE; +- break; +- } +- pclose(pipe); +- +- if (found) { ++ if (match_file_string(pc->namelist, kt->proc_version, buffer)) { + if (CRASHDEBUG(1)) { +- fprintf(fp, "/proc/version:\n%s", kt->proc_version); ++ fprintf(fp, "/proc/version:\n%s\n", kt->proc_version); + fprintf(fp, "%s:\n%s", pc->namelist, buffer); + } + return; + } + +- if (find_booted_system_map()) ++ error(WARNING, "%s%sand /proc/version do not match!\n\n", ++ pc->namelist, ++ strlen(pc->namelist) > 39 ? "\n " : " "); ++ ++ /* ++ * find_booted_system_map() requires VTOP(), which used to be a ++ * hardwired masking of the kernel address. But some architectures ++ * may not know what their physical base address is at this point, ++ * and others may have different machdep->kvbase values, so for all ++ * but the 0-based kernel virtual address architectures, bail out ++ * here with a relevant error message. ++ */ ++ if (!machine_type("S390") && !machine_type("S390X")) { ++ p1 = &kt->proc_version[strlen("Linux version ")]; ++ p2 = strstr(p1, " "); ++ *p2 = NULLCHAR; ++ error(WARNING, "/proc/version indicates kernel version: %s\n", p1); ++ error(FATAL, "please use the vmlinux file for that kernel version, or try using\n" ++ " the System.map for that kernel version as an additional argument.\n", p1); ++ clean_exit(1); ++ } ++ ++ if (find_booted_system_map()) + pc->flags |= SYSMAP; + } + +@@ -303,14 +320,12 @@ + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) + cnt++; + +- if ((searchdirs = (char **)malloc(cnt * sizeof(char *))) +- == NULL) { ++ if ((searchdirs = calloc(cnt, sizeof(char *))) == NULL) { + error(INFO, "/usr/src/ directory list malloc: %s\n", + strerror(errno)); + closedir(dirp); + return default_searchdirs; + } +- BZERO(searchdirs, cnt * sizeof(char *)); + + for (i = 0; i < DEFAULT_SEARCHDIRS; i++) + searchdirs[i] = default_searchdirs[i]; +@@ -345,6 +360,16 @@ + closedir(dirp); + + searchdirs[cnt] = NULL; ++ } else { ++ if ((searchdirs = calloc(cnt, sizeof(char *))) == NULL) { ++ error(INFO, "search directory list malloc: %s\n", ++ strerror(errno)); ++ closedir(dirp); ++ return default_searchdirs; ++ } ++ for (i = 0; i < DEFAULT_SEARCHDIRS; i++) ++ searchdirs[i] = default_searchdirs[i]; ++ cnt = DEFAULT_SEARCHDIRS; + } + + if (redhat_kernel_directory_v1(dirbuf)) { +@@ -483,13 +508,11 @@ + find_booted_kernel(void) + { + char kernel[BUFSIZE]; +- char command[BUFSIZE]; + char buffer[BUFSIZE]; + char **searchdirs; + int i, preferred, wrapped; + DIR *dirp; + struct dirent *dp; +- FILE *pipe; + int found; + + pc->flags |= FINDKERNEL; +@@ -538,24 +561,11 @@ + !is_elf_file(kernel)) + continue; + +- sprintf(command, "/usr/bin/strings %s", kernel); +- if ((pipe = popen(command, "r")) == NULL) { +- error(INFO, "%s: %s\n", +- kernel, strerror(errno)); +- continue; +- } +- + if (CRASHDEBUG(1)) + fprintf(fp, "find_booted_kernel: check: %s\n", + kernel); + +- while (fgets(buffer, BUFSIZE-1, pipe)) { +- if (STREQ(buffer, kt->proc_version)) { +- found = TRUE; +- break; +- } +- } +- pclose(pipe); ++ found = match_file_string(kernel, kt->proc_version, buffer); + + if (found) + break; +@@ -701,6 +711,8 @@ + + fclose(version); + ++ strip_linefeeds(kt->proc_version); ++ + return TRUE; + } + +@@ -797,30 +809,14 @@ + static int + verify_utsname(char *system_map) + { +- char command[BUFSIZE]; + char buffer[BUFSIZE]; +- FILE *pipe; +- int found; + ulong value; + struct new_utsname new_utsname; + +- sprintf(command, "/usr/bin/strings %s", system_map); +- if ((pipe = popen(command, "r")) == NULL) +- return FALSE; +- + if (CRASHDEBUG(1)) + fprintf(fp, "verify_utsname: check: %s\n", system_map); + +- found = FALSE; +- while (fgets(buffer, BUFSIZE-1, pipe)) { +- if (strstr(buffer, "D system_utsname")) { +- found = TRUE; +- break; +- } +- } +- pclose(pipe); +- +- if (!found) ++ if (!match_file_string(system_map, "D system_utsname", buffer)) + return FALSE; + + if (extract_hex(buffer, &value, NULLCHAR, TRUE) && +@@ -941,9 +937,10 @@ + { + char command[BUFSIZE]; + char buf[BUFSIZE]; +- char *retbuf; ++ char *retbuf, *start, *end, *module; + FILE *pipe; +- int done; ++ regex_t regex; ++ int regex_used, done; + + if (!file_exists("/usr/bin/find", NULL) || + !file_exists("/bin/echo", NULL) || +@@ -962,20 +959,34 @@ + + done = FALSE; + retbuf = NULL; ++ regex_used = ((start = strstr(file, "[")) && ++ (end = strstr(file, "]")) && (start < end) && ++ (regcomp(®ex, file, 0) == 0)); + + while (fgets(buf, BUFSIZE-1, pipe) || !done) { + if (STREQ(buf, "search done\n")) { + done = TRUE; + break; + } +- if (!retbuf && ++ if (!retbuf && !regex_used && + STREQ((char *)basename(strip_linefeeds(buf)), file)) { + retbuf = GETBUF(strlen(buf)+1); + strcpy(retbuf, buf); + } ++ if (!retbuf && regex_used) { ++ module = basename(strip_linefeeds(buf)); ++ if (regexec(®ex, module, 0, NULL, 0) == 0) { ++ retbuf = GETBUF(strlen(buf)+1); ++ strcpy(retbuf, buf); ++ } ++ } + } + ++ if (regex_used) ++ regfree(®ex); ++ + pclose(pipe); ++ + return retbuf; + } + +@@ -1125,6 +1136,8 @@ + { + int i; + int c, found; ++ struct task_context *tc, *namespace_context; ++ ulong value; + char *spec_string; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; +@@ -1133,7 +1146,9 @@ + int flags = 0; + int save_next; + +- while ((c = getopt(argcnt, args, "if")) != EOF) { ++ namespace_context = pid_to_context(1); ++ ++ while ((c = getopt(argcnt, args, "ifn:")) != EOF) { + switch(c) + { + case 'i': +@@ -1144,6 +1159,19 @@ + flags |= MOUNT_PRINT_FILES; + break; + ++ case 'n': ++ switch (str_to_context(optarg, &value, &tc)) { ++ case STR_PID: ++ case STR_TASK: ++ namespace_context = tc; ++ break; ++ case STR_INVALID: ++ error(FATAL, "invalid task or pid value: %s\n", ++ optarg); ++ break; ++ } ++ break; ++ + default: + argerrs++; + break; +@@ -1162,7 +1190,7 @@ + shift_string_left(spec_string, 2); + + open_tmpfile(); +- show_mounts(0, MOUNT_PRINT_ALL); ++ show_mounts(0, MOUNT_PRINT_ALL, namespace_context); + + found = FALSE; + rewind(pc->tmpfile); +@@ -1181,16 +1209,20 @@ + continue; + + for (i = 0; i < c; i++) { +- if (STREQ(arglist[i], spec_string)) ++ if (PATHEQ(arglist[i], spec_string)) + found = TRUE; + } + if (found) { + fp = pc->saved_fp; + if (flags) { + sscanf(buf2,"%lx",&vfsmount); +- show_mounts(vfsmount, flags); ++ show_mounts(vfsmount, flags, ++ namespace_context); + } else { +- fprintf(fp, mount_hdr); ++ if (!(pc->curcmd_flags & HEADER_PRINTED)) { ++ fprintf(fp, mount_hdr); ++ pc->curcmd_flags |= HEADER_PRINTED; ++ } + fprintf(fp, buf2); + } + found = FALSE; +@@ -1200,7 +1232,7 @@ + close_tmpfile(); + } while (args[++optind]); + } else +- show_mounts(0, flags); ++ show_mounts(0, flags, namespace_context); + } + + /* +@@ -1208,7 +1240,7 @@ + */ + + static void +-show_mounts(ulong one_vfsmount, int flags) ++show_mounts(ulong one_vfsmount, int flags, struct task_context *namespace_context) + { + ulong one_vfsmount_list; + long sb_s_files; +@@ -1246,7 +1278,7 @@ + mount_cnt = 1; + mntlist = &one_vfsmount_list; + } else +- mntlist = get_mount_list(&mount_cnt); ++ mntlist = get_mount_list(&mount_cnt, namespace_context); + + if (!strlen(mount_hdr)) { + devlen = strlen("DEVNAME"); +@@ -1408,11 +1440,11 @@ + * Allocate and fill a list of the currently-mounted vfsmount pointers. + */ + static ulong * +-get_mount_list(int *cntptr) ++get_mount_list(int *cntptr, struct task_context *namespace_context) + { + struct list_data list_data, *ld; + int mount_cnt; +- ulong *mntlist, namespace, root; ++ ulong *mntlist, namespace, root, nsproxy, mnt_ns; + struct task_context *tc; + + ld = &list_data; +@@ -1421,9 +1453,26 @@ + if (symbol_exists("vfsmntlist")) { + get_symbol_data("vfsmntlist", sizeof(void *), &ld->start); + ld->end = symbol_value("vfsmntlist"); ++ } else if (VALID_MEMBER(task_struct_nsproxy)) { ++ tc = namespace_context; ++ ++ readmem(tc->task + OFFSET(task_struct_nsproxy), KVADDR, ++ &nsproxy, sizeof(void *), "task nsproxy", ++ FAULT_ON_ERROR); ++ if (!readmem(nsproxy + OFFSET(nsproxy_mnt_ns), KVADDR, ++ &mnt_ns, sizeof(void *), "nsproxy mnt_ns", ++ RETURN_ON_ERROR|QUIET)) ++ error(FATAL, "cannot determine mount list location!\n"); ++ if (!readmem(mnt_ns + OFFSET(mnt_namespace_root), KVADDR, ++ &root, sizeof(void *), "mnt_namespace root", ++ RETURN_ON_ERROR|QUIET)) ++ error(FATAL, "cannot determine mount list location!\n"); ++ ++ ld->start = root + OFFSET(vfsmount_mnt_list); ++ ld->end = mnt_ns + OFFSET(mnt_namespace_list); ++ + } else if (VALID_MEMBER(namespace_root)) { +- if (!(tc = pid_to_context(1))) +- tc = CURRENT_CONTEXT(); ++ tc = namespace_context; + + readmem(tc->task + OFFSET(task_struct_namespace), KVADDR, + &namespace, sizeof(void *), "task namespace", +@@ -1497,7 +1546,7 @@ + goto nopath; + + if (VALID_MEMBER(file_f_vfsmnt)) { +- mntlist = get_mount_list(&mount_cnt); ++ mntlist = get_mount_list(&mount_cnt, pid_to_context(1)); + vfsmount_buf = GETBUF(SIZE(vfsmount)); + + for (m = found = 0, vfsmnt = mntlist; +@@ -1706,15 +1755,30 @@ + MEMBER_OFFSET_INIT(fs_struct_pwd, "fs_struct", "pwd"); + MEMBER_OFFSET_INIT(fs_struct_rootmnt, "fs_struct", "rootmnt"); + MEMBER_OFFSET_INIT(fs_struct_pwdmnt, "fs_struct", "pwdmnt"); +- MEMBER_OFFSET_INIT(files_struct_max_fds, "files_struct", "max_fds"); +- MEMBER_OFFSET_INIT(files_struct_max_fdset, "files_struct", "max_fdset"); +- MEMBER_OFFSET_INIT(files_struct_open_fds, "files_struct", "open_fds"); + MEMBER_OFFSET_INIT(files_struct_open_fds_init, + "files_struct", "open_fds_init"); +- MEMBER_OFFSET_INIT(files_struct_fd, "files_struct", "fd"); ++ MEMBER_OFFSET_INIT(files_struct_fdt, "files_struct", "fdt"); ++ if (VALID_MEMBER(files_struct_fdt)) { ++ MEMBER_OFFSET_INIT(fdtable_max_fds, "fdtable", "max_fds"); ++ MEMBER_OFFSET_INIT(fdtable_max_fdset, "fdtable", "max_fdset"); ++ MEMBER_OFFSET_INIT(fdtable_open_fds, "fdtable", "open_fds"); ++ MEMBER_OFFSET_INIT(fdtable_fd, "fdtable", "fd"); ++ } else { ++ MEMBER_OFFSET_INIT(files_struct_max_fds, "files_struct", "max_fds"); ++ MEMBER_OFFSET_INIT(files_struct_max_fdset, "files_struct", "max_fdset"); ++ MEMBER_OFFSET_INIT(files_struct_open_fds, "files_struct", "open_fds"); ++ MEMBER_OFFSET_INIT(files_struct_fd, "files_struct", "fd"); ++ } + MEMBER_OFFSET_INIT(file_f_dentry, "file", "f_dentry"); + MEMBER_OFFSET_INIT(file_f_vfsmnt, "file", "f_vfsmnt"); + MEMBER_OFFSET_INIT(file_f_count, "file", "f_count"); ++ MEMBER_OFFSET_INIT(path_mnt, "path", "mnt"); ++ MEMBER_OFFSET_INIT(path_dentry, "path", "dentry"); ++ if (INVALID_MEMBER(file_f_dentry)) { ++ MEMBER_OFFSET_INIT(file_f_path, "file", "f_path"); ++ ASSIGN_OFFSET(file_f_dentry) = OFFSET(file_f_path) + OFFSET(path_dentry); ++ ASSIGN_OFFSET(file_f_vfsmnt) = OFFSET(file_f_path) + OFFSET(path_mnt); ++ } + MEMBER_OFFSET_INIT(dentry_d_inode, "dentry", "d_inode"); + MEMBER_OFFSET_INIT(dentry_d_parent, "dentry", "d_parent"); + MEMBER_OFFSET_INIT(dentry_d_covers, "dentry", "d_covers"); +@@ -1736,10 +1800,15 @@ + MEMBER_OFFSET_INIT(vfsmount_mnt_mountpoint, + "vfsmount", "mnt_mountpoint"); + MEMBER_OFFSET_INIT(namespace_root, "namespace", "root"); ++ MEMBER_OFFSET_INIT(task_struct_nsproxy, "task_struct", "nsproxy"); + if (VALID_MEMBER(namespace_root)) { + MEMBER_OFFSET_INIT(namespace_list, "namespace", "list"); + MEMBER_OFFSET_INIT(task_struct_namespace, + "task_struct", "namespace"); ++ } else if (VALID_MEMBER(task_struct_nsproxy)) { ++ MEMBER_OFFSET_INIT(nsproxy_mnt_ns, "nsproxy", "mnt_ns"); ++ MEMBER_OFFSET_INIT(mnt_namespace_root, "mnt_namespace", "root"); ++ MEMBER_OFFSET_INIT(mnt_namespace_list, "mnt_namespace", "list"); + } else if (THIS_KERNEL_VERSION >= LINUX(2,4,20)) { + if (CRASHDEBUG(2)) + fprintf(fp, "hardwiring namespace stuff\n"); +@@ -1762,6 +1831,8 @@ + STRUCT_SIZE_INIT(umode_t, "umode_t"); + STRUCT_SIZE_INIT(dentry, "dentry"); + STRUCT_SIZE_INIT(files_struct, "files_struct"); ++ if (VALID_MEMBER(files_struct_fdt)) ++ STRUCT_SIZE_INIT(fdtable, "fdtable"); + STRUCT_SIZE_INIT(file, "file"); + STRUCT_SIZE_INIT(inode, "inode"); + STRUCT_SIZE_INIT(vfsmount, "vfsmount"); +@@ -1777,8 +1848,12 @@ + + if (symbol_exists("height_to_maxindex")) { + int tmp; +- ARRAY_LENGTH_INIT(tmp, height_to_maxindex, +- "height_to_maxindex", NULL, 0); ++ if (LKCD_KERNTYPES()) ++ ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxindex", ++ "radix_tree_preload.nodes", NULL, 0); ++ else ++ ARRAY_LENGTH_INIT(tmp, height_to_maxindex, ++ "height_to_maxindex", NULL, 0); + STRUCT_SIZE_INIT(radix_tree_root, "radix_tree_root"); + STRUCT_SIZE_INIT(radix_tree_node, "radix_tree_node"); + MEMBER_OFFSET_INIT(radix_tree_root_height, +@@ -1798,35 +1873,35 @@ + goto show_hit_rates; + + for (i = 0; i < FILE_CACHE; i++) +- fprintf(stderr, " cached_file[%2d]: %lx (%ld)\n", ++ fprintf(fp, " cached_file[%2d]: %lx (%ld)\n", + i, ft->cached_file[i], + ft->cached_file_hits[i]); +- fprintf(stderr, " file_cache: %lx\n", (ulong)ft->file_cache); +- fprintf(stderr, " file_cache_index: %d\n", ft->file_cache_index); +- fprintf(stderr, " file_cache_fills: %ld\n", ft->file_cache_fills); ++ fprintf(fp, " file_cache: %lx\n", (ulong)ft->file_cache); ++ fprintf(fp, " file_cache_index: %d\n", ft->file_cache_index); ++ fprintf(fp, " file_cache_fills: %ld\n", ft->file_cache_fills); + + for (i = 0; i < DENTRY_CACHE; i++) +- fprintf(stderr, " cached_dentry[%2d]: %lx (%ld)\n", ++ fprintf(fp, " cached_dentry[%2d]: %lx (%ld)\n", + i, ft->cached_dentry[i], + ft->cached_dentry_hits[i]); +- fprintf(stderr, " dentry_cache: %lx\n", (ulong)ft->dentry_cache); +- fprintf(stderr, "dentry_cache_index: %d\n", ft->dentry_cache_index); +- fprintf(stderr, "dentry_cache_fills: %ld\n", ft->dentry_cache_fills); ++ fprintf(fp, " dentry_cache: %lx\n", (ulong)ft->dentry_cache); ++ fprintf(fp, "dentry_cache_index: %d\n", ft->dentry_cache_index); ++ fprintf(fp, "dentry_cache_fills: %ld\n", ft->dentry_cache_fills); + + for (i = 0; i < INODE_CACHE; i++) +- fprintf(stderr, " cached_inode[%2d]: %lx (%ld)\n", ++ fprintf(fp, " cached_inode[%2d]: %lx (%ld)\n", + i, ft->cached_inode[i], + ft->cached_inode_hits[i]); +- fprintf(stderr, " inode_cache: %lx\n", (ulong)ft->inode_cache); +- fprintf(stderr, " inode_cache_index: %d\n", ft->inode_cache_index); +- fprintf(stderr, " inode_cache_fills: %ld\n", ft->inode_cache_fills); ++ fprintf(fp, " inode_cache: %lx\n", (ulong)ft->inode_cache); ++ fprintf(fp, " inode_cache_index: %d\n", ft->inode_cache_index); ++ fprintf(fp, " inode_cache_fills: %ld\n", ft->inode_cache_fills); + + show_hit_rates: + if (ft->file_cache_fills) { + for (i = fhits = 0; i < FILE_CACHE; i++) + fhits += ft->cached_file_hits[i]; + +- fprintf(stderr, " file hit rate: %2ld%% (%ld of %ld)\n", ++ fprintf(fp, " file hit rate: %2ld%% (%ld of %ld)\n", + (fhits * 100)/ft->file_cache_fills, + fhits, ft->file_cache_fills); + } +@@ -1835,7 +1910,7 @@ + for (i = dhits = 0; i < DENTRY_CACHE; i++) + dhits += ft->cached_dentry_hits[i]; + +- fprintf(stderr, " dentry hit rate: %2ld%% (%ld of %ld)\n", ++ fprintf(fp, " dentry hit rate: %2ld%% (%ld of %ld)\n", + (dhits * 100)/ft->dentry_cache_fills, + dhits, ft->dentry_cache_fills); + } +@@ -1844,7 +1919,7 @@ + for (i = ihits = 0; i < INODE_CACHE; i++) + ihits += ft->cached_inode_hits[i]; + +- fprintf(stderr, " inode hit rate: %2ld%% (%ld of %ld)\n", ++ fprintf(fp, " inode hit rate: %2ld%% (%ld of %ld)\n", + (ihits * 100)/ft->inode_cache_fills, + ihits, ft->inode_cache_fills); + } +@@ -1998,8 +2073,9 @@ + open_files_dump(ulong task, int flags, struct reference *ref) + { + struct task_context *tc; +- ulong files_struct_addr; +- char *files_struct_buf; ++ ulong files_struct_addr; ++ ulong fdtable_addr = 0; ++ char *files_struct_buf, *fdtable_buf = NULL; + ulong fs_struct_addr; + char *dentry_buf, *fs_struct_buf; + ulong root_dentry, pwd_dentry; +@@ -2012,7 +2088,7 @@ + ulong fd; + ulong file; + ulong value; +- int i, j; ++ int i, j, use_path; + int header_printed = 0; + char root_pathname[BUFSIZE]; + char pwd_pathname[BUFSIZE]; +@@ -2027,6 +2103,8 @@ + BZERO(root_pathname, BUFSIZE); + BZERO(pwd_pathname, BUFSIZE); + files_struct_buf = GETBUF(SIZE(files_struct)); ++ if (VALID_STRUCT(fdtable)) ++ fdtable_buf = GETBUF(SIZE(fdtable)); + fill_task_struct(task); + + sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n", +@@ -2051,7 +2129,12 @@ + readmem(fs_struct_addr, KVADDR, fs_struct_buf, SIZE(fs_struct), + "fs_struct buffer", FAULT_ON_ERROR); + +- root_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_root)); ++ use_path = (MEMBER_TYPE("fs_struct", "root") == TYPE_CODE_STRUCT); ++ if (use_path) ++ root_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_root) + ++ OFFSET(path_dentry)); ++ else ++ root_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_root)); + + if (root_dentry) { + if (VALID_MEMBER(fs_struct_rootmnt)) { +@@ -2059,13 +2142,23 @@ + OFFSET(fs_struct_rootmnt)); + get_pathname(root_dentry, root_pathname, + BUFSIZE, 1, vfsmnt); ++ } else if (use_path) { ++ vfsmnt = ULONG(fs_struct_buf + ++ OFFSET(fs_struct_root) + ++ OFFSET(path_mnt)); ++ get_pathname(root_dentry, root_pathname, ++ BUFSIZE, 1, vfsmnt); + } else { + get_pathname(root_dentry, root_pathname, + BUFSIZE, 1, 0); + } + } + +- pwd_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_pwd)); ++ if (use_path) ++ pwd_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_pwd) + ++ OFFSET(path_dentry)); ++ else ++ pwd_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_pwd)); + + if (pwd_dentry) { + if (VALID_MEMBER(fs_struct_pwdmnt)) { +@@ -2073,6 +2166,13 @@ + OFFSET(fs_struct_pwdmnt)); + get_pathname(pwd_dentry, pwd_pathname, + BUFSIZE, 1, vfsmnt); ++ } else if (use_path) { ++ vfsmnt = ULONG(fs_struct_buf + ++ OFFSET(fs_struct_pwd) + ++ OFFSET(path_mnt)); ++ get_pathname(pwd_dentry, pwd_pathname, ++ BUFSIZE, 1, vfsmnt); ++ + } else { + get_pathname(pwd_dentry, pwd_pathname, + BUFSIZE, 1, 0); +@@ -2107,24 +2207,45 @@ + + files_struct_addr = ULONG(tt->task_struct + OFFSET(task_struct_files)); + +- if (files_struct_addr) { +- readmem(files_struct_addr, KVADDR, files_struct_buf, +- SIZE(files_struct), "files_struct buffer", +- FAULT_ON_ERROR); +- +- max_fdset = INT(files_struct_buf + ++ if (files_struct_addr) { ++ readmem(files_struct_addr, KVADDR, files_struct_buf, ++ SIZE(files_struct), "files_struct buffer", ++ FAULT_ON_ERROR); ++ ++ if (VALID_MEMBER(files_struct_max_fdset)) { ++ max_fdset = INT(files_struct_buf + + OFFSET(files_struct_max_fdset)); + +- max_fds = INT(files_struct_buf + +- OFFSET(files_struct_max_fds)); +- } ++ max_fds = INT(files_struct_buf + ++ OFFSET(files_struct_max_fds)); ++ } ++ } + +- if (!files_struct_addr || max_fdset == 0 || max_fds == 0) { ++ if (VALID_MEMBER(files_struct_fdt)) { ++ fdtable_addr = ULONG(files_struct_buf + OFFSET(files_struct_fdt)); ++ ++ if (fdtable_addr) { ++ readmem(fdtable_addr, KVADDR, fdtable_buf, ++ SIZE(fdtable), "fdtable buffer", FAULT_ON_ERROR); ++ if (VALID_MEMBER(fdtable_max_fdset)) ++ max_fdset = INT(fdtable_buf + ++ OFFSET(fdtable_max_fdset)); ++ else ++ max_fdset = -1; ++ max_fds = INT(fdtable_buf + ++ OFFSET(fdtable_max_fds)); ++ } ++ } ++ ++ if ((VALID_MEMBER(files_struct_fdt) && !fdtable_addr) || ++ !files_struct_addr || max_fdset == 0 || max_fds == 0) { + if (ref) { + if (ref->cmdflags & FILES_REF_FOUND) + fprintf(fp, "\n"); + } else + fprintf(fp, "No open files\n"); ++ if (fdtable_buf) ++ FREEBUF(fdtable_buf); + FREEBUF(files_struct_buf); + return; + } +@@ -2146,8 +2267,12 @@ + } + } + +- open_fds_addr = ULONG(files_struct_buf + +- OFFSET(files_struct_open_fds)); ++ if (VALID_MEMBER(fdtable_open_fds)) ++ open_fds_addr = ULONG(fdtable_buf + ++ OFFSET(fdtable_open_fds)); ++ else ++ open_fds_addr = ULONG(files_struct_buf + ++ OFFSET(files_struct_open_fds)); + + if (open_fds_addr) { + if (VALID_MEMBER(files_struct_open_fds_init) && +@@ -2157,16 +2282,21 @@ + OFFSET(files_struct_open_fds_init), + &open_fds, sizeof(fd_set)); + else +- readmem(open_fds_addr, KVADDR, &open_fds, +- sizeof(fd_set), "files_struct open_fds", ++ readmem(open_fds_addr, KVADDR, &open_fds, ++ sizeof(fd_set), "fdtable open_fds", + FAULT_ON_ERROR); + } + +- fd = ULONG(files_struct_buf + OFFSET(files_struct_fd)); ++ if (VALID_MEMBER(fdtable_fd)) ++ fd = ULONG(fdtable_buf + OFFSET(fdtable_fd)); ++ else ++ fd = ULONG(files_struct_buf + OFFSET(files_struct_fd)); + + if (!open_fds_addr || !fd) { + if (ref && (ref->cmdflags & FILES_REF_FOUND)) + fprintf(fp, "\n"); ++ if (fdtable_buf) ++ FREEBUF(fdtable_buf); + FREEBUF(files_struct_buf); + return; + } +@@ -2175,7 +2305,8 @@ + for (;;) { + unsigned long set; + i = j * __NFDBITS; +- if (i >= max_fdset || i >= max_fds) ++ if (((max_fdset >= 0) && (i >= max_fdset)) || ++ (i >= max_fds)) + break; + set = open_fds.__fds_bits[j++]; + while (set) { +@@ -2187,7 +2318,7 @@ + if (ref && file) { + open_tmpfile(); + if (file_dump(file, 0, 0, i, +- DUMP_FULL_NAME)) { ++ DUMP_FULL_NAME|DUMP_EMPTY_FILE)) { + BZERO(buf4, BUFSIZE); + rewind(pc->tmpfile); + fgets(buf4, BUFSIZE, +@@ -2205,8 +2336,8 @@ + fprintf(fp, files_header); + header_printed = 1; + } +- file_dump(file, 0, 0, i, +- DUMP_FULL_NAME); ++ file_dump(file, 0, 0, i, ++ DUMP_FULL_NAME|DUMP_EMPTY_FILE); + } + } + i++; +@@ -2220,6 +2351,8 @@ + if (ref && (ref->cmdflags & FILES_REF_FOUND)) + fprintf(fp, "\n"); + ++ if (fdtable_buf) ++ FREEBUF(fdtable_buf); + FREEBUF(files_struct_buf); + } + +@@ -2248,6 +2381,8 @@ + } + + for (i = 1; i < 4; i++) { ++ if (STREQ(arglist[i], "?")) ++ continue; + vaddr = htol(arglist[i], FAULT_ON_ERROR, NULL); + if (vaddr == ref->hexval) + return TRUE; +@@ -2401,16 +2536,60 @@ + dentry = ULONG(file_buf + OFFSET(file_f_dentry)); + } + +- if (!dentry) ++ if (!dentry) { ++ if (flags & DUMP_EMPTY_FILE) { ++ fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n", ++ fd, ++ space(MINSPACE), ++ mkstring(buf1, VADDR_PRLEN, ++ CENTER|RJUST|LONG_HEX, ++ MKSTR(file)), ++ space(MINSPACE), ++ mkstring(buf2, VADDR_PRLEN, ++ CENTER|LONG_HEX|ZERO_FILL, ++ MKSTR(dentry)), ++ space(MINSPACE), ++ mkstring(buf3, VADDR_PRLEN, ++ CENTER, ++ "?"), ++ space(MINSPACE), ++ "? ", ++ space(MINSPACE), ++ "?"); ++ return TRUE; ++ } + return FALSE; ++ } + + if (!inode) { + dentry_buf = fill_dentry_cache(dentry); + inode = ULONG(dentry_buf + OFFSET(dentry_d_inode)); + } + +- if (!inode) ++ if (!inode) { ++ if (flags & DUMP_EMPTY_FILE) { ++ fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n", ++ fd, ++ space(MINSPACE), ++ mkstring(buf1, VADDR_PRLEN, ++ CENTER|RJUST|LONG_HEX, ++ MKSTR(file)), ++ space(MINSPACE), ++ mkstring(buf2, VADDR_PRLEN, ++ CENTER|RJUST|LONG_HEX, ++ MKSTR(dentry)), ++ space(MINSPACE), ++ mkstring(buf3, VADDR_PRLEN, ++ CENTER|LONG_HEX|ZERO_FILL, ++ MKSTR(inode)), ++ space(MINSPACE), ++ "? ", ++ space(MINSPACE), ++ "?"); ++ return TRUE; ++ } + return FALSE; ++ } + + inode_buf = fill_inode_cache(inode); + +@@ -2494,6 +2673,20 @@ + } + + /* ++ * Get the vfsmnt associated with a file. ++ */ ++ulong ++file_to_vfsmnt(ulong file) ++{ ++ char *file_buf; ++ ulong vfsmnt; ++ ++ file_buf = fill_file_cache(file); ++ vfsmnt = ULONG(file_buf + OFFSET(file_f_vfsmnt)); ++ return vfsmnt; ++} ++ ++/* + * get_pathname() fills in a pathname string for an ending dentry + * See __d_path() in the kernel for help fixing problems. + */ +@@ -3069,7 +3262,7 @@ + char modname1[BUFSIZE]; + char modname2[BUFSIZE]; + char *name; +- int use_module; ++ int use_module, crashbuiltin; + struct stat stat1, stat2; + + pc->flags |= DEVMEM; +@@ -3077,7 +3270,7 @@ + goto live_report; + + pc->live_memsrc = "/dev/mem"; +- use_module = FALSE; ++ use_module = crashbuiltin = FALSE; + + if (file_exists("/dev/mem", &stat1) && + file_exists(pc->memory_device, &stat2) && +@@ -3114,6 +3307,10 @@ + } + + pclose(pipe); ++ ++ if (!use_module && file_exists("/dev/crash", &stat1) && ++ S_ISCHR(stat1.st_mode)) ++ crashbuiltin = TRUE; + } + + if (use_module) { +@@ -3124,6 +3321,15 @@ + pc->live_memsrc = pc->memory_device; + } + ++ if (crashbuiltin) { ++ pc->flags &= ~DEVMEM; ++ pc->flags |= CRASHBUILTIN; ++ pc->readmem = read_memory_device; ++ pc->writemem = write_memory_device; ++ pc->live_memsrc = pc->memory_device; ++ pc->memory_module = NULL; ++ } ++ + live_report: + if (CRASHDEBUG(1)) + fprintf(fp, "get_live_memory_source: %s\n", pc->live_memsrc); +@@ -3305,10 +3511,11 @@ + /* + * If we're here, the memory driver module is being requested: + * +- * 1. If the module is not already loaded, insmod it. +- * 2. Determine the misc driver minor device number that it was assigned. +- * 3. Create (or verify) the device file. +- * 4. Then just open it. ++ * 1. If /dev/crash is built into the kernel, just open it. ++ * 2. If the module is not already loaded, insmod it. ++ * 3. Determine the misc driver minor device number that it was assigned. ++ * 4. Create (or verify) the device file. ++ * 5. Then just open it. + */ + + static int +@@ -3316,10 +3523,14 @@ + { + dev_t dev; + ++ if (pc->flags & CRASHBUILTIN) ++ goto open_device; ++ + if (!memory_driver_module_loaded(NULL)) { + if (!insmod_memory_driver_module()) + return FALSE; +- } ++ } else ++ pc->flags |= MODPRELOAD; + + if (!get_memory_driver_dev(&dev)) + return FALSE; +@@ -3327,6 +3538,7 @@ + if (!create_memory_device(dev)) + return FALSE; + ++open_device: + if ((pc->mfd = open(pc->memory_device, O_RDONLY)) < 0) { + error(INFO, "%s: open: %s\n", pc->memory_device, + strerror(errno)); +@@ -3378,10 +3590,14 @@ + #define RADIX_TREE_MAP_SHIFT 6 + #define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) + #define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) ++#define RADIX_TREE_TAGS 2 ++#define RADIX_TREE_TAG_LONGS \ ++ ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG) + + struct radix_tree_node { + unsigned int count; + void *slots[RADIX_TREE_MAP_SIZE]; ++ unsigned long tags[RADIX_TREE_TAGS][RADIX_TREE_TAG_LONGS]; + }; + + /* +@@ -3533,16 +3749,15 @@ + radix_tree_lookup(ulong root_rnode, ulong index, int height) + { + unsigned int shift; +- struct radix_tree_node **slot; ++ void *slot; + struct radix_tree_node slotbuf; +- void **kslotp, **uslotp; + + shift = (height-1) * RADIX_TREE_MAP_SHIFT; +- kslotp = (void **)root_rnode; ++ ++ readmem(root_rnode, KVADDR, &slot, sizeof(void *), ++ "radix_tree_root rnode", FAULT_ON_ERROR); + + while (height > 0) { +- readmem((ulong)kslotp, KVADDR, &slot, sizeof(void *), +- "radix_tree_node slot", FAULT_ON_ERROR); + + if (slot == NULL) + return NULL; +@@ -3551,15 +3766,13 @@ + sizeof(struct radix_tree_node), + "radix_tree_node struct", FAULT_ON_ERROR); + +- uslotp = (void **) +- (slotbuf.slots + ((index >> shift) & RADIX_TREE_MAP_MASK)); +- kslotp = *uslotp; +- ++ slot = slotbuf.slots[((index >> shift) & RADIX_TREE_MAP_MASK)]; ++ + shift -= RADIX_TREE_MAP_SHIFT; + height--; + } + +- return (void *) kslotp; ++ return slot; + } + + int +@@ -3575,3 +3788,29 @@ + + return TRUE; + } ++ ++static int ++match_file_string(char *filename, char *string, char *buffer) ++{ ++ int found; ++ char command[BUFSIZE]; ++ FILE *pipe; ++ ++ ++ sprintf(command, "/usr/bin/strings %s", filename); ++ if ((pipe = popen(command, "r")) == NULL) { ++ error(INFO, "%s: %s\n", filename, strerror(errno)); ++ return FALSE; ++ } ++ ++ found = FALSE; ++ while (fgets(buffer, BUFSIZE-1, pipe)) { ++ if (strstr(buffer, string)) { ++ found = TRUE; ++ break; ++ } ++ } ++ pclose(pipe); ++ ++ return found; ++} +--- crash/cmdline.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/cmdline.c 2009-01-26 14:54:24.000000000 -0500 +@@ -1,8 +1,8 @@ + /* cmdline.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 +@@ -34,6 +34,7 @@ + static void wait_for_children(ulong); + #define ZOMBIES_ONLY (1) + #define ALL_CHILDREN (2) ++int shell_command(char *); + + #define READLINE_LIBRARY + +@@ -70,15 +71,45 @@ + * program invocation. + * 4. from a terminal. + * 5. from a pipe, if stdin is a pipe rather than a terminal. ++ * ++ * But first, handle the interruption of an input file caused ++ * by a FATAL error in one of its commands. ++ * + */ +- if (pc->flags & RCHOME_IFILE) ++ if (pc->ifile_in_progress) { ++ switch (pc->ifile_in_progress) ++ { ++ case RCHOME_IFILE: ++ pc->flags |= INIT_IFILE|RCHOME_IFILE; ++ sprintf(pc->command_line, "< %s/.%src", ++ pc->home, pc->program_name); ++ break; ++ case RCLOCAL_IFILE: ++ sprintf(pc->command_line, "< .%src", pc->program_name); ++ pc->flags |= INIT_IFILE|RCLOCAL_IFILE; ++ break; ++ case CMDLINE_IFILE: ++ sprintf(pc->command_line, "< %s", pc->input_file); ++ pc->flags |= INIT_IFILE|CMDLINE_IFILE; ++ break; ++ case RUNTIME_IFILE: ++ sprintf(pc->command_line, "%s", pc->runtime_ifile_cmd); ++ pc->flags |= IFILE_ERROR; ++ break; ++ default: ++ error(FATAL, "invalid input file\n"); ++ } ++ } else if (pc->flags & RCHOME_IFILE) { + sprintf(pc->command_line, "< %s/.%src", + pc->home, pc->program_name); +- else if (pc->flags & RCLOCAL_IFILE) ++ pc->flags |= INIT_IFILE; ++ } else if (pc->flags & RCLOCAL_IFILE) { + sprintf(pc->command_line, "< .%src", pc->program_name); +- else if (pc->flags & CMDLINE_IFILE) ++ pc->flags |= INIT_IFILE; ++ } else if (pc->flags & CMDLINE_IFILE) { + sprintf(pc->command_line, "< %s", pc->input_file); +- else if (pc->flags & TTY) { ++ pc->flags |= INIT_IFILE; ++ } else if (pc->flags & TTY) { + if (!(pc->readline = readline(pc->prompt))) { + args[0] = NULL; + fprintf(fp, "\n"); +@@ -276,6 +307,108 @@ + } + + /* ++ * Pager arguments. ++ */ ++ ++static char *less_argv[5] = { ++ "/usr/bin/less", ++ "-E", ++ "-X", ++ "-Ps -- MORE -- forward\\: , or j backward\\: b or k quit\\: q", ++ NULL ++}; ++ ++static char *more_argv[2] = { ++ "/bin/more", ++ NULL ++}; ++ ++static char **CRASHPAGER_argv = NULL; ++ ++int ++CRASHPAGER_valid(void) ++{ ++ int i, c; ++ char *env, *CRASHPAGER_buf; ++ char *arglist[MAXARGS]; ++ ++ if (CRASHPAGER_argv) ++ return TRUE; ++ ++ if (!(env = getenv("CRASHPAGER"))) ++ return FALSE; ++ ++ if (strstr(env, "|") || strstr(env, "<") || strstr(env, ">")) { ++ error(INFO, ++ "CRASHPAGER ignored: contains invalid character: \"%s\"\n", ++ env); ++ return FALSE; ++ } ++ ++ if ((CRASHPAGER_buf = (char *)malloc(strlen(env)+1)) == NULL) ++ return FALSE; ++ ++ strcpy(CRASHPAGER_buf, env); ++ ++ if (!(c = parse_line(CRASHPAGER_buf, arglist)) || ++ !file_exists(arglist[0], NULL) || access(arglist[0], X_OK) || ++ !(CRASHPAGER_argv = (char **)malloc(sizeof(char *) * (c+1)))) { ++ free(CRASHPAGER_buf); ++ if (strlen(env)) ++ error(INFO, ++ "CRASHPAGER ignored: \"%s\"\n", env); ++ return FALSE; ++ } ++ ++ for (i = 0; i < c; i++) ++ CRASHPAGER_argv[i] = arglist[i]; ++ CRASHPAGER_argv[i] = NULL; ++ ++ return TRUE; ++} ++ ++/* ++ * Set up a command string buffer for error/help output. ++ */ ++char * ++setup_scroll_command(void) ++{ ++ char *buf; ++ long i, len; ++ ++ if (!(pc->flags & SCROLL)) ++ return NULL; ++ ++ switch (pc->scroll_command) ++ { ++ case SCROLL_LESS: ++ buf = GETBUF(strlen(less_argv[0])+1); ++ strcpy(buf, less_argv[0]); ++ break; ++ case SCROLL_MORE: ++ buf = GETBUF(strlen(more_argv[0])+1); ++ strcpy(buf, more_argv[0]); ++ break; ++ case SCROLL_CRASHPAGER: ++ for (i = len = 0; CRASHPAGER_argv[i]; i++) ++ len += strlen(CRASHPAGER_argv[i])+1; ++ ++ buf = GETBUF(len); ++ ++ for (i = 0; CRASHPAGER_argv[i]; i++) { ++ sprintf(&buf[strlen(buf)], "%s%s", ++ i ? " " : "", ++ CRASHPAGER_argv[i]); ++ } ++ break; ++ default: ++ return NULL; ++ } ++ ++ return buf; ++} ++ ++/* + * Parse the command line for pipe or redirect characters: + * + * 1. if a "|" character is found, popen() what comes after it, and +@@ -349,10 +482,8 @@ + if (LASTCHAR(p) == '|') + error(FATAL_RESTART, "pipe to nowhere?\n"); + +- if (pc->redirect & REDIRECT_SHELL_COMMAND) { +- system(p); +- return REDIRECT_SHELL_COMMAND; +- } ++ if (pc->redirect & REDIRECT_SHELL_COMMAND) ++ return shell_command(p); + + if ((pipe = popen(p, "w")) == NULL) { + error(INFO, "cannot open pipe\n"); +@@ -415,6 +546,9 @@ + return REDIRECT_FAILURE; + } + ++ if (pc->flags & IFILE_ERROR) ++ append = TRUE; ++ + if ((ofile = + fopen(p, append ? "a+" : "w+")) == NULL) { + error(INFO, "unable to open %s\n", p); +@@ -464,10 +598,13 @@ + switch (pc->scroll_command) + { + case SCROLL_LESS: +- strcpy(pc->pipe_command, "/usr/bin/less"); ++ strcpy(pc->pipe_command, less_argv[0]); + break; + case SCROLL_MORE: +- strcpy(pc->pipe_command, "/bin/more"); ++ strcpy(pc->pipe_command, more_argv[0]); ++ break; ++ case SCROLL_CRASHPAGER: ++ strcpy(pc->pipe_command, CRASHPAGER_argv[0]); + break; + } + +@@ -691,16 +828,13 @@ + void + cmdline_init(void) + { +- int fd; ++ int fd = 0; + + /* + * Stash a copy of the original termios setup. + * Build a raw version for quick use for each command entry. + */ +- if (isatty(fileno(stdin))) { +- if ((fd = open("/dev/tty", O_RDONLY)) < 0) +- error(FATAL, "/dev/tty: %s\n", strerror(errno)); +- ++ if (isatty(fileno(stdin)) && ((fd = open("/dev/tty", O_RDONLY)) >= 0)) { + if (tcgetattr(fd, &pc->termios_orig) == -1) + error(FATAL, "tcgetattr /dev/tty: %s\n", + strerror(errno)); +@@ -724,8 +858,10 @@ + readline_init(); + } + else { +- fprintf(fp, pc->flags & SILENT ? +- "" : " NOTE: stdin: not a tty\n"); ++ if (fd < 0) ++ error(INFO, "/dev/tty: %s\n", strerror(errno)); ++ if (!(pc->flags & SILENT)) ++ fprintf(fp, "NOTE: stdin: not a tty\n\n"); + fflush(fp); + pc->flags &= ~TTY; + } +@@ -839,13 +975,15 @@ + restore_sanity(void) + { + int fd, waitstatus; ++ struct extension_table *ext; ++ struct command_table_entry *cp; + + if (pc->stdpipe) { + close(fileno(pc->stdpipe)); + pc->stdpipe = NULL; + if (pc->stdpipe_pid && PID_ALIVE(pc->stdpipe_pid)) { + while (!waitpid(pc->stdpipe_pid, &waitstatus, WNOHANG)) +- ; ++ stall(1000); + } + pc->stdpipe_pid = 0; + } +@@ -855,12 +993,16 @@ + console("wait for redirect %d->%d to finish...\n", + pc->pipe_shell_pid, pc->pipe_pid); + if (pc->pipe_pid) +- while (PID_ALIVE(pc->pipe_pid)) ++ while (PID_ALIVE(pc->pipe_pid)) { + waitpid(pc->pipe_pid, &waitstatus, WNOHANG); ++ stall(1000); ++ } + if (pc->pipe_shell_pid) +- while (PID_ALIVE(pc->pipe_shell_pid)) ++ while (PID_ALIVE(pc->pipe_shell_pid)) { + waitpid(pc->pipe_shell_pid, + &waitstatus, WNOHANG); ++ stall(1000); ++ } + pc->pipe_pid = 0; + } + if (pc->ifile_pipe) { +@@ -872,12 +1014,16 @@ + (FROM_INPUT_FILE|REDIRECT_TO_PIPE|REDIRECT_PID_KNOWN))) { + console("wait for redirect %d->%d to finish...\n", + pc->pipe_shell_pid, pc->pipe_pid); +- while (PID_ALIVE(pc->pipe_pid)) ++ while (PID_ALIVE(pc->pipe_pid)) { + waitpid(pc->pipe_pid, &waitstatus, WNOHANG); ++ stall(1000); ++ } + if (pc->pipe_shell_pid) +- while (PID_ALIVE(pc->pipe_shell_pid)) ++ while (PID_ALIVE(pc->pipe_shell_pid)) { + waitpid(pc->pipe_shell_pid, + &waitstatus, WNOHANG); ++ stall(1000); ++ } + if (pc->redirect & (REDIRECT_MULTI_PIPE)) + wait_for_children(ALL_CHILDREN); + } +@@ -918,13 +1064,20 @@ + + wait_for_children(ZOMBIES_ONLY); + +- pc->flags &= ~(RUNTIME_IFILE|_SIGINT_); ++ pc->flags &= ~(INIT_IFILE|RUNTIME_IFILE|IFILE_ERROR|_SIGINT_|PLEASE_WAIT); + pc->sigint_cnt = 0; + pc->redirect = 0; + pc->pipe_command[0] = NULLCHAR; + pc->pipe_pid = 0; + pc->pipe_shell_pid = 0; + pc->sbrk = sbrk(0); ++ if ((pc->curcmd_flags & (UD2A_INSTRUCTION|BAD_INSTRUCTION)) == ++ (UD2A_INSTRUCTION|BAD_INSTRUCTION)) ++ error(WARNING, "A (bad) instruction was noted in last disassembly.\n" ++ " Use \"dis -b [number]\" to set/restore the number of\n" ++ " encoded bytes to skip after a ud2a (BUG) instruction.\n"); ++ pc->curcmd_flags = 0; ++ pc->curcmd_private = 0; + + restore_gdb_sanity(); + +@@ -942,6 +1095,16 @@ + clear_vma_cache(); + clear_active_set(); + ++ /* ++ * Call the cleanup() function of any extension. ++ */ ++ for (ext = extension_table; ext; ext = ext->next) { ++ for (cp = ext->command_table; cp->name; cp++) { ++ if (cp->flags & CLEANUP) ++ (*cp->func)(); ++ } ++ } ++ + if (CRASHDEBUG(4)) { + dump_filesys_table(0); + dump_vma_cache(0); +@@ -961,6 +1124,8 @@ + { + int fd; + ++ pc->flags &= ~IFILE_ERROR; ++ + if (pc->ifile_pipe) { + close(fileno(pc->ifile_pipe)); + pc->ifile_pipe = NULL; +@@ -1076,7 +1241,6 @@ + } else + this = 0; + +- + if (pc->flags & RUNTIME_IFILE) { + error(INFO, "embedded input files not allowed!\n"); + return; +@@ -1111,6 +1275,30 @@ + pc->flags |= RUNTIME_IFILE; + incoming_fp = fp; + ++ /* ++ * Handle runtime commands that use input files. ++ */ ++ if ((pc->ifile_in_progress = this) == 0) { ++ if (!pc->runtime_ifile_cmd) { ++ if (!(pc->runtime_ifile_cmd = (char *)malloc(BUFSIZE))) { ++ error(INFO, ++ "cannot malloc input file command line buffer\n"); ++ return; ++ } ++ BZERO(pc->runtime_ifile_cmd, BUFSIZE); ++ } ++ if (!strlen(pc->runtime_ifile_cmd)) ++ strcpy(pc->runtime_ifile_cmd, pc->orig_line); ++ pc->ifile_in_progress = RUNTIME_IFILE; ++ } ++ ++ /* ++ * If there's an offset, then there was a FATAL error caused ++ * by the last command executed from the input file. ++ */ ++ if (pc->ifile_offset) ++ fseek(pc->ifile, (long)pc->ifile_offset, SEEK_SET); ++ + while (fgets(buf, BUFSIZE-1, pc->ifile)) { + /* + * Restore normal environment. +@@ -1120,6 +1308,8 @@ + BZERO(pc->command_line, BUFSIZE); + BZERO(pc->orig_line, BUFSIZE); + ++ pc->ifile_offset = ftell(pc->ifile); ++ + if (STRNEQ(buf, "#") || STREQ(buf, "\n")) + continue; + +@@ -1168,6 +1358,10 @@ + fclose(pc->ifile); + pc->ifile = NULL; + pc->flags &= ~RUNTIME_IFILE; ++ pc->ifile_offset = 0; ++ if (pc->runtime_ifile_cmd) ++ BZERO(pc->runtime_ifile_cmd, BUFSIZE); ++ pc->ifile_in_progress = 0; + } + + /* +@@ -1706,15 +1900,20 @@ + error(FATAL, + "scrolling must be turned off when repeating an input file\n"); + ++ pc->curcmd_flags |= REPEAT; ++ + while (TRUE) { + optind = 0; +-console("exec_command...\n"); ++ + exec_command(); + free_all_bufs(); + + if (received_SIGINT() || !output_open()) + break; + ++ if (!(pc->curcmd_flags & REPEAT)) ++ break; ++ + if (delay) + sleep(delay); + +@@ -1829,19 +2028,6 @@ + * Set up the standard output pipe using whichever was selected during init. + */ + +-static char *less_argv[5] = { +- "/usr/bin/less", +- "-E", +- "-X", +- "-Ps -- MORE -- forward\\: , or j backward\\: b or k quit\\: q", +- NULL +-}; +- +-static char *more_argv[2] = { +- "/bin/more", +- NULL +-}; +- + static int + setup_stdpipe(void) + { +@@ -1877,6 +2063,9 @@ + case SCROLL_MORE: + strcpy(pc->pipe_command, more_argv[0]); + break; ++ case SCROLL_CRASHPAGER: ++ strcpy(pc->pipe_command, CRASHPAGER_argv[0]); ++ break; + } + + if (CRASHDEBUG(2)) +@@ -1905,10 +2094,16 @@ + path = more_argv[0]; + execv(path, more_argv); + break; ++ ++ case SCROLL_CRASHPAGER: ++ path = CRASHPAGER_argv[0]; ++ execv(path, CRASHPAGER_argv); ++ break; + } + +- perror("child execv failed"); +- return(clean_exit(1)); ++ perror(path); ++ fprintf(stderr, "execv of scroll command failed\n"); ++ exit(1); + } + } + +@@ -1939,5 +2134,38 @@ + fprintf(fp, "wait_for_children: reaped %d\n", pid); + break; + } ++ stall(1000); + } + } ++ ++/* ++ * Run an escaped shell command, redirecting the output to ++ * the current output file. ++ */ ++int ++shell_command(char *cmd) ++{ ++ FILE *pipe; ++ char buf[BUFSIZE]; ++ ++ if ((pipe = popen(cmd, "r")) == NULL) { ++ error(INFO, "cannot open pipe: %s\n", cmd); ++ pc->redirect &= ~REDIRECT_SHELL_COMMAND; ++ pc->redirect |= REDIRECT_FAILURE; ++ return REDIRECT_FAILURE; ++ } ++ ++ while (fgets(buf, BUFSIZE, pipe)) ++ fprintf(fp, buf); ++ pclose(pipe); ++ ++ return REDIRECT_SHELL_COMMAND; ++} ++ ++int minimal_functions(char *name) ++{ ++ return STREQ("log", name) || STREQ("help",name) || \ ++ STREQ("dis", name) || STREQ("q", name) || \ ++ STREQ("sym", name) || STREQ("exit", name)|| \ ++ STREQ("rd", name) || STREQ("eval", name) ; ++} +--- crash/lkcd_common.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_common.c 2007-11-19 10:48:18.000000000 -0500 +@@ -3,8 +3,8 @@ + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. + * Copyright (C) 2002 Silicon Graphics, Inc. + * Copyright (C) 2002 Free Software Foundation, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2007 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2007 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 +@@ -53,6 +53,8 @@ + + struct lkcd_environment lkcd_environment = { 0 }; + struct lkcd_environment *lkcd = &lkcd_environment; ++static int uncompress_errloc; ++static int uncompress_recover(unsigned char *, ulong, unsigned char *, ulong); + + ulonglong + fix_lkcd_address(ulonglong addr) +@@ -62,7 +64,7 @@ + + for (i = 0; i < lkcd->fix_addr_num; i++) { + if ( (addr >=lkcd->fix_addr[i].task) && +- (addr <= lkcd->fix_addr[i].task + STACKSIZE())){ ++ (addr < lkcd->fix_addr[i].task + STACKSIZE())){ + + offset = addr - lkcd->fix_addr[i].task; + addr = lkcd->fix_addr[i].saddr + offset; +@@ -208,6 +210,7 @@ + + case LKCD_DUMP_V8: + case LKCD_DUMP_V9: ++ case LKCD_DUMP_V10: + lkcd->version = LKCD_DUMP_V8; + return TRUE; + +@@ -623,6 +626,10 @@ + { + static int i = 0; + ++ if (pc->flags & SILENT) { ++ return; ++ } ++ + switch (++i%4) { + case 0: + lkcd_print("|\b"); +@@ -667,6 +674,8 @@ + { + uint64_t zone, page; + int ii, ret; ++ int max_zones; ++ struct physmem_zone *zones; + + zone = paddr & lkcd->zone_mask; + +@@ -693,19 +702,21 @@ + lkcd->num_zones++; + } + ++retry: + /* find the zone */ + for (ii=0; ii < lkcd->num_zones; ii++) { + if (lkcd->zones[ii].start == zone) { + if (lkcd->zones[ii].pages[page].offset != 0) { + if (lkcd->zones[ii].pages[page].offset != off) { +- error(INFO, "conflicting page: zone %lld, " ++ if (CRASHDEBUG(1)) ++ error(INFO, "LKCD: conflicting page: zone %lld, " + "page %lld: %lld, %lld != %lld\n", + (unsigned long long)zone, + (unsigned long long)page, + (unsigned long long)paddr, + (unsigned long long)off, + (unsigned long long)lkcd->zones[ii].pages[page].offset); +- abort(); ++ return -1; + } + ret = 0; + } else { +@@ -734,8 +745,20 @@ + ret = 1; + lkcd->num_zones++; + } else { +- lkcd_print("fixme, need to add more zones (ZONE_ALLOC)\n"); +- exit(1); ++ /* need to expand zone */ ++ max_zones = lkcd->max_zones * 2; ++ zones = malloc(max_zones * sizeof(struct physmem_zone)); ++ if (!zones) { ++ return -1; /* This should be fatal */ ++ } ++ BZERO(zones, max_zones * sizeof(struct physmem_zone)); ++ memcpy(zones, lkcd->zones, ++ lkcd->max_zones * sizeof(struct physmem_zone)); ++ free(lkcd->zones); ++ ++ lkcd->zones = zones; ++ lkcd->max_zones = max_zones; ++ goto retry; + } + } + +@@ -765,11 +788,32 @@ + } + + ++#ifdef IA64 ++ ++int ++lkcd_get_kernel_start(ulong *addr) ++{ ++ if (!addr) ++ return 0; ++ ++ switch (lkcd->version) ++ { ++ case LKCD_DUMP_V8: ++ case LKCD_DUMP_V9: ++ return lkcd_get_kernel_start_v8(addr); ++ ++ default: ++ return 0; ++ } ++} ++ ++#endif ++ + + int + lkcd_lseek(physaddr_t paddr) + { +- long i; ++ long i = 0; + int err; + int eof; + void *dp; +@@ -814,7 +858,7 @@ + lseek(lkcd->fd, lkcd->page_offset_max, SEEK_SET); + eof = FALSE; + while (!eof) { +- if( (i%2048) == 0) { ++ if( (i++%2048) == 0) { + lkcd_speedo(); + } + +@@ -1164,40 +1208,103 @@ + return 1; + } + ++/* Returns the bit offset if it's able to correct, or negative if not */ ++static int ++uncompress_recover(unsigned char *dest, ulong destlen, ++ unsigned char *source, ulong sourcelen) ++{ ++ int byte, bit; ++ ulong retlen = destlen; ++ int good_decomp = 0, good_rv = -1; ++ ++ /* Generate all single bit errors */ ++ if (sourcelen > 16384) { ++ lkcd_print("uncompress_recover: sourcelen %ld too long\n", ++ sourcelen); ++ return(-1); ++ } ++ for (byte = 0; byte < sourcelen; byte++) { ++ for (bit = 0; bit < 8; bit++) { ++ source[byte] ^= (1 << bit); ++ ++ if (uncompress(dest, &retlen, source, sourcelen) == Z_OK && ++ retlen == destlen) { ++ good_decomp++; ++ lkcd_print("good for flipping byte %d bit %d\n", ++ byte, bit); ++ good_rv = bit + byte * 8; ++ } ++ ++ /* Put it back */ ++ source[byte] ^= (1 << bit); ++ } ++ } ++ if (good_decomp == 0) { ++ lkcd_print("Could not correct gzip errors.\n"); ++ return -2; ++ } else if (good_decomp > 1) { ++ lkcd_print("Too many valid gzip decompressions: %d.\n", good_decomp); ++ return -3; ++ } else { ++ source[good_rv >> 8] ^= 1 << (good_rv % 8); ++ uncompress(dest, &retlen, source, sourcelen); ++ source[good_rv >> 8] ^= 1 << (good_rv % 8); ++ return good_rv; ++ } ++} ++ ++ + /* + * Uncompress a gzip'd buffer. ++ * ++ * Returns FALSE on error. If set, then ++ * a non-negative value of uncompress_errloc indicates the location of ++ * a single-bit error, and the data may be used. + */ + static int + lkcd_uncompress_gzip(unsigned char *dest, ulong destlen, + unsigned char *source, ulong sourcelen) + { + ulong retlen = destlen; ++ int rc = FALSE; + + switch (uncompress(dest, &retlen, source, sourcelen)) + { + case Z_OK: + if (retlen == destlen) +- return TRUE; ++ rc = TRUE; ++ break; + + lkcd_print("uncompress: returned length not page size: %ld\n", + retlen); +- return FALSE; ++ rc = FALSE; ++ break; + + case Z_MEM_ERROR: + lkcd_print("uncompress: Z_MEM_ERROR (not enough memory)\n"); +- return FALSE; ++ rc = FALSE; ++ break; + + case Z_BUF_ERROR: + lkcd_print("uncompress: " + "Z_BUF_ERROR (not enough room in output buffer)\n"); +- return FALSE; ++ rc = FALSE; ++ break; + + case Z_DATA_ERROR: + lkcd_print("uncompress: Z_DATA_ERROR (input data corrupted)\n"); +- return FALSE; ++ rc = FALSE; ++ break; ++ default: ++ rc = FALSE; ++ break; + } + +- return FALSE; ++ if (rc == FALSE) { ++ uncompress_errloc = ++ uncompress_recover(dest, destlen, source, sourcelen); ++ } ++ return rc; + } + + +@@ -1252,8 +1359,9 @@ + dp_flags = lkcd->get_dp_flags(); + dp_address = lkcd->get_dp_address(); + +- if (dp_flags & LKCD_DUMP_END) ++ if (dp_flags & LKCD_DUMP_END) { + return LKCD_DUMPFILE_END; ++ } + + if ((lkcd->flags & LKCD_VALID) && (page > lkcd->total_pages)) + lkcd->total_pages = page; +@@ -1315,3 +1423,15 @@ + } + } + ++int ++get_lkcd_regs_for_cpu(struct bt_info *bt, ulong *eip, ulong *esp) ++{ ++ switch (lkcd->version) { ++ case LKCD_DUMP_V8: ++ case LKCD_DUMP_V9: ++ return get_lkcd_regs_for_cpu_v8(bt, eip, esp); ++ default: ++ return -1; ++ } ++} ++ +--- crash/extensions.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/extensions.c 2007-09-24 15:49:04.000000000 -0400 +@@ -18,9 +18,6 @@ + #include "defs.h" + #include + +-static void load_extension(char *); +-static void unload_extension(char *); +- + #define DUMP_EXTENSIONS (0) + #define LOAD_EXTENSION (1) + #define UNLOAD_EXTENSION (2) +@@ -110,6 +107,7 @@ + void + dump_extension_table(int verbose) + { ++ int i; + struct extension_table *ext; + struct command_table_entry *cp; + char buf[BUFSIZE]; +@@ -120,23 +118,37 @@ + + if (verbose) { + for (ext = extension_table; ext; ext = ext->next) { +- fprintf(fp, " filename: %s\n", ext->filename); +- fprintf(fp, " handle: %lx\n", (ulong)ext->handle); +- fprintf(fp, "command_table: %lx (", +- (ulong)ext->command_table); +- for (others = 0, cp = ext->command_table; cp->name;cp++) +- fprintf(fp, "%s%s%s", others++ ? " " : "", +- cp->name, cp->help_data ? "*" : ""); +- fprintf(fp, ")\n"); +- fprintf(fp, " flags: %lx (", ext->flags); ++ fprintf(fp, " filename: %s\n", ext->filename); ++ fprintf(fp, " handle: %lx\n", (ulong)ext->handle); ++ ++ ++ fprintf(fp, " flags: %lx (", ext->flags); + others = 0; + if (ext->flags & REGISTERED) + fprintf(fp, "%sREGISTERED", others++ ? + "|" : ""); + fprintf(fp, ")\n"); +- fprintf(fp, " next: %lx\n", (ulong)ext->next); +- fprintf(fp, " prev: %lx\n%s", +- (ulong)ext->prev, ext->next ? "\n" : ""); ++ fprintf(fp, " next: %lx\n", (ulong)ext->next); ++ fprintf(fp, " prev: %lx\n", (ulong)ext->prev); ++ ++ for (i = 0, cp = ext->command_table; cp->name; cp++, i++) { ++ fprintf(fp, "command_table[%d]: %lx\n", i, (ulong)cp); ++ fprintf(fp, " name: %s\n", cp->name); ++ fprintf(fp, " func: %lx\n", (ulong)cp->func); ++ fprintf(fp, " help_data: %lx\n", (ulong)cp->help_data); ++ fprintf(fp, " flags: %lx (", cp->flags); ++ others = 0; ++ if (cp->flags & CLEANUP) ++ fprintf(fp, "%sCLEANUP", others++ ? "|" : ""); ++ if (cp->flags & REFRESH_TASK_TABLE) ++ fprintf(fp, "%sREFRESH_TASK_TABLE", others++ ? "|" : ""); ++ if (cp->flags & HIDDEN_COMMAND) ++ fprintf(fp, "%sHIDDEN_COMMAND", others++ ? "|" : ""); ++ fprintf(fp, ")\n"); ++ } ++ ++ if (ext->next) ++ fprintf(fp, "\n"); + } + return; + } +@@ -171,7 +183,7 @@ + /* + * Load an extension library. + */ +-static void ++void + load_extension(char *lib) + { + struct extension_table *ext; +@@ -208,7 +220,7 @@ + * _init() function before dlopen() returns below. + */ + pc->curext = ext; +- ext->handle = dlopen(ext->filename, RTLD_NOW); ++ ext->handle = dlopen(ext->filename, RTLD_NOW|RTLD_GLOBAL); + + if (!ext->handle) { + strcpy(buf, dlerror()); +@@ -252,7 +264,7 @@ + /* + * Unload all, or as specified, extension libraries. + */ +-static void ++void + unload_extension(char *lib) + { + struct extension_table *ext; +@@ -342,4 +354,23 @@ + pc->curext->flags |= REGISTERED; /* Mark of approval */ + } + ++/* ++ * Hooks for sial. ++ */ ++unsigned long ++get_curtask(void) ++{ ++ return CURRENT_TASK(); ++} ++ ++char * ++crash_global_cmd(void) ++{ ++ return pc->curcmd; ++} + ++struct command_table_entry * ++crash_cmd_table(void) ++{ ++ return pc->cmd_table; ++} +--- crash/s390.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/s390.c 2009-01-15 14:07:23.000000000 -0500 +@@ -1,9 +1,9 @@ + /* s390.c - core analysis suite + * + * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. +- * Copyright (C) 2005 Michael Holzheu, IBM Corporation ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2005, 2006 Michael Holzheu, IBM Corporation + * + * 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 +@@ -21,17 +21,6 @@ + #define S390_WORD_SIZE 4 + #define S390_ADDR_MASK 0x7fffffff + +-#define S390_PAGE_SHIFT 12 +-#define S390_PAGE_SIZE (1UL << S390_PAGE_SHIFT) +-#define S390_PAGE_MASK (~(S390_PAGE_SIZE-1)) +- +-#define S390_PGDIR_SHIFT 20 +-#define S390_PGDIR_SIZE (1UL << S390_PGDIR_SHIFT) +-#define S390_PGDIR_MASK (~(S390_PGDIR_SIZE-1)) +- +-#define S390_PTRS_PER_PGD 2048 +-#define S390_PTRS_PER_PTE 256 +- + #define S390_PMD_BASE_MASK (~((1UL<<6)-1)) + #define S390_PT_BASE_MASK S390_PMD_BASE_MASK + #define S390_PAGE_BASE_MASK (~((1UL<<12)-1)) +@@ -44,26 +33,10 @@ + #define S390_PAGE_INVALID 0x400 /* HW invalid */ + #define S390_PAGE_INVALID_MASK 0x601ULL /* for linux 2.6 */ + #define S390_PAGE_INVALID_NONE 0x401ULL /* for linux 2.6 */ +-#define S390_PAGE_TABLE_LEN 0xf /* only full page-tables */ +-#define S390_PAGE_TABLE_INV 0x20 /* invalid page-table */ + + #define S390_PTE_INVALID_MASK 0x80000900 + #define S390_PTE_INVALID(x) ((x) & S390_PTE_INVALID_MASK) + +-#define S390_PMD_INVALID_MASK 0x80000000 +-#define S390_PMD_INVALID(x) ((x) & S390_PMD_INVALID_MASK) +- +-/* pgd/pmd/pte query macros */ +-#define s390_pmd_none(x) ((x) & S390_PAGE_TABLE_INV) +-#define s390_pmd_bad(x) (((x) & (~S390_PMD_BASE_MASK & \ +- ~S390_PAGE_TABLE_INV)) != \ +- S390_PAGE_TABLE_LEN) +- +-#define s390_pte_none(x) (((x) & (S390_PAGE_INVALID | S390_RO_S390 | \ +- S390_PAGE_PRESENT)) == \ +- S390_PAGE_INVALID) +- +- + #define ASYNC_STACK_SIZE STACKSIZE() // can be 4096 or 8192 + #define KERNEL_STACK_SIZE STACKSIZE() // can be 4096 or 8192 + +@@ -73,8 +46,6 @@ + * declarations of static functions + */ + static void s390_print_lowcore(char*, struct bt_info*,int); +-static unsigned long s390_pgd_offset(unsigned long, unsigned long); +-static unsigned long s390_pte_offset(unsigned long, unsigned long); + static int s390_kvtop(struct task_context *, ulong, physaddr_t *, int); + static int s390_uvtop(struct task_context *, ulong, physaddr_t *, int); + static int s390_vtop(unsigned long, ulong, physaddr_t*, int); +@@ -86,7 +57,6 @@ + static ulong s390_processor_speed(void); + static int s390_eframe_search(struct bt_info *); + static void s390_back_trace_cmd(struct bt_info *); +-static void s390_back_trace(struct gnu_request *, struct bt_info *); + static void s390_dump_irq(int); + static void s390_get_stack_frame(struct bt_info *, ulong *, ulong *); + static int s390_dis_filter(ulong, char *); +@@ -158,7 +128,10 @@ + machdep->nr_irqs = 0; /* TBD */ + machdep->vmalloc_start = s390_vmalloc_start; + machdep->dump_irq = s390_dump_irq; +- machdep->hz = HZ; ++ if (!machdep->hz) ++ machdep->hz = HZ; ++ machdep->section_size_bits = _SECTION_SIZE_BITS; ++ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; + break; + + case POST_INIT: +@@ -178,8 +151,6 @@ + fprintf(fp, " flags: %lx (", machdep->flags); + if (machdep->flags & KSYMS_START) + fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); +- if (machdep->flags & SYSRQ) +- fprintf(fp, "%sSYSRQ", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); +@@ -226,23 +197,12 @@ + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); ++ fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); ++ fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); + fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); + } + + /* +- * Check if address is in the vmalloc area +- */ +-int +-s390_IS_VMALLOC_ADDR(ulong addr) +-{ +- static unsigned long high_memory = 0; +- if(!high_memory){ +- high_memory = s390_vmalloc_start(); +- } +- return (addr > high_memory); +-} +- +-/* + * Check if address is in context's address space + */ + static int +@@ -293,7 +253,7 @@ + /* + * Check if page is mapped + */ +-inline int ++static inline int + s390_pte_present(unsigned long x) + { + if(THIS_KERNEL_VERSION >= LINUX(2,6,0)) { +@@ -307,60 +267,87 @@ + /* + * page table traversal functions + */ +-static unsigned long +-s390_pgd_offset(unsigned long pgd_base, unsigned long vaddr) +-{ +- unsigned long pgd_off, pmd_base; +- +- pgd_off = ((vaddr >> S390_PGDIR_SHIFT) & (S390_PTRS_PER_PGD - 1)) +- * S390_WORD_SIZE; +- readmem(pgd_base + pgd_off, PHYSADDR, &pmd_base,sizeof(long), +- "pgd_base",FAULT_ON_ERROR); +- return pmd_base; +-} + +-unsigned long s390_pte_offset(unsigned long pte_base, unsigned long vaddr) ++/* Segment table traversal function */ ++static ulong _kl_sg_table_deref_s390(ulong vaddr, ulong table, int len) + { +- unsigned pte_off, pte_val; ++ ulong offset, entry; ++ ++ offset = ((vaddr >> 20) & 0x7ffUL) * 4; ++ if (offset >= (len + 1)*64) ++ /* Offset is over the table limit. */ ++ return 0; ++ readmem(table + offset, KVADDR, &entry, sizeof(entry), "entry", ++ FAULT_ON_ERROR); + +- pte_off = ((vaddr >> S390_PAGE_SHIFT) & (S390_PTRS_PER_PTE - 1)) +- * S390_WORD_SIZE; +- readmem(pte_base + pte_off, PHYSADDR, &pte_val, sizeof(long), +- "pte_val",FAULT_ON_ERROR); +- return pte_val; ++ /* ++ * Check if the segment table entry could be read and doesn't have ++ * any of the reserved bits set. ++ */ ++ if (entry & 0x80000000UL) ++ return 0; ++ /* Check if the segment table entry has the invalid bit set. */ ++ if (entry & 0x40UL) ++ return 0; ++ /* Segment table entry is valid and well formed. */ ++ return entry; ++} ++ ++/* Page table traversal function */ ++static ulong _kl_pg_table_deref_s390(ulong vaddr, ulong table, int len) ++{ ++ ulong offset, entry; ++ ++ offset = ((vaddr >> 12) & 0xffUL) * 4; ++ if (offset >= (len + 1)*64) ++ /* Offset is over the table limit. */ ++ return 0; ++ readmem(table + offset, KVADDR, &entry, sizeof(entry), "entry", ++ FAULT_ON_ERROR); ++ /* ++ * Check if the page table entry could be read and doesn't have ++ * any of the reserved bits set. ++ */ ++ if (entry & 0x80000900UL) ++ return 0; ++ /* Check if the page table entry has the invalid bit set. */ ++ if (entry & 0x400UL) ++ return 0; ++ /* Page table entry is valid and well formed. */ ++ return entry; + } + +-/* +- * Generic vtop function for user and kernel addresses +- */ ++/* lookup virtual address in page tables */ + static int +-s390_vtop(unsigned long pgd_base, ulong kvaddr, physaddr_t *paddr, int verbose) ++s390_vtop(unsigned long table, ulong vaddr, physaddr_t *phys_addr, int verbose) + { +- unsigned pte_base, pte_val; ++ ulong entry, paddr; ++ int len; + +- /* get the pgd entry */ +- pte_base = s390_pgd_offset(pgd_base,kvaddr); +- if(S390_PMD_INVALID(pte_base) || +- s390_pmd_bad(pte_base) || +- s390_pmd_none(pte_base)) { +- *paddr = 0; +- return FALSE; +- } +- /* get the pte */ +- pte_base = pte_base & S390_PT_BASE_MASK; +- pte_val = s390_pte_offset(pte_base,kvaddr); +- if(S390_PTE_INVALID(pte_val) || +- s390_pte_none(pte_val)){ +- *paddr = 0; ++ /* ++ * Get the segment table entry. ++ * We assume that the segment table length field in the asce ++ * is set to the maximum value of 127 (which translates to ++ * a segment table with 2048 entries) and that the addressing ++ * mode is 31 bit. ++ */ ++ entry = _kl_sg_table_deref_s390(vaddr, table, 127); ++ if (!entry) + return FALSE; +- } +- if(!s390_pte_present(pte_val)){ +- /* swapped out */ +- *paddr = pte_val; ++ table = entry & 0x7ffffc00UL; ++ len = entry & 0xfUL; ++ ++ /* Get the page table entry */ ++ entry = _kl_pg_table_deref_s390(vaddr, table, len); ++ if (!entry) + return FALSE; +- } +- *paddr = (pte_val & S390_PAGE_BASE_MASK) | +- (kvaddr & (~(S390_PAGE_MASK))); ++ ++ /* Isolate the page origin from the page table entry. */ ++ paddr = entry & 0x7ffff000UL; ++ ++ /* Add the page offset and return the final value. */ ++ *phys_addr = paddr + (vaddr & 0xfffUL); ++ + return TRUE; + } + +@@ -422,6 +409,10 @@ + if (STREQ(name, "Letext") || STREQ(name, "gcc2_compiled.")) + return FALSE; + ++ /* reject L2^B symbols */ ++ if (strstr(name, "L2\002") == name) ++ return FALSE; ++ + /* throw away all symbols containing a '.' */ + for(i = 0; i < strlen(name);i++){ + if(name[i] == '.') +@@ -483,7 +474,7 @@ + return FALSE; + } + fprintf(fp,"PTE PHYSICAL FLAGS\n"); +- fprintf(fp,"%08x %08x",pte, pte & S390_PAGE_BASE_MASK); ++ fprintf(fp,"%08lx %08lx",pte, pte & S390_PAGE_BASE_MASK); + fprintf(fp," ("); + if(pte & S390_PAGE_INVALID) + fprintf(fp,"INVALID "); +@@ -510,7 +501,7 @@ + /* + * returns cpu number of task + */ +-int ++static int + s390_cpu_of_task(unsigned long task) + { + int cpu; +@@ -551,12 +542,13 @@ + return FALSE; + } else { + /* Linux 2.6 */ +- unsigned long runqueue_addr, runqueue_offset, per_cpu_offset; ++ unsigned long runqueue_addr, runqueue_offset; + unsigned long cpu_offset, per_cpu_offset_addr, running_task; +- char runqueue[4096]; ++ char *runqueue; + int cpu; + + cpu = s390_cpu_of_task(task); ++ runqueue = GETBUF(SIZE(runqueue)); + + runqueue_offset=symbol_value("per_cpu__runqueues"); + per_cpu_offset_addr=symbol_value("__per_cpu_offset"); +@@ -564,10 +556,10 @@ + &cpu_offset, sizeof(long),"per_cpu_offset", + FAULT_ON_ERROR); + runqueue_addr=runqueue_offset + cpu_offset; +- readmem(runqueue_addr,KVADDR,&runqueue,sizeof(runqueue), ++ readmem(runqueue_addr,KVADDR,runqueue,SIZE(runqueue), + "runqueue", FAULT_ON_ERROR); +- running_task = *((unsigned long*)&runqueue[MEMBER_OFFSET( +- "runqueue", "curr")]); ++ running_task = ULONG(runqueue + OFFSET(runqueue_curr)); ++ FREEBUF(runqueue); + if(running_task == task) + return TRUE; + else +@@ -700,7 +692,7 @@ + } else if(skip_first_frame){ + skip_first_frame=0; + } else { +- fprintf(fp," #%i [%08x] ",i,backchain); ++ fprintf(fp," #%i [%08lx] ",i,backchain); + fprintf(fp,"%s at %x\n", closest_symbol(r14), r14); + if (bt->flags & BT_LINE_NUMBERS) + s390_dump_line_number(r14); +@@ -716,13 +708,15 @@ + frame_size = stack_base - old_backchain + + KERNEL_STACK_SIZE; + } else { +- frame_size = backchain - old_backchain; ++ frame_size = MIN((backchain - old_backchain), ++ (stack_base - old_backchain + ++ KERNEL_STACK_SIZE)); + } + for(j=0; j< frame_size; j+=4){ + if(j % 16 == 0){ +- fprintf(fp,"\n%08x: ",old_backchain+j); ++ fprintf(fp,"\n%08lx: ",old_backchain+j); + } +- fprintf(fp," %08x",ULONG(&stack[old_backchain - ++ fprintf(fp," %08lx",ULONG(&stack[old_backchain - + stack_base + j])); + } + fprintf(fp,"\n\n"); +@@ -771,10 +765,10 @@ + return; + } + fprintf(fp," LOWCORE INFO:\n"); +- fprintf(fp," -psw : %#010x %#010x\n", tmp[0], ++ fprintf(fp," -psw : %#010lx %#010lx\n", tmp[0], + tmp[1]); + if(show_symbols){ +- fprintf(fp," -function : %s at %x\n", ++ fprintf(fp," -function : %s at %lx\n", + closest_symbol(tmp[1] & S390_ADDR_MASK), + tmp[1] & S390_ADDR_MASK); + if (bt->flags & BT_LINE_NUMBERS) +@@ -783,12 +777,12 @@ + ptr = lc + MEMBER_OFFSET("_lowcore","cpu_timer_save_area"); + tmp[0]=UINT(ptr); + tmp[1]=UINT(ptr + S390_WORD_SIZE); +- fprintf(fp," -cpu timer: %#010x %#010x\n", tmp[0],tmp[1]); ++ fprintf(fp," -cpu timer: %#010lx %#010lx\n", tmp[0],tmp[1]); + + ptr = lc + MEMBER_OFFSET("_lowcore","clock_comp_save_area"); + tmp[0]=UINT(ptr); + tmp[1]=UINT(ptr + S390_WORD_SIZE); +- fprintf(fp," -clock cmp: %#010x %#010x\n", tmp[0], tmp[1]); ++ fprintf(fp," -clock cmp: %#010lx %#010lx\n", tmp[0], tmp[1]); + + fprintf(fp," -general registers:\n"); + ptr = lc + MEMBER_OFFSET("_lowcore","gpregs_save_area"); +@@ -796,25 +790,25 @@ + tmp[1]=ULONG(ptr + S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0],tmp[1],tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0],tmp[1],tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 8 * S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 9 * S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 10* S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 11* S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0],tmp[1],tmp[2],tmp[3]); + tmp[0]=ULONG(ptr + 12* S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 13* S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 14* S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 15* S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + + fprintf(fp," -access registers:\n"); +@@ -823,25 +817,25 @@ + tmp[1]=ULONG(ptr + S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 8 * S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 9 * S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 10* S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 11* S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 12* S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 13* S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 14* S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 15* S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + + fprintf(fp," -control registers:\n"); +@@ -850,26 +844,26 @@ + tmp[1]=ULONG(ptr + S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + + tmp[0]=ULONG(ptr); + tmp[1]=ULONG(ptr + S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 2 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 3 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + tmp[0]=ULONG(ptr + 4 * S390_WORD_SIZE); + tmp[1]=ULONG(ptr + 5 * S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 6 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 7 * S390_WORD_SIZE); +- fprintf(fp," %#010x %#010x %#010x %#010x\n", ++ fprintf(fp," %#010lx %#010lx %#010lx %#010lx\n", + tmp[0], tmp[1], tmp[2], tmp[3]); + + ptr = lc + MEMBER_OFFSET("_lowcore","floating_pt_save_area"); +@@ -878,8 +872,8 @@ + tmp[1]=ULONG(ptr + 2 * S390_WORD_SIZE); + tmp[2]=ULONG(ptr + 4 * S390_WORD_SIZE); + tmp[3]=ULONG(ptr + 6 * S390_WORD_SIZE); +- fprintf(fp," %#018llx %#018llx\n", tmp[0], tmp[1]); +- fprintf(fp," %#018llx %#018llx\n", tmp[2], tmp[3]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[0], tmp[1]); ++ fprintf(fp," %#018lx %#018lx\n", tmp[2], tmp[3]); + } + + /* +--- crash/lkcd_vmdump_v1.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_vmdump_v1.h 2008-02-20 12:12:46.000000000 -0500 +@@ -1,8 +1,8 @@ + /* lkcd_vmdump_v1.h - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006 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 +@@ -34,6 +34,7 @@ + #include /* for utsname structure */ + #endif + #ifndef IA64 ++typedef unsigned int u32; + #include /* for pt_regs */ + #endif + +@@ -114,8 +115,12 @@ + + /* the dump registers */ + #ifndef IA64 ++#ifndef S390 ++#ifndef S390X + struct pt_regs dh_regs; + #endif ++#endif ++#endif + + /* the address of the current task */ + struct task_struct *dh_current_task; +--- crash/xendump.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/xendump.h 2007-03-15 09:06:19.000000000 -0400 +@@ -0,0 +1,177 @@ ++/* ++ * xendump.h ++ * ++ * Copyright (C) 2006, 2007 David Anderson ++ * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved. ++ * ++ * This software may be freely redistributed under the terms of the ++ * GNU General Public License. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#include ++#include ++ ++#define XC_SAVE_SIGNATURE "LinuxGuestRecord" ++#define XC_CORE_MAGIC 0xF00FEBED ++#define XC_CORE_MAGIC_HVM 0xF00FEBEE ++ ++/* ++ * From xenctrl.h, but probably not on most host machines. ++ */ ++typedef struct xc_core_header { ++ unsigned int xch_magic; ++ unsigned int xch_nr_vcpus; ++ unsigned int xch_nr_pages; ++ unsigned int xch_ctxt_offset; ++ unsigned int xch_index_offset; ++ unsigned int xch_pages_offset; ++} xc_core_header_t; ++ ++struct pfn_offset_cache { ++ off_t file_offset; ++ ulong pfn; ++ ulong cnt; ++}; ++#define PFN_TO_OFFSET_CACHE_ENTRIES (5000) ++ ++struct elf_index_pfn { ++ ulong index; ++ ulong pfn; ++}; ++#define INDEX_PFN_COUNT (128) ++ ++struct last_batch { ++ ulong index; ++ ulong start; ++ ulong end; ++ ulong accesses; ++ ulong duplicates; ++}; ++ ++struct xendump_data { ++ ulong flags; /* XENDUMP_LOCAL, plus anything else... */ ++ int xfd; ++ int pc_next; ++ uint page_size; ++ FILE *ofp; ++ char *page; ++ ulong accesses; ++ ulong cache_hits; ++ ulong redundant; ++ ulong last_pfn; ++ struct pfn_offset_cache *poc; ++ ++ struct xc_core_data { ++ int p2m_frames; ++ ulong *p2m_frame_index_list; ++ struct xc_core_header header; ++ int elf_class; ++ uint64_t format_version; ++ off_t elf_strtab_offset; ++ off_t shared_info_offset; ++ off_t ia64_mapped_regs_offset; ++ struct elf_index_pfn elf_index_pfn[INDEX_PFN_COUNT]; ++ struct last_batch last_batch; ++ Elf32_Ehdr *elf32; ++ Elf64_Ehdr *elf64; ++ } xc_core; ++ ++ struct xc_save_data { ++ ulong nr_pfns; ++ int vmconfig_size; ++ char *vmconfig_buf; ++ ulong *p2m_frame_list; ++ uint pfns_not; ++ off_t pfns_not_offset; ++ off_t vcpu_ctxt_offset; ++ off_t shared_info_page_offset; ++ off_t *batch_offsets; ++ ulong batch_count; ++ ulong *region_pfn_type; ++ ulong ia64_version; ++ ulong *ia64_page_offsets; ++ } xc_save; ++ ++ ulong panic_pc; ++ ulong panic_sp; ++}; ++ ++#define XC_SAVE (XENDUMP_LOCAL << 1) ++#define XC_CORE_ORIG (XENDUMP_LOCAL << 2) ++#define XC_CORE_P2M_CREATE (XENDUMP_LOCAL << 3) ++#define XC_CORE_PFN_CREATE (XENDUMP_LOCAL << 4) ++#define XC_CORE_NO_P2M (XENDUMP_LOCAL << 5) ++#define XC_SAVE_IA64 (XENDUMP_LOCAL << 6) ++#define XC_CORE_64BIT_HOST (XENDUMP_LOCAL << 7) ++#define XC_CORE_ELF (XENDUMP_LOCAL << 8) ++ ++#define MACHINE_BYTE_ORDER() \ ++ (machine_type("X86") || \ ++ machine_type("X86_64") || \ ++ machine_type("IA64") ? __LITTLE_ENDIAN : __BIG_ENDIAN) ++ ++#define BYTE_SWAP_REQUIRED(endian) (endian != MACHINE_BYTE_ORDER()) ++ ++static inline uint32_t ++swab32(uint32_t x) ++{ ++ return (((x & 0x000000ffU) << 24) | ++ ((x & 0x0000ff00U) << 8) | ++ ((x & 0x00ff0000U) >> 8) | ++ ((x & 0xff000000U) >> 24)); ++} ++ ++#define MFN_NOT_FOUND (-1) ++#define PFN_NOT_FOUND (-1) ++ ++#define INVALID_MFN (~0UL) ++ ++/* ++ * ia64 "xm save" format is completely different than the others. ++ */ ++typedef struct xen_domctl_arch_setup { ++ uint64_t flags; /* XEN_DOMAINSETUP_* */ ++/* #ifdef __ia64__ */ ++ uint64_t bp; /* mpaddr of boot param area */ ++ uint64_t maxmem; /* Highest memory address for MDT. */ ++ uint64_t xsi_va; /* Xen shared_info area virtual address. */ ++ uint32_t hypercall_imm; /* Break imm for Xen hypercalls. */ ++/* #endif */ ++} xen_domctl_arch_setup_t; ++ ++/* ++ * xc_core ELF note, which differs from the standard Elf[32|64]_Nhdr ++ * structure by the additional name field. ++ */ ++struct elfnote { ++ uint32_t namesz; ++ uint32_t descsz; ++ uint32_t type; ++ char name[4]; ++}; ++ ++#define XEN_ELFNOTE_DUMPCORE_NONE 0x2000000 ++#define XEN_ELFNOTE_DUMPCORE_HEADER 0x2000001 ++#define XEN_ELFNOTE_DUMPCORE_XEN_VERSION 0x2000002 ++#define XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION 0x2000003 ++ ++struct xen_dumpcore_elfnote_header_desc { ++ uint64_t xch_magic; ++ uint64_t xch_nr_vcpus; ++ uint64_t xch_nr_pages; ++ uint64_t xch_page_size; ++}; ++ ++#define FORMAT_VERSION_0000000000000001 0x0000000000000001ULL ++ ++struct xen_dumpcore_elfnote_format_version_desc { ++ uint64_t version; ++}; ++ ++struct xen_dumpcore_p2m { ++ uint64_t pfn; ++ uint64_t gmfn; ++}; +--- crash/xen_hyper_command.c.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/xen_hyper_command.c 2008-12-03 12:03:24.000000000 -0500 +@@ -0,0 +1,1857 @@ ++/* ++ * xen_hyper_command.c ++ * ++ * Portions Copyright (C) 2006-2007 Fujitsu Limited ++ * Portions Copyright (C) 2006-2007 VA Linux Systems Japan K.K. ++ * ++ * Authors: Itsuro Oda ++ * Fumihiko Kakuma ++ * ++ * This file is part of Xencrash. ++ * ++ * Xencrash 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 (version 2 of the License). ++ * ++ * Xencrash 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with Xencrash; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "defs.h" ++ ++#ifdef XEN_HYPERVISOR_ARCH ++#include "xen_hyper_defs.h" ++ ++#ifdef X86 ++char *xhregt[] = { ++ "ebx", "ecx", "edx", "esi", "edi", "ebp", "eax", "ds", "es", ++ "fs", "gs", "orig_eax", "eip", "cs", "eflags", "esp", "ss", ++ NULL ++}; ++#endif ++ ++#ifdef X86_64 ++char *xhregt[] = { ++ "r15", "r14", "r13", "r12", "rbp", "rbx", "r11", "r10", "r9", "r8", ++ "rax", "rcx", "rdx", "rsi", "rdi", "orig_rax", "rip", "cs", "eflags", ++ "rsp", "ss", "fs", "gs", "ds", "es", "fs", "gs", ++ NULL ++}; ++#endif ++ ++#ifdef IA64 ++char *xhregt[] = { ++ "aaa", "bbb", ++ NULL ++}; ++#endif ++ ++static void xen_hyper_do_domain(struct xen_hyper_cmd_args *da); ++static void xen_hyper_do_doms(struct xen_hyper_cmd_args *da); ++static void xen_hyper_show_doms(struct xen_hyper_domain_context *dc); ++static void xen_hyper_do_dumpinfo(ulong flag, struct xen_hyper_cmd_args *dia); ++static void xen_hyper_show_dumpinfo(ulong flag, ++ struct xen_hyper_dumpinfo_context *dic); ++static void xen_hyper_do_pcpus(ulong flag, struct xen_hyper_cmd_args *pca); ++static void xen_hyper_show_pcpus(ulong flag, struct xen_hyper_pcpu_context *pcc); ++static void xen_hyper_do_sched(ulong flag, struct xen_hyper_cmd_args *scha); ++static void xen_hyper_show_sched(ulong flag, struct xen_hyper_sched_context *schc); ++static void xen_hyper_do_vcpu(struct xen_hyper_cmd_args *vca); ++static void xen_hyper_do_vcpus(struct xen_hyper_cmd_args *vca); ++static void xen_hyper_show_vcpus(struct xen_hyper_vcpu_context *vcc); ++static char *xen_hyper_domain_to_type(ulong domain, int *type, char *buf, int verbose); ++static char *xen_hyper_domain_context_to_type( ++ struct xen_hyper_domain_context *dc, int *type, char *buf, int verbose); ++static int xen_hyper_str_to_domain_context(char *string, ulong *value, ++ struct xen_hyper_domain_context **dcp); ++static int xen_hyper_str_to_dumpinfo_context(char *string, ulong *value, struct xen_hyper_dumpinfo_context **dicp); ++static int xen_hyper_strvcpu_to_vcpu_context(char *string, ulong *value, ++ struct xen_hyper_vcpu_context **vccp); ++static int ++xen_hyper_strid_to_vcpu_context(char *strdom, char *strvc, ulong *valdom, ++ ulong *valvc, struct xen_hyper_vcpu_context **vccp); ++static int xen_hyper_str_to_pcpu_context(char *string, ulong *value, ++ struct xen_hyper_pcpu_context **pccp); ++ ++/* ++ * Display domain struct. ++ */ ++void ++xen_hyper_cmd_domain(void) ++{ ++ struct xen_hyper_cmd_args da; ++ struct xen_hyper_domain_context *dc; ++ ulong val; ++ int c, cnt, type, bogus; ++ ++ BZERO(&da, sizeof(struct xen_hyper_cmd_args)); ++ while ((c = getopt(argcnt, args, "")) != EOF) { ++ switch(c) ++ { ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ cnt = bogus = 0; ++ while (args[optind]) { ++ if (IS_A_NUMBER(args[optind])) { ++ type = xen_hyper_str_to_domain_context(args[optind], &val, &dc); ++ switch (type) { ++ case XEN_HYPER_STR_DID: ++ case XEN_HYPER_STR_DOMAIN: ++ da.value[cnt] = val; ++ da.type[cnt] = type; ++ da.addr[cnt] = dc->domain; ++ da.context[cnt] = dc; ++ cnt++; ++ break; ++ case XEN_HYPER_STR_INVALID: ++ error(INFO, "invalid domain or id value: %s\n\n", ++ args[optind]); ++ bogus++; ++ } ++ } else { ++ error(FATAL, "invalid address: %s\n", ++ args[optind]); ++ } ++ optind++; ++ } ++ da.cnt = cnt; ++ if (bogus && !cnt) { ++ return; ++ } ++ ++ xen_hyper_do_domain(&da); ++} ++ ++/* ++ * Do the work requested by xen_hyper_cmd_dom(). ++ */ ++static void ++xen_hyper_do_domain(struct xen_hyper_cmd_args *da) ++{ ++ int i; ++ ++ if (da->cnt) { ++ if (da->cnt == 1) { ++ xhdt->last = da->context[0]; ++ } ++ for (i = 0; i < da->cnt; i++) { ++ dump_struct("domain", da->addr[i], 0); ++ } ++ } else { ++ dump_struct("domain", xhdt->last->domain, 0); ++ } ++} ++ ++/* ++ * Display domain status. ++ */ ++void ++xen_hyper_cmd_doms(void) ++{ ++ struct xen_hyper_cmd_args da; ++ struct xen_hyper_domain_context *dc; ++ ulong val; ++ int c, cnt, type, bogus; ++ ++ BZERO(&da, sizeof(struct xen_hyper_cmd_args)); ++ while ((c = getopt(argcnt, args, "")) != EOF) { ++ switch(c) ++ { ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ cnt = bogus = 0; ++ while (args[optind]) { ++ if (IS_A_NUMBER(args[optind])) { ++ type = xen_hyper_str_to_domain_context(args[optind], &val, &dc); ++ switch (type) { ++ case XEN_HYPER_STR_DID: ++ case XEN_HYPER_STR_DOMAIN: ++ da.value[cnt] = val; ++ da.type[cnt] = type; ++ da.addr[cnt] = dc->domain; ++ da.context[cnt] = dc; ++ cnt++; ++ break; ++ case XEN_HYPER_STR_INVALID: ++ error(INFO, "invalid domain or id value: %s\n\n", ++ args[optind]); ++ bogus++; ++ } ++ } else { ++ error(FATAL, "invalid address: %s\n", ++ args[optind]); ++ } ++ optind++; ++ } ++ da.cnt = cnt; ++ if (bogus && !cnt) { ++ return; ++ } ++ ++ xen_hyper_do_doms(&da); ++} ++ ++/* ++ * Do the work requested by xen_hyper_cmd_doms(). ++ */ ++static void ++xen_hyper_do_doms(struct xen_hyper_cmd_args *da) ++{ ++ struct xen_hyper_domain_context *dca; ++ char buf1[XEN_HYPER_CMD_BUFSIZE]; ++ char buf2[XEN_HYPER_CMD_BUFSIZE]; ++ int i; ++ ++ sprintf(buf1, " DID %s ST T ", ++ mkstring(buf2, VADDR_PRLEN, CENTER|RJUST, "DOMAIN")); ++ mkstring(&buf1[strlen(buf1)], INT_PRLEN, CENTER|RJUST, "MAXPAGE"); ++ strncat(buf1, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf1)-1); ++ mkstring(&buf1[strlen(buf1)], INT_PRLEN, CENTER|RJUST, "TOTPAGE"); ++ strncat(buf1, " VCPU ", XEN_HYPER_CMD_BUFSIZE-strlen(buf1)-1); ++ mkstring(&buf1[strlen(buf1)], VADDR_PRLEN, CENTER|RJUST, "SHARED_I"); ++ strncat(buf1, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf1)-1); ++ mkstring(&buf1[strlen(buf1)], LONG_PRLEN, CENTER|RJUST, "P2M_MFN"); ++ fprintf(fp, "%s\n", buf1); ++ if (da->cnt) { ++ for (i = 0; i < da->cnt; i++) { ++ xen_hyper_show_doms(da->context[i]); ++ } ++ } else { ++ for (i = 0, dca=xhdt->context_array; i < XEN_HYPER_NR_DOMAINS(); ++ i++, dca++) { ++ xen_hyper_show_doms(dca); ++ } ++ } ++} ++ ++static void ++xen_hyper_show_doms(struct xen_hyper_domain_context *dc) ++{ ++ char *act, *crash; ++ uint cpuid; ++ int type, i, j; ++ struct xen_hyper_pcpu_context *pcc; ++#if defined(X86) || defined(X86_64) ++ char *shared_info; ++#elif defined(IA64) ++ char *domain_struct; ++ ulong pgd; ++#endif ++ char buf1[XEN_HYPER_CMD_BUFSIZE]; ++ char buf2[XEN_HYPER_CMD_BUFSIZE]; ++ ++ if (!(dc->domain)) { ++ return; ++ } ++ ++#if defined(X86) || defined(X86_64) ++ shared_info = GETBUF(XEN_HYPER_SIZE(shared_info)); ++ if (dc->shared_info) { ++ if (!readmem(dc->shared_info, KVADDR, shared_info, ++ XEN_HYPER_SIZE(shared_info), "fill_shared_info_struct", ++ ACTIVE() ? (RETURN_ON_ERROR|QUIET) : RETURN_ON_ERROR)) { ++ error(WARNING, "cannot fill shared_info struct.\n"); ++ BZERO(shared_info, XEN_HYPER_SIZE(shared_info)); ++ } ++ } ++#elif defined(IA64) ++ if ((domain_struct = xen_hyper_read_domain(dc->domain)) == NULL) { ++ error(FATAL, "cannot read domain.\n"); ++ } ++#endif ++ act = NULL; ++ for_cpu_indexes(i, cpuid) ++ { ++ pcc = xen_hyper_id_to_pcpu_context(cpuid); ++ for (j = 0; j < dc->vcpu_cnt; j++) { ++ if (pcc->current_vcpu == dc->vcpu[j]) { ++ act = ">"; ++ break; ++ } ++ } ++ if (act) break; ++ } ++ if (act == NULL) act = " "; ++ if (xht->crashing_vcc && dc->domain == xht->crashing_vcc->domain) { ++ crash = "*"; ++ } else { ++ crash = " "; ++ } ++ sprintf(buf1, "%s%s%5d ", act, crash, dc->domain_id); ++ mkstring(&buf1[strlen(buf1)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, (char *)(dc->domain)); ++ strncat(buf1, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf1)-1); ++ sprintf(&buf1[strlen(buf1)], "%s ", ++ xen_hyper_domain_state_string(dc, buf2, !VERBOSE)); ++ sprintf(&buf1[strlen(buf1)], "%s ", ++ xen_hyper_domain_context_to_type(dc, &type, buf2, !VERBOSE)); ++ mkstring(&buf1[strlen(buf1)], INT_PRLEN, CENTER|INT_HEX|RJUST, ++ MKSTR((long)(dc->max_pages))); ++ strncat(buf1, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf1)-1); ++ mkstring(&buf1[strlen(buf1)], INT_PRLEN, CENTER|INT_HEX|RJUST, ++ MKSTR((long)(dc->tot_pages))); ++ sprintf(&buf1[strlen(buf1)], " %3d ", dc->vcpu_cnt); ++ mkstring(&buf1[strlen(buf1)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(dc->shared_info)); ++ strncat(buf1, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf1)-1); ++#if defined(X86) || defined(X86_64) ++ if (dc->shared_info) { ++ mkstring(&buf1[strlen(buf1)], LONG_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(ULONG(shared_info + ++ XEN_HYPER_OFFSET(shared_info_arch) + ++ XEN_HYPER_OFFSET(arch_shared_info_pfn_to_mfn_frame_list_list))) ++ ); ++ } else { ++ mkstring(&buf1[strlen(buf1)], LONG_PRLEN, CENTER|RJUST, "----"); ++ } ++ FREEBUF(shared_info); ++#elif defined(IA64) ++ pgd = ULONG(domain_struct + XEN_HYPER_OFFSET(domain_arch) + ++ XEN_HYPER_OFFSET(arch_domain_mm) + ++ XEN_HYPER_OFFSET(mm_struct_pgd)); ++ if (pgd) { ++ mkstring(&buf1[strlen(buf1)], LONG_PRLEN, ++ CENTER|LONG_HEX|RJUST, ++ MKSTR((pgd - DIRECTMAP_VIRT_START) >> machdep->pageshift)); ++ } else { ++ mkstring(&buf1[strlen(buf1)], LONG_PRLEN, CENTER|RJUST, "----"); ++ } ++#endif ++ ++ fprintf(fp, "%s\n", buf1); ++} ++ ++/* ++ * Display ELF Notes information. ++ */ ++void ++xen_hyper_cmd_dumpinfo(void) ++{ ++ struct xen_hyper_cmd_args dia; ++ ulong flag; ++ ulong val; ++ struct xen_hyper_dumpinfo_context *dic; ++ int c, cnt, type, bogus; ++ ++ BZERO(&dia, sizeof(struct xen_hyper_cmd_args)); ++ flag= 0; ++ while ((c = getopt(argcnt, args, "rt")) != EOF) { ++ switch(c) ++ { ++ case 't': ++ flag |= XEN_HYPER_DUMPINFO_TIME; ++ break; ++ case 'r': ++ flag |= XEN_HYPER_DUMPINFO_REGS; ++ break; ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ cnt = bogus = 0; ++ while (args[optind]) { ++ if (IS_A_NUMBER(args[optind])) { ++ type = xen_hyper_str_to_dumpinfo_context(args[optind], &val, &dic); ++ switch (type) ++ { ++ case XEN_HYPER_STR_PCID: ++ case XEN_HYPER_STR_ADDR: ++ dia.value[cnt] = val; ++ dia.type[cnt] = type; ++ dia.context[cnt] = dic; ++ cnt++; ++ break; ++ ++ case XEN_HYPER_STR_INVALID: ++ error(INFO, "invalid note address or id " ++ "value: %s\n\n", args[optind]); ++ bogus++; ++ break; ++ } ++ } else { ++ error(INFO, "invalid note address or id " ++ "value: %s\n\n", args[optind]); ++ } ++ optind++; ++ } ++ dia.cnt = cnt; ++ if (!cnt && bogus) { ++ return; ++ } ++ ++ xen_hyper_do_dumpinfo(flag, &dia); ++} ++ ++/* ++ * Do the work requested by xen_hyper_cmd_dumpinfo(). ++ */ ++static void ++xen_hyper_do_dumpinfo(ulong flag, struct xen_hyper_cmd_args *dia) ++{ ++ struct xen_hyper_dumpinfo_context *dic; ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ int i, cnt; ++ ++ if (dia->cnt) { ++ cnt = dia->cnt; ++ } else { ++ cnt = XEN_HYPER_NR_PCPUS(); ++ } ++ for (i = 0; i < cnt; i++) { ++ if (i == 0 || flag & XEN_HYPER_DUMPINFO_REGS || ++ flag & XEN_HYPER_DUMPINFO_TIME) { ++ if (i) { ++ fprintf(fp, "\n"); ++ } ++ sprintf(buf, " PCID "); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "ENOTE"); ++// sprintf(&buf[strlen(buf)], " PID PPID PGRP SID"); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "CORE"); ++ if (xhdit->note_ver >= XEN_HYPER_ELF_NOTE_V2) { ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "XEN_CORE"); ++ } ++ if (xhdit->note_ver >= XEN_HYPER_ELF_NOTE_V3) { ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "XEN_INFO"); ++ } ++ fprintf(fp, "%s\n", buf); ++ } ++ if (dia->cnt) { ++ dic = dia->context[i]; ++ } else { ++ dic = xen_hyper_id_to_dumpinfo_context(xht->cpu_idxs[i]); ++ } ++ xen_hyper_show_dumpinfo(flag, dic); ++ } ++} ++ ++static void ++xen_hyper_show_dumpinfo(ulong flag, struct xen_hyper_dumpinfo_context *dic) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ char *note_buf; ++ ulong addr; ++ ulong *regs; ++ long tv_sec, tv_usec; ++ int i, regcnt; ++ ++ if (!dic || !dic->note) { ++ return; ++ } ++ ++ note_buf = dic->ELF_Prstatus_ptr; ++ sprintf(buf, "%5d ", dic->pcpu_id); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(dic->note)); ++ ++#if 0 ++ pid = INT(note_buf + XEN_HYPER_OFFSET(ELF_Prstatus_pr_pid)); ++ sprintf(&buf[strlen(buf)], " %5d ", pid); ++ pid = INT(note_buf + XEN_HYPER_OFFSET(ELF_Prstatus_pr_ppid)); ++ sprintf(&buf[strlen(buf)], "%5d ", pid); ++ pid = INT(note_buf + XEN_HYPER_OFFSET(ELF_Prstatus_pr_pgrp)); ++ sprintf(&buf[strlen(buf)], "%5d ", pid); ++ pid = INT(note_buf + XEN_HYPER_OFFSET(ELF_Prstatus_pr_sid)); ++ sprintf(&buf[strlen(buf)], "%5d", pid); ++#endif ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(dic->note)); ++ if (xhdit->note_ver >= XEN_HYPER_ELF_NOTE_V2) { ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(dic->note + xhdit->core_size)); ++ } ++ if (xhdit->note_ver >= XEN_HYPER_ELF_NOTE_V3) { ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ if (xhdit->xen_info_cpu == dic->pcpu_id) ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(dic->note + xhdit->core_size + xhdit->xen_core_size)); ++ else ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "--"); ++ ++ } ++ ++ fprintf(fp, "%s\n", buf); ++ ++ if (flag & XEN_HYPER_DUMPINFO_TIME) { ++ sprintf(buf, " "); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "tv_sec"); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "tv_usec"); ++ fprintf(fp, "%s\n", buf); ++ ++ addr = (ulong)note_buf + ++ XEN_HYPER_OFFSET(ELF_Prstatus_pr_utime); ++ for (i = 0; i < 4; i++, addr += XEN_HYPER_SIZE(ELF_Timeval)) { ++ switch (i) ++ { ++ case 0: ++ sprintf(buf, " pr_utime "); ++ break; ++ case 1: ++ sprintf(buf, " pr_stime "); ++ break; ++ case 2: ++ sprintf(buf, " pr_cutime "); ++ break; ++ case 3: ++ sprintf(buf, " pr_cstime "); ++ break; ++ } ++ tv_sec = LONG(addr + ++ XEN_HYPER_OFFSET(ELF_Timeval_tv_sec)); ++ tv_usec = LONG(addr + ++ XEN_HYPER_OFFSET(ELF_Timeval_tv_sec) + ++ XEN_HYPER_OFFSET(ELF_Timeval_tv_usec)); ++ mkstring(&buf[strlen(buf)], LONG_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(tv_sec)); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], LONG_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(tv_usec)); ++ fprintf(fp, "%s\n", buf); ++ } ++ } ++ ++ if (flag & XEN_HYPER_DUMPINFO_REGS) { ++ regcnt = XEN_HYPER_SIZE(ELF_Gregset) / sizeof(long); ++ addr = (ulong)note_buf + ++ XEN_HYPER_OFFSET(ELF_Prstatus_pr_reg); ++ regs = (ulong *)addr; ++ fprintf(fp, "Register information(%lx):\n", ++ dic->note + xhdit->core_offset + XEN_HYPER_OFFSET(ELF_Prstatus_pr_reg)); ++ for (i = 0; i < regcnt; i++, regs++) { ++ if (xhregt[i] == NULL) { ++ break; ++ } ++ fprintf(fp, " %s = ", xhregt[i]); ++ fprintf(fp, "0x%s\n", ++ mkstring(buf, LONG_PRLEN, LONG_HEX|LJUST, MKSTR(*regs))); ++ } ++ } ++} ++ ++/* ++ * Dump the Xen conring in chronological order. ++ */ ++void ++xen_hyper_cmd_log(void) ++{ ++ int c; ++ ++ while ((c = getopt(argcnt, args, "")) != EOF) { ++ switch(c) ++ { ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ xen_hyper_dump_log(); ++} ++ ++void ++xen_hyper_dump_log(void) ++{ ++ uint conringc, conringp; ++ uint warp, start, len, idx, i; ++ ulong conring; ++ char *buf; ++ char last; ++ ++ conring = symbol_value("conring"); ++ get_symbol_data("conringc", sizeof(uint), &conringc); ++ get_symbol_data("conringp", sizeof(uint), &conringp); ++ warp = FALSE; ++ if (conringp >= XEN_HYPER_CONRING_SIZE) { ++ if ((start = conringp & (XEN_HYPER_CONRING_SIZE - 1))) { ++ warp = TRUE; ++ } ++ } else { ++ start = 0; ++ } ++ ++ buf = GETBUF(XEN_HYPER_CONRING_SIZE); ++ readmem(conring, KVADDR, buf, XEN_HYPER_CONRING_SIZE, ++ "conring contents", FAULT_ON_ERROR); ++ idx = start; ++ len = XEN_HYPER_CONRING_SIZE; ++ ++wrap_around: ++ for (i = idx; i < len; i++) { ++ if (buf[i]) { ++ fputc(ascii(buf[i]) ? buf[i] : '.', fp); ++ last = buf[i]; ++ } ++ } ++ if (warp) { ++ len = idx; ++ idx = 0; ++ warp = FALSE; ++ goto wrap_around; ++ } ++ if (last != '\n') { ++ fprintf(fp, "\n"); ++ } ++ FREEBUF(buf); ++} ++ ++/* ++ * Display physical cpu information. ++ */ ++void ++xen_hyper_cmd_pcpus(void) ++{ ++ struct xen_hyper_cmd_args pca; ++ struct xen_hyper_pcpu_context *pcc; ++ ulong flag; ++ ulong val; ++ int c, cnt, type, bogus; ++ ++ BZERO(&pca, sizeof(struct xen_hyper_cmd_args)); ++ flag= 0; ++ while ((c = getopt(argcnt, args, "rt")) != EOF) { ++ switch(c) ++ { ++ case 'r': ++ flag |= XEN_HYPER_PCPUS_REGS; ++ break; ++ case 't': ++ flag |= XEN_HYPER_PCPUS_TSS; ++ break; ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ cnt = bogus = 0; ++ while (args[optind]) { ++ if (IS_A_NUMBER(args[optind])) { ++ type = xen_hyper_str_to_pcpu_context(args[optind], &val, &pcc); ++ switch (type) { ++ case XEN_HYPER_STR_PCID: ++ case XEN_HYPER_STR_PCPU: ++ pca.value[cnt] = val; ++ pca.type[cnt] = type; ++ pca.addr[cnt] = pcc->pcpu; ++ pca.context[cnt] = pcc; ++ cnt++; ++ break; ++ case XEN_HYPER_STR_INVALID: ++ error(INFO, "invalid pcpu or id value: %s\n\n", ++ args[optind]); ++ bogus++; ++ } ++ } else { ++ error(FATAL, "invalid address: %s\n", ++ args[optind]); ++ } ++ optind++; ++ } ++ pca.cnt = cnt; ++ if (bogus && !cnt) { ++ return; ++ } ++ ++ xen_hyper_do_pcpus(flag, &pca); ++} ++ ++/* ++ * Do the work requested by xen_hyper_cmd_pcpu(). ++ */ ++static void ++xen_hyper_do_pcpus(ulong flag, struct xen_hyper_cmd_args *pca) ++{ ++ struct xen_hyper_pcpu_context *pcc; ++ uint cpuid; ++ int i; ++ ++ if (pca->cnt) { ++ for (i = 0; i < pca->cnt; i++) { ++ xen_hyper_show_pcpus(flag, pca->context[i]); ++ flag |= XEN_HYPER_PCPUS_1STCALL; ++ } ++ } else { ++ for_cpu_indexes(i, cpuid) ++ { ++ pcc = xen_hyper_id_to_pcpu_context(cpuid); ++ xen_hyper_show_pcpus(flag, pcc); ++ flag |= XEN_HYPER_PCPUS_1STCALL; ++ } ++ } ++} ++ ++static void ++xen_hyper_show_pcpus(ulong flag, struct xen_hyper_pcpu_context *pcc) ++{ ++ char *act = " "; ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ ++ if (!(pcc->pcpu)) { ++ return; ++ } ++ if (XEN_HYPER_CRASHING_CPU() == pcc->processor_id) { ++ act = " *"; ++ } ++ if ((flag & XEN_HYPER_PCPUS_REGS) || (flag & XEN_HYPER_PCPUS_TSS) || ++ !(flag & XEN_HYPER_PCPUS_1STCALL)) { ++ if (((flag & XEN_HYPER_PCPUS_REGS) || (flag & XEN_HYPER_PCPUS_TSS)) && ++ (flag & XEN_HYPER_PCPUS_1STCALL)) { ++ fprintf(fp, "\n"); ++ } ++ sprintf(buf, " PCID "); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "PCPU"); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "CUR-VCPU"); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "TSS"); ++ fprintf(fp, "%s\n", buf); ++ } ++ ++ sprintf(buf, "%s%5d ", act, pcc->processor_id); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, MKSTR(pcc->pcpu)); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(pcc->current_vcpu)); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(pcc->init_tss)); ++ fprintf(fp, "%s\n", buf); ++ if (flag & XEN_HYPER_PCPUS_REGS) { ++ fprintf(fp, "Register information:\n"); ++ dump_struct("cpu_user_regs", pcc->guest_cpu_user_regs, 0); ++ } ++ if (flag & XEN_HYPER_PCPUS_TSS) { ++ fprintf(fp, "init_tss information:\n"); ++ dump_struct("tss_struct", pcc->init_tss, 0); ++ } ++} ++ ++/* ++ * Display schedule info. ++ */ ++void ++xen_hyper_cmd_sched(void) ++{ ++ struct xen_hyper_cmd_args scha; ++ struct xen_hyper_pcpu_context *pcc; ++ ulong flag; ++ ulong val; ++ int c, cnt, type, bogus; ++ ++ BZERO(&scha, sizeof(struct xen_hyper_cmd_args)); ++ flag = 0; ++ while ((c = getopt(argcnt, args, "v")) != EOF) { ++ switch(c) ++ { ++ case 'v': ++ flag |= XEN_HYPER_SCHED_VERBOSE; ++ break; ++ ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ cnt = bogus = 0; ++ while (args[optind]) { ++ if (IS_A_NUMBER(args[optind])) { ++ type = xen_hyper_str_to_pcpu_context(args[optind], &val, &pcc); ++ switch (type) { ++ case XEN_HYPER_STR_PCID: ++ scha.value[cnt] = val; ++ scha.type[cnt] = type; ++ scha.context[cnt] = &xhscht->sched_context_array[val]; ++ cnt++; ++ break; ++ case XEN_HYPER_STR_PCPU: ++ case XEN_HYPER_STR_INVALID: ++ error(INFO, "invalid pcpu id value: %s\n\n", ++ args[optind]); ++ bogus++; ++ } ++ } else { ++ error(FATAL, "invalid address: %s\n", ++ args[optind]); ++ } ++ optind++; ++ } ++ scha.cnt = cnt; ++ if (bogus && !cnt) { ++ return; ++ } ++ ++ xen_hyper_do_sched(flag, &scha); ++} ++ ++/* ++ * Do the work requested by xen_hyper_cmd_pcpu(). ++ */ ++static void ++xen_hyper_do_sched(ulong flag, struct xen_hyper_cmd_args *scha) ++{ ++ struct xen_hyper_sched_context *schc; ++ uint cpuid; ++ int i; ++ ++ fprintf(fp, "Scheduler name : %s\n\n", xhscht->name); ++ ++ if (scha->cnt) { ++ for (i = 0; i < scha->cnt; i++) { ++ xen_hyper_show_sched(flag, scha->context[i]); ++ flag |= XEN_HYPER_SCHED_1STCALL; ++ } ++ } else { ++ for_cpu_indexes(i, cpuid) ++ { ++ schc = &xhscht->sched_context_array[cpuid]; ++ xen_hyper_show_sched(flag, schc); ++ flag |= XEN_HYPER_SCHED_1STCALL; ++ } ++ } ++} ++ ++static void ++xen_hyper_show_sched(ulong flag, struct xen_hyper_sched_context *schc) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ ++ if (!(schc->schedule_data)) { ++ return; ++ } ++ if ((flag & XEN_HYPER_SCHED_VERBOSE) || ++ !(flag & XEN_HYPER_SCHED_1STCALL)) { ++ if ((flag & XEN_HYPER_SCHED_1STCALL) && ++ (flag & XEN_HYPER_SCHED_VERBOSE)) { ++ fprintf(fp, "\n"); ++ } ++ sprintf(buf, " CPU "); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "SCH-DATA"); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "SCH-PRIV"); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "CUR-VCPU"); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|RJUST, "IDL-VCPU"); ++ if (XEN_HYPER_VALID_MEMBER(schedule_data_tick)) { ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], LONG_PRLEN, CENTER|RJUST, "TICK"); ++ } ++ fprintf(fp, "%s\n", buf); ++ } ++ ++ sprintf(buf, "%5d ", schc->cpu_id); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(schc->schedule_data)); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(schc->sched_priv)); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(schc->curr)); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(schc->idle)); ++ if (XEN_HYPER_VALID_MEMBER(schedule_data_tick)) { ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ mkstring(&buf[strlen(buf)], LONG_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(schc->tick)); ++ } ++ fprintf(fp, "%s\n", buf); ++ if (flag & XEN_HYPER_SCHED_VERBOSE) { ++ ; ++ } ++} ++ ++/* ++ * Display general system info. ++ */ ++void ++xen_hyper_cmd_sys(void) ++{ ++ int c; ++ ulong sflag; ++ ++ sflag = FALSE; ++ ++ while ((c = getopt(argcnt, args, "c")) != EOF) { ++ switch(c) ++ { ++ case 'c': ++ sflag = TRUE; ++ break; ++ ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ if (!args[optind]) { ++ if (sflag) ++ fprintf(fp, "No support argument\n"); ++ /* display config info here. */ ++ else ++ xen_hyper_display_sys_stats(); ++ return; ++ } ++} ++ ++/* ++ * Display system stats at init-time or for the sys command. ++ */ ++void ++xen_hyper_display_sys_stats(void) ++{ ++ struct new_utsname *uts; ++ char buf1[XEN_HYPER_CMD_BUFSIZE]; ++ char buf2[XEN_HYPER_CMD_BUFSIZE]; ++ ulong mhz; ++ int len, flag; ++ ++ uts = &xht->utsname; ++ len = 11; ++ flag = XEN_HYPER_PRI_R; ++ ++ /* ++ * 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: ++ XEN_HYPER_PRI(fp, len, "KERNEL: ", buf1, flag, ++ (buf1, "%s (temporary)\n", pc->namelist)); ++ break; ++ ++ case (NAMELIST_UNLINKED|NAMELIST_SAVED): ++ case NAMELIST_LOCAL: ++ XEN_HYPER_PRI(fp, len, "KERNEL: ", buf1, flag, ++ (buf1, "%s\n", pc->namelist)); ++ break; ++ ++ } ++ } else { ++ if (pc->system_map) { ++ XEN_HYPER_PRI(fp, len, "SYSTEM MAP: ", buf1, flag, ++ (buf1, "%s\n", pc->system_map)); ++ XEN_HYPER_PRI(fp, len, "DEBUG KERNEL: ", buf1, flag, ++ (buf1, "%s\n", pc->namelist)); ++ } else { ++ XEN_HYPER_PRI(fp, len, "KERNEL: ", buf1, flag, ++ (buf1, "%s\n", pc->namelist)); ++ } ++ } ++ ++ if (pc->debuginfo_file) { ++ XEN_HYPER_PRI(fp, len, "DEBUGINFO: ", buf1, flag, ++ (buf1, "%s\n", pc->debuginfo_file)); ++ } else if (pc->namelist_debug) { ++ XEN_HYPER_PRI(fp, len, "DEBUG KERNEL: ", buf1, flag, ++ (buf1, "%s\n", pc->namelist_debug)); ++ } ++ ++ XEN_HYPER_PRI_CONST(fp, len, "DUMPFILE: ", flag); ++ 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 ++ fprintf(fp, "%s", pc->dumpfile); ++ ++ fprintf(fp, "\n"); ++ } ++ ++ XEN_HYPER_PRI(fp, len, "CPUS: ", buf1, flag, ++ (buf1, "%d\n", XEN_HYPER_NR_PCPUS())); ++ XEN_HYPER_PRI(fp, len, "DOMAINS: ", buf1, flag, ++ (buf1, "%d\n", XEN_HYPER_NR_DOMAINS())); ++ /* !!!Display a date here if it can be found. */ ++ XEN_HYPER_PRI(fp, len, "UPTIME: ", buf1, flag, ++ (buf1, "%s\n", (xen_hyper_get_uptime_hyper() ? ++ convert_time(xen_hyper_get_uptime_hyper(), buf2) : "--:--:--"))); ++ /* !!!Display a version here if it can be found. */ ++ XEN_HYPER_PRI_CONST(fp, len, "MACHINE: ", flag); ++ if (strlen(uts->machine)) { ++ fprintf(fp, "%s ", uts->machine); ++ } else { ++ fprintf(fp, "unknown "); ++ } ++ if ((mhz = machdep->processor_speed())) ++ fprintf(fp, "(%ld Mhz)\n", mhz); ++ else ++ fprintf(fp, "(unknown Mhz)\n"); ++ XEN_HYPER_PRI(fp, len, "MEMORY: ", buf1, flag, ++ (buf1, "%s\n", get_memory_size(buf2))); ++ if (XENDUMP_DUMPFILE() && (kt->xen_flags & XEN_SUSPEND)) ++ return; ++} ++ ++/* ++ * Display vcpu struct. ++ */ ++void ++xen_hyper_cmd_vcpu(void) ++{ ++ struct xen_hyper_cmd_args vca; ++ struct xen_hyper_vcpu_context *vcc; ++ ulong flag; ++ ulong valvc, valdom; ++ int c, cnt, type, bogus; ++ ++ BZERO(&vca, sizeof(struct xen_hyper_cmd_args)); ++ flag = 0; ++ while ((c = getopt(argcnt, args, "i")) != EOF) { ++ switch(c) ++ { ++ case 'i': ++ flag |= XEN_HYPER_VCPUS_ID; ++ break; ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ cnt = bogus = 0; ++ while (args[optind]) { ++ if (IS_A_NUMBER(args[optind])) { ++ if (flag & XEN_HYPER_VCPUS_ID) { ++ type = xen_hyper_strid_to_vcpu_context( ++ args[optind], args[optind+1], ++ &valdom, &valvc, &vcc); ++ } else { ++ type = xen_hyper_strvcpu_to_vcpu_context( ++ args[optind], &valvc, &vcc); ++ } ++ switch (type) { ++ case XEN_HYPER_STR_VCID: ++ case XEN_HYPER_STR_VCPU: ++ vca.value[cnt] = valvc; ++ vca.type[cnt] = type; ++ vca.addr[cnt] = vcc->vcpu; ++ vca.context[cnt] = vcc; ++ cnt++; ++ break; ++ case XEN_HYPER_STR_INVALID: ++ error(INFO, "invalid vcpu or id value: %s\n\n", ++ args[optind]); ++ bogus++; ++ } ++ } else { ++ error(FATAL, "invalid address: %s\n", ++ args[optind]); ++ } ++ optind++; ++ if (flag & XEN_HYPER_VCPUS_ID) optind++; ++ } ++ vca.cnt = cnt; ++ if (bogus && !cnt) { ++ return; ++ } ++ ++ xen_hyper_do_vcpu(&vca); ++} ++ ++/* ++ * Do the work requested by xen_hyper_cmd_vcpu(). ++ */ ++static void ++xen_hyper_do_vcpu(struct xen_hyper_cmd_args *vca) ++{ ++ int i; ++ ++ if (vca->cnt) { ++ if (vca->cnt == 1) { ++ xhvct->last = vca->context[0]; ++ } ++ for (i = 0; i < vca->cnt; i++) { ++ dump_struct("vcpu", vca->addr[i], 0); ++ } ++ } else { ++ dump_struct("vcpu", xhvct->last->vcpu, 0); ++ } ++} ++ ++/* ++ * Display vcpu status. ++ */ ++void ++xen_hyper_cmd_vcpus(void) ++{ ++ struct xen_hyper_cmd_args vca; ++ struct xen_hyper_vcpu_context *vcc; ++ ulong flag; ++ ulong valvc, valdom; ++ int c, cnt, type, bogus; ++ ++ BZERO(&vca, sizeof(struct xen_hyper_cmd_args)); ++ flag = 0; ++ while ((c = getopt(argcnt, args, "i")) != EOF) { ++ switch(c) ++ { ++ case 'i': ++ flag |= XEN_HYPER_VCPUS_ID; ++ break; ++ default: ++ argerrs++; ++ break; ++ } ++ } ++ ++ if (argerrs) ++ cmd_usage(pc->curcmd, SYNOPSIS); ++ ++ cnt = bogus = 0; ++ while (args[optind]) { ++ if (IS_A_NUMBER(args[optind])) { ++ if (flag & XEN_HYPER_VCPUS_ID) { ++ type = xen_hyper_strid_to_vcpu_context( ++ args[optind], args[optind+1], ++ &valdom, &valvc, &vcc); ++ } else { ++ type = xen_hyper_strvcpu_to_vcpu_context( ++ args[optind], &valvc, &vcc); ++ } ++ switch (type) { ++ case XEN_HYPER_STR_VCID: ++ case XEN_HYPER_STR_VCPU: ++ vca.value[cnt] = valvc; ++ vca.type[cnt] = type; ++ vca.addr[cnt] = vcc->vcpu; ++ vca.context[cnt] = vcc; ++ cnt++; ++ break; ++ case XEN_HYPER_STR_INVALID: ++ error(INFO, "invalid vcpu or id value: %s\n\n", ++ args[optind]); ++ bogus++; ++ } ++ } else { ++ error(FATAL, "invalid address: %s\n", ++ args[optind]); ++ } ++ optind++; ++ } ++ vca.cnt = cnt; ++ if (bogus && !cnt) { ++ return; ++ } ++ ++ xen_hyper_do_vcpus(&vca); ++} ++ ++/* ++ * Do the work requested by xen_hyper_cmd_vcpus(). ++ */ ++static void ++xen_hyper_do_vcpus(struct xen_hyper_cmd_args *vca) ++{ ++ struct xen_hyper_vcpu_context_array *vcca; ++ struct xen_hyper_vcpu_context *vcc; ++ char buf1[XEN_HYPER_CMD_BUFSIZE]; ++ char buf2[XEN_HYPER_CMD_BUFSIZE]; ++ int i, j; ++ ++ fprintf(fp, " VCID PCID %s ST T DOMID %s\n", ++ mkstring(buf1, VADDR_PRLEN, CENTER|RJUST, "VCPU"), ++ mkstring(buf2, VADDR_PRLEN, CENTER|RJUST, "DOMAIN")); ++ if (vca->cnt) { ++ for (i = 0; i < vca->cnt; i++) { ++ xen_hyper_show_vcpus(vca->context[i]); ++ } ++ } else { ++ for (i = 0, vcca = xhvct->vcpu_context_arrays; ++ i < XEN_HYPER_NR_DOMAINS(); i++, vcca++) { ++ for (j = 0, vcc = vcca->context_array; ++ j < vcca->context_array_valid; j++, vcc++) { ++ xen_hyper_show_vcpus(vcc); ++ } ++ } ++ } ++} ++ ++static void ++xen_hyper_show_vcpus(struct xen_hyper_vcpu_context *vcc) ++{ ++ int type; ++ char *act, *crash; ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ struct xen_hyper_pcpu_context *pcc; ++ domid_t domid; ++ ++ if (!(vcc->vcpu)) { ++ return; ++ } ++ if((pcc = xen_hyper_id_to_pcpu_context(vcc->processor))) { ++ if (pcc->current_vcpu == vcc->vcpu) { ++ act = ">"; ++ } else { ++ act = " "; ++ } ++ } else { ++ act = " "; ++ } ++ if (xht->crashing_vcc && vcc->vcpu == xht->crashing_vcc->vcpu) { ++ crash = "*"; ++ } else { ++ crash = " "; ++ } ++ sprintf(buf, "%s%s%5d %5d ", act, crash, vcc->vcpu_id, vcc->processor); ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(vcc->vcpu)); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ xen_hyper_vcpu_state_string(vcc, &buf[strlen(buf)], !VERBOSE); ++ strncat(buf, " ", XEN_HYPER_CMD_BUFSIZE-strlen(buf)-1); ++ xen_hyper_domain_to_type(vcc->domain, &type, &buf[strlen(buf)], !VERBOSE); ++ if ((domid = xen_hyper_domain_to_id(vcc->domain)) == XEN_HYPER_DOMAIN_ID_INVALID) { ++ sprintf(&buf[strlen(buf)], " ????? "); ++ } else { ++ sprintf(&buf[strlen(buf)], " %5d ", domid); ++ } ++ mkstring(&buf[strlen(buf)], VADDR_PRLEN, CENTER|LONG_HEX|RJUST, ++ MKSTR(vcc->domain)); ++ fprintf(fp, "%s\n", buf); ++} ++ ++ ++ ++/* ++ * Get string for domain status. ++ * - This may need some data in domain struct. ++ */ ++char * ++xen_hyper_domain_state_string(struct xen_hyper_domain_context *dc, ++ char *buf, int verbose) ++{ ++ ulong stat; ++ ++ stat = xen_hyper_domain_state(dc); ++ ++ if (stat == XEN_HYPER_DOMF_ERROR) { ++ sprintf(buf, verbose ? "(unknown)" : "??"); ++ } else if (XEN_HYPER_VALID_MEMBER(domain_domain_flags)) { ++ if (stat & XEN_HYPER_DOMF_shutdown) { ++ sprintf(buf, verbose ? "DOMAIN_SHUTDOWN" : "SF"); ++ } else if (stat & XEN_HYPER_DOMF_dying) { ++ sprintf(buf, verbose ? "DOMAIN_DYING" : "DY"); ++ } else if (stat & XEN_HYPER_DOMF_ctrl_pause) { ++ sprintf(buf, verbose ? "DOMAIN_CTRL_PAUSE" : "CP"); ++ } else if (stat & XEN_HYPER_DOMF_polling) { ++ sprintf(buf, verbose ? "DOMAIN_POLLING" : "PO"); ++ } else if (stat & XEN_HYPER_DOMF_paused) { ++ sprintf(buf, verbose ? "DOMAIN_PAUSED" : "PA"); ++ } else { ++ sprintf(buf, verbose ? "DOMAIN_RUNNING" : "RU"); ++ } ++ } else { ++ if (stat & XEN_HYPER_DOMS_shutdown) { ++ sprintf(buf, verbose ? "DOMAIN_SHUTDOWN" : "SF"); ++ } else if (stat & XEN_HYPER_DOMS_shuttingdown) { ++ sprintf(buf, verbose ? "DOMAIN_SHUTTINGDOWN" : "SH"); ++ } else if (stat & XEN_HYPER_DOMS_dying) { ++ sprintf(buf, verbose ? "DOMAIN_DYING" : "DY"); ++ } else if (stat & XEN_HYPER_DOMS_ctrl_pause) { ++ sprintf(buf, verbose ? "DOMAIN_CTRL_PAUSE" : "CP"); ++ } else if (stat & XEN_HYPER_DOMS_polling) { ++ sprintf(buf, verbose ? "DOMAIN_POLLING" : "PO"); ++ } else { ++ sprintf(buf, verbose ? "DOMAIN_RUNNING" : "RU"); ++ } ++ } ++ ++ return buf; ++} ++ ++/* ++ * Get string for vcpu status. ++ * - This may need some data in vcpu struct. ++ */ ++char * ++xen_hyper_vcpu_state_string(struct xen_hyper_vcpu_context *vcc, ++ char *buf, int verbose) ++{ ++ int stat; ++ ++ stat = xen_hyper_vcpu_state(vcc); ++ ++ if (stat == XEN_HYPER_RUNSTATE_ERROR) { ++ sprintf(buf, verbose ? "(unknown)" : "??"); ++ } else if (stat == XEN_HYPER_RUNSTATE_running || ++ stat == XEN_HYPER_RUNSTATE_runnable) { ++ sprintf(buf, verbose ? "VCPU_RUNNING" : "RU"); ++ } else if (stat == XEN_HYPER_RUNSTATE_blocked) { ++ sprintf(buf, verbose ? "VCPU_BLOCKED" : "BL"); ++ } else if (stat == XEN_HYPER_RUNSTATE_offline) { ++ sprintf(buf, verbose ? "VCPU_OFFLINE" : "OF"); ++ } else { ++ sprintf(buf, verbose ? "(unknown)" : "??"); ++ } ++ ++ return buf; ++} ++ ++/* ++ * Get domain type from domain address. ++ */ ++static char * ++xen_hyper_domain_to_type(ulong domain, int *type, char *buf, int verbose) ++{ ++ struct xen_hyper_domain_context *dc; ++ ++ if ((dc = xen_hyper_domain_to_domain_context(domain)) == NULL) { ++ error(WARNING, "cannot get context from domain address.\n"); ++ return NULL; ++ } ++ return xen_hyper_domain_context_to_type(dc, type, buf, verbose); ++} ++ ++/* ++ * Get domain type from domain context. ++ */ ++static char * ++xen_hyper_domain_context_to_type(struct xen_hyper_domain_context *dc, int *type, ++ char *buf, int verbose) ++{ ++ if (!dc) { ++ *type = XEN_HYPER_DOMAIN_TYPE_INVALID; ++ return NULL; ++ } else if (dc->domain_id == XEN_HYPER_DOMID_IO) { ++ *type = XEN_HYPER_DOMAIN_TYPE_IO; ++ sprintf(buf, verbose ? "dom_io" : "O"); ++ } else if (dc->domain_id == XEN_HYPER_DOMID_XEN) { ++ *type = XEN_HYPER_DOMAIN_TYPE_XEN; ++ sprintf(buf, verbose ? "dom_xen" : "X"); ++ } else if (dc->domain_id == XEN_HYPER_DOMID_IDLE) { ++ *type = XEN_HYPER_DOMAIN_TYPE_IDLE; ++ sprintf(buf, verbose ? "idle domain" : "I"); ++ } else if (dc == xhdt->dom0) { ++ *type = XEN_HYPER_DOMAIN_TYPE_DOM0; ++ sprintf(buf, verbose ? "domain 0" : "0"); ++ } else { ++ *type = XEN_HYPER_DOMAIN_TYPE_GUEST; ++ sprintf(buf, verbose ? "domain U" : "U"); ++ } ++ return buf; ++} ++ ++/* ++ * Check a type for value. And return domain context. ++ */ ++static int ++xen_hyper_str_to_domain_context(char *string, ulong *value, ++ struct xen_hyper_domain_context **dcp) ++{ ++ ulong dvalue, hvalue; ++ int found, type; ++ char *s; ++ struct xen_hyper_domain_context *dc_did, *dc_ddc, *dc_hid, *dc_hdc; ++ ++ if (string == NULL) { ++ error(INFO, "received NULL string\n"); ++ return STR_INVALID; ++ } ++ ++ s = string; ++ dvalue = hvalue = BADADDR; ++ ++ if (decimal(s, 0)) ++ dvalue = dtol(s, RETURN_ON_ERROR, NULL); ++ ++ if (hexadecimal(s, 0)) { ++ if (STRNEQ(s, "0x") || STRNEQ(s, "0X")) ++ s += 2; ++ if (strlen(s) <= MAX_HEXADDR_STRLEN) ++ hvalue = htol(s, RETURN_ON_ERROR, NULL); ++ } ++ ++ found = 0; ++ dc_did = dc_ddc = dc_hid = dc_hdc = NULL; ++ type = XEN_HYPER_STR_INVALID; ++ ++ if (dvalue != BADADDR) { ++ if ((dc_did = xen_hyper_id_to_domain_context(dvalue))) ++ found++; ++ if ((dc_ddc = xen_hyper_domain_to_domain_context(dvalue))) ++ found++; ++ } ++ ++ if ((hvalue != BADADDR) && (dvalue != hvalue)) { ++ if ((dc_hid = xen_hyper_id_to_domain_context(hvalue))) ++ found++; ++ if ((dc_hdc = xen_hyper_domain_to_domain_context(hvalue))) ++ found++; ++ } ++ ++ switch (found) ++ { ++ case 2: ++ if (dc_did && dc_hid) { ++ *dcp = dc_did; ++ *value = dvalue; ++ type = STR_PID; ++ } ++ break; ++ ++ case 1: ++ if (dc_did) { ++ *dcp = dc_did; ++ *value = dvalue; ++ type = XEN_HYPER_STR_DID; ++ } ++ ++ if (dc_ddc) { ++ *dcp = dc_ddc; ++ *value = dvalue; ++ type = XEN_HYPER_STR_DOMAIN; ++ } ++ ++ if (dc_hid) { ++ *dcp = dc_hid; ++ *value = hvalue; ++ type = XEN_HYPER_STR_DID; ++ } ++ ++ if (dc_hdc) { ++ *dcp = dc_hdc; ++ *value = hvalue; ++ type = XEN_HYPER_STR_DOMAIN; ++ } ++ break; ++ } ++ ++ return type; ++} ++ ++ ++ ++/* ++ * Display a vcpu context. ++ */ ++void ++xen_hyper_show_vcpu_context(struct xen_hyper_vcpu_context *vcc) ++{ ++ char buf[XEN_HYPER_CMD_BUFSIZE]; ++ struct xen_hyper_pcpu_context *pcc; ++ struct xen_hyper_domain_context *dc; ++ int len, flag; ++ ++ len = 6; ++ len += pc->flags & RUNTIME ? 0 : 5; ++ flag = XEN_HYPER_PRI_R; ++ ++ if (!(pcc = xen_hyper_id_to_pcpu_context(vcc->processor))) { ++ error(WARNING, "cannot get pcpu context vcpu belongs.\n"); ++ return; ++ } ++ if (!(dc = xen_hyper_domain_to_domain_context(vcc->domain))) { ++ error(WARNING, "cannot get domain context vcpu belongs.\n"); ++ return; ++ } ++ XEN_HYPER_PRI(fp, len, "PCPU-ID: ", buf, flag, ++ (buf, "%d\n", vcc->processor)); ++ XEN_HYPER_PRI(fp, len, "PCPU: ", buf, flag, ++ (buf, "%lx\n", pcc->pcpu)); ++ XEN_HYPER_PRI(fp, len, "VCPU-ID: ", buf, flag, ++ (buf, "%d\n", vcc->vcpu_id)); ++ XEN_HYPER_PRI(fp, len, "VCPU: ", buf, flag, ++ (buf, "%lx ", vcc->vcpu)); ++ fprintf(fp, "(%s)\n", xen_hyper_vcpu_state_string(vcc, buf, VERBOSE)); ++ XEN_HYPER_PRI(fp, len, "DOMAIN-ID: ", buf, flag, ++ (buf, "%d\n", dc->domain_id)); ++ XEN_HYPER_PRI(fp, len, "DOMAIN: ", buf, flag, ++ (buf, "%lx ", vcc->domain)); ++ fprintf(fp, "(%s)\n", xen_hyper_domain_state_string(dc, buf, VERBOSE)); ++ XEN_HYPER_PRI_CONST(fp, len, "STATE: ", flag); ++ if (machdep->flags & HWRESET) { ++ fprintf(fp, "HARDWARE RESET"); ++ } else if (machdep->flags & INIT) { ++ fprintf(fp, "INIT"); ++ } else if (xen_hyper_is_vcpu_crash(vcc)) { ++ fprintf(fp, "CRASH"); ++ } else { ++ fprintf(fp, "ACTIVE"); ++ } ++ ++ fprintf(fp, "\n"); ++} ++ ++/* ++ * Check a type for value. And return dump information context address. ++ */ ++static int ++xen_hyper_str_to_dumpinfo_context(char *string, ulong *value, ++ struct xen_hyper_dumpinfo_context **dicp) ++{ ++ ulong dvalue, hvalue; ++ struct xen_hyper_dumpinfo_context *note_did, *note_hid; ++ struct xen_hyper_dumpinfo_context *note_dad, *note_had; ++ int found, type; ++ char *s; ++ ++ if (string == NULL) { ++ error(INFO, "received NULL string\n"); ++ return STR_INVALID; ++ } ++ ++ s = string; ++ dvalue = hvalue = BADADDR; ++ ++ if (decimal(s, 0)) ++ dvalue = dtol(s, RETURN_ON_ERROR, NULL); ++ if (hexadecimal(s, 0)) { ++ if (STRNEQ(s, "0x") || STRNEQ(s, "0X")) ++ s += 2; ++ if (strlen(s) <= MAX_HEXADDR_STRLEN) ++ hvalue = htol(s, RETURN_ON_ERROR, NULL); ++ } ++ ++ found = 0; ++ note_did = note_hid = note_dad = note_had = 0; ++ type = XEN_HYPER_STR_INVALID; ++ ++ if (dvalue != BADADDR) { ++ if (dvalue > XEN_HYPER_MAX_CPUS()) { ++ note_dad = xen_hyper_note_to_dumpinfo_context(dvalue); ++ } else { ++ note_did = xen_hyper_id_to_dumpinfo_context(dvalue); ++ } ++ found++; ++ } ++ if ((hvalue != BADADDR)) { ++ if (hvalue > XEN_HYPER_MAX_CPUS()) { ++ note_had = xen_hyper_note_to_dumpinfo_context(hvalue); ++ } else { ++ note_hid = xen_hyper_id_to_dumpinfo_context(hvalue); ++ } ++ found++; ++ } ++ ++ switch (found) ++ { ++ case 2: ++ if (note_did && note_hid) { ++ *value = dvalue; ++ *dicp = note_did; ++ type = XEN_HYPER_STR_PCID; ++ } ++ break; ++ case 1: ++ if (note_did) { ++ *value = dvalue; ++ *dicp = note_did; ++ type = XEN_HYPER_STR_PCID; ++ } ++ ++ if (note_hid) { ++ *value = hvalue; ++ *dicp = note_hid; ++ type = XEN_HYPER_STR_PCID; ++ } ++ ++ if (note_dad) { ++ *value = dvalue; ++ *dicp = note_dad; ++ type = XEN_HYPER_STR_ADDR; ++ } ++ ++ if (note_had) { ++ *value = hvalue; ++ *dicp = note_had; ++ type = XEN_HYPER_STR_ADDR; ++ } ++ break; ++ } ++ ++ return type; ++} ++ ++/* ++ * Check a type for value. And return vcpu context. ++ */ ++static int ++xen_hyper_strvcpu_to_vcpu_context(char *string, ulong *value, ++ struct xen_hyper_vcpu_context **vccp) ++{ ++ ulong dvalue, hvalue; ++ int found, type; ++ char *s; ++ struct xen_hyper_vcpu_context *vcc_dvc, *vcc_hvc; ++ ++ if (string == NULL) { ++ error(INFO, "received NULL string\n"); ++ return STR_INVALID; ++ } ++ ++ s = string; ++ dvalue = hvalue = BADADDR; ++ ++ if (decimal(s, 0)) ++ dvalue = dtol(s, RETURN_ON_ERROR, NULL); ++ ++ if (hexadecimal(s, 0)) { ++ if (STRNEQ(s, "0x") || STRNEQ(s, "0X")) ++ s += 2; ++ if (strlen(s) <= MAX_HEXADDR_STRLEN) ++ hvalue = htol(s, RETURN_ON_ERROR, NULL); ++ } ++ ++ found = 0; ++ vcc_dvc = vcc_hvc = NULL; ++ type = XEN_HYPER_STR_INVALID; ++ ++ if (dvalue != BADADDR) { ++ if ((vcc_dvc = xen_hyper_vcpu_to_vcpu_context(dvalue))) ++ found++; ++ } ++ ++ if ((hvalue != BADADDR) && (dvalue != hvalue)) { ++ if ((vcc_hvc = xen_hyper_vcpu_to_vcpu_context(hvalue))) ++ found++; ++ } ++ ++ switch (found) ++ { ++ case 1: ++ if (vcc_dvc) { ++ *vccp = vcc_dvc; ++ *value = dvalue; ++ type = XEN_HYPER_STR_VCPU; ++ } ++ ++ if (vcc_hvc) { ++ *vccp = vcc_hvc; ++ *value = hvalue; ++ type = XEN_HYPER_STR_VCPU; ++ } ++ break; ++ } ++ ++ return type; ++} ++ ++/* ++ * Check a type for id value. And return vcpu context. ++ */ ++static int ++xen_hyper_strid_to_vcpu_context(char *strdom, char *strvc, ulong *valdom, ++ ulong *valvc, struct xen_hyper_vcpu_context **vccp) ++{ ++ ulong dvalue, hvalue; ++ int found, type; ++ char *s; ++ struct xen_hyper_vcpu_context *vcc_did, *vcc_hid; ++ struct xen_hyper_domain_context *dc; ++ ++ if (strdom == NULL || strvc == NULL) { ++ error(INFO, "received NULL string\n"); ++ return STR_INVALID; ++ } ++ ++ if (xen_hyper_str_to_domain_context(strdom, valdom, &dc) == ++ XEN_HYPER_STR_INVALID) { ++ error(INFO, "invalid domain id string.\n"); ++ return STR_INVALID; ++ } ++ ++ s = strvc; ++ dvalue = hvalue = BADADDR; ++ if (decimal(s, 0)) ++ dvalue = dtol(s, RETURN_ON_ERROR, NULL); ++ ++ if (hexadecimal(s, 0)) { ++ if (STRNEQ(s, "0x") || STRNEQ(s, "0X")) ++ s += 2; ++ if (strlen(s) <= MAX_HEXADDR_STRLEN) ++ hvalue = htol(s, RETURN_ON_ERROR, NULL); ++ } ++ ++ found = 0; ++ vcc_did = vcc_hid = NULL; ++ type = XEN_HYPER_STR_INVALID; ++ ++ if (dvalue != BADADDR) { ++ if ((vcc_did = xen_hyper_id_to_vcpu_context(dc->domain, ++ XEN_HYPER_DOMAIN_ID_INVALID, dvalue))) ++ found++; ++ } ++ ++ if ((hvalue != BADADDR) && (dvalue != hvalue)) { ++ if ((vcc_hid = xen_hyper_id_to_vcpu_context(dc->domain, ++ XEN_HYPER_DOMAIN_ID_INVALID, hvalue))) ++ found++; ++ } ++ ++ switch (found) ++ { ++ case 2: ++ if (vcc_did && vcc_hid) { ++ *vccp = vcc_did; ++ *valvc = dvalue; ++ type = XEN_HYPER_STR_VCID; ++ } ++ break; ++ case 1: ++ if (vcc_did) { ++ *vccp = vcc_did; ++ *valvc = dvalue; ++ type = XEN_HYPER_STR_VCID; ++ } ++ ++ if (vcc_hid) { ++ *vccp = vcc_hid; ++ *valvc = hvalue; ++ type = XEN_HYPER_STR_VCID; ++ } ++ break; ++ } ++ ++ return type; ++} ++ ++/* ++ * Check a type for value. And return pcpu context. ++ */ ++static int ++xen_hyper_str_to_pcpu_context(char *string, ulong *value, ++ struct xen_hyper_pcpu_context **pccp) ++{ ++ ulong dvalue, hvalue; ++ int found, type; ++ char *s; ++ struct xen_hyper_pcpu_context *pcc_did, *pcc_dpc, *pcc_hid, *pcc_hpc; ++ ++ if (string == NULL) { ++ error(INFO, "received NULL string\n"); ++ return STR_INVALID; ++ } ++ ++ s = string; ++ dvalue = hvalue = BADADDR; ++ ++ if (decimal(s, 0)) ++ dvalue = dtol(s, RETURN_ON_ERROR, NULL); ++ ++ if (hexadecimal(s, 0)) { ++ if (STRNEQ(s, "0x") || STRNEQ(s, "0X")) ++ s += 2; ++ if (strlen(s) <= MAX_HEXADDR_STRLEN) ++ hvalue = htol(s, RETURN_ON_ERROR, NULL); ++ } ++ ++ found = 0; ++ pcc_did = pcc_dpc = pcc_hid = pcc_hpc = NULL; ++ type = XEN_HYPER_STR_INVALID; ++ ++ if (dvalue != BADADDR) { ++ if ((pcc_did = xen_hyper_id_to_pcpu_context(dvalue))) ++ found++; ++ if ((pcc_dpc = xen_hyper_pcpu_to_pcpu_context(dvalue))) ++ found++; ++ } ++ ++ if ((hvalue != BADADDR) && (dvalue != hvalue)) { ++ if ((pcc_hid = xen_hyper_id_to_pcpu_context(hvalue))) ++ found++; ++ if ((pcc_hpc = xen_hyper_pcpu_to_pcpu_context(hvalue))) ++ found++; ++ } ++ ++ switch (found) ++ { ++ case 2: ++ if (pcc_did && pcc_hid) { ++ *pccp = pcc_did; ++ *value = dvalue; ++ type = STR_PID; ++ } ++ break; ++ ++ case 1: ++ if (pcc_did) { ++ *pccp = pcc_did; ++ *value = dvalue; ++ type = XEN_HYPER_STR_PCID; ++ } ++ ++ if (pcc_dpc) { ++ *pccp = pcc_dpc; ++ *value = dvalue; ++ type = XEN_HYPER_STR_PCPU; ++ } ++ ++ if (pcc_hid) { ++ *pccp = pcc_hid; ++ *value = hvalue; ++ type = XEN_HYPER_STR_PCID; ++ } ++ ++ if (pcc_hpc) { ++ *pccp = pcc_hpc; ++ *value = hvalue; ++ type = XEN_HYPER_STR_PCPU; ++ } ++ break; ++ } ++ ++ return type; ++} ++ ++#endif +--- crash/netdump.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/netdump.h 2009-01-27 11:42:12.000000000 -0500 +@@ -1,7 +1,7 @@ + /* netdump.h + * +- * Copyright (C) 2002, 2003, 2004, 2005 David Anderson +- * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 David Anderson ++ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms of the + * GNU General Public License. +@@ -24,3 +24,100 @@ + + #define NT_TASKSTRUCT 4 + #define NT_DISKDUMP 0x70000001 ++ ++#ifdef NOTDEF ++/* ++ * Note: Based upon the original, abandoned, proposal for ++ * its contents -- keep around for potential future use. ++ */ ++#ifndef NT_KDUMPINFO ++#define NT_KDUMPINFO 7 ++#endif ++ ++#endif /* NOTDEF */ ++ ++struct pt_load_segment { ++ off_t file_offset; ++ physaddr_t phys_start; ++ physaddr_t phys_end; ++ physaddr_t zero_fill; ++}; ++ ++struct vmcore_data { ++ ulong flags; ++ int ndfd; ++ FILE *ofp; ++ uint header_size; ++ char *elf_header; ++ uint num_pt_load_segments; ++ struct pt_load_segment *pt_load_segments; ++ Elf32_Ehdr *elf32; ++ Elf32_Phdr *notes32; ++ Elf32_Phdr *load32; ++ Elf64_Ehdr *elf64; ++ Elf64_Phdr *notes64; ++ Elf64_Phdr *load64; ++ void *nt_prstatus; ++ void *nt_prpsinfo; ++ void *nt_taskstruct; ++ ulong task_struct; ++ uint page_size; ++ ulong switch_stack; ++ uint num_prstatus_notes; ++ void *nt_prstatus_percpu[NR_CPUS]; ++ struct xen_kdump_data *xen_kdump_data; ++ void *vmcoreinfo; ++ uint size_vmcoreinfo; ++}; ++ ++/* ++ * ELF note types for Xen dom0/hypervisor kdumps. ++ * The comments below are from xen/include/public/elfnote.h. ++ */ ++ ++/* ++ * System information exported through crash notes. ++ * ++ * The kexec / kdump code will create one XEN_ELFNOTE_CRASH_INFO ++ * note in case of a system crash. This note will contain various ++ * information about the system, see xen/include/xen/elfcore.h. ++ */ ++#define XEN_ELFNOTE_CRASH_INFO 0x1000001 ++ ++/* ++ * System registers exported through crash notes. ++ * ++ * The kexec / kdump code will create one XEN_ELFNOTE_CRASH_REGS ++ * note per cpu in case of a system crash. This note is architecture ++ * specific and will contain registers not saved in the "CORE" note. ++ * See xen/include/xen/elfcore.h for more information. ++ */ ++#define XEN_ELFNOTE_CRASH_REGS 0x1000002 ++ ++ ++/* ++ * For (temporary) backwards compatibility. ++ */ ++#define NT_XEN_KDUMP_CR3 0x10000001 ++ ++struct xen_kdump_data { ++ ulong flags; ++ ulong cr3; ++ ulong p2m_mfn; ++ char *page; ++ ulong last_mfn_read; ++ ulong last_pmd_read; ++ ulong cache_hits; ++ ulong accesses; ++ int p2m_frames; ++ ulong *p2m_mfn_frame_list; ++ ulong xen_phys_start; ++ int xen_major_version; ++ int xen_minor_version; ++}; ++ ++#define KDUMP_P2M_INIT (0x1) ++#define KDUMP_CR3 (0x2) ++#define KDUMP_MFN_LIST (0x4) ++ ++#define P2M_FAILURE ((physaddr_t)(0xffffffffffffffffLL)) +--- crash/lkcd_fix_mem.h.orig 2009-02-12 09:31:03.000000000 -0500 ++++ crash/lkcd_fix_mem.h 2007-10-30 10:51:55.000000000 -0400 +@@ -1,3 +1,5 @@ ++/* OBSOLETE */ ++ + #ifdef IA64 + + #define UTSNAME_ENTRY_SZ 65 diff --git a/crash.spec b/crash.spec index e0d474d..1a05ec4 100644 --- a/crash.spec +++ b/crash.spec @@ -1,20 +1,20 @@ # # crash core analysis suite # -Summary: crash utility for live systems; netdump, diskdump, kdump, LKCD or mcore dumpfiles +Summary: Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles Name: crash Version: 4.0 -Release: 7 +Release: 7.7.1%{?dist} License: GPLv2 Group: Development/Debuggers Source: %{name}-%{version}.tar.gz URL: http://people.redhat.com/anderson ExclusiveOS: Linux ExclusiveArch: i386 ia64 x86_64 ppc64 s390 s390x -Buildroot: %{_tmppath}/%{name}-root +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot-%(%{__id_u} -n) BuildRequires: ncurses-devel zlib-devel Requires: binutils -Patch0: crash.patch +Patch0: crash.patch-4.0-7.7 %description The core analysis suite is a self-contained tool that can be used to @@ -23,8 +23,8 @@ netdump, diskdump and kdump packages from Red Hat Linux, the mcore kernel patch offered by Mission Critical Linux, or the LKCD kernel patch. %package devel -Requires: %{name} = %{version} -Summary: crash utility for live systems; netdump, diskdump, kdump, LKCD or mcore dumpfiles +Requires: %{name} = %{version}, zlib-devel +Summary: kernel crash analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles Group: Development/Debuggers %description devel @@ -34,36 +34,41 @@ netdump, diskdump and kdump packages from Red Hat Linux, the mcore kernel patch offered by Mission Critical Linux, or the LKCD kernel patch. %prep -%setup -n %{name}-%{version} -%patch0 -p1 -b crash.patch +%setup -n %{name}-%{version} -q +%patch0 -p1 -b crash.patch-4.0-7.7 %build make RPMPKG="%{version}-%{release}" %install rm -rf %{buildroot} -mkdir -p %{buildroot}/usr/bin +mkdir -p %{buildroot}%{_bindir} make DESTDIR=%{buildroot} install mkdir -p %{buildroot}%{_mandir}/man8 -cp crash.8 %{buildroot}%{_mandir}/man8/crash.8 +cp -p crash.8 %{buildroot}%{_mandir}/man8/crash.8 mkdir -p %{buildroot}%{_includedir}/crash chmod 0644 defs.h -cp defs.h %{buildroot}%{_includedir}/crash +cp -p defs.h %{buildroot}%{_includedir}/crash %clean rm -rf %{buildroot} %files -%defattr(-,root,root) -/usr/bin/crash +%defattr(-,root,root,-) +%{_bindir}/crash %{_mandir}/man8/crash.8* -%doc README +%doc README COPYING %files devel -%defattr(-,root,root) +%defattr(-,root,root,-) %{_includedir}/* %changelog +* Thu Feb 19 2009 Dave Anderson - 4.0-7.7.1 +- Updates to this file per crash merge review +- Update to upstream version 4.0-7.7. Full changelog viewable in: + http://people.redhat.com/anderson/crash.changelog.html + * Tue Jul 15 2008 Tom "spot" Callaway 4.0-7 - fix license tag