xref: /openbmc/linux/arch/ia64/kernel/crash.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a7956113SZou Nan hai /*
3a7956113SZou Nan hai  * arch/ia64/kernel/crash.c
4a7956113SZou Nan hai  *
5a7956113SZou Nan hai  * Architecture specific (ia64) functions for kexec based crash dumps.
6a7956113SZou Nan hai  *
7a7956113SZou Nan hai  * Created by: Khalid Aziz <khalid.aziz@hp.com>
8a7956113SZou Nan hai  * Copyright (C) 2005 Hewlett-Packard Development Company, L.P.
9a7956113SZou Nan hai  * Copyright (C) 2005 Intel Corp	Zou Nan hai <nanhai.zou@intel.com>
10a7956113SZou Nan hai  *
11a7956113SZou Nan hai  */
12a7956113SZou Nan hai #include <linux/smp.h>
13a7956113SZou Nan hai #include <linux/delay.h>
14a7956113SZou Nan hai #include <linux/crash_dump.h>
1557c8a661SMike Rapoport #include <linux/memblock.h>
16a7956113SZou Nan hai #include <linux/kexec.h>
17a7956113SZou Nan hai #include <linux/elfcore.h>
18a5b7c61eSMasahiro Yamada #include <linux/reboot.h>
19a7956113SZou Nan hai #include <linux/sysctl.h>
20a7956113SZou Nan hai #include <linux/init.h>
211eeb66a1SChristoph Hellwig #include <linux/kdebug.h>
22a7956113SZou Nan hai 
23a7956113SZou Nan hai #include <asm/mca.h>
24a7956113SZou Nan hai 
25a7956113SZou Nan hai int kdump_status[NR_CPUS];
260ac1facaSSimon Horman static atomic_t kdump_cpu_frozen;
27a7956113SZou Nan hai atomic_t kdump_in_progress;
281726b088SHidetoshi Seto static int kdump_freeze_monarch;
290ac1facaSSimon Horman static int kdump_on_init = 1;
30b0247a55SHidetoshi Seto static int kdump_on_fatal_mca = 1;
31a7956113SZou Nan hai 
32a7956113SZou Nan hai extern void ia64_dump_cpu_regs(void *);
33a7956113SZou Nan hai 
34a7956113SZou Nan hai static DEFINE_PER_CPU(struct elf_prstatus, elf_prstatus);
35a7956113SZou Nan hai 
36a7956113SZou Nan hai void
crash_save_this_cpu(void)37ccbebdacSAl Viro crash_save_this_cpu(void)
38a7956113SZou Nan hai {
39a7956113SZou Nan hai 	void *buf;
40a7956113SZou Nan hai 	unsigned long cfm, sof, sol;
41a7956113SZou Nan hai 
42a7956113SZou Nan hai 	int cpu = smp_processor_id();
43a7956113SZou Nan hai 	struct elf_prstatus *prstatus = &per_cpu(elf_prstatus, cpu);
44a7956113SZou Nan hai 
45a7956113SZou Nan hai 	elf_greg_t *dst = (elf_greg_t *)&(prstatus->pr_reg);
46a7956113SZou Nan hai 	memset(prstatus, 0, sizeof(*prstatus));
47f2485a2dSAl Viro 	prstatus->common.pr_pid = current->pid;
48a7956113SZou Nan hai 
49a7956113SZou Nan hai 	ia64_dump_cpu_regs(dst);
50a7956113SZou Nan hai 	cfm = dst[43];
51a7956113SZou Nan hai 	sol = (cfm >> 7) & 0x7f;
52a7956113SZou Nan hai 	sof = cfm & 0x7f;
53a7956113SZou Nan hai 	dst[46] = (unsigned long)ia64_rse_skip_regs((unsigned long *)dst[46],
54a7956113SZou Nan hai 			sof - sol);
55a7956113SZou Nan hai 
56a7956113SZou Nan hai 	buf = (u64 *) per_cpu_ptr(crash_notes, cpu);
57a7956113SZou Nan hai 	if (!buf)
58a7956113SZou Nan hai 		return;
596672f76aSSimon Horman 	buf = append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS, prstatus,
60a7956113SZou Nan hai 			sizeof(*prstatus));
61a7956113SZou Nan hai 	final_note(buf);
62a7956113SZou Nan hai }
63a7956113SZou Nan hai 
64bcb9b99dSMagnus Damm #ifdef CONFIG_SMP
65a7956113SZou Nan hai static int
kdump_wait_cpu_freeze(void)66a7956113SZou Nan hai kdump_wait_cpu_freeze(void)
67a7956113SZou Nan hai {
68a7956113SZou Nan hai 	int cpu_num = num_online_cpus() - 1;
69a7956113SZou Nan hai 	int timeout = 1000;
70a7956113SZou Nan hai 	while(timeout-- > 0) {
710ac1facaSSimon Horman 		if (atomic_read(&kdump_cpu_frozen) == cpu_num)
72a7956113SZou Nan hai 			return 0;
73a7956113SZou Nan hai 		udelay(1000);
74a7956113SZou Nan hai 	}
75a7956113SZou Nan hai 	return 1;
76a7956113SZou Nan hai }
77bcb9b99dSMagnus Damm #endif
78a7956113SZou Nan hai 
79a7956113SZou Nan hai void
machine_crash_shutdown(struct pt_regs * pt)80a7956113SZou Nan hai machine_crash_shutdown(struct pt_regs *pt)
81a7956113SZou Nan hai {
82a7956113SZou Nan hai 	/* This function is only called after the system
83a7956113SZou Nan hai 	 * has paniced or is otherwise in a critical state.
84a7956113SZou Nan hai 	 * The minimum amount of code to allow a kexec'd kernel
85a7956113SZou Nan hai 	 * to run successfully needs to happen here.
86a7956113SZou Nan hai 	 *
87a7956113SZou Nan hai 	 * In practice this means shooting down the other cpus in
88a7956113SZou Nan hai 	 * an SMP system.
89a7956113SZou Nan hai 	 */
90a7956113SZou Nan hai 	kexec_disable_iosapic();
91a7956113SZou Nan hai #ifdef CONFIG_SMP
921726b088SHidetoshi Seto 	/*
931726b088SHidetoshi Seto 	 * If kdump_on_init is set and an INIT is asserted here, kdump will
941726b088SHidetoshi Seto 	 * be started again via INIT monarch.
951726b088SHidetoshi Seto 	 */
961726b088SHidetoshi Seto 	local_irq_disable();
971726b088SHidetoshi Seto 	ia64_set_psr_mc();	/* mask MCA/INIT */
981726b088SHidetoshi Seto 	if (atomic_inc_return(&kdump_in_progress) != 1)
991726b088SHidetoshi Seto 		unw_init_running(kdump_cpu_freeze, NULL);
1001726b088SHidetoshi Seto 
1011726b088SHidetoshi Seto 	/*
1021726b088SHidetoshi Seto 	 * Now this cpu is ready for kdump.
1031726b088SHidetoshi Seto 	 * Stop all others by IPI or INIT.  They could receive INIT from
1041726b088SHidetoshi Seto 	 * outside and might be INIT monarch, but only thing they have to
1051726b088SHidetoshi Seto 	 * do is falling into kdump_cpu_freeze().
1061726b088SHidetoshi Seto 	 *
1071726b088SHidetoshi Seto 	 * If an INIT is asserted here:
1081726b088SHidetoshi Seto 	 * - All receivers might be slaves, since some of cpus could already
1091726b088SHidetoshi Seto 	 *   be frozen and INIT might be masked on monarch.  In this case,
1100cced40eSHidetoshi Seto 	 *   all slaves will be frozen soon since kdump_in_progress will let
1110cced40eSHidetoshi Seto 	 *   them into DIE_INIT_SLAVE_LEAVE.
1121726b088SHidetoshi Seto 	 * - One might be a monarch, but INIT rendezvous will fail since
1131726b088SHidetoshi Seto 	 *   at least this cpu already have INIT masked so it never join
1141726b088SHidetoshi Seto 	 *   to the rendezvous.  In this case, all slaves and monarch will
1150cced40eSHidetoshi Seto 	 *   be frozen soon with no wait since the INIT rendezvous is skipped
1160cced40eSHidetoshi Seto 	 *   by kdump_in_progress.
1171726b088SHidetoshi Seto 	 */
118a7956113SZou Nan hai 	kdump_smp_send_stop();
1190ac1facaSSimon Horman 	/* not all cpu response to IPI, send INIT to freeze them */
1205959906eSHidetoshi Seto 	if (kdump_wait_cpu_freeze()) {
121a7956113SZou Nan hai 		kdump_smp_send_init();
1225959906eSHidetoshi Seto 		/* wait again, don't go ahead if possible */
1235959906eSHidetoshi Seto 		kdump_wait_cpu_freeze();
124a7956113SZou Nan hai 	}
125a7956113SZou Nan hai #endif
126a7956113SZou Nan hai }
127a7956113SZou Nan hai 
128a7956113SZou Nan hai static void
machine_kdump_on_init(void)129a7956113SZou Nan hai machine_kdump_on_init(void)
130a7956113SZou Nan hai {
131072f042dSTakao Indoh 	crash_save_vmcoreinfo();
132a7956113SZou Nan hai 	local_irq_disable();
133a7956113SZou Nan hai 	kexec_disable_iosapic();
134a7956113SZou Nan hai 	machine_kexec(ia64_kimage);
135a7956113SZou Nan hai }
136a7956113SZou Nan hai 
137a7956113SZou Nan hai void
kdump_cpu_freeze(struct unw_frame_info * info,void * arg)138a7956113SZou Nan hai kdump_cpu_freeze(struct unw_frame_info *info, void *arg)
139a7956113SZou Nan hai {
140a7956113SZou Nan hai 	int cpuid;
1414295ab34SHidetoshi Seto 
142a7956113SZou Nan hai 	local_irq_disable();
143a7956113SZou Nan hai 	cpuid = smp_processor_id();
144a7956113SZou Nan hai 	crash_save_this_cpu();
145a7956113SZou Nan hai 	current->thread.ksp = (__u64)info->sw - 16;
1464295ab34SHidetoshi Seto 
1474295ab34SHidetoshi Seto 	ia64_set_psr_mc();	/* mask MCA/INIT and stop reentrance */
1484295ab34SHidetoshi Seto 
1490ac1facaSSimon Horman 	atomic_inc(&kdump_cpu_frozen);
150a7956113SZou Nan hai 	kdump_status[cpuid] = 1;
151a7956113SZou Nan hai 	mb();
152a7956113SZou Nan hai 	for (;;)
153a7956113SZou Nan hai 		cpu_relax();
154a7956113SZou Nan hai }
155a7956113SZou Nan hai 
156a7956113SZou Nan hai static int
kdump_init_notifier(struct notifier_block * self,unsigned long val,void * data)157a7956113SZou Nan hai kdump_init_notifier(struct notifier_block *self, unsigned long val, void *data)
158a7956113SZou Nan hai {
159a7956113SZou Nan hai 	struct ia64_mca_notify_die *nd;
160a7956113SZou Nan hai 	struct die_args *args = data;
161a7956113SZou Nan hai 
1625959906eSHidetoshi Seto 	if (atomic_read(&kdump_in_progress)) {
1635959906eSHidetoshi Seto 		switch (val) {
1645959906eSHidetoshi Seto 		case DIE_INIT_MONARCH_LEAVE:
1655959906eSHidetoshi Seto 			if (!kdump_freeze_monarch)
1665959906eSHidetoshi Seto 				break;
167df561f66SGustavo A. R. Silva 			fallthrough;
1685959906eSHidetoshi Seto 		case DIE_INIT_SLAVE_LEAVE:
1690cced40eSHidetoshi Seto 		case DIE_INIT_MONARCH_ENTER:
1705959906eSHidetoshi Seto 		case DIE_MCA_RENDZVOUS_LEAVE:
1715959906eSHidetoshi Seto 			unw_init_running(kdump_cpu_freeze, NULL);
1725959906eSHidetoshi Seto 			break;
1735959906eSHidetoshi Seto 		}
1745959906eSHidetoshi Seto 	}
1755959906eSHidetoshi Seto 
176b0247a55SHidetoshi Seto 	if (!kdump_on_init && !kdump_on_fatal_mca)
177a7956113SZou Nan hai 		return NOTIFY_DONE;
178a7956113SZou Nan hai 
1792010d7feSKenji Kaneshige 	if (!ia64_kimage) {
1802010d7feSKenji Kaneshige 		if (val == DIE_INIT_MONARCH_LEAVE)
1812010d7feSKenji Kaneshige 			ia64_mca_printk(KERN_NOTICE
1822010d7feSKenji Kaneshige 					"%s: kdump not configured\n",
183d4ed8084SHarvey Harrison 					__func__);
1842010d7feSKenji Kaneshige 		return NOTIFY_DONE;
1852010d7feSKenji Kaneshige 	}
1862010d7feSKenji Kaneshige 
187311f594dSJay Lan 	if (val != DIE_INIT_MONARCH_LEAVE &&
188311f594dSJay Lan 	    val != DIE_INIT_MONARCH_PROCESS &&
189a7956113SZou Nan hai 	    val != DIE_MCA_MONARCH_LEAVE)
190a7956113SZou Nan hai 		return NOTIFY_DONE;
191a7956113SZou Nan hai 
192a7956113SZou Nan hai 	nd = (struct ia64_mca_notify_die *)args->err;
193a7956113SZou Nan hai 
194a7956113SZou Nan hai 	switch (val) {
195311f594dSJay Lan 	case DIE_INIT_MONARCH_PROCESS:
1965959906eSHidetoshi Seto 		/* Reason code 1 means machine check rendezvous*/
1975959906eSHidetoshi Seto 		if (kdump_on_init && (nd->sos->rv_rc != 1)) {
1981726b088SHidetoshi Seto 			if (atomic_inc_return(&kdump_in_progress) != 1)
1991726b088SHidetoshi Seto 				kdump_freeze_monarch = 1;
200b0247a55SHidetoshi Seto 		}
201311f594dSJay Lan 		break;
202311f594dSJay Lan 	case DIE_INIT_MONARCH_LEAVE:
2035959906eSHidetoshi Seto 		/* Reason code 1 means machine check rendezvous*/
2045959906eSHidetoshi Seto 		if (kdump_on_init && (nd->sos->rv_rc != 1))
205a7956113SZou Nan hai 			machine_kdump_on_init();
206a7956113SZou Nan hai 		break;
207a7956113SZou Nan hai 	case DIE_MCA_MONARCH_LEAVE:
2084fa2f0e6SHidetoshi Seto 		/* *(nd->data) indicate if MCA is recoverable */
2094fa2f0e6SHidetoshi Seto 		if (kdump_on_fatal_mca && !(*(nd->data))) {
2100cced40eSHidetoshi Seto 			if (atomic_inc_return(&kdump_in_progress) == 1)
211a7956113SZou Nan hai 				machine_kdump_on_init();
2121726b088SHidetoshi Seto 			/* We got fatal MCA while kdump!? No way!! */
2131726b088SHidetoshi Seto 		}
214a7956113SZou Nan hai 		break;
215a7956113SZou Nan hai 	}
216a7956113SZou Nan hai 	return NOTIFY_DONE;
217a7956113SZou Nan hai }
218a7956113SZou Nan hai 
219a7956113SZou Nan hai #ifdef CONFIG_SYSCTL
2202841efa6SJoe Perches static struct ctl_table kdump_ctl_table[] = {
221a7956113SZou Nan hai 	{
222a7956113SZou Nan hai 		.procname = "kdump_on_init",
223a7956113SZou Nan hai 		.data = &kdump_on_init,
224a7956113SZou Nan hai 		.maxlen = sizeof(int),
225a7956113SZou Nan hai 		.mode = 0644,
2266d456111SEric W. Biederman 		.proc_handler = proc_dointvec,
227a7956113SZou Nan hai 	},
228b0247a55SHidetoshi Seto 	{
229b0247a55SHidetoshi Seto 		.procname = "kdump_on_fatal_mca",
230b0247a55SHidetoshi Seto 		.data = &kdump_on_fatal_mca,
231b0247a55SHidetoshi Seto 		.maxlen = sizeof(int),
232b0247a55SHidetoshi Seto 		.mode = 0644,
2336d456111SEric W. Biederman 		.proc_handler = proc_dointvec,
234b0247a55SHidetoshi Seto 	},
235d00faf81SEric W. Biederman 	{ }
236a7956113SZou Nan hai };
237a7956113SZou Nan hai #endif
238a7956113SZou Nan hai 
239a7956113SZou Nan hai static int
machine_crash_setup(void)240a7956113SZou Nan hai machine_crash_setup(void)
241a7956113SZou Nan hai {
242311f594dSJay Lan 	/* be notified before default_monarch_init_process */
243a7956113SZou Nan hai 	static struct notifier_block kdump_init_notifier_nb = {
244a7956113SZou Nan hai 		.notifier_call = kdump_init_notifier,
245311f594dSJay Lan 		.priority = 1,
246a7956113SZou Nan hai 	};
247a7956113SZou Nan hai 	int ret;
248a7956113SZou Nan hai 	if((ret = register_die_notifier(&kdump_init_notifier_nb)) != 0)
249a7956113SZou Nan hai 		return ret;
250a7956113SZou Nan hai #ifdef CONFIG_SYSCTL
251*03860ef0SLuis Chamberlain 	register_sysctl("kernel", kdump_ctl_table);
252a7956113SZou Nan hai #endif
253a7956113SZou Nan hai 	return 0;
254a7956113SZou Nan hai }
255a7956113SZou Nan hai 
256a7956113SZou Nan hai __initcall(machine_crash_setup);
257a7956113SZou Nan hai 
258