xref: /linux/arch/s390/kernel/nmi.c (revision 052ff461c8427629aee887ccc27478fc7373237c)
1f5daba1dSHeiko Carstens /*
2f5daba1dSHeiko Carstens  *   Machine check handler
3f5daba1dSHeiko Carstens  *
4f5daba1dSHeiko Carstens  *    Copyright IBM Corp. 2000,2009
5f5daba1dSHeiko Carstens  *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
6f5daba1dSHeiko Carstens  *		 Martin Schwidefsky <schwidefsky@de.ibm.com>,
7f5daba1dSHeiko Carstens  *		 Cornelia Huck <cornelia.huck@de.ibm.com>,
8f5daba1dSHeiko Carstens  *		 Heiko Carstens <heiko.carstens@de.ibm.com>,
9f5daba1dSHeiko Carstens  */
10f5daba1dSHeiko Carstens 
11*052ff461SHeiko Carstens #include <linux/kernel_stat.h>
12f5daba1dSHeiko Carstens #include <linux/init.h>
13f5daba1dSHeiko Carstens #include <linux/errno.h>
1481f64b87SHeiko Carstens #include <linux/hardirq.h>
15f5daba1dSHeiko Carstens #include <linux/time.h>
16f5daba1dSHeiko Carstens #include <linux/module.h>
17f5daba1dSHeiko Carstens #include <asm/lowcore.h>
18f5daba1dSHeiko Carstens #include <asm/smp.h>
19f5daba1dSHeiko Carstens #include <asm/etr.h>
2076d4e00aSMartin Schwidefsky #include <asm/cputime.h>
21f5daba1dSHeiko Carstens #include <asm/nmi.h>
22f5daba1dSHeiko Carstens #include <asm/crw.h>
23f5daba1dSHeiko Carstens 
24f5daba1dSHeiko Carstens struct mcck_struct {
25f5daba1dSHeiko Carstens 	int kill_task;
26f5daba1dSHeiko Carstens 	int channel_report;
27f5daba1dSHeiko Carstens 	int warning;
28f5daba1dSHeiko Carstens 	unsigned long long mcck_code;
29f5daba1dSHeiko Carstens };
30f5daba1dSHeiko Carstens 
31f5daba1dSHeiko Carstens static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);
32f5daba1dSHeiko Carstens 
33f5daba1dSHeiko Carstens static NORET_TYPE void s390_handle_damage(char *msg)
34f5daba1dSHeiko Carstens {
35f5daba1dSHeiko Carstens 	smp_send_stop();
36f5daba1dSHeiko Carstens 	disabled_wait((unsigned long) __builtin_return_address(0));
37f5daba1dSHeiko Carstens 	while (1);
38f5daba1dSHeiko Carstens }
39f5daba1dSHeiko Carstens 
40f5daba1dSHeiko Carstens /*
41f5daba1dSHeiko Carstens  * Main machine check handler function. Will be called with interrupts enabled
42f5daba1dSHeiko Carstens  * or disabled and machine checks enabled or disabled.
43f5daba1dSHeiko Carstens  */
44f5daba1dSHeiko Carstens void s390_handle_mcck(void)
45f5daba1dSHeiko Carstens {
46f5daba1dSHeiko Carstens 	unsigned long flags;
47f5daba1dSHeiko Carstens 	struct mcck_struct mcck;
48f5daba1dSHeiko Carstens 
49f5daba1dSHeiko Carstens 	/*
50f5daba1dSHeiko Carstens 	 * Disable machine checks and get the current state of accumulated
51f5daba1dSHeiko Carstens 	 * machine checks. Afterwards delete the old state and enable machine
52f5daba1dSHeiko Carstens 	 * checks again.
53f5daba1dSHeiko Carstens 	 */
54f5daba1dSHeiko Carstens 	local_irq_save(flags);
55f5daba1dSHeiko Carstens 	local_mcck_disable();
56f5daba1dSHeiko Carstens 	mcck = __get_cpu_var(cpu_mcck);
57f5daba1dSHeiko Carstens 	memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct));
58f5daba1dSHeiko Carstens 	clear_thread_flag(TIF_MCCK_PENDING);
59f5daba1dSHeiko Carstens 	local_mcck_enable();
60f5daba1dSHeiko Carstens 	local_irq_restore(flags);
61f5daba1dSHeiko Carstens 
62f5daba1dSHeiko Carstens 	if (mcck.channel_report)
63f5daba1dSHeiko Carstens 		crw_handle_channel_report();
64f5daba1dSHeiko Carstens 	/*
657b886416SHeiko Carstens 	 * A warning may remain for a prolonged period on the bare iron.
667b886416SHeiko Carstens 	 * (actually until the machine is powered off, or the problem is gone)
677b886416SHeiko Carstens 	 * So we just stop listening for the WARNING MCH and avoid continuously
68f5daba1dSHeiko Carstens 	 * being interrupted.  One caveat is however, that we must do this per
69f5daba1dSHeiko Carstens 	 * processor and cannot use the smp version of ctl_clear_bit().
70f5daba1dSHeiko Carstens 	 * On VM we only get one interrupt per virtally presented machinecheck.
717b886416SHeiko Carstens 	 * Though one suffices, we may get one interrupt per (virtual) cpu.
72f5daba1dSHeiko Carstens 	 */
73f5daba1dSHeiko Carstens 	if (mcck.warning) {	/* WARNING pending ? */
74f5daba1dSHeiko Carstens 		static int mchchk_wng_posted = 0;
757b886416SHeiko Carstens 
767b886416SHeiko Carstens 		/* Use single cpu clear, as we cannot handle smp here. */
77f5daba1dSHeiko Carstens 		__ctl_clear_bit(14, 24);	/* Disable WARNING MCH */
78f5daba1dSHeiko Carstens 		if (xchg(&mchchk_wng_posted, 1) == 0)
79f5daba1dSHeiko Carstens 			kill_cad_pid(SIGPWR, 1);
80f5daba1dSHeiko Carstens 	}
81f5daba1dSHeiko Carstens 	if (mcck.kill_task) {
82f5daba1dSHeiko Carstens 		local_irq_enable();
83f5daba1dSHeiko Carstens 		printk(KERN_EMERG "mcck: Terminating task because of machine "
84f5daba1dSHeiko Carstens 		       "malfunction (code 0x%016llx).\n", mcck.mcck_code);
85f5daba1dSHeiko Carstens 		printk(KERN_EMERG "mcck: task: %s, pid: %d.\n",
86f5daba1dSHeiko Carstens 		       current->comm, current->pid);
87f5daba1dSHeiko Carstens 		do_exit(SIGSEGV);
88f5daba1dSHeiko Carstens 	}
89f5daba1dSHeiko Carstens }
90f5daba1dSHeiko Carstens EXPORT_SYMBOL_GPL(s390_handle_mcck);
91f5daba1dSHeiko Carstens 
92f5daba1dSHeiko Carstens /*
93f5daba1dSHeiko Carstens  * returns 0 if all registers could be validated
94f5daba1dSHeiko Carstens  * returns 1 otherwise
95f5daba1dSHeiko Carstens  */
96f5daba1dSHeiko Carstens static int notrace s390_revalidate_registers(struct mci *mci)
97f5daba1dSHeiko Carstens {
98f5daba1dSHeiko Carstens 	int kill_task;
99f5daba1dSHeiko Carstens 	u64 zero;
100f5daba1dSHeiko Carstens 	void *fpt_save_area, *fpt_creg_save_area;
101f5daba1dSHeiko Carstens 
102f5daba1dSHeiko Carstens 	kill_task = 0;
103f5daba1dSHeiko Carstens 	zero = 0;
104f5daba1dSHeiko Carstens 
105f5daba1dSHeiko Carstens 	if (!mci->gr) {
106f5daba1dSHeiko Carstens 		/*
107f5daba1dSHeiko Carstens 		 * General purpose registers couldn't be restored and have
108f5daba1dSHeiko Carstens 		 * unknown contents. Process needs to be terminated.
109f5daba1dSHeiko Carstens 		 */
110f5daba1dSHeiko Carstens 		kill_task = 1;
111f5daba1dSHeiko Carstens 	}
112f5daba1dSHeiko Carstens 	if (!mci->fp) {
113f5daba1dSHeiko Carstens 		/*
114f5daba1dSHeiko Carstens 		 * Floating point registers can't be restored and
115f5daba1dSHeiko Carstens 		 * therefore the process needs to be terminated.
116f5daba1dSHeiko Carstens 		 */
117f5daba1dSHeiko Carstens 		kill_task = 1;
118f5daba1dSHeiko Carstens 	}
119f5daba1dSHeiko Carstens #ifndef CONFIG_64BIT
120f5daba1dSHeiko Carstens 	asm volatile(
121f5daba1dSHeiko Carstens 		"	ld	0,0(%0)\n"
122f5daba1dSHeiko Carstens 		"	ld	2,8(%0)\n"
123f5daba1dSHeiko Carstens 		"	ld	4,16(%0)\n"
124f5daba1dSHeiko Carstens 		"	ld	6,24(%0)"
125f5daba1dSHeiko Carstens 		: : "a" (&S390_lowcore.floating_pt_save_area));
126f5daba1dSHeiko Carstens #endif
127f5daba1dSHeiko Carstens 
128f5daba1dSHeiko Carstens 	if (MACHINE_HAS_IEEE) {
129f5daba1dSHeiko Carstens #ifdef CONFIG_64BIT
130f5daba1dSHeiko Carstens 		fpt_save_area = &S390_lowcore.floating_pt_save_area;
131f5daba1dSHeiko Carstens 		fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area;
132f5daba1dSHeiko Carstens #else
133f5daba1dSHeiko Carstens 		fpt_save_area = (void *) S390_lowcore.extended_save_area_addr;
134f5daba1dSHeiko Carstens 		fpt_creg_save_area = fpt_save_area + 128;
135f5daba1dSHeiko Carstens #endif
136f5daba1dSHeiko Carstens 		if (!mci->fc) {
137f5daba1dSHeiko Carstens 			/*
138f5daba1dSHeiko Carstens 			 * Floating point control register can't be restored.
139f5daba1dSHeiko Carstens 			 * Task will be terminated.
140f5daba1dSHeiko Carstens 			 */
141f5daba1dSHeiko Carstens 			asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
142f5daba1dSHeiko Carstens 			kill_task = 1;
143f5daba1dSHeiko Carstens 
144f5daba1dSHeiko Carstens 		} else
145f5daba1dSHeiko Carstens 			asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
146f5daba1dSHeiko Carstens 
147f5daba1dSHeiko Carstens 		asm volatile(
148f5daba1dSHeiko Carstens 			"	ld	0,0(%0)\n"
149f5daba1dSHeiko Carstens 			"	ld	1,8(%0)\n"
150f5daba1dSHeiko Carstens 			"	ld	2,16(%0)\n"
151f5daba1dSHeiko Carstens 			"	ld	3,24(%0)\n"
152f5daba1dSHeiko Carstens 			"	ld	4,32(%0)\n"
153f5daba1dSHeiko Carstens 			"	ld	5,40(%0)\n"
154f5daba1dSHeiko Carstens 			"	ld	6,48(%0)\n"
155f5daba1dSHeiko Carstens 			"	ld	7,56(%0)\n"
156f5daba1dSHeiko Carstens 			"	ld	8,64(%0)\n"
157f5daba1dSHeiko Carstens 			"	ld	9,72(%0)\n"
158f5daba1dSHeiko Carstens 			"	ld	10,80(%0)\n"
159f5daba1dSHeiko Carstens 			"	ld	11,88(%0)\n"
160f5daba1dSHeiko Carstens 			"	ld	12,96(%0)\n"
161f5daba1dSHeiko Carstens 			"	ld	13,104(%0)\n"
162f5daba1dSHeiko Carstens 			"	ld	14,112(%0)\n"
163f5daba1dSHeiko Carstens 			"	ld	15,120(%0)\n"
164f5daba1dSHeiko Carstens 			: : "a" (fpt_save_area));
165f5daba1dSHeiko Carstens 	}
166f5daba1dSHeiko Carstens 	/* Revalidate access registers */
167f5daba1dSHeiko Carstens 	asm volatile(
168f5daba1dSHeiko Carstens 		"	lam	0,15,0(%0)"
169f5daba1dSHeiko Carstens 		: : "a" (&S390_lowcore.access_regs_save_area));
170f5daba1dSHeiko Carstens 	if (!mci->ar) {
171f5daba1dSHeiko Carstens 		/*
172f5daba1dSHeiko Carstens 		 * Access registers have unknown contents.
173f5daba1dSHeiko Carstens 		 * Terminating task.
174f5daba1dSHeiko Carstens 		 */
175f5daba1dSHeiko Carstens 		kill_task = 1;
176f5daba1dSHeiko Carstens 	}
177f5daba1dSHeiko Carstens 	/* Revalidate control registers */
178f5daba1dSHeiko Carstens 	if (!mci->cr) {
179f5daba1dSHeiko Carstens 		/*
180f5daba1dSHeiko Carstens 		 * Control registers have unknown contents.
181f5daba1dSHeiko Carstens 		 * Can't recover and therefore stopping machine.
182f5daba1dSHeiko Carstens 		 */
183f5daba1dSHeiko Carstens 		s390_handle_damage("invalid control registers.");
184f5daba1dSHeiko Carstens 	} else {
185f5daba1dSHeiko Carstens #ifdef CONFIG_64BIT
186f5daba1dSHeiko Carstens 		asm volatile(
187f5daba1dSHeiko Carstens 			"	lctlg	0,15,0(%0)"
188f5daba1dSHeiko Carstens 			: : "a" (&S390_lowcore.cregs_save_area));
189f5daba1dSHeiko Carstens #else
190f5daba1dSHeiko Carstens 		asm volatile(
191f5daba1dSHeiko Carstens 			"	lctl	0,15,0(%0)"
192f5daba1dSHeiko Carstens 			: : "a" (&S390_lowcore.cregs_save_area));
193f5daba1dSHeiko Carstens #endif
194f5daba1dSHeiko Carstens 	}
195f5daba1dSHeiko Carstens 	/*
196f5daba1dSHeiko Carstens 	 * We don't even try to revalidate the TOD register, since we simply
197f5daba1dSHeiko Carstens 	 * can't write something sensible into that register.
198f5daba1dSHeiko Carstens 	 */
199f5daba1dSHeiko Carstens #ifdef CONFIG_64BIT
200f5daba1dSHeiko Carstens 	/*
201f5daba1dSHeiko Carstens 	 * See if we can revalidate the TOD programmable register with its
202f5daba1dSHeiko Carstens 	 * old contents (should be zero) otherwise set it to zero.
203f5daba1dSHeiko Carstens 	 */
204f5daba1dSHeiko Carstens 	if (!mci->pr)
205f5daba1dSHeiko Carstens 		asm volatile(
206f5daba1dSHeiko Carstens 			"	sr	0,0\n"
207f5daba1dSHeiko Carstens 			"	sckpf"
208f5daba1dSHeiko Carstens 			: : : "0", "cc");
209f5daba1dSHeiko Carstens 	else
210f5daba1dSHeiko Carstens 		asm volatile(
211f5daba1dSHeiko Carstens 			"	l	0,0(%0)\n"
212f5daba1dSHeiko Carstens 			"	sckpf"
213f5daba1dSHeiko Carstens 			: : "a" (&S390_lowcore.tod_progreg_save_area)
214f5daba1dSHeiko Carstens 			: "0", "cc");
215f5daba1dSHeiko Carstens #endif
216f5daba1dSHeiko Carstens 	/* Revalidate clock comparator register */
217e8129c64SHeiko Carstens 	if (S390_lowcore.clock_comparator == -1)
218e8129c64SHeiko Carstens 		set_clock_comparator(S390_lowcore.mcck_clock);
219e8129c64SHeiko Carstens 	else
220e8129c64SHeiko Carstens 		set_clock_comparator(S390_lowcore.clock_comparator);
221f5daba1dSHeiko Carstens 	/* Check if old PSW is valid */
222f5daba1dSHeiko Carstens 	if (!mci->wp)
223f5daba1dSHeiko Carstens 		/*
224f5daba1dSHeiko Carstens 		 * Can't tell if we come from user or kernel mode
225f5daba1dSHeiko Carstens 		 * -> stopping machine.
226f5daba1dSHeiko Carstens 		 */
227f5daba1dSHeiko Carstens 		s390_handle_damage("old psw invalid.");
228f5daba1dSHeiko Carstens 
229f5daba1dSHeiko Carstens 	if (!mci->ms || !mci->pm || !mci->ia)
230f5daba1dSHeiko Carstens 		kill_task = 1;
231f5daba1dSHeiko Carstens 
232f5daba1dSHeiko Carstens 	return kill_task;
233f5daba1dSHeiko Carstens }
234f5daba1dSHeiko Carstens 
235f5daba1dSHeiko Carstens #define MAX_IPD_COUNT	29
236f5daba1dSHeiko Carstens #define MAX_IPD_TIME	(5 * 60 * USEC_PER_SEC) /* 5 minutes */
237f5daba1dSHeiko Carstens 
238f5daba1dSHeiko Carstens #define ED_STP_ISLAND	6	/* External damage STP island check */
239f5daba1dSHeiko Carstens #define ED_STP_SYNC	7	/* External damage STP sync check */
240f5daba1dSHeiko Carstens #define ED_ETR_SYNC	12	/* External damage ETR sync check */
241f5daba1dSHeiko Carstens #define ED_ETR_SWITCH	13	/* External damage ETR switch to local */
242f5daba1dSHeiko Carstens 
243f5daba1dSHeiko Carstens /*
244f5daba1dSHeiko Carstens  * machine check handler.
245f5daba1dSHeiko Carstens  */
246f5daba1dSHeiko Carstens void notrace s390_do_machine_check(struct pt_regs *regs)
247f5daba1dSHeiko Carstens {
248f5daba1dSHeiko Carstens 	static int ipd_count;
249f5daba1dSHeiko Carstens 	static DEFINE_SPINLOCK(ipd_lock);
250f5daba1dSHeiko Carstens 	static unsigned long long last_ipd;
251f5daba1dSHeiko Carstens 	struct mcck_struct *mcck;
252f5daba1dSHeiko Carstens 	unsigned long long tmp;
253f5daba1dSHeiko Carstens 	struct mci *mci;
254f5daba1dSHeiko Carstens 	int umode;
255f5daba1dSHeiko Carstens 
25681f64b87SHeiko Carstens 	nmi_enter();
2576377981fSMartin Schwidefsky 	s390_idle_check(regs, S390_lowcore.mcck_clock,
2586377981fSMartin Schwidefsky 			S390_lowcore.mcck_enter_timer);
259*052ff461SHeiko Carstens 	kstat_cpu(smp_processor_id()).irqs[NMI_NMI]++;
260f5daba1dSHeiko Carstens 	mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
261f5daba1dSHeiko Carstens 	mcck = &__get_cpu_var(cpu_mcck);
262f5daba1dSHeiko Carstens 	umode = user_mode(regs);
263f5daba1dSHeiko Carstens 
264f5daba1dSHeiko Carstens 	if (mci->sd) {
265f5daba1dSHeiko Carstens 		/* System damage -> stopping machine */
266f5daba1dSHeiko Carstens 		s390_handle_damage("received system damage machine check.");
267f5daba1dSHeiko Carstens 	}
268f5daba1dSHeiko Carstens 	if (mci->pd) {
269f5daba1dSHeiko Carstens 		if (mci->b) {
270f5daba1dSHeiko Carstens 			/* Processing backup -> verify if we can survive this */
271f5daba1dSHeiko Carstens 			u64 z_mcic, o_mcic, t_mcic;
272f5daba1dSHeiko Carstens #ifdef CONFIG_64BIT
273f5daba1dSHeiko Carstens 			z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<29);
274f5daba1dSHeiko Carstens 			o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
275f5daba1dSHeiko Carstens 				  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
276f5daba1dSHeiko Carstens 				  1ULL<<30 | 1ULL<<21 | 1ULL<<20 | 1ULL<<17 |
277f5daba1dSHeiko Carstens 				  1ULL<<16);
278f5daba1dSHeiko Carstens #else
279f5daba1dSHeiko Carstens 			z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<57 | 1ULL<<50 |
280f5daba1dSHeiko Carstens 				  1ULL<<29);
281f5daba1dSHeiko Carstens 			o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
282f5daba1dSHeiko Carstens 				  1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
283f5daba1dSHeiko Carstens 				  1ULL<<30 | 1ULL<<20 | 1ULL<<17 | 1ULL<<16);
284f5daba1dSHeiko Carstens #endif
285f5daba1dSHeiko Carstens 			t_mcic = *(u64 *)mci;
286f5daba1dSHeiko Carstens 
287f5daba1dSHeiko Carstens 			if (((t_mcic & z_mcic) != 0) ||
288f5daba1dSHeiko Carstens 			    ((t_mcic & o_mcic) != o_mcic)) {
289f5daba1dSHeiko Carstens 				s390_handle_damage("processing backup machine "
290f5daba1dSHeiko Carstens 						   "check with damage.");
291f5daba1dSHeiko Carstens 			}
292f5daba1dSHeiko Carstens 
293f5daba1dSHeiko Carstens 			/*
294f5daba1dSHeiko Carstens 			 * Nullifying exigent condition, therefore we might
295f5daba1dSHeiko Carstens 			 * retry this instruction.
296f5daba1dSHeiko Carstens 			 */
297f5daba1dSHeiko Carstens 			spin_lock(&ipd_lock);
298f5daba1dSHeiko Carstens 			tmp = get_clock();
299f5daba1dSHeiko Carstens 			if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME)
300f5daba1dSHeiko Carstens 				ipd_count++;
301f5daba1dSHeiko Carstens 			else
302f5daba1dSHeiko Carstens 				ipd_count = 1;
303f5daba1dSHeiko Carstens 			last_ipd = tmp;
304f5daba1dSHeiko Carstens 			if (ipd_count == MAX_IPD_COUNT)
305f5daba1dSHeiko Carstens 				s390_handle_damage("too many ipd retries.");
306f5daba1dSHeiko Carstens 			spin_unlock(&ipd_lock);
307f5daba1dSHeiko Carstens 		} else {
308f5daba1dSHeiko Carstens 			/* Processing damage -> stopping machine */
309f5daba1dSHeiko Carstens 			s390_handle_damage("received instruction processing "
310f5daba1dSHeiko Carstens 					   "damage machine check.");
311f5daba1dSHeiko Carstens 		}
312f5daba1dSHeiko Carstens 	}
313f5daba1dSHeiko Carstens 	if (s390_revalidate_registers(mci)) {
314f5daba1dSHeiko Carstens 		if (umode) {
315f5daba1dSHeiko Carstens 			/*
316f5daba1dSHeiko Carstens 			 * Couldn't restore all register contents while in
317f5daba1dSHeiko Carstens 			 * user mode -> mark task for termination.
318f5daba1dSHeiko Carstens 			 */
319f5daba1dSHeiko Carstens 			mcck->kill_task = 1;
320f5daba1dSHeiko Carstens 			mcck->mcck_code = *(unsigned long long *) mci;
321f5daba1dSHeiko Carstens 			set_thread_flag(TIF_MCCK_PENDING);
322f5daba1dSHeiko Carstens 		} else {
323f5daba1dSHeiko Carstens 			/*
324f5daba1dSHeiko Carstens 			 * Couldn't restore all register contents while in
325f5daba1dSHeiko Carstens 			 * kernel mode -> stopping machine.
326f5daba1dSHeiko Carstens 			 */
327f5daba1dSHeiko Carstens 			s390_handle_damage("unable to revalidate registers.");
328f5daba1dSHeiko Carstens 		}
329f5daba1dSHeiko Carstens 	}
330f5daba1dSHeiko Carstens 	if (mci->cd) {
331f5daba1dSHeiko Carstens 		/* Timing facility damage */
332f5daba1dSHeiko Carstens 		s390_handle_damage("TOD clock damaged");
333f5daba1dSHeiko Carstens 	}
334f5daba1dSHeiko Carstens 	if (mci->ed && mci->ec) {
335f5daba1dSHeiko Carstens 		/* External damage */
336f5daba1dSHeiko Carstens 		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
337f5daba1dSHeiko Carstens 			etr_sync_check();
338f5daba1dSHeiko Carstens 		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
339f5daba1dSHeiko Carstens 			etr_switch_to_local();
340f5daba1dSHeiko Carstens 		if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC))
341f5daba1dSHeiko Carstens 			stp_sync_check();
342f5daba1dSHeiko Carstens 		if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND))
343f5daba1dSHeiko Carstens 			stp_island_check();
344f5daba1dSHeiko Carstens 	}
345f5daba1dSHeiko Carstens 	if (mci->se)
346f5daba1dSHeiko Carstens 		/* Storage error uncorrected */
347f5daba1dSHeiko Carstens 		s390_handle_damage("received storage error uncorrected "
348f5daba1dSHeiko Carstens 				   "machine check.");
349f5daba1dSHeiko Carstens 	if (mci->ke)
350f5daba1dSHeiko Carstens 		/* Storage key-error uncorrected */
351f5daba1dSHeiko Carstens 		s390_handle_damage("received storage key-error uncorrected "
352f5daba1dSHeiko Carstens 				   "machine check.");
353f5daba1dSHeiko Carstens 	if (mci->ds && mci->fa)
354f5daba1dSHeiko Carstens 		/* Storage degradation */
355f5daba1dSHeiko Carstens 		s390_handle_damage("received storage degradation machine "
356f5daba1dSHeiko Carstens 				   "check.");
357f5daba1dSHeiko Carstens 	if (mci->cp) {
358f5daba1dSHeiko Carstens 		/* Channel report word pending */
359f5daba1dSHeiko Carstens 		mcck->channel_report = 1;
360f5daba1dSHeiko Carstens 		set_thread_flag(TIF_MCCK_PENDING);
361f5daba1dSHeiko Carstens 	}
362f5daba1dSHeiko Carstens 	if (mci->w) {
363f5daba1dSHeiko Carstens 		/* Warning pending */
364f5daba1dSHeiko Carstens 		mcck->warning = 1;
365f5daba1dSHeiko Carstens 		set_thread_flag(TIF_MCCK_PENDING);
366f5daba1dSHeiko Carstens 	}
36781f64b87SHeiko Carstens 	nmi_exit();
368f5daba1dSHeiko Carstens }
369f5daba1dSHeiko Carstens 
370f5daba1dSHeiko Carstens static int __init machine_check_init(void)
371f5daba1dSHeiko Carstens {
372f5daba1dSHeiko Carstens 	ctl_set_bit(14, 25);	/* enable external damage MCH */
373f5daba1dSHeiko Carstens 	ctl_set_bit(14, 27);	/* enable system recovery MCH */
374f5daba1dSHeiko Carstens 	ctl_set_bit(14, 24);	/* enable warning MCH */
375f5daba1dSHeiko Carstens 	return 0;
376f5daba1dSHeiko Carstens }
377f5daba1dSHeiko Carstens arch_initcall(machine_check_init);
378