|
@@ -11,9 +11,12 @@
|
|
|
#include <linux/types.h>
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/smp.h>
|
|
|
+#include <linux/irq.h>
|
|
|
#include <linux/reboot.h>
|
|
|
#include <linux/kexec.h>
|
|
|
#include <linux/delay.h>
|
|
|
+#include <linux/elf.h>
|
|
|
+#include <linux/elfcore.h>
|
|
|
|
|
|
#include <asm/processor.h>
|
|
|
#include <asm/hardirq.h>
|
|
@@ -24,6 +27,71 @@
|
|
|
/* This keeps a track of which one is crashing cpu. */
|
|
|
static int crashing_cpu;
|
|
|
|
|
|
+static u32 *append_elf_note(u32 *buf, char *name, unsigned type,
|
|
|
+ void *data, size_t data_len)
|
|
|
+{
|
|
|
+ struct elf_note note;
|
|
|
+
|
|
|
+ note.n_namesz = strlen(name) + 1;
|
|
|
+ note.n_descsz = data_len;
|
|
|
+ note.n_type = type;
|
|
|
+ memcpy(buf, ¬e, sizeof(note));
|
|
|
+ buf += (sizeof(note) +3)/4;
|
|
|
+ memcpy(buf, name, note.n_namesz);
|
|
|
+ buf += (note.n_namesz + 3)/4;
|
|
|
+ memcpy(buf, data, note.n_descsz);
|
|
|
+ buf += (note.n_descsz + 3)/4;
|
|
|
+
|
|
|
+ return buf;
|
|
|
+}
|
|
|
+
|
|
|
+static void final_note(u32 *buf)
|
|
|
+{
|
|
|
+ struct elf_note note;
|
|
|
+
|
|
|
+ note.n_namesz = 0;
|
|
|
+ note.n_descsz = 0;
|
|
|
+ note.n_type = 0;
|
|
|
+ memcpy(buf, ¬e, sizeof(note));
|
|
|
+}
|
|
|
+
|
|
|
+static void crash_save_this_cpu(struct pt_regs *regs, int cpu)
|
|
|
+{
|
|
|
+ struct elf_prstatus prstatus;
|
|
|
+ u32 *buf;
|
|
|
+
|
|
|
+ if ((cpu < 0) || (cpu >= NR_CPUS))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Using ELF notes here is opportunistic.
|
|
|
+ * I need a well defined structure format
|
|
|
+ * for the data I pass, and I need tags
|
|
|
+ * on the data to indicate what information I have
|
|
|
+ * squirrelled away. ELF notes happen to provide
|
|
|
+ * all of that that no need to invent something new.
|
|
|
+ */
|
|
|
+
|
|
|
+ buf = (u32*)per_cpu_ptr(crash_notes, cpu);
|
|
|
+
|
|
|
+ if (!buf)
|
|
|
+ return;
|
|
|
+
|
|
|
+ memset(&prstatus, 0, sizeof(prstatus));
|
|
|
+ prstatus.pr_pid = current->pid;
|
|
|
+ elf_core_copy_regs(&prstatus.pr_reg, regs);
|
|
|
+ buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus,
|
|
|
+ sizeof(prstatus));
|
|
|
+ final_note(buf);
|
|
|
+}
|
|
|
+
|
|
|
+static void crash_save_self(struct pt_regs *regs)
|
|
|
+{
|
|
|
+ int cpu;
|
|
|
+
|
|
|
+ cpu = smp_processor_id();
|
|
|
+ crash_save_this_cpu(regs, cpu);
|
|
|
+}
|
|
|
+
|
|
|
#ifdef CONFIG_SMP
|
|
|
static atomic_t waiting_for_crash_ipi;
|
|
|
|
|
@@ -38,6 +106,7 @@ static int crash_nmi_callback(struct pt_regs *regs, int cpu)
|
|
|
return 1;
|
|
|
local_irq_disable();
|
|
|
|
|
|
+ crash_save_this_cpu(regs, cpu);
|
|
|
disable_local_APIC();
|
|
|
atomic_dec(&waiting_for_crash_ipi);
|
|
|
/* Assume hlt works */
|
|
@@ -113,4 +182,5 @@ void machine_crash_shutdown(struct pt_regs *regs)
|
|
|
disable_IO_APIC();
|
|
|
#endif
|
|
|
|
|
|
+ crash_save_self(regs);
|
|
|
}
|