crash.c 5.06 KB
Newer Older
Zou Nan hai's avatar
Zou Nan hai committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * arch/ia64/kernel/crash.c
 *
 * Architecture specific (ia64) functions for kexec based crash dumps.
 *
 * Created by: Khalid Aziz <khalid.aziz@hp.com>
 * Copyright (C) 2005 Hewlett-Packard Development Company, L.P.
 * Copyright (C) 2005 Intel Corp	Zou Nan hai <nanhai.zou@intel.com>
 *
 */
#include <linux/smp.h>
#include <linux/delay.h>
#include <linux/crash_dump.h>
#include <linux/bootmem.h>
#include <linux/kexec.h>
#include <linux/elfcore.h>
#include <linux/sysctl.h>
#include <linux/init.h>
19
#include <linux/kdebug.h>
Zou Nan hai's avatar
Zou Nan hai committed
20
21
22
23

#include <asm/mca.h>

int kdump_status[NR_CPUS];
Simon Horman's avatar
Simon Horman committed
24
static atomic_t kdump_cpu_frozen;
Zou Nan hai's avatar
Zou Nan hai committed
25
atomic_t kdump_in_progress;
Simon Horman's avatar
Simon Horman committed
26
static int kdump_on_init = 1;
Zou Nan hai's avatar
Zou Nan hai committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

static inline Elf64_Word
*append_elf_note(Elf64_Word *buf, char *name, unsigned type, void *data,
		size_t data_len)
{
	struct elf_note *note = (struct elf_note *)buf;
	note->n_namesz = strlen(name) + 1;
	note->n_descsz = data_len;
	note->n_type   = type;
	buf += (sizeof(*note) + 3)/4;
	memcpy(buf, name, note->n_namesz);
	buf += (note->n_namesz + 3)/4;
	memcpy(buf, data, data_len);
	buf += (data_len + 3)/4;
	return buf;
}

static void
final_note(void *buf)
{
	memset(buf, 0, sizeof(struct elf_note));
}

extern void ia64_dump_cpu_regs(void *);

static DEFINE_PER_CPU(struct elf_prstatus, elf_prstatus);

void
Al Viro's avatar
Al Viro committed
55
crash_save_this_cpu(void)
Zou Nan hai's avatar
Zou Nan hai committed
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
{
	void *buf;
	unsigned long cfm, sof, sol;

	int cpu = smp_processor_id();
	struct elf_prstatus *prstatus = &per_cpu(elf_prstatus, cpu);

	elf_greg_t *dst = (elf_greg_t *)&(prstatus->pr_reg);
	memset(prstatus, 0, sizeof(*prstatus));
	prstatus->pr_pid = current->pid;

	ia64_dump_cpu_regs(dst);
	cfm = dst[43];
	sol = (cfm >> 7) & 0x7f;
	sof = cfm & 0x7f;
	dst[46] = (unsigned long)ia64_rse_skip_regs((unsigned long *)dst[46],
			sof - sol);

	buf = (u64 *) per_cpu_ptr(crash_notes, cpu);
	if (!buf)
		return;
	buf = append_elf_note(buf, "CORE", NT_PRSTATUS, prstatus,
			sizeof(*prstatus));
	final_note(buf);
}

82
#ifdef CONFIG_SMP
Zou Nan hai's avatar
Zou Nan hai committed
83
84
85
86
87
88
static int
kdump_wait_cpu_freeze(void)
{
	int cpu_num = num_online_cpus() - 1;
	int timeout = 1000;
	while(timeout-- > 0) {
Simon Horman's avatar
Simon Horman committed
89
		if (atomic_read(&kdump_cpu_frozen) == cpu_num)
Zou Nan hai's avatar
Zou Nan hai committed
90
91
92
93
94
			return 0;
		udelay(1000);
	}
	return 1;
}
95
#endif
Zou Nan hai's avatar
Zou Nan hai committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

void
machine_crash_shutdown(struct pt_regs *pt)
{
	/* This function is only called after the system
	 * has paniced or is otherwise in a critical state.
	 * The minimum amount of code to allow a kexec'd kernel
	 * to run successfully needs to happen here.
	 *
	 * In practice this means shooting down the other cpus in
	 * an SMP system.
	 */
	kexec_disable_iosapic();
#ifdef CONFIG_SMP
	kdump_smp_send_stop();
Simon Horman's avatar
Simon Horman committed
111
	/* not all cpu response to IPI, send INIT to freeze them */
Zou Nan hai's avatar
Zou Nan hai committed
112
113
114
115
116
117
118
119
120
	if (kdump_wait_cpu_freeze() && kdump_on_init) 	{
		kdump_smp_send_init();
	}
#endif
}

