crash.c 4.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * Architecture specific (PPC64) functions for kexec based crash dumps.
 *
 * Copyright (C) 2005, IBM Corp.
 *
 * Created by: Haren Myneni
 *
 * This source code is licensed under the GNU General Public License,
 * Version 2.  See the file COPYING for more details.
 *
 */

#undef DEBUG

#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/reboot.h>
#include <linux/kexec.h>
#include <linux/bootmem.h>
#include <linux/crash_dump.h>
#include <linux/delay.h>
#include <linux/elf.h>
#include <linux/elfcore.h>
#include <linux/init.h>
25
#include <linux/irq.h>
26 27 28 29 30 31 32
#include <linux/types.h>

#include <asm/processor.h>
#include <asm/machdep.h>
#include <asm/kdump.h>
#include <asm/lmb.h>
#include <asm/firmware.h>
33
#include <asm/smp.h>
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

#ifdef DEBUG
#include <asm/udbg.h>
#define DBG(fmt...) udbg_printf(fmt)
#else
#define DBG(fmt...)
#endif

/* This keeps a track of which one is crashing cpu. */
int crashing_cpu = -1;

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, &note, 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, &note, 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.
	 */
88 89 90 91
	buf = (u32*)per_cpu_ptr(crash_notes, cpu);
	if (!buf) 
		return;

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	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);
}

#ifdef CONFIG_SMP
static atomic_t waiting_for_crash_ipi;

void crash_ipi_callback(struct pt_regs *regs)
{
	int cpu = smp_processor_id();

	if (cpu == crashing_cpu)
		return;

	if (!cpu_online(cpu))
		return;

	if (ppc_md.kexec_cpu_down)
		ppc_md.kexec_cpu_down(1, 1);

	local_irq_disable();

	crash_save_this_cpu(regs, cpu);
	atomic_dec(&waiting_for_crash_ipi);
	kexec_smp_wait();
	/* NOTREACHED */
}

static void crash_kexec_prepare_cpus(void)
{
	unsigned int msecs;

	atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);

	crash_send_ipi(crash_ipi_callback);
	smp_wmb();

	/*
	 * FIXME: Until we will have the way to stop other CPUSs reliabally,
	 * the crash CPU will send an IPI and wait for other CPUs to
	 * respond. If not, proceed the kexec boot even though we failed to
	 * capture other CPU states.
138
	 * Delay of at least 10 seconds.
139
	 */
140 141
	printk(KERN_ALERT "Sending IPI to other cpus...\n");
	msecs = 10000;
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	while ((atomic_read(&waiting_for_crash_ipi) > 0) && (--msecs > 0)) {
		barrier();
		mdelay(1);
	}

	/* Would it be better to replace the trap vector here? */

	/*
	 * FIXME: In case if we do not get all CPUs, one possibility: ask the
	 * user to do soft reset such that we get all.
	 * IPI handler is already set by the panic cpu initially. Therefore,
	 * all cpus could invoke this handler from die() and the panic CPU
	 * will call machine_kexec() directly from this handler to do
	 * kexec boot.
	 */
	if (atomic_read(&waiting_for_crash_ipi))
		printk(KERN_ALERT "done waiting: %d cpus not responding\n",
			atomic_read(&waiting_for_crash_ipi));
	/* Leave the IPI callback set */
}
#else
static void crash_kexec_prepare_cpus(void)
{
	/*
	 * move the secondarys to us so that we can copy
	 * the new kernel 0-0x100 safely
	 *
	 * do this if kexec in setup.c ?
	 */
	smp_release_cpus();
}

#endif

void default_machine_crash_shutdown(struct pt_regs *regs)
{
178 179
	unsigned int irq;

180 181
	/*
	 * This function is only called after the system
L
Lee Revell 已提交
182
	 * has panicked or is otherwise in a critical state.
183 184 185 186 187 188 189 190 191
	 * The minimum amount of code to allow a kexec'd kernel
	 * to run successfully needs to happen here.
	 *
	 * In practice this means stopping other cpus in
	 * an SMP system.
	 * The kernel is broken so disable interrupts.
	 */
	local_irq_disable();

192 193 194 195 196 197 198 199 200 201
	for_each_irq(irq) {
		struct irq_desc *desc = irq_descp(irq);

		if (desc->status & IRQ_INPROGRESS)
			desc->handler->end(irq);

		if (!(desc->status & IRQ_DISABLED))
			desc->handler->disable(irq);
	}

202 203 204 205 206 207 208 209 210
	if (ppc_md.kexec_cpu_down)
		ppc_md.kexec_cpu_down(1, 0);

	/*
	 * Make a note of crashing cpu. Will be used in machine_kexec
	 * such that another IPI will not be sent.
	 */
	crashing_cpu = smp_processor_id();
	crash_kexec_prepare_cpus();
211
	crash_save_this_cpu(regs, crashing_cpu);
212
}