Commit f5daba1d4116d964435ddd99f32b6c80448a496b
Committed by
Martin Schwidefsky
1 parent
70193af918
Exists in
master
and in
7 other branches
[S390] split/move machine check handler code
Split machine check handler code and move it to cio and kernel code where it belongs to. No functional change. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Showing 15 changed files with 677 additions and 676 deletions Side-by-side Diff
- arch/s390/include/asm/crw.h
- arch/s390/include/asm/nmi.h
- arch/s390/kernel/Makefile
- arch/s390/kernel/nmi.c
- arch/s390/kernel/process.c
- arch/s390/kvm/kvm-s390.c
- drivers/s390/Makefile
- drivers/s390/cio/Makefile
- drivers/s390/cio/chp.c
- drivers/s390/cio/chsc.c
- drivers/s390/cio/cio.c
- drivers/s390/cio/crw.c
- drivers/s390/cio/css.c
- drivers/s390/s390mach.c
- drivers/s390/s390mach.h
arch/s390/include/asm/crw.h
1 | +/* | |
2 | + * Data definitions for channel report processing | |
3 | + * Copyright IBM Corp. 2000,2009 | |
4 | + * Author(s): Ingo Adlung <adlung@de.ibm.com>, | |
5 | + * Martin Schwidefsky <schwidefsky@de.ibm.com>, | |
6 | + * Cornelia Huck <cornelia.huck@de.ibm.com>, | |
7 | + * Heiko Carstens <heiko.carstens@de.ibm.com>, | |
8 | + */ | |
9 | + | |
10 | +#ifndef _ASM_S390_CRW_H | |
11 | +#define _ASM_S390_CRW_H | |
12 | + | |
13 | +#include <linux/types.h> | |
14 | + | |
15 | +/* | |
16 | + * Channel Report Word | |
17 | + */ | |
18 | +struct crw { | |
19 | + __u32 res1 : 1; /* reserved zero */ | |
20 | + __u32 slct : 1; /* solicited */ | |
21 | + __u32 oflw : 1; /* overflow */ | |
22 | + __u32 chn : 1; /* chained */ | |
23 | + __u32 rsc : 4; /* reporting source code */ | |
24 | + __u32 anc : 1; /* ancillary report */ | |
25 | + __u32 res2 : 1; /* reserved zero */ | |
26 | + __u32 erc : 6; /* error-recovery code */ | |
27 | + __u32 rsid : 16; /* reporting-source ID */ | |
28 | +} __attribute__ ((packed)); | |
29 | + | |
30 | +typedef void (*crw_handler_t)(struct crw *, struct crw *, int); | |
31 | + | |
32 | +extern int crw_register_handler(int rsc, crw_handler_t handler); | |
33 | +extern void crw_unregister_handler(int rsc); | |
34 | +extern void crw_handle_channel_report(void); | |
35 | + | |
36 | +#define NR_RSCS 16 | |
37 | + | |
38 | +#define CRW_RSC_MONITOR 0x2 /* monitoring facility */ | |
39 | +#define CRW_RSC_SCH 0x3 /* subchannel */ | |
40 | +#define CRW_RSC_CPATH 0x4 /* channel path */ | |
41 | +#define CRW_RSC_CONFIG 0x9 /* configuration-alert facility */ | |
42 | +#define CRW_RSC_CSS 0xB /* channel subsystem */ | |
43 | + | |
44 | +#define CRW_ERC_EVENT 0x00 /* event information pending */ | |
45 | +#define CRW_ERC_AVAIL 0x01 /* available */ | |
46 | +#define CRW_ERC_INIT 0x02 /* initialized */ | |
47 | +#define CRW_ERC_TERROR 0x03 /* temporary error */ | |
48 | +#define CRW_ERC_IPARM 0x04 /* installed parm initialized */ | |
49 | +#define CRW_ERC_TERM 0x05 /* terminal */ | |
50 | +#define CRW_ERC_PERRN 0x06 /* perm. error, fac. not init */ | |
51 | +#define CRW_ERC_PERRI 0x07 /* perm. error, facility init */ | |
52 | +#define CRW_ERC_PMOD 0x08 /* installed parameters modified */ | |
53 | + | |
54 | +static inline int stcrw(struct crw *pcrw) | |
55 | +{ | |
56 | + int ccode; | |
57 | + | |
58 | + asm volatile( | |
59 | + " stcrw 0(%2)\n" | |
60 | + " ipm %0\n" | |
61 | + " srl %0,28\n" | |
62 | + : "=d" (ccode), "=m" (*pcrw) | |
63 | + : "a" (pcrw) | |
64 | + : "cc" ); | |
65 | + return ccode; | |
66 | +} | |
67 | + | |
68 | +#endif /* _ASM_S390_CRW_H */ |
arch/s390/include/asm/nmi.h
1 | +/* | |
2 | + * Machine check handler definitions | |
3 | + * | |
4 | + * Copyright IBM Corp. 2000,2009 | |
5 | + * Author(s): Ingo Adlung <adlung@de.ibm.com>, | |
6 | + * Martin Schwidefsky <schwidefsky@de.ibm.com>, | |
7 | + * Cornelia Huck <cornelia.huck@de.ibm.com>, | |
8 | + * Heiko Carstens <heiko.carstens@de.ibm.com>, | |
9 | + */ | |
10 | + | |
11 | +#ifndef _ASM_S390_NMI_H | |
12 | +#define _ASM_S390_NMI_H | |
13 | + | |
14 | +#include <linux/types.h> | |
15 | + | |
16 | +struct mci { | |
17 | + __u32 sd : 1; /* 00 system damage */ | |
18 | + __u32 pd : 1; /* 01 instruction-processing damage */ | |
19 | + __u32 sr : 1; /* 02 system recovery */ | |
20 | + __u32 : 1; /* 03 */ | |
21 | + __u32 cd : 1; /* 04 timing-facility damage */ | |
22 | + __u32 ed : 1; /* 05 external damage */ | |
23 | + __u32 : 1; /* 06 */ | |
24 | + __u32 dg : 1; /* 07 degradation */ | |
25 | + __u32 w : 1; /* 08 warning pending */ | |
26 | + __u32 cp : 1; /* 09 channel-report pending */ | |
27 | + __u32 sp : 1; /* 10 service-processor damage */ | |
28 | + __u32 ck : 1; /* 11 channel-subsystem damage */ | |
29 | + __u32 : 2; /* 12-13 */ | |
30 | + __u32 b : 1; /* 14 backed up */ | |
31 | + __u32 : 1; /* 15 */ | |
32 | + __u32 se : 1; /* 16 storage error uncorrected */ | |
33 | + __u32 sc : 1; /* 17 storage error corrected */ | |
34 | + __u32 ke : 1; /* 18 storage-key error uncorrected */ | |
35 | + __u32 ds : 1; /* 19 storage degradation */ | |
36 | + __u32 wp : 1; /* 20 psw mwp validity */ | |
37 | + __u32 ms : 1; /* 21 psw mask and key validity */ | |
38 | + __u32 pm : 1; /* 22 psw program mask and cc validity */ | |
39 | + __u32 ia : 1; /* 23 psw instruction address validity */ | |
40 | + __u32 fa : 1; /* 24 failing storage address validity */ | |
41 | + __u32 : 1; /* 25 */ | |
42 | + __u32 ec : 1; /* 26 external damage code validity */ | |
43 | + __u32 fp : 1; /* 27 floating point register validity */ | |
44 | + __u32 gr : 1; /* 28 general register validity */ | |
45 | + __u32 cr : 1; /* 29 control register validity */ | |
46 | + __u32 : 1; /* 30 */ | |
47 | + __u32 st : 1; /* 31 storage logical validity */ | |
48 | + __u32 ie : 1; /* 32 indirect storage error */ | |
49 | + __u32 ar : 1; /* 33 access register validity */ | |
50 | + __u32 da : 1; /* 34 delayed access exception */ | |
51 | + __u32 : 7; /* 35-41 */ | |
52 | + __u32 pr : 1; /* 42 tod programmable register validity */ | |
53 | + __u32 fc : 1; /* 43 fp control register validity */ | |
54 | + __u32 ap : 1; /* 44 ancillary report */ | |
55 | + __u32 : 1; /* 45 */ | |
56 | + __u32 ct : 1; /* 46 cpu timer validity */ | |
57 | + __u32 cc : 1; /* 47 clock comparator validity */ | |
58 | + __u32 : 16; /* 47-63 */ | |
59 | +}; | |
60 | + | |
61 | +struct pt_regs; | |
62 | + | |
63 | +extern void s390_handle_mcck(void); | |
64 | +extern void s390_do_machine_check(struct pt_regs *regs); | |
65 | + | |
66 | +#endif /* _ASM_S390_NMI_H */ |
arch/s390/kernel/Makefile
... | ... | @@ -22,7 +22,7 @@ |
22 | 22 | obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o \ |
23 | 23 | processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ |
24 | 24 | s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o \ |
25 | - vdso.o vtime.o sysinfo.o | |
25 | + vdso.o vtime.o sysinfo.o nmi.o | |
26 | 26 | |
27 | 27 | obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) |
28 | 28 | obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) |
arch/s390/kernel/nmi.c
1 | +/* | |
2 | + * Machine check handler | |
3 | + * | |
4 | + * Copyright IBM Corp. 2000,2009 | |
5 | + * Author(s): Ingo Adlung <adlung@de.ibm.com>, | |
6 | + * Martin Schwidefsky <schwidefsky@de.ibm.com>, | |
7 | + * Cornelia Huck <cornelia.huck@de.ibm.com>, | |
8 | + * Heiko Carstens <heiko.carstens@de.ibm.com>, | |
9 | + */ | |
10 | + | |
11 | +#include <linux/init.h> | |
12 | +#include <linux/errno.h> | |
13 | +#include <linux/time.h> | |
14 | +#include <linux/module.h> | |
15 | +#include <asm/lowcore.h> | |
16 | +#include <asm/smp.h> | |
17 | +#include <asm/etr.h> | |
18 | +#include <asm/cpu.h> | |
19 | +#include <asm/nmi.h> | |
20 | +#include <asm/crw.h> | |
21 | + | |
22 | +struct mcck_struct { | |
23 | + int kill_task; | |
24 | + int channel_report; | |
25 | + int warning; | |
26 | + unsigned long long mcck_code; | |
27 | +}; | |
28 | + | |
29 | +static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck); | |
30 | + | |
31 | +static NORET_TYPE void s390_handle_damage(char *msg) | |
32 | +{ | |
33 | + smp_send_stop(); | |
34 | + disabled_wait((unsigned long) __builtin_return_address(0)); | |
35 | + while (1); | |
36 | +} | |
37 | + | |
38 | +/* | |
39 | + * Main machine check handler function. Will be called with interrupts enabled | |
40 | + * or disabled and machine checks enabled or disabled. | |
41 | + */ | |
42 | +void s390_handle_mcck(void) | |
43 | +{ | |
44 | + unsigned long flags; | |
45 | + struct mcck_struct mcck; | |
46 | + | |
47 | + /* | |
48 | + * Disable machine checks and get the current state of accumulated | |
49 | + * machine checks. Afterwards delete the old state and enable machine | |
50 | + * checks again. | |
51 | + */ | |
52 | + local_irq_save(flags); | |
53 | + local_mcck_disable(); | |
54 | + mcck = __get_cpu_var(cpu_mcck); | |
55 | + memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct)); | |
56 | + clear_thread_flag(TIF_MCCK_PENDING); | |
57 | + local_mcck_enable(); | |
58 | + local_irq_restore(flags); | |
59 | + | |
60 | + if (mcck.channel_report) | |
61 | + crw_handle_channel_report(); | |
62 | + | |
63 | +#ifdef CONFIG_MACHCHK_WARNING | |
64 | +/* | |
65 | + * The warning may remain for a prolonged period on the bare iron. | |
66 | + * (actually till the machine is powered off, or until the problem is gone) | |
67 | + * So we just stop listening for the WARNING MCH and prevent continuously | |
68 | + * being interrupted. One caveat is however, that we must do this per | |
69 | + * processor and cannot use the smp version of ctl_clear_bit(). | |
70 | + * On VM we only get one interrupt per virtally presented machinecheck. | |
71 | + * Though one suffices, we may get one interrupt per (virtual) processor. | |
72 | + */ | |
73 | + if (mcck.warning) { /* WARNING pending ? */ | |
74 | + static int mchchk_wng_posted = 0; | |
75 | + /* | |
76 | + * Use single machine clear, as we cannot handle smp right now | |
77 | + */ | |
78 | + __ctl_clear_bit(14, 24); /* Disable WARNING MCH */ | |
79 | + if (xchg(&mchchk_wng_posted, 1) == 0) | |
80 | + kill_cad_pid(SIGPWR, 1); | |
81 | + } | |
82 | +#endif | |
83 | + | |
84 | + if (mcck.kill_task) { | |
85 | + local_irq_enable(); | |
86 | + printk(KERN_EMERG "mcck: Terminating task because of machine " | |
87 | + "malfunction (code 0x%016llx).\n", mcck.mcck_code); | |
88 | + printk(KERN_EMERG "mcck: task: %s, pid: %d.\n", | |
89 | + current->comm, current->pid); | |
90 | + do_exit(SIGSEGV); | |
91 | + } | |
92 | +} | |
93 | +EXPORT_SYMBOL_GPL(s390_handle_mcck); | |
94 | + | |
95 | +/* | |
96 | + * returns 0 if all registers could be validated | |
97 | + * returns 1 otherwise | |
98 | + */ | |
99 | +static int notrace s390_revalidate_registers(struct mci *mci) | |
100 | +{ | |
101 | + int kill_task; | |
102 | + u64 tmpclock; | |
103 | + u64 zero; | |
104 | + void *fpt_save_area, *fpt_creg_save_area; | |
105 | + | |
106 | + kill_task = 0; | |
107 | + zero = 0; | |
108 | + | |
109 | + if (!mci->gr) { | |
110 | + /* | |
111 | + * General purpose registers couldn't be restored and have | |
112 | + * unknown contents. Process needs to be terminated. | |
113 | + */ | |
114 | + kill_task = 1; | |
115 | + } | |
116 | + if (!mci->fp) { | |
117 | + /* | |
118 | + * Floating point registers can't be restored and | |
119 | + * therefore the process needs to be terminated. | |
120 | + */ | |
121 | + kill_task = 1; | |
122 | + } | |
123 | +#ifndef CONFIG_64BIT | |
124 | + asm volatile( | |
125 | + " ld 0,0(%0)\n" | |
126 | + " ld 2,8(%0)\n" | |
127 | + " ld 4,16(%0)\n" | |
128 | + " ld 6,24(%0)" | |
129 | + : : "a" (&S390_lowcore.floating_pt_save_area)); | |
130 | +#endif | |
131 | + | |
132 | + if (MACHINE_HAS_IEEE) { | |
133 | +#ifdef CONFIG_64BIT | |
134 | + fpt_save_area = &S390_lowcore.floating_pt_save_area; | |
135 | + fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area; | |
136 | +#else | |
137 | + fpt_save_area = (void *) S390_lowcore.extended_save_area_addr; | |
138 | + fpt_creg_save_area = fpt_save_area + 128; | |
139 | +#endif | |
140 | + if (!mci->fc) { | |
141 | + /* | |
142 | + * Floating point control register can't be restored. | |
143 | + * Task will be terminated. | |
144 | + */ | |
145 | + asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero)); | |
146 | + kill_task = 1; | |
147 | + | |
148 | + } else | |
149 | + asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area)); | |
150 | + | |
151 | + asm volatile( | |
152 | + " ld 0,0(%0)\n" | |
153 | + " ld 1,8(%0)\n" | |
154 | + " ld 2,16(%0)\n" | |
155 | + " ld 3,24(%0)\n" | |
156 | + " ld 4,32(%0)\n" | |
157 | + " ld 5,40(%0)\n" | |
158 | + " ld 6,48(%0)\n" | |
159 | + " ld 7,56(%0)\n" | |
160 | + " ld 8,64(%0)\n" | |
161 | + " ld 9,72(%0)\n" | |
162 | + " ld 10,80(%0)\n" | |
163 | + " ld 11,88(%0)\n" | |
164 | + " ld 12,96(%0)\n" | |
165 | + " ld 13,104(%0)\n" | |
166 | + " ld 14,112(%0)\n" | |
167 | + " ld 15,120(%0)\n" | |
168 | + : : "a" (fpt_save_area)); | |
169 | + } | |
170 | + /* Revalidate access registers */ | |
171 | + asm volatile( | |
172 | + " lam 0,15,0(%0)" | |
173 | + : : "a" (&S390_lowcore.access_regs_save_area)); | |
174 | + if (!mci->ar) { | |
175 | + /* | |
176 | + * Access registers have unknown contents. | |
177 | + * Terminating task. | |
178 | + */ | |
179 | + kill_task = 1; | |
180 | + } | |
181 | + /* Revalidate control registers */ | |
182 | + if (!mci->cr) { | |
183 | + /* | |
184 | + * Control registers have unknown contents. | |
185 | + * Can't recover and therefore stopping machine. | |
186 | + */ | |
187 | + s390_handle_damage("invalid control registers."); | |
188 | + } else { | |
189 | +#ifdef CONFIG_64BIT | |
190 | + asm volatile( | |
191 | + " lctlg 0,15,0(%0)" | |
192 | + : : "a" (&S390_lowcore.cregs_save_area)); | |
193 | +#else | |
194 | + asm volatile( | |
195 | + " lctl 0,15,0(%0)" | |
196 | + : : "a" (&S390_lowcore.cregs_save_area)); | |
197 | +#endif | |
198 | + } | |
199 | + /* | |
200 | + * We don't even try to revalidate the TOD register, since we simply | |
201 | + * can't write something sensible into that register. | |
202 | + */ | |
203 | +#ifdef CONFIG_64BIT | |
204 | + /* | |
205 | + * See if we can revalidate the TOD programmable register with its | |
206 | + * old contents (should be zero) otherwise set it to zero. | |
207 | + */ | |
208 | + if (!mci->pr) | |
209 | + asm volatile( | |
210 | + " sr 0,0\n" | |
211 | + " sckpf" | |
212 | + : : : "0", "cc"); | |
213 | + else | |
214 | + asm volatile( | |
215 | + " l 0,0(%0)\n" | |
216 | + " sckpf" | |
217 | + : : "a" (&S390_lowcore.tod_progreg_save_area) | |
218 | + : "0", "cc"); | |
219 | +#endif | |
220 | + /* Revalidate clock comparator register */ | |
221 | + asm volatile( | |
222 | + " stck 0(%1)\n" | |
223 | + " sckc 0(%1)" | |
224 | + : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory"); | |
225 | + | |
226 | + /* Check if old PSW is valid */ | |
227 | + if (!mci->wp) | |
228 | + /* | |
229 | + * Can't tell if we come from user or kernel mode | |
230 | + * -> stopping machine. | |
231 | + */ | |
232 | + s390_handle_damage("old psw invalid."); | |
233 | + | |
234 | + if (!mci->ms || !mci->pm || !mci->ia) | |
235 | + kill_task = 1; | |
236 | + | |
237 | + return kill_task; | |
238 | +} | |
239 | + | |
240 | +#define MAX_IPD_COUNT 29 | |
241 | +#define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */ | |
242 | + | |
243 | +#define ED_STP_ISLAND 6 /* External damage STP island check */ | |
244 | +#define ED_STP_SYNC 7 /* External damage STP sync check */ | |
245 | +#define ED_ETR_SYNC 12 /* External damage ETR sync check */ | |
246 | +#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */ | |
247 | + | |
248 | +/* | |
249 | + * machine check handler. | |
250 | + */ | |
251 | +void notrace s390_do_machine_check(struct pt_regs *regs) | |
252 | +{ | |
253 | + static int ipd_count; | |
254 | + static DEFINE_SPINLOCK(ipd_lock); | |
255 | + static unsigned long long last_ipd; | |
256 | + struct mcck_struct *mcck; | |
257 | + unsigned long long tmp; | |
258 | + struct mci *mci; | |
259 | + int umode; | |
260 | + | |
261 | + lockdep_off(); | |
262 | + s390_idle_check(); | |
263 | + | |
264 | + mci = (struct mci *) &S390_lowcore.mcck_interruption_code; | |
265 | + mcck = &__get_cpu_var(cpu_mcck); | |
266 | + umode = user_mode(regs); | |
267 | + | |
268 | + if (mci->sd) { | |
269 | + /* System damage -> stopping machine */ | |
270 | + s390_handle_damage("received system damage machine check."); | |
271 | + } | |
272 | + if (mci->pd) { | |
273 | + if (mci->b) { | |
274 | + /* Processing backup -> verify if we can survive this */ | |
275 | + u64 z_mcic, o_mcic, t_mcic; | |
276 | +#ifdef CONFIG_64BIT | |
277 | + z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<29); | |
278 | + o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 | | |
279 | + 1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 | | |
280 | + 1ULL<<30 | 1ULL<<21 | 1ULL<<20 | 1ULL<<17 | | |
281 | + 1ULL<<16); | |
282 | +#else | |
283 | + z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<57 | 1ULL<<50 | | |
284 | + 1ULL<<29); | |
285 | + o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 | | |
286 | + 1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 | | |
287 | + 1ULL<<30 | 1ULL<<20 | 1ULL<<17 | 1ULL<<16); | |
288 | +#endif | |
289 | + t_mcic = *(u64 *)mci; | |
290 | + | |
291 | + if (((t_mcic & z_mcic) != 0) || | |
292 | + ((t_mcic & o_mcic) != o_mcic)) { | |
293 | + s390_handle_damage("processing backup machine " | |
294 | + "check with damage."); | |
295 | + } | |
296 | + | |
297 | + /* | |
298 | + * Nullifying exigent condition, therefore we might | |
299 | + * retry this instruction. | |
300 | + */ | |
301 | + spin_lock(&ipd_lock); | |
302 | + tmp = get_clock(); | |
303 | + if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME) | |
304 | + ipd_count++; | |
305 | + else | |
306 | + ipd_count = 1; | |
307 | + last_ipd = tmp; | |
308 | + if (ipd_count == MAX_IPD_COUNT) | |
309 | + s390_handle_damage("too many ipd retries."); | |
310 | + spin_unlock(&ipd_lock); | |
311 | + } else { | |
312 | + /* Processing damage -> stopping machine */ | |
313 | + s390_handle_damage("received instruction processing " | |
314 | + "damage machine check."); | |
315 | + } | |
316 | + } | |
317 | + if (s390_revalidate_registers(mci)) { | |
318 | + if (umode) { | |
319 | + /* | |
320 | + * Couldn't restore all register contents while in | |
321 | + * user mode -> mark task for termination. | |
322 | + */ | |
323 | + mcck->kill_task = 1; | |
324 | + mcck->mcck_code = *(unsigned long long *) mci; | |
325 | + set_thread_flag(TIF_MCCK_PENDING); | |
326 | + } else { | |
327 | + /* | |
328 | + * Couldn't restore all register contents while in | |
329 | + * kernel mode -> stopping machine. | |
330 | + */ | |
331 | + s390_handle_damage("unable to revalidate registers."); | |
332 | + } | |
333 | + } | |
334 | + if (mci->cd) { | |
335 | + /* Timing facility damage */ | |
336 | + s390_handle_damage("TOD clock damaged"); | |
337 | + } | |
338 | + if (mci->ed && mci->ec) { | |
339 | + /* External damage */ | |
340 | + if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC)) | |
341 | + etr_sync_check(); | |
342 | + if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH)) | |
343 | + etr_switch_to_local(); | |
344 | + if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC)) | |
345 | + stp_sync_check(); | |
346 | + if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND)) | |
347 | + stp_island_check(); | |
348 | + } | |
349 | + if (mci->se) | |
350 | + /* Storage error uncorrected */ | |
351 | + s390_handle_damage("received storage error uncorrected " | |
352 | + "machine check."); | |
353 | + if (mci->ke) | |
354 | + /* Storage key-error uncorrected */ | |
355 | + s390_handle_damage("received storage key-error uncorrected " | |
356 | + "machine check."); | |
357 | + if (mci->ds && mci->fa) | |
358 | + /* Storage degradation */ | |
359 | + s390_handle_damage("received storage degradation machine " | |
360 | + "check."); | |
361 | + if (mci->cp) { | |
362 | + /* Channel report word pending */ | |
363 | + mcck->channel_report = 1; | |
364 | + set_thread_flag(TIF_MCCK_PENDING); | |
365 | + } | |
366 | + if (mci->w) { | |
367 | + /* Warning pending */ | |
368 | + mcck->warning = 1; | |
369 | + set_thread_flag(TIF_MCCK_PENDING); | |
370 | + } | |
371 | + lockdep_on(); | |
372 | +} | |
373 | + | |
374 | +static int __init machine_check_init(void) | |
375 | +{ | |
376 | + ctl_set_bit(14, 25); /* enable external damage MCH */ | |
377 | + ctl_set_bit(14, 27); /* enable system recovery MCH */ | |
378 | +#ifdef CONFIG_MACHCHK_WARNING | |
379 | + ctl_set_bit(14, 24); /* enable warning MCH */ | |
380 | +#endif | |
381 | + return 0; | |
382 | +} | |
383 | +arch_initcall(machine_check_init); |
arch/s390/kernel/process.c
... | ... | @@ -39,6 +39,7 @@ |
39 | 39 | #include <asm/processor.h> |
40 | 40 | #include <asm/irq.h> |
41 | 41 | #include <asm/timer.h> |
42 | +#include <asm/nmi.h> | |
42 | 43 | #include "entry.h" |
43 | 44 | |
44 | 45 | asmlinkage void ret_from_fork(void) asm ("ret_from_fork"); |
... | ... | @@ -68,7 +69,6 @@ |
68 | 69 | return sf->gprs[8]; |
69 | 70 | } |
70 | 71 | |
71 | -extern void s390_handle_mcck(void); | |
72 | 72 | /* |
73 | 73 | * The idle loop on a S390... |
74 | 74 | */ |
arch/s390/kvm/kvm-s390.c
... | ... | @@ -23,7 +23,7 @@ |
23 | 23 | #include <linux/timer.h> |
24 | 24 | #include <asm/lowcore.h> |
25 | 25 | #include <asm/pgtable.h> |
26 | - | |
26 | +#include <asm/nmi.h> | |
27 | 27 | #include "kvm-s390.h" |
28 | 28 | #include "gaccess.h" |
29 | 29 | |
... | ... | @@ -439,8 +439,6 @@ |
439 | 439 | { |
440 | 440 | return -EINVAL; /* not implemented yet */ |
441 | 441 | } |
442 | - | |
443 | -extern void s390_handle_mcck(void); | |
444 | 442 | |
445 | 443 | static void __vcpu_run(struct kvm_vcpu *vcpu) |
446 | 444 | { |
drivers/s390/Makefile
drivers/s390/cio/Makefile
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | # |
4 | 4 | |
5 | 5 | obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o scsw.o \ |
6 | - fcx.o itcw.o | |
6 | + fcx.o itcw.o crw.o | |
7 | 7 | ccw_device-objs += device.o device_fsm.o device_ops.o |
8 | 8 | ccw_device-objs += device_id.o device_pgid.o device_status.o |
9 | 9 | obj-y += ccw_device.o cmf.o |
drivers/s390/cio/chp.c
... | ... | @@ -17,8 +17,8 @@ |
17 | 17 | #include <linux/errno.h> |
18 | 18 | #include <asm/chpid.h> |
19 | 19 | #include <asm/sclp.h> |
20 | +#include <asm/crw.h> | |
20 | 21 | |
21 | -#include "../s390mach.h" | |
22 | 22 | #include "cio.h" |
23 | 23 | #include "css.h" |
24 | 24 | #include "ioasm.h" |
25 | 25 | |
... | ... | @@ -706,12 +706,12 @@ |
706 | 706 | struct chp_id chpid; |
707 | 707 | int ret; |
708 | 708 | |
709 | - ret = s390_register_crw_handler(CRW_RSC_CPATH, chp_process_crw); | |
709 | + ret = crw_register_handler(CRW_RSC_CPATH, chp_process_crw); | |
710 | 710 | if (ret) |
711 | 711 | return ret; |
712 | 712 | chp_wq = create_singlethread_workqueue("cio_chp"); |
713 | 713 | if (!chp_wq) { |
714 | - s390_unregister_crw_handler(CRW_RSC_CPATH); | |
714 | + crw_unregister_handler(CRW_RSC_CPATH); | |
715 | 715 | return -ENOMEM; |
716 | 716 | } |
717 | 717 | INIT_WORK(&cfg_work, cfg_func); |
drivers/s390/cio/chsc.c
... | ... | @@ -19,8 +19,8 @@ |
19 | 19 | #include <asm/cio.h> |
20 | 20 | #include <asm/chpid.h> |
21 | 21 | #include <asm/chsc.h> |
22 | +#include <asm/crw.h> | |
22 | 23 | |
23 | -#include "../s390mach.h" | |
24 | 24 | #include "css.h" |
25 | 25 | #include "cio.h" |
26 | 26 | #include "cio_debug.h" |
... | ... | @@ -820,7 +820,7 @@ |
820 | 820 | "chsc machine checks!\n"); |
821 | 821 | return -ENOMEM; |
822 | 822 | } |
823 | - ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw); | |
823 | + ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); | |
824 | 824 | if (ret) |
825 | 825 | kfree(sei_page); |
826 | 826 | return ret; |
... | ... | @@ -828,7 +828,7 @@ |
828 | 828 | |
829 | 829 | void __init chsc_free_sei_area(void) |
830 | 830 | { |
831 | - s390_unregister_crw_handler(CRW_RSC_CSS); | |
831 | + crw_unregister_handler(CRW_RSC_CSS); | |
832 | 832 | kfree(sei_page); |
833 | 833 | } |
834 | 834 |
drivers/s390/cio/cio.c
... | ... | @@ -30,6 +30,8 @@ |
30 | 30 | #include <asm/isc.h> |
31 | 31 | #include <asm/cpu.h> |
32 | 32 | #include <asm/fcx.h> |
33 | +#include <asm/nmi.h> | |
34 | +#include <asm/crw.h> | |
33 | 35 | #include "cio.h" |
34 | 36 | #include "css.h" |
35 | 37 | #include "chsc.h" |
... | ... | @@ -38,7 +40,6 @@ |
38 | 40 | #include "blacklist.h" |
39 | 41 | #include "cio_debug.h" |
40 | 42 | #include "chp.h" |
41 | -#include "../s390mach.h" | |
42 | 43 | |
43 | 44 | debug_info_t *cio_debug_msg_id; |
44 | 45 | debug_info_t *cio_debug_trace_id; |
drivers/s390/cio/crw.c
1 | +/* | |
2 | + * Channel report handling code | |
3 | + * | |
4 | + * Copyright IBM Corp. 2000,2009 | |
5 | + * Author(s): Ingo Adlung <adlung@de.ibm.com>, | |
6 | + * Martin Schwidefsky <schwidefsky@de.ibm.com>, | |
7 | + * Cornelia Huck <cornelia.huck@de.ibm.com>, | |
8 | + * Heiko Carstens <heiko.carstens@de.ibm.com>, | |
9 | + */ | |
10 | + | |
11 | +#include <linux/semaphore.h> | |
12 | +#include <linux/kthread.h> | |
13 | +#include <linux/init.h> | |
14 | +#include <asm/crw.h> | |
15 | + | |
16 | +static struct semaphore crw_semaphore; | |
17 | +static crw_handler_t crw_handlers[NR_RSCS]; | |
18 | + | |
19 | +/** | |
20 | + * crw_register_handler() - register a channel report word handler | |
21 | + * @rsc: reporting source code to handle | |
22 | + * @handler: handler to be registered | |
23 | + * | |
24 | + * Returns %0 on success and a negative error value otherwise. | |
25 | + */ | |
26 | +int crw_register_handler(int rsc, crw_handler_t handler) | |
27 | +{ | |
28 | + if ((rsc < 0) || (rsc >= NR_RSCS)) | |
29 | + return -EINVAL; | |
30 | + if (!cmpxchg(&crw_handlers[rsc], NULL, handler)) | |
31 | + return 0; | |
32 | + return -EBUSY; | |
33 | +} | |
34 | + | |
35 | +/** | |
36 | + * crw_unregister_handler() - unregister a channel report word handler | |
37 | + * @rsc: reporting source code to handle | |
38 | + */ | |
39 | +void crw_unregister_handler(int rsc) | |
40 | +{ | |
41 | + if ((rsc < 0) || (rsc >= NR_RSCS)) | |
42 | + return; | |
43 | + xchg(&crw_handlers[rsc], NULL); | |
44 | + synchronize_sched(); | |
45 | +} | |
46 | + | |
47 | +/* | |
48 | + * Retrieve CRWs and call function to handle event. | |
49 | + */ | |
50 | +static int crw_collect_info(void *unused) | |
51 | +{ | |
52 | + struct crw crw[2]; | |
53 | + int ccode; | |
54 | + unsigned int chain; | |
55 | + int ignore; | |
56 | + | |
57 | +repeat: | |
58 | + ignore = down_interruptible(&crw_semaphore); | |
59 | + chain = 0; | |
60 | + while (1) { | |
61 | + if (unlikely(chain > 1)) { | |
62 | + struct crw tmp_crw; | |
63 | + | |
64 | + printk(KERN_WARNING"%s: Code does not support more " | |
65 | + "than two chained crws; please report to " | |
66 | + "linux390@de.ibm.com!\n", __func__); | |
67 | + ccode = stcrw(&tmp_crw); | |
68 | + printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, " | |
69 | + "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | |
70 | + __func__, tmp_crw.slct, tmp_crw.oflw, | |
71 | + tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc, | |
72 | + tmp_crw.erc, tmp_crw.rsid); | |
73 | + printk(KERN_WARNING"%s: This was crw number %x in the " | |
74 | + "chain\n", __func__, chain); | |
75 | + if (ccode != 0) | |
76 | + break; | |
77 | + chain = tmp_crw.chn ? chain + 1 : 0; | |
78 | + continue; | |
79 | + } | |
80 | + ccode = stcrw(&crw[chain]); | |
81 | + if (ccode != 0) | |
82 | + break; | |
83 | + printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, " | |
84 | + "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | |
85 | + crw[chain].slct, crw[chain].oflw, crw[chain].chn, | |
86 | + crw[chain].rsc, crw[chain].anc, crw[chain].erc, | |
87 | + crw[chain].rsid); | |
88 | + /* Check for overflows. */ | |
89 | + if (crw[chain].oflw) { | |
90 | + int i; | |
91 | + | |
92 | + pr_debug("%s: crw overflow detected!\n", __func__); | |
93 | + for (i = 0; i < NR_RSCS; i++) { | |
94 | + if (crw_handlers[i]) | |
95 | + crw_handlers[i](NULL, NULL, 1); | |
96 | + } | |
97 | + chain = 0; | |
98 | + continue; | |
99 | + } | |
100 | + if (crw[0].chn && !chain) { | |
101 | + chain++; | |
102 | + continue; | |
103 | + } | |
104 | + if (crw_handlers[crw[chain].rsc]) | |
105 | + crw_handlers[crw[chain].rsc](&crw[0], | |
106 | + chain ? &crw[1] : NULL, | |
107 | + 0); | |
108 | + /* chain is always 0 or 1 here. */ | |
109 | + chain = crw[chain].chn ? chain + 1 : 0; | |
110 | + } | |
111 | + goto repeat; | |
112 | + return 0; | |
113 | +} | |
114 | + | |
115 | +void crw_handle_channel_report(void) | |
116 | +{ | |
117 | + up(&crw_semaphore); | |
118 | +} | |
119 | + | |
120 | +/* | |
121 | + * Separate initcall needed for semaphore initialization since | |
122 | + * crw_handle_channel_report might be called before crw_machine_check_init. | |
123 | + */ | |
124 | +static int __init crw_init_semaphore(void) | |
125 | +{ | |
126 | + init_MUTEX_LOCKED(&crw_semaphore); | |
127 | + return 0; | |
128 | +} | |
129 | +pure_initcall(crw_init_semaphore); | |
130 | + | |
131 | +/* | |
132 | + * Machine checks for the channel subsystem must be enabled | |
133 | + * after the channel subsystem is initialized | |
134 | + */ | |
135 | +static int __init crw_machine_check_init(void) | |
136 | +{ | |
137 | + struct task_struct *task; | |
138 | + | |
139 | + task = kthread_run(crw_collect_info, NULL, "kmcheck"); | |
140 | + if (IS_ERR(task)) | |
141 | + return PTR_ERR(task); | |
142 | + ctl_set_bit(14, 28); /* enable channel report MCH */ | |
143 | + return 0; | |
144 | +} | |
145 | +device_initcall(crw_machine_check_init); |
drivers/s390/cio/css.c
... | ... | @@ -18,8 +18,8 @@ |
18 | 18 | #include <linux/list.h> |
19 | 19 | #include <linux/reboot.h> |
20 | 20 | #include <asm/isc.h> |
21 | +#include <asm/crw.h> | |
21 | 22 | |
22 | -#include "../s390mach.h" | |
23 | 23 | #include "css.h" |
24 | 24 | #include "cio.h" |
25 | 25 | #include "cio_debug.h" |
... | ... | @@ -765,7 +765,7 @@ |
765 | 765 | if (ret) |
766 | 766 | goto out; |
767 | 767 | |
768 | - ret = s390_register_crw_handler(CRW_RSC_SCH, css_process_crw); | |
768 | + ret = crw_register_handler(CRW_RSC_SCH, css_process_crw); | |
769 | 769 | if (ret) |
770 | 770 | goto out; |
771 | 771 | |
... | ... | @@ -845,7 +845,7 @@ |
845 | 845 | out_bus: |
846 | 846 | bus_unregister(&css_bus_type); |
847 | 847 | out: |
848 | - s390_unregister_crw_handler(CRW_RSC_CSS); | |
848 | + crw_unregister_handler(CRW_RSC_CSS); | |
849 | 849 | chsc_free_sei_area(); |
850 | 850 | kfree(slow_subchannel_set); |
851 | 851 | pr_alert("The CSS device driver initialization failed with " |
drivers/s390/s390mach.c
1 | -/* | |
2 | - * drivers/s390/s390mach.c | |
3 | - * S/390 machine check handler | |
4 | - * | |
5 | - * Copyright IBM Corp. 2000,2008 | |
6 | - * Author(s): Ingo Adlung (adlung@de.ibm.com) | |
7 | - * Martin Schwidefsky (schwidefsky@de.ibm.com) | |
8 | - * Cornelia Huck <cornelia.huck@de.ibm.com> | |
9 | - */ | |
10 | - | |
11 | -#include <linux/init.h> | |
12 | -#include <linux/sched.h> | |
13 | -#include <linux/errno.h> | |
14 | -#include <linux/workqueue.h> | |
15 | -#include <linux/time.h> | |
16 | -#include <linux/device.h> | |
17 | -#include <linux/kthread.h> | |
18 | -#include <asm/etr.h> | |
19 | -#include <asm/lowcore.h> | |
20 | -#include <asm/cio.h> | |
21 | -#include <asm/cpu.h> | |
22 | -#include "s390mach.h" | |
23 | - | |
24 | -static struct semaphore m_sem; | |
25 | - | |
26 | -static NORET_TYPE void | |
27 | -s390_handle_damage(char *msg) | |
28 | -{ | |
29 | -#ifdef CONFIG_SMP | |
30 | - smp_send_stop(); | |
31 | -#endif | |
32 | - disabled_wait((unsigned long) __builtin_return_address(0)); | |
33 | - for(;;); | |
34 | -} | |
35 | - | |
36 | -static crw_handler_t crw_handlers[NR_RSCS]; | |
37 | - | |
38 | -/** | |
39 | - * s390_register_crw_handler() - register a channel report word handler | |
40 | - * @rsc: reporting source code to handle | |
41 | - * @handler: handler to be registered | |
42 | - * | |
43 | - * Returns %0 on success and a negative error value otherwise. | |
44 | - */ | |
45 | -int s390_register_crw_handler(int rsc, crw_handler_t handler) | |
46 | -{ | |
47 | - if ((rsc < 0) || (rsc >= NR_RSCS)) | |
48 | - return -EINVAL; | |
49 | - if (!cmpxchg(&crw_handlers[rsc], NULL, handler)) | |
50 | - return 0; | |
51 | - return -EBUSY; | |
52 | -} | |
53 | - | |
54 | -/** | |
55 | - * s390_unregister_crw_handler() - unregister a channel report word handler | |
56 | - * @rsc: reporting source code to handle | |
57 | - */ | |
58 | -void s390_unregister_crw_handler(int rsc) | |
59 | -{ | |
60 | - if ((rsc < 0) || (rsc >= NR_RSCS)) | |
61 | - return; | |
62 | - xchg(&crw_handlers[rsc], NULL); | |
63 | - synchronize_sched(); | |
64 | -} | |
65 | - | |
66 | -/* | |
67 | - * Retrieve CRWs and call function to handle event. | |
68 | - */ | |
69 | -static int s390_collect_crw_info(void *param) | |
70 | -{ | |
71 | - struct crw crw[2]; | |
72 | - int ccode; | |
73 | - struct semaphore *sem; | |
74 | - unsigned int chain; | |
75 | - int ignore; | |
76 | - | |
77 | - sem = (struct semaphore *)param; | |
78 | -repeat: | |
79 | - ignore = down_interruptible(sem); | |
80 | - chain = 0; | |
81 | - while (1) { | |
82 | - if (unlikely(chain > 1)) { | |
83 | - struct crw tmp_crw; | |
84 | - | |
85 | - printk(KERN_WARNING"%s: Code does not support more " | |
86 | - "than two chained crws; please report to " | |
87 | - "linux390@de.ibm.com!\n", __func__); | |
88 | - ccode = stcrw(&tmp_crw); | |
89 | - printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, " | |
90 | - "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | |
91 | - __func__, tmp_crw.slct, tmp_crw.oflw, | |
92 | - tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc, | |
93 | - tmp_crw.erc, tmp_crw.rsid); | |
94 | - printk(KERN_WARNING"%s: This was crw number %x in the " | |
95 | - "chain\n", __func__, chain); | |
96 | - if (ccode != 0) | |
97 | - break; | |
98 | - chain = tmp_crw.chn ? chain + 1 : 0; | |
99 | - continue; | |
100 | - } | |
101 | - ccode = stcrw(&crw[chain]); | |
102 | - if (ccode != 0) | |
103 | - break; | |
104 | - printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, " | |
105 | - "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | |
106 | - crw[chain].slct, crw[chain].oflw, crw[chain].chn, | |
107 | - crw[chain].rsc, crw[chain].anc, crw[chain].erc, | |
108 | - crw[chain].rsid); | |
109 | - /* Check for overflows. */ | |
110 | - if (crw[chain].oflw) { | |
111 | - int i; | |
112 | - | |
113 | - pr_debug("%s: crw overflow detected!\n", __func__); | |
114 | - for (i = 0; i < NR_RSCS; i++) { | |
115 | - if (crw_handlers[i]) | |
116 | - crw_handlers[i](NULL, NULL, 1); | |
117 | - } | |
118 | - chain = 0; | |
119 | - continue; | |
120 | - } | |
121 | - if (crw[0].chn && !chain) { | |
122 | - chain++; | |
123 | - continue; | |
124 | - } | |
125 | - if (crw_handlers[crw[chain].rsc]) | |
126 | - crw_handlers[crw[chain].rsc](&crw[0], | |
127 | - chain ? &crw[1] : NULL, | |
128 | - 0); | |
129 | - /* chain is always 0 or 1 here. */ | |
130 | - chain = crw[chain].chn ? chain + 1 : 0; | |
131 | - } | |
132 | - goto repeat; | |
133 | - return 0; | |
134 | -} | |
135 | - | |
136 | -struct mcck_struct { | |
137 | - int kill_task; | |
138 | - int channel_report; | |
139 | - int warning; | |
140 | - unsigned long long mcck_code; | |
141 | -}; | |
142 | - | |
143 | -static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck); | |
144 | - | |
145 | -/* | |
146 | - * Main machine check handler function. Will be called with interrupts enabled | |
147 | - * or disabled and machine checks enabled or disabled. | |
148 | - */ | |
149 | -void | |
150 | -s390_handle_mcck(void) | |
151 | -{ | |
152 | - unsigned long flags; | |
153 | - struct mcck_struct mcck; | |
154 | - | |
155 | - /* | |
156 | - * Disable machine checks and get the current state of accumulated | |
157 | - * machine checks. Afterwards delete the old state and enable machine | |
158 | - * checks again. | |
159 | - */ | |
160 | - local_irq_save(flags); | |
161 | - local_mcck_disable(); | |
162 | - mcck = __get_cpu_var(cpu_mcck); | |
163 | - memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct)); | |
164 | - clear_thread_flag(TIF_MCCK_PENDING); | |
165 | - local_mcck_enable(); | |
166 | - local_irq_restore(flags); | |
167 | - | |
168 | - if (mcck.channel_report) | |
169 | - up(&m_sem); | |
170 | - | |
171 | -#ifdef CONFIG_MACHCHK_WARNING | |
172 | -/* | |
173 | - * The warning may remain for a prolonged period on the bare iron. | |
174 | - * (actually till the machine is powered off, or until the problem is gone) | |
175 | - * So we just stop listening for the WARNING MCH and prevent continuously | |
176 | - * being interrupted. One caveat is however, that we must do this per | |
177 | - * processor and cannot use the smp version of ctl_clear_bit(). | |
178 | - * On VM we only get one interrupt per virtally presented machinecheck. | |
179 | - * Though one suffices, we may get one interrupt per (virtual) processor. | |
180 | - */ | |
181 | - if (mcck.warning) { /* WARNING pending ? */ | |
182 | - static int mchchk_wng_posted = 0; | |
183 | - /* | |
184 | - * Use single machine clear, as we cannot handle smp right now | |
185 | - */ | |
186 | - __ctl_clear_bit(14, 24); /* Disable WARNING MCH */ | |
187 | - if (xchg(&mchchk_wng_posted, 1) == 0) | |
188 | - kill_cad_pid(SIGPWR, 1); | |
189 | - } | |
190 | -#endif | |
191 | - | |
192 | - if (mcck.kill_task) { | |
193 | - local_irq_enable(); | |
194 | - printk(KERN_EMERG "mcck: Terminating task because of machine " | |
195 | - "malfunction (code 0x%016llx).\n", mcck.mcck_code); | |
196 | - printk(KERN_EMERG "mcck: task: %s, pid: %d.\n", | |
197 | - current->comm, current->pid); | |
198 | - do_exit(SIGSEGV); | |
199 | - } | |
200 | -} | |
201 | -EXPORT_SYMBOL_GPL(s390_handle_mcck); | |
202 | - | |
203 | -/* | |
204 | - * returns 0 if all registers could be validated | |
205 | - * returns 1 otherwise | |
206 | - */ | |
207 | -static int | |
208 | -s390_revalidate_registers(struct mci *mci) | |
209 | -{ | |
210 | - int kill_task; | |
211 | - u64 tmpclock; | |
212 | - u64 zero; | |
213 | - void *fpt_save_area, *fpt_creg_save_area; | |
214 | - | |
215 | - kill_task = 0; | |
216 | - zero = 0; | |
217 | - /* General purpose registers */ | |
218 | - if (!mci->gr) | |
219 | - /* | |
220 | - * General purpose registers couldn't be restored and have | |
221 | - * unknown contents. Process needs to be terminated. | |
222 | - */ | |
223 | - kill_task = 1; | |
224 | - | |
225 | - /* Revalidate floating point registers */ | |
226 | - if (!mci->fp) | |
227 | - /* | |
228 | - * Floating point registers can't be restored and | |
229 | - * therefore the process needs to be terminated. | |
230 | - */ | |
231 | - kill_task = 1; | |
232 | - | |
233 | -#ifndef CONFIG_64BIT | |
234 | - asm volatile( | |
235 | - " ld 0,0(%0)\n" | |
236 | - " ld 2,8(%0)\n" | |
237 | - " ld 4,16(%0)\n" | |
238 | - " ld 6,24(%0)" | |
239 | - : : "a" (&S390_lowcore.floating_pt_save_area)); | |
240 | -#endif | |
241 | - | |
242 | - if (MACHINE_HAS_IEEE) { | |
243 | -#ifdef CONFIG_64BIT | |
244 | - fpt_save_area = &S390_lowcore.floating_pt_save_area; | |
245 | - fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area; | |
246 | -#else | |
247 | - fpt_save_area = (void *) S390_lowcore.extended_save_area_addr; | |
248 | - fpt_creg_save_area = fpt_save_area+128; | |
249 | -#endif | |
250 | - /* Floating point control register */ | |
251 | - if (!mci->fc) { | |
252 | - /* | |
253 | - * Floating point control register can't be restored. | |
254 | - * Task will be terminated. | |
255 | - */ | |
256 | - asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero)); | |
257 | - kill_task = 1; | |
258 | - | |
259 | - } else | |
260 | - asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area)); | |
261 | - | |
262 | - asm volatile( | |
263 | - " ld 0,0(%0)\n" | |
264 | - " ld 1,8(%0)\n" | |
265 | - " ld 2,16(%0)\n" | |
266 | - " ld 3,24(%0)\n" | |
267 | - " ld 4,32(%0)\n" | |
268 | - " ld 5,40(%0)\n" | |
269 | - " ld 6,48(%0)\n" | |
270 | - " ld 7,56(%0)\n" | |
271 | - " ld 8,64(%0)\n" | |
272 | - " ld 9,72(%0)\n" | |
273 | - " ld 10,80(%0)\n" | |
274 | - " ld 11,88(%0)\n" | |
275 | - " ld 12,96(%0)\n" | |
276 | - " ld 13,104(%0)\n" | |
277 | - " ld 14,112(%0)\n" | |
278 | - " ld 15,120(%0)\n" | |
279 | - : : "a" (fpt_save_area)); | |
280 | - } | |
281 | - | |
282 | - /* Revalidate access registers */ | |
283 | - asm volatile( | |
284 | - " lam 0,15,0(%0)" | |
285 | - : : "a" (&S390_lowcore.access_regs_save_area)); | |
286 | - if (!mci->ar) | |
287 | - /* | |
288 | - * Access registers have unknown contents. | |
289 | - * Terminating task. | |
290 | - */ | |
291 | - kill_task = 1; | |
292 | - | |
293 | - /* Revalidate control registers */ | |
294 | - if (!mci->cr) | |
295 | - /* | |
296 | - * Control registers have unknown contents. | |
297 | - * Can't recover and therefore stopping machine. | |
298 | - */ | |
299 | - s390_handle_damage("invalid control registers."); | |
300 | - else | |
301 | -#ifdef CONFIG_64BIT | |
302 | - asm volatile( | |
303 | - " lctlg 0,15,0(%0)" | |
304 | - : : "a" (&S390_lowcore.cregs_save_area)); | |
305 | -#else | |
306 | - asm volatile( | |
307 | - " lctl 0,15,0(%0)" | |
308 | - : : "a" (&S390_lowcore.cregs_save_area)); | |
309 | -#endif | |
310 | - | |
311 | - /* | |
312 | - * We don't even try to revalidate the TOD register, since we simply | |
313 | - * can't write something sensible into that register. | |
314 | - */ | |
315 | - | |
316 | -#ifdef CONFIG_64BIT | |
317 | - /* | |
318 | - * See if we can revalidate the TOD programmable register with its | |
319 | - * old contents (should be zero) otherwise set it to zero. | |
320 | - */ | |
321 | - if (!mci->pr) | |
322 | - asm volatile( | |
323 | - " sr 0,0\n" | |
324 | - " sckpf" | |
325 | - : : : "0", "cc"); | |
326 | - else | |
327 | - asm volatile( | |
328 | - " l 0,0(%0)\n" | |
329 | - " sckpf" | |
330 | - : : "a" (&S390_lowcore.tod_progreg_save_area) | |
331 | - : "0", "cc"); | |
332 | -#endif | |
333 | - | |
334 | - /* Revalidate clock comparator register */ | |
335 | - asm volatile( | |
336 | - " stck 0(%1)\n" | |
337 | - " sckc 0(%1)" | |
338 | - : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory"); | |
339 | - | |
340 | - /* Check if old PSW is valid */ | |
341 | - if (!mci->wp) | |
342 | - /* | |
343 | - * Can't tell if we come from user or kernel mode | |
344 | - * -> stopping machine. | |
345 | - */ | |
346 | - s390_handle_damage("old psw invalid."); | |
347 | - | |
348 | - if (!mci->ms || !mci->pm || !mci->ia) | |
349 | - kill_task = 1; | |
350 | - | |
351 | - return kill_task; | |
352 | -} | |
353 | - | |
354 | -#define MAX_IPD_COUNT 29 | |
355 | -#define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */ | |
356 | - | |
357 | -/* | |
358 | - * machine check handler. | |
359 | - */ | |
360 | -void notrace s390_do_machine_check(struct pt_regs *regs) | |
361 | -{ | |
362 | - static DEFINE_SPINLOCK(ipd_lock); | |
363 | - static unsigned long long last_ipd; | |
364 | - static int ipd_count; | |
365 | - unsigned long long tmp; | |
366 | - struct mci *mci; | |
367 | - struct mcck_struct *mcck; | |
368 | - int umode; | |
369 | - | |
370 | - lockdep_off(); | |
371 | - | |
372 | - s390_idle_check(); | |
373 | - | |
374 | - mci = (struct mci *) &S390_lowcore.mcck_interruption_code; | |
375 | - mcck = &__get_cpu_var(cpu_mcck); | |
376 | - umode = user_mode(regs); | |
377 | - | |
378 | - if (mci->sd) | |
379 | - /* System damage -> stopping machine */ | |
380 | - s390_handle_damage("received system damage machine check."); | |
381 | - | |
382 | - if (mci->pd) { | |
383 | - if (mci->b) { | |
384 | - /* Processing backup -> verify if we can survive this */ | |
385 | - u64 z_mcic, o_mcic, t_mcic; | |
386 | -#ifdef CONFIG_64BIT | |
387 | - z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<29); | |
388 | - o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 | | |
389 | - 1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 | | |
390 | - 1ULL<<30 | 1ULL<<21 | 1ULL<<20 | 1ULL<<17 | | |
391 | - 1ULL<<16); | |
392 | -#else | |
393 | - z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<57 | 1ULL<<50 | | |
394 | - 1ULL<<29); | |
395 | - o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 | | |
396 | - 1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 | | |
397 | - 1ULL<<30 | 1ULL<<20 | 1ULL<<17 | 1ULL<<16); | |
398 | -#endif | |
399 | - t_mcic = *(u64 *)mci; | |
400 | - | |
401 | - if (((t_mcic & z_mcic) != 0) || | |
402 | - ((t_mcic & o_mcic) != o_mcic)) { | |
403 | - s390_handle_damage("processing backup machine " | |
404 | - "check with damage."); | |
405 | - } | |
406 | - | |
407 | - /* | |
408 | - * Nullifying exigent condition, therefore we might | |
409 | - * retry this instruction. | |
410 | - */ | |
411 | - | |
412 | - spin_lock(&ipd_lock); | |
413 | - | |
414 | - tmp = get_clock(); | |
415 | - | |
416 | - if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME) | |
417 | - ipd_count++; | |
418 | - else | |
419 | - ipd_count = 1; | |
420 | - | |
421 | - last_ipd = tmp; | |
422 | - | |
423 | - if (ipd_count == MAX_IPD_COUNT) | |
424 | - s390_handle_damage("too many ipd retries."); | |
425 | - | |
426 | - spin_unlock(&ipd_lock); | |
427 | - } | |
428 | - else { | |
429 | - /* Processing damage -> stopping machine */ | |
430 | - s390_handle_damage("received instruction processing " | |
431 | - "damage machine check."); | |
432 | - } | |
433 | - } | |
434 | - if (s390_revalidate_registers(mci)) { | |
435 | - if (umode) { | |
436 | - /* | |
437 | - * Couldn't restore all register contents while in | |
438 | - * user mode -> mark task for termination. | |
439 | - */ | |
440 | - mcck->kill_task = 1; | |
441 | - mcck->mcck_code = *(unsigned long long *) mci; | |
442 | - set_thread_flag(TIF_MCCK_PENDING); | |
443 | - } | |
444 | - else | |
445 | - /* | |
446 | - * Couldn't restore all register contents while in | |
447 | - * kernel mode -> stopping machine. | |
448 | - */ | |
449 | - s390_handle_damage("unable to revalidate registers."); | |
450 | - } | |
451 | - | |
452 | - if (mci->cd) { | |
453 | - /* Timing facility damage */ | |
454 | - s390_handle_damage("TOD clock damaged"); | |
455 | - } | |
456 | - | |
457 | - if (mci->ed && mci->ec) { | |
458 | - /* External damage */ | |
459 | - if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC)) | |
460 | - etr_sync_check(); | |
461 | - if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH)) | |
462 | - etr_switch_to_local(); | |
463 | - if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC)) | |
464 | - stp_sync_check(); | |
465 | - if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND)) | |
466 | - stp_island_check(); | |
467 | - } | |
468 | - | |
469 | - if (mci->se) | |
470 | - /* Storage error uncorrected */ | |
471 | - s390_handle_damage("received storage error uncorrected " | |
472 | - "machine check."); | |
473 | - | |
474 | - if (mci->ke) | |
475 | - /* Storage key-error uncorrected */ | |
476 | - s390_handle_damage("received storage key-error uncorrected " | |
477 | - "machine check."); | |
478 | - | |
479 | - if (mci->ds && mci->fa) | |
480 | - /* Storage degradation */ | |
481 | - s390_handle_damage("received storage degradation machine " | |
482 | - "check."); | |
483 | - | |
484 | - if (mci->cp) { | |
485 | - /* Channel report word pending */ | |
486 | - mcck->channel_report = 1; | |
487 | - set_thread_flag(TIF_MCCK_PENDING); | |
488 | - } | |
489 | - | |
490 | - if (mci->w) { | |
491 | - /* Warning pending */ | |
492 | - mcck->warning = 1; | |
493 | - set_thread_flag(TIF_MCCK_PENDING); | |
494 | - } | |
495 | - lockdep_on(); | |
496 | -} | |
497 | - | |
498 | -/* | |
499 | - * s390_init_machine_check | |
500 | - * | |
501 | - * initialize machine check handling | |
502 | - */ | |
503 | -static int | |
504 | -machine_check_init(void) | |
505 | -{ | |
506 | - init_MUTEX_LOCKED(&m_sem); | |
507 | - ctl_set_bit(14, 25); /* enable external damage MCH */ | |
508 | - ctl_set_bit(14, 27); /* enable system recovery MCH */ | |
509 | -#ifdef CONFIG_MACHCHK_WARNING | |
510 | - ctl_set_bit(14, 24); /* enable warning MCH */ | |
511 | -#endif | |
512 | - return 0; | |
513 | -} | |
514 | - | |
515 | -/* | |
516 | - * Initialize the machine check handler really early to be able to | |
517 | - * catch all machine checks that happen during boot | |
518 | - */ | |
519 | -arch_initcall(machine_check_init); | |
520 | - | |
521 | -/* | |
522 | - * Machine checks for the channel subsystem must be enabled | |
523 | - * after the channel subsystem is initialized | |
524 | - */ | |
525 | -static int __init | |
526 | -machine_check_crw_init (void) | |
527 | -{ | |
528 | - struct task_struct *task; | |
529 | - | |
530 | - task = kthread_run(s390_collect_crw_info, &m_sem, "kmcheck"); | |
531 | - if (IS_ERR(task)) | |
532 | - return PTR_ERR(task); | |
533 | - ctl_set_bit(14, 28); /* enable channel report MCH */ | |
534 | - return 0; | |
535 | -} | |
536 | - | |
537 | -device_initcall (machine_check_crw_init); |
drivers/s390/s390mach.h
1 | -/* | |
2 | - * drivers/s390/s390mach.h | |
3 | - * S/390 data definitions for machine check processing | |
4 | - * | |
5 | - * S390 version | |
6 | - * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation | |
7 | - * Author(s): Ingo Adlung (adlung@de.ibm.com) | |
8 | - */ | |
9 | - | |
10 | -#ifndef __s390mach_h | |
11 | -#define __s390mach_h | |
12 | - | |
13 | -#include <asm/types.h> | |
14 | - | |
15 | -struct mci { | |
16 | - __u32 sd : 1; /* 00 system damage */ | |
17 | - __u32 pd : 1; /* 01 instruction-processing damage */ | |
18 | - __u32 sr : 1; /* 02 system recovery */ | |
19 | - __u32 to_be_defined_1 : 1; /* 03 */ | |
20 | - __u32 cd : 1; /* 04 timing-facility damage */ | |
21 | - __u32 ed : 1; /* 05 external damage */ | |
22 | - __u32 to_be_defined_2 : 1; /* 06 */ | |
23 | - __u32 dg : 1; /* 07 degradation */ | |
24 | - __u32 w : 1; /* 08 warning pending */ | |
25 | - __u32 cp : 1; /* 09 channel-report pending */ | |
26 | - __u32 sp : 1; /* 10 service-processor damage */ | |
27 | - __u32 ck : 1; /* 11 channel-subsystem damage */ | |
28 | - __u32 to_be_defined_3 : 2; /* 12-13 */ | |
29 | - __u32 b : 1; /* 14 backed up */ | |
30 | - __u32 to_be_defined_4 : 1; /* 15 */ | |
31 | - __u32 se : 1; /* 16 storage error uncorrected */ | |
32 | - __u32 sc : 1; /* 17 storage error corrected */ | |
33 | - __u32 ke : 1; /* 18 storage-key error uncorrected */ | |
34 | - __u32 ds : 1; /* 19 storage degradation */ | |
35 | - __u32 wp : 1; /* 20 psw mwp validity */ | |
36 | - __u32 ms : 1; /* 21 psw mask and key validity */ | |
37 | - __u32 pm : 1; /* 22 psw program mask and cc validity */ | |
38 | - __u32 ia : 1; /* 23 psw instruction address validity */ | |
39 | - __u32 fa : 1; /* 24 failing storage address validity */ | |
40 | - __u32 to_be_defined_5 : 1; /* 25 */ | |
41 | - __u32 ec : 1; /* 26 external damage code validity */ | |
42 | - __u32 fp : 1; /* 27 floating point register validity */ | |
43 | - __u32 gr : 1; /* 28 general register validity */ | |
44 | - __u32 cr : 1; /* 29 control register validity */ | |
45 | - __u32 to_be_defined_6 : 1; /* 30 */ | |
46 | - __u32 st : 1; /* 31 storage logical validity */ | |
47 | - __u32 ie : 1; /* 32 indirect storage error */ | |
48 | - __u32 ar : 1; /* 33 access register validity */ | |
49 | - __u32 da : 1; /* 34 delayed access exception */ | |
50 | - __u32 to_be_defined_7 : 7; /* 35-41 */ | |
51 | - __u32 pr : 1; /* 42 tod programmable register validity */ | |
52 | - __u32 fc : 1; /* 43 fp control register validity */ | |
53 | - __u32 ap : 1; /* 44 ancillary report */ | |
54 | - __u32 to_be_defined_8 : 1; /* 45 */ | |
55 | - __u32 ct : 1; /* 46 cpu timer validity */ | |
56 | - __u32 cc : 1; /* 47 clock comparator validity */ | |
57 | - __u32 to_be_defined_9 : 16; /* 47-63 */ | |
58 | -}; | |
59 | - | |
60 | -/* | |
61 | - * Channel Report Word | |
62 | - */ | |
63 | -struct crw { | |
64 | - __u32 res1 : 1; /* reserved zero */ | |
65 | - __u32 slct : 1; /* solicited */ | |
66 | - __u32 oflw : 1; /* overflow */ | |
67 | - __u32 chn : 1; /* chained */ | |
68 | - __u32 rsc : 4; /* reporting source code */ | |
69 | - __u32 anc : 1; /* ancillary report */ | |
70 | - __u32 res2 : 1; /* reserved zero */ | |
71 | - __u32 erc : 6; /* error-recovery code */ | |
72 | - __u32 rsid : 16; /* reporting-source ID */ | |
73 | -} __attribute__ ((packed)); | |
74 | - | |
75 | -typedef void (*crw_handler_t)(struct crw *, struct crw *, int); | |
76 | - | |
77 | -extern int s390_register_crw_handler(int rsc, crw_handler_t handler); | |
78 | -extern void s390_unregister_crw_handler(int rsc); | |
79 | - | |
80 | -#define NR_RSCS 16 | |
81 | - | |
82 | -#define CRW_RSC_MONITOR 0x2 /* monitoring facility */ | |
83 | -#define CRW_RSC_SCH 0x3 /* subchannel */ | |
84 | -#define CRW_RSC_CPATH 0x4 /* channel path */ | |
85 | -#define CRW_RSC_CONFIG 0x9 /* configuration-alert facility */ | |
86 | -#define CRW_RSC_CSS 0xB /* channel subsystem */ | |
87 | - | |
88 | -#define CRW_ERC_EVENT 0x00 /* event information pending */ | |
89 | -#define CRW_ERC_AVAIL 0x01 /* available */ | |
90 | -#define CRW_ERC_INIT 0x02 /* initialized */ | |
91 | -#define CRW_ERC_TERROR 0x03 /* temporary error */ | |
92 | -#define CRW_ERC_IPARM 0x04 /* installed parm initialized */ | |
93 | -#define CRW_ERC_TERM 0x05 /* terminal */ | |
94 | -#define CRW_ERC_PERRN 0x06 /* perm. error, fac. not init */ | |
95 | -#define CRW_ERC_PERRI 0x07 /* perm. error, facility init */ | |
96 | -#define CRW_ERC_PMOD 0x08 /* installed parameters modified */ | |
97 | - | |
98 | -static inline int stcrw(struct crw *pcrw ) | |
99 | -{ | |
100 | - int ccode; | |
101 | - | |
102 | - __asm__ __volatile__( | |
103 | - "stcrw 0(%2)\n\t" | |
104 | - "ipm %0\n\t" | |
105 | - "srl %0,28\n\t" | |
106 | - : "=d" (ccode), "=m" (*pcrw) | |
107 | - : "a" (pcrw) | |
108 | - : "cc" ); | |
109 | - return ccode; | |
110 | -} | |
111 | - | |
112 | -#define ED_ETR_SYNC 12 /* External damage ETR sync check */ | |
113 | -#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */ | |
114 | - | |
115 | -#define ED_STP_SYNC 7 /* External damage STP sync check */ | |
116 | -#define ED_STP_ISLAND 6 /* External damage STP island check */ | |
117 | - | |
118 | -struct pt_regs; | |
119 | - | |
120 | -void s390_handle_mcck(void); | |
121 | -void s390_do_machine_check(struct pt_regs *regs); | |
122 | -#endif /* __s390mach */ |