[RFC Patch 6/6] Crash: Recognise slim coredumps and process new elf-note sections

K.Prasad
2011-05-26T17:26:39Z

Crash: Recognise slim coredumps and process new elf-note sections

The Linux kernel will begin to support SlimDump for certain types of crashes
and the 'crash' tool needs to recognise them. For these types of coredumps, it
need not lookout for usual elf-structures and start gdb. Also process new
elf-note sections that contain additional information about the crash.

Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com>
---
diskdump.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
netdump.c | 8 +++++
x86.h | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 183 insertions(+)

Index: crash-5.1.5.slim_kdump/x86.h
===================================================================
--- /dev/null
+++ crash-5.1.5.slim_kdump/x86.h
@@ -0,0 +1,91 @@
+/*
+ * x86.h - x86 Architecture specific definitions
+ *
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2011
+ *
+ * Author: K.Prasad <prasad@linux.vnet.ibm.com>
+ */
+
+typedef unsigned long long u64;
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+
+#define __u64 u64
+#define __u32 u32
+#define __u16 u16
+#define __u8 u8
+
+/* Mask for finding the address mode in IA32_MCi_MISC[8:6] register */
+#define MCI_MISC_ADDR_MODE 0X1C0
+/* Number of bits to shift the IA32_MCi_MISC to read the address-mode bits */
+#define MISC_ADDR_MODE_POS 6
+
+/* Address Modes in IA32_MCi_MISC[8:6] */
+#define MCM_ADDR_SEGOFF 0 /* segment offset */
+#define MCM_ADDR_LINEAR 1 /* linear address */
+#define MCM_ADDR_PHYS 2 /* physical address */
+#define MCM_ADDR_MEM 3 /* memory address */
+#define MCM_ADDR_GENERIC 7 /* generic */
+
+#define MCI_STATUS_MISCV (1ULL<<59) /* misc error reg. valid */
+#define MCI_STATUS_ADDRV (1ULL<<58) /* addr reg. valid */
+
+#define PAGE_SHIFT 12
+
+static const char *mce_addr_mode[] =
+{
+ "Segment offset", /* MCM_ADDR_SEGOFF */
+ "Linear address", /* MCM_ADDR_LINEAR */
+ "Physical address", /* MCM_ADDR_PHYS */
+ "Memory address", /* MCM_ADDR_MEM */
+ "", /* reserved */
+ "", /* reserved */
+ "", /* reserved */
+ "Generic" /* MCM_ADDR_GENERIC */
+};
+
+/*
+ * kernel structure: Keep this in sync with the definition in
+ * arch/x86/include/asm/mce.h of linux source code.
+ *
+ * Fields are zero when not available
+ *
+ */
+struct mce {
+ __u64 status;
+ __u64 misc;
+ __u64 addr;
+ __u64 mcgstatus;
+ __u64 ip;
+ __u64 tsc; /* cpu time stamp counter */
+ __u64 time; /* wall time_t when error was detected */
+ __u8 cpuvendor; /* cpu vendor as encoded in system.h */
+ __u8 pad1;
+ __u16 pad2;
+ __u32 cpuid; /* CPUID 1 EAX */
+ __u8 cs; /* code segment */
+ __u8 bank; /* machine check bank */
+ __u8 cpu; /* cpu number; obsolete; use extcpu now */
+ __u8 finished; /* entry is valid */
+ __u32 extcpu; /* linux cpu number that detected the error */
+ __u32 socketid; /* CPU socket ID */
+ __u32 apicid; /* CPU initial apic ID */
+ __u64 mcgcap; /* MCGCAP MSR: machine check capabilities of CPU */
+ __u64 aux0;
+ __u64 aux1;
+};
Index: crash-5.1.5.slim_kdump/netdump.c
===================================================================
--- crash-5.1.5.slim_kdump.orig/netdump.c
+++ crash-5.1.5.slim_kdump/netdump.c
@@ -331,6 +331,10 @@ is_netdump(char *file, ulong source_quer
}
nd->notes32 = (Elf32_Phdr *)
&nd->elf_header[sizeof(Elf32_Ehdr)];
+ if (machdep->process_elf_notes)
+ machdep->process_elf_notes((char *)nd->elf32 +
+ nd->notes32->p_offset,
+ nd->notes32->p_filesz);
nd->load32 = (Elf32_Phdr *)
&nd->elf_header[sizeof(Elf32_Ehdr)+sizeof(Elf32_Phdr)];
if (DUMPFILE_FORMAT(nd->flags) == NETDUMP_ELF32)
@@ -360,6 +364,10 @@ is_netdump(char *file, ulong source_quer
}
nd->notes64 = (Elf64_Phdr *)
&nd->elf_header[sizeof(Elf64_Ehdr)];
+ if (machdep->process_elf_notes)
+ machdep->process_elf_notes((char *)nd->elf64 +
+ nd->notes64->p_offset,
+ nd->notes64->p_filesz);
nd->load64 = (Elf64_Phdr *)
&nd->elf_header[sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)];
if (DUMPFILE_FORMAT(nd->flags) == NETDUMP_ELF64)
Index: crash-5.1.5.slim_kdump/diskdump.c
===================================================================
--- crash-5.1.5.slim_kdump.orig/diskdump.c
+++ crash-5.1.5.slim_kdump/diskdump.c
@@ -231,6 +231,27 @@ open_dump_file(char *file)
dd->dfd = fd;
return TRUE;
}
+#if defined(X86_64) || defined(X86)
+#include "x86.h"
+
+/*
+ * Check if the address reported by the CPU is in a format we can parse.
+ * It would be possible to add code for most other cases, but all would
+ * be somewhat complicated (e.g. segment offset would require an instruction
+ * parser). So only support physical addresses up to page granuality for now.
+ *
+ * Function derived from arch/x86/kernel/cpu/mcheck/mce.c in Linux source
+ *
+ */
+static int mce_usable_address(struct mce *m)
+{
+ if (!(m->status & MCI_STATUS_MISCV) || !(m->status & MCI_STATUS_ADDRV))
+ return 0;
+ if ((m->misc & 0x3f) > PAGE_SHIFT)
+ return 0;
+ return 1;
+}
+#endif /* defined(X86_64) || defined(X86) */