static void
machine_kdump_on_init(void)
{
121
122
123
124
125
	if (!ia64_kimage) {
		printk(KERN_NOTICE "machine_kdump_on_init(): "
				"kdump not configured\n");
		return;
	}
Zou Nan hai's avatar
Zou Nan hai committed
126
127
128
129
130
131
132
133
134
135
136
137
138
	local_irq_disable();
	kexec_disable_iosapic();
	machine_kexec(ia64_kimage);
}

void
kdump_cpu_freeze(struct unw_frame_info *info, void *arg)
{
	int cpuid;
	local_irq_disable();
	cpuid = smp_processor_id();
	crash_save_this_cpu();
	current->thread.ksp = (__u64)info->sw - 16;
Simon Horman's avatar
Simon Horman committed
139
	atomic_inc(&kdump_cpu_frozen);
Zou Nan hai's avatar
Zou Nan hai committed
140
141
	kdump_status[cpuid] = 1;
	mb();
142
143
#ifdef CONFIG_HOTPLUG_CPU
	if (cpuid != 0)
Zou Nan hai's avatar
Zou Nan hai committed
144
		ia64_jump_to_sal(&sal_boot_rendez_state[cpuid]);
145
146
147
#endif
	for (;;)
		cpu_relax();
Zou Nan hai's avatar
Zou Nan hai committed
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
}

static int
kdump_init_notifier(struct notifier_block *self, unsigned long val, void *data)
{
	struct ia64_mca_notify_die *nd;
	struct die_args *args = data;

	if (!kdump_on_init)
		return NOTIFY_DONE;

	if (val != DIE_INIT_MONARCH_ENTER &&
	    val != DIE_INIT_SLAVE_ENTER &&
	    val != DIE_MCA_RENDZVOUS_LEAVE &&
	    val != DIE_MCA_MONARCH_LEAVE)
		return NOTIFY_DONE;

	nd = (struct ia64_mca_notify_die *)args->err;
	/* Reason code 1 means machine check rendezous*/
Jay Lan's avatar
Jay Lan committed
167
	if ((val == DIE_INIT_MONARCH_ENTER || val == DIE_INIT_SLAVE_ENTER) &&
Zou Nan hai's avatar
Zou Nan hai committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
		 nd->sos->rv_rc == 1)
		return NOTIFY_DONE;

	switch (val) {
		case DIE_INIT_MONARCH_ENTER:
			machine_kdump_on_init();
			break;
		case DIE_INIT_SLAVE_ENTER:
			unw_init_running(kdump_cpu_freeze, NULL);
			break;
		case DIE_MCA_RENDZVOUS_LEAVE:
			if (atomic_read(&kdump_in_progress))
				unw_init_running(kdump_cpu_freeze, NULL);
			break;
		case DIE_MCA_MONARCH_LEAVE:
		     /* die_register->signr indicate if MCA is recoverable */
			if (!args->signr)
				machine_kdump_on_init();
			break;
	}
	return NOTIFY_DONE;
}

#ifdef CONFIG_SYSCTL
static ctl_table kdump_on_init_table[] = {
	{
		.ctl_name = CTL_UNNUMBERED,
		.procname = "kdump_on_init",
		.data = &kdump_on_init,
		.maxlen = sizeof(int),
		.mode = 0644,
		.proc_handler = &proc_dointvec,
	},
	{ .ctl_name = 0 }
};

static ctl_table sys_table[] = {
	{
	  .ctl_name = CTL_KERN,
	  .procname = "kernel",
	  .mode = 0555,
	  .child = kdump_on_init_table,
	},
	{ .ctl_name = 0 }
};
#endif

static int
machine_crash_setup(void)
{
	static struct notifier_block kdump_init_notifier_nb = {
		.notifier_call = kdump_init_notifier,
	};
	int ret;
	if((ret = register_die_notifier(&kdump_init_notifier_nb)) != 0)
		return ret;
#ifdef CONFIG_SYSCTL
225
	register_sysctl_table(sys_table);
Zou Nan hai's avatar
Zou Nan hai committed
226
227
228
229
230
231
#endif
	return 0;
}

__initcall(machine_crash_setup);