void
x86_process_elf_notes(void *note_ptr, unsigned long size_note)
@@ -239,10 +260,43 @@ x86_process_elf_notes(void *note_ptr, un
Elf64_Nhdr *note64 = NULL;
size_t tot, len = 0;
int num = 0;
+#if defined(X86_64) || defined(X86)
+ struct mce *mce;
+ ushort addr_mode;
+#endif /* defined(X86_64) || defined(X86) */

for (tot = 0; tot < size_note; tot += len) {
if (machine_type("X86_64")) {
note64 = note_ptr + tot;
+#ifdef X86_64
+ /*
+ * If vmcore is generated due to fatal Machine Check
+ * Exception, we only have a 'slim' crashdump. Don't
+ * analyse further, inform the user about it and exit.
+ */
+ if (note64->n_type == NT_MCE) {
+ fprintf(fp, "\"System crashed due to a hardware"
+ " memory error. No coredump"
+ " available.\"\n");
+
+ /* Do we have a copy of 'struct mce'? */
+ if (note64->n_descsz == 0)
+ goto exit;
+
+ mce = (struct mce *)((char *)note64 +
+ sizeof(Elf64_Nhdr) + note64->n_namesz);
+ if (!mce_usable_address(mce))
+ goto exit;
+
+ addr_mode = (mce->misc >> MISC_ADDR_MODE_POS) &
+ MCI_MISC_ADDR_MODE;
+ fprintf(fp, "Memory error occured at %llx "
+ "(address type: %s\n)", mce->addr,
+ mce_addr_mode[addr_mode]);
+exit:
+ clean_exit(0);
+ }
+#endif /* X86_64 */

if (note64->n_type == NT_PRSTATUS) {
dd->nt_prstatus_percpu[num] = note64;
@@ -255,6 +309,36 @@ x86_process_elf_notes(void *note_ptr, un
} else if (machine_type("X86")) {
note32 = note_ptr + tot;

+#ifdef X86
+ /*
+ * If vmcore is generated due to fatal Machine Check
+ * Exception, we only have a 'slim' crashdump. Don't
+ * analyse further, inform the user about it and exit.
+ */
+ if (note32->n_type == NT_MCE) {
+ fprintf(fp, "\"System crashed due to a hardware"
+ " memory error. No coredump"
+ " available.\"\n");
+
+ /* Do we have a copy of 'struct mce'? */
+ if (note32->n_descsz == 0)
+ goto exit;
+
+ mce = (struct mce *)((char *)note32 +
+ sizeof(Elf32_Nhdr) + note32->n_namesz);
+ if (!mce_usable_address(mce))
+ goto exit;
+
+ addr_mode = (mce->misc >> MISC_ADDR_MODE_POS) &
+ MCI_MISC_ADDR_MODE;
+ fprintf(fp, "Memory error occured at %llx "
+ "(address type: %s\n)", mce->addr,
+ mce_addr_mode[addr_mode]);
+exit:
+ clean_exit(0);
+ }
+#endif /* X86 */
+
if (note32->n_type == NT_PRSTATUS) {
dd->nt_prstatus_percpu[num] = note32;
num++;