diff options
author | Michael Holzheu <holzheu@de.ibm.com> | 2006-09-20 09:58:49 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2006-09-20 09:58:49 -0400 |
commit | ff6b8ea68f4b7353f88b97024f28127e2148aa00 (patch) | |
tree | 67ebb74cbbc042d99325ff33c3f80e4b3e0a1c42 /arch | |
parent | 331c982d4a6b43cdc0d056956a1cae8a7d6237bf (diff) |
[S390] ipl/dump on panic.
It is now possible to specify a ccw/fcp dump device which is used to
automatically create a system dump in case of a kernel panic. The dump
device can be configured under /sys/firmware/dump.
In addition it is now possible to specify a ccw/fcp device which is used
for the next reboot of Linux. The reipl device can be configured under
/sys/firmware/reipl.
Signed-off-by: Michael Holzheu <holzheu@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/s390/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/s390/kernel/head31.S | 3 | ||||
-rw-r--r-- | arch/s390/kernel/head64.S | 3 | ||||
-rw-r--r-- | arch/s390/kernel/ipl.c | 942 | ||||
-rw-r--r-- | arch/s390/kernel/reipl.S | 33 | ||||
-rw-r--r-- | arch/s390/kernel/reipl64.S | 34 | ||||
-rw-r--r-- | arch/s390/kernel/reipl_diag.c | 39 | ||||
-rw-r--r-- | arch/s390/kernel/setup.c | 220 | ||||
-rw-r--r-- | arch/s390/kernel/smp.c | 10 |
9 files changed, 1002 insertions, 284 deletions
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 33a5069c0e16..aa978978d3d1 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
@@ -6,7 +6,7 @@ EXTRA_AFLAGS := -traditional | |||
6 | 6 | ||
7 | obj-y := bitmap.o traps.o time.o process.o \ | 7 | obj-y := bitmap.o traps.o time.o process.o \ |
8 | setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ | 8 | setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ |
9 | semaphore.o s390_ext.o debug.o profile.o irq.o reipl_diag.o | 9 | semaphore.o s390_ext.o debug.o profile.o irq.o ipl.o |
10 | 10 | ||
11 | obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) | 11 | obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) |
12 | obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) | 12 | obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) |
diff --git a/arch/s390/kernel/head31.S b/arch/s390/kernel/head31.S index 0e46077d7140..d8bb68a72527 100644 --- a/arch/s390/kernel/head31.S +++ b/arch/s390/kernel/head31.S | |||
@@ -38,6 +38,7 @@ startup:basr %r13,0 # get base | |||
38 | startup_continue: | 38 | startup_continue: |
39 | basr %r13,0 # get base | 39 | basr %r13,0 # get base |
40 | .LPG1: GET_IPL_DEVICE | 40 | .LPG1: GET_IPL_DEVICE |
41 | mvi __LC_AR_MODE_ID,0 # set ESA flag (mode 0) | ||
41 | lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers | 42 | lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers |
42 | l %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area | 43 | l %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area |
43 | # move IPL device to lowcore | 44 | # move IPL device to lowcore |
@@ -274,6 +275,8 @@ startup_continue: | |||
274 | .Lparmaddr: .long PARMAREA | 275 | .Lparmaddr: .long PARMAREA |
275 | .Lsccbaddr: .long .Lsccb | 276 | .Lsccbaddr: .long .Lsccb |
276 | .org 0x12000 | 277 | .org 0x12000 |
278 | .globl s390_readinfo_sccb | ||
279 | s390_readinfo_sccb: | ||
277 | .Lsccb: | 280 | .Lsccb: |
278 | .hword 0x1000 # length, one page | 281 | .hword 0x1000 # length, one page |
279 | .byte 0x00,0x00,0x00 | 282 | .byte 0x00,0x00,0x00 |
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 3e0341acd04e..c2005101fee1 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S | |||
@@ -41,6 +41,7 @@ startup_continue: | |||
41 | srl %r13,1 | 41 | srl %r13,1 |
42 | GET_IPL_DEVICE | 42 | GET_IPL_DEVICE |
43 | lhi %r1,1 # mode 1 = esame | 43 | lhi %r1,1 # mode 1 = esame |
44 | mvi __LC_AR_MODE_ID,1 # set esame flag | ||
44 | slr %r0,%r0 # set cpuid to zero | 45 | slr %r0,%r0 # set cpuid to zero |
45 | sigp %r1,%r0,0x12 # switch to esame mode | 46 | sigp %r1,%r0,0x12 # switch to esame mode |
46 | sam64 # switch to 64 bit mode | 47 | sam64 # switch to 64 bit mode |
@@ -269,6 +270,8 @@ startup_continue: | |||
269 | .quad PARMAREA | 270 | .quad PARMAREA |
270 | 271 | ||
271 | .org 0x12000 | 272 | .org 0x12000 |
273 | .globl s390_readinfo_sccb | ||
274 | s390_readinfo_sccb: | ||
272 | .Lsccb: | 275 | .Lsccb: |
273 | .hword 0x1000 # length, one page | 276 | .hword 0x1000 # length, one page |
274 | .byte 0x00,0x00,0x00 | 277 | .byte 0x00,0x00,0x00 |
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c new file mode 100644 index 000000000000..105ee15a2b31 --- /dev/null +++ b/arch/s390/kernel/ipl.c | |||
@@ -0,0 +1,942 @@ | |||
1 | /* | ||
2 | * arch/s390/kernel/ipl.c | ||
3 | * ipl/reipl/dump support for Linux on s390. | ||
4 | * | ||
5 | * Copyright (C) IBM Corp. 2005,2006 | ||
6 | * Author(s): Michael Holzheu <holzheu@de.ibm.com> | ||
7 | * Heiko Carstens <heiko.carstens@de.ibm.com> | ||
8 | * Volker Sameske <sameske@de.ibm.com> | ||
9 | */ | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/reboot.h> | ||
16 | #include <asm/smp.h> | ||
17 | #include <asm/setup.h> | ||
18 | #include <asm/cpcmd.h> | ||
19 | #include <asm/cio.h> | ||
20 | |||
21 | #define IPL_PARM_BLOCK_VERSION 0 | ||
22 | |||
23 | enum ipl_type { | ||
24 | IPL_TYPE_NONE = 1, | ||
25 | IPL_TYPE_UNKNOWN = 2, | ||
26 | IPL_TYPE_CCW = 4, | ||
27 | IPL_TYPE_FCP = 8, | ||
28 | }; | ||
29 | |||
30 | #define IPL_NONE_STR "none" | ||
31 | #define IPL_UNKNOWN_STR "unknown" | ||
32 | #define IPL_CCW_STR "ccw" | ||
33 | #define IPL_FCP_STR "fcp" | ||
34 | |||
35 | static char *ipl_type_str(enum ipl_type type) | ||
36 | { | ||
37 | switch (type) { | ||
38 | case IPL_TYPE_NONE: | ||
39 | return IPL_NONE_STR; | ||
40 | case IPL_TYPE_CCW: | ||
41 | return IPL_CCW_STR; | ||
42 | case IPL_TYPE_FCP: | ||
43 | return IPL_FCP_STR; | ||
44 | case IPL_TYPE_UNKNOWN: | ||
45 | default: | ||
46 | return IPL_UNKNOWN_STR; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | enum ipl_method { | ||
51 | IPL_METHOD_NONE, | ||
52 | IPL_METHOD_CCW_CIO, | ||
53 | IPL_METHOD_CCW_DIAG, | ||
54 | IPL_METHOD_CCW_VM, | ||
55 | IPL_METHOD_FCP_RO_DIAG, | ||
56 | IPL_METHOD_FCP_RW_DIAG, | ||
57 | IPL_METHOD_FCP_RO_VM, | ||
58 | }; | ||
59 | |||
60 | enum shutdown_action { | ||
61 | SHUTDOWN_REIPL, | ||
62 | SHUTDOWN_DUMP, | ||
63 | SHUTDOWN_STOP, | ||
64 | }; | ||
65 | |||
66 | #define SHUTDOWN_REIPL_STR "reipl" | ||
67 | #define SHUTDOWN_DUMP_STR "dump" | ||
68 | #define SHUTDOWN_STOP_STR "stop" | ||
69 | |||
70 | static char *shutdown_action_str(enum shutdown_action action) | ||
71 | { | ||
72 | switch (action) { | ||
73 | case SHUTDOWN_REIPL: | ||
74 | return SHUTDOWN_REIPL_STR; | ||
75 | case SHUTDOWN_DUMP: | ||
76 | return SHUTDOWN_DUMP_STR; | ||
77 | case SHUTDOWN_STOP: | ||
78 | return SHUTDOWN_STOP_STR; | ||
79 | default: | ||
80 | BUG(); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | enum diag308_subcode { | ||
85 | DIAG308_IPL = 3, | ||
86 | DIAG308_DUMP = 4, | ||
87 | DIAG308_SET = 5, | ||
88 | DIAG308_STORE = 6, | ||
89 | }; | ||
90 | |||
91 | enum diag308_ipl_type { | ||
92 | DIAG308_IPL_TYPE_FCP = 0, | ||
93 | DIAG308_IPL_TYPE_CCW = 2, | ||
94 | }; | ||
95 | |||
96 | enum diag308_opt { | ||
97 | DIAG308_IPL_OPT_IPL = 0x10, | ||
98 | DIAG308_IPL_OPT_DUMP = 0x20, | ||
99 | }; | ||
100 | |||
101 | enum diag308_rc { | ||
102 | DIAG308_RC_OK = 1, | ||
103 | }; | ||
104 | |||
105 | static int diag308_set_works = 0; | ||
106 | |||
107 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; | ||
108 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; | ||
109 | static enum ipl_method reipl_method = IPL_METHOD_NONE; | ||
110 | static struct ipl_parameter_block *reipl_block_fcp; | ||
111 | static struct ipl_parameter_block *reipl_block_ccw; | ||
112 | |||
113 | static int dump_capabilities = IPL_TYPE_NONE; | ||
114 | static enum ipl_type dump_type = IPL_TYPE_NONE; | ||
115 | static enum ipl_method dump_method = IPL_METHOD_NONE; | ||
116 | static struct ipl_parameter_block *dump_block_fcp; | ||
117 | static struct ipl_parameter_block *dump_block_ccw; | ||
118 | |||
119 | static enum shutdown_action on_panic_action = SHUTDOWN_STOP; | ||
120 | |||
121 | static int diag308(unsigned long subcode, void *addr) | ||
122 | { | ||
123 | register unsigned long _addr asm("0") = (unsigned long)addr; | ||
124 | register unsigned long _rc asm("1") = 0; | ||
125 | |||
126 | asm volatile ( | ||
127 | " diag %0,%2,0x308\n" | ||
128 | "0: \n" | ||
129 | ".section __ex_table,\"a\"\n" | ||
130 | #ifdef CONFIG_64BIT | ||
131 | " .align 8\n" | ||
132 | " .quad 0b, 0b\n" | ||
133 | #else | ||
134 | " .align 4\n" | ||
135 | " .long 0b, 0b\n" | ||
136 | #endif | ||
137 | ".previous\n" | ||
138 | : "+d" (_addr), "+d" (_rc) | ||
139 | : "d" (subcode) : "cc", "memory" ); | ||
140 | |||
141 | return _rc; | ||
142 | } | ||
143 | |||
144 | /* SYSFS */ | ||
145 | |||
146 | #define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \ | ||
147 | static ssize_t sys_##_prefix##_##_name##_show(struct subsystem *subsys, \ | ||
148 | char *page) \ | ||
149 | { \ | ||
150 | return sprintf(page, _format, _value); \ | ||
151 | } \ | ||
152 | static struct subsys_attribute sys_##_prefix##_##_name##_attr = \ | ||
153 | __ATTR(_name, S_IRUGO, sys_##_prefix##_##_name##_show, NULL); | ||
154 | |||
155 | #define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \ | ||
156 | static ssize_t sys_##_prefix##_##_name##_show(struct subsystem *subsys, \ | ||
157 | char *page) \ | ||
158 | { \ | ||
159 | return sprintf(page, _fmt_out, \ | ||
160 | (unsigned long long) _value); \ | ||
161 | } \ | ||
162 | static ssize_t sys_##_prefix##_##_name##_store(struct subsystem *subsys,\ | ||
163 | const char *buf, size_t len) \ | ||
164 | { \ | ||
165 | unsigned long long value; \ | ||
166 | if (sscanf(buf, _fmt_in, &value) != 1) \ | ||
167 | return -EINVAL; \ | ||
168 | _value = value; \ | ||
169 | return len; \ | ||
170 | } \ | ||
171 | static struct subsys_attribute sys_##_prefix##_##_name##_attr = \ | ||
172 | __ATTR(_name,(S_IRUGO | S_IWUSR), \ | ||
173 | sys_##_prefix##_##_name##_show, \ | ||
174 | sys_##_prefix##_##_name##_store); | ||
175 | |||
176 | static void make_attrs_ro(struct attribute **attrs) | ||
177 | { | ||
178 | while (*attrs) { | ||
179 | (*attrs)->mode = S_IRUGO; | ||
180 | attrs++; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * ipl section | ||
186 | */ | ||
187 | |||
188 | static enum ipl_type ipl_get_type(void) | ||
189 | { | ||
190 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; | ||
191 | |||
192 | if (!IPL_DEVNO_VALID) | ||
193 | return IPL_TYPE_UNKNOWN; | ||
194 | if (!IPL_PARMBLOCK_VALID) | ||
195 | return IPL_TYPE_CCW; | ||
196 | if (ipl->hdr.version > IPL_MAX_SUPPORTED_VERSION) | ||
197 | return IPL_TYPE_UNKNOWN; | ||
198 | if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP) | ||
199 | return IPL_TYPE_UNKNOWN; | ||
200 | return IPL_TYPE_FCP; | ||
201 | } | ||
202 | |||
203 | static ssize_t ipl_type_show(struct subsystem *subsys, char *page) | ||
204 | { | ||
205 | return sprintf(page, "%s\n", ipl_type_str(ipl_get_type())); | ||
206 | } | ||
207 | |||
208 | static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); | ||
209 | |||
210 | static ssize_t sys_ipl_device_show(struct subsystem *subsys, char *page) | ||
211 | { | ||
212 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; | ||
213 | |||
214 | switch (ipl_get_type()) { | ||
215 | case IPL_TYPE_CCW: | ||
216 | return sprintf(page, "0.0.%04x\n", ipl_devno); | ||
217 | case IPL_TYPE_FCP: | ||
218 | return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno); | ||
219 | default: | ||
220 | return 0; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | static struct subsys_attribute sys_ipl_device_attr = | ||
225 | __ATTR(device, S_IRUGO, sys_ipl_device_show, NULL); | ||
226 | |||
227 | static ssize_t ipl_parameter_read(struct kobject *kobj, char *buf, loff_t off, | ||
228 | size_t count) | ||
229 | { | ||
230 | unsigned int size = IPL_PARMBLOCK_SIZE; | ||
231 | |||
232 | if (off > size) | ||
233 | return 0; | ||
234 | if (off + count > size) | ||
235 | count = size - off; | ||
236 | memcpy(buf, (void *)IPL_PARMBLOCK_START + off, count); | ||
237 | return count; | ||
238 | } | ||
239 | |||
240 | static struct bin_attribute ipl_parameter_attr = { | ||
241 | .attr = { | ||
242 | .name = "binary_parameter", | ||
243 | .mode = S_IRUGO, | ||
244 | .owner = THIS_MODULE, | ||
245 | }, | ||
246 | .size = PAGE_SIZE, | ||
247 | .read = &ipl_parameter_read, | ||
248 | }; | ||
249 | |||
250 | static ssize_t ipl_scp_data_read(struct kobject *kobj, char *buf, loff_t off, | ||
251 | size_t count) | ||
252 | { | ||
253 | unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len; | ||
254 | void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data; | ||
255 | |||
256 | if (off > size) | ||
257 | return 0; | ||
258 | if (off + count > size) | ||
259 | count = size - off; | ||
260 | memcpy(buf, scp_data + off, count); | ||
261 | return count; | ||
262 | } | ||
263 | |||
264 | static struct bin_attribute ipl_scp_data_attr = { | ||
265 | .attr = { | ||
266 | .name = "scp_data", | ||
267 | .mode = S_IRUGO, | ||
268 | .owner = THIS_MODULE, | ||
269 | }, | ||
270 | .size = PAGE_SIZE, | ||
271 | .read = &ipl_scp_data_read, | ||
272 | }; | ||
273 | |||
274 | /* FCP ipl device attributes */ | ||
275 | |||
276 | DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n", (unsigned long long) | ||
277 | IPL_PARMBLOCK_START->ipl_info.fcp.wwpn); | ||
278 | DEFINE_IPL_ATTR_RO(ipl_fcp, lun, "0x%016llx\n", (unsigned long long) | ||
279 | IPL_PARMBLOCK_START->ipl_info.fcp.lun); | ||
280 | DEFINE_IPL_ATTR_RO(ipl_fcp, bootprog, "%lld\n", (unsigned long long) | ||
281 | IPL_PARMBLOCK_START->ipl_info.fcp.bootprog); | ||
282 | DEFINE_IPL_ATTR_RO(ipl_fcp, br_lba, "%lld\n", (unsigned long long) | ||
283 | IPL_PARMBLOCK_START->ipl_info.fcp.br_lba); | ||
284 | |||
285 | static struct attribute *ipl_fcp_attrs[] = { | ||
286 | &sys_ipl_type_attr.attr, | ||
287 | &sys_ipl_device_attr.attr, | ||
288 | &sys_ipl_fcp_wwpn_attr.attr, | ||
289 | &sys_ipl_fcp_lun_attr.attr, | ||
290 | &sys_ipl_fcp_bootprog_attr.attr, | ||
291 | &sys_ipl_fcp_br_lba_attr.attr, | ||
292 | NULL, | ||
293 | }; | ||
294 | |||
295 | static struct attribute_group ipl_fcp_attr_group = { | ||
296 | .attrs = ipl_fcp_attrs, | ||
297 | }; | ||
298 | |||
299 | /* CCW ipl device attributes */ | ||
300 | |||
301 | static struct attribute *ipl_ccw_attrs[] = { | ||
302 | &sys_ipl_type_attr.attr, | ||
303 | &sys_ipl_device_attr.attr, | ||
304 | NULL, | ||
305 | }; | ||
306 | |||
307 | static struct attribute_group ipl_ccw_attr_group = { | ||
308 | .attrs = ipl_ccw_attrs, | ||
309 | }; | ||
310 | |||
311 | /* UNKNOWN ipl device attributes */ | ||
312 | |||
313 | static struct attribute *ipl_unknown_attrs[] = { | ||
314 | &sys_ipl_type_attr.attr, | ||
315 | NULL, | ||
316 | }; | ||
317 | |||
318 | static struct attribute_group ipl_unknown_attr_group = { | ||
319 | .attrs = ipl_unknown_attrs, | ||
320 | }; | ||
321 | |||
322 | static decl_subsys(ipl, NULL, NULL); | ||
323 | |||
324 | /* | ||
325 | * reipl section | ||
326 | */ | ||
327 | |||
328 | /* FCP reipl device attributes */ | ||
329 | |||
330 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", | ||
331 | reipl_block_fcp->ipl_info.fcp.wwpn); | ||
332 | DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n", | ||
333 | reipl_block_fcp->ipl_info.fcp.lun); | ||
334 | DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n", | ||
335 | reipl_block_fcp->ipl_info.fcp.bootprog); | ||
336 | DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n", | ||
337 | reipl_block_fcp->ipl_info.fcp.br_lba); | ||
338 | DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
339 | reipl_block_fcp->ipl_info.fcp.devno); | ||
340 | |||
341 | static struct attribute *reipl_fcp_attrs[] = { | ||
342 | &sys_reipl_fcp_device_attr.attr, | ||
343 | &sys_reipl_fcp_wwpn_attr.attr, | ||
344 | &sys_reipl_fcp_lun_attr.attr, | ||
345 | &sys_reipl_fcp_bootprog_attr.attr, | ||
346 | &sys_reipl_fcp_br_lba_attr.attr, | ||
347 | NULL, | ||
348 | }; | ||
349 | |||
350 | static struct attribute_group reipl_fcp_attr_group = { | ||
351 | .name = IPL_FCP_STR, | ||
352 | .attrs = reipl_fcp_attrs, | ||
353 | }; | ||
354 | |||
355 | /* CCW reipl device attributes */ | ||
356 | |||
357 | DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
358 | reipl_block_ccw->ipl_info.ccw.devno); | ||
359 | |||
360 | static struct attribute *reipl_ccw_attrs[] = { | ||
361 | &sys_reipl_ccw_device_attr.attr, | ||
362 | NULL, | ||
363 | }; | ||
364 | |||
365 | static struct attribute_group reipl_ccw_attr_group = { | ||
366 | .name = IPL_CCW_STR, | ||
367 | .attrs = reipl_ccw_attrs, | ||
368 | }; | ||
369 | |||
370 | /* reipl type */ | ||
371 | |||
372 | static int reipl_set_type(enum ipl_type type) | ||
373 | { | ||
374 | if (!(reipl_capabilities & type)) | ||
375 | return -EINVAL; | ||
376 | |||
377 | switch(type) { | ||
378 | case IPL_TYPE_CCW: | ||
379 | if (MACHINE_IS_VM) | ||
380 | reipl_method = IPL_METHOD_CCW_VM; | ||
381 | else | ||
382 | reipl_method = IPL_METHOD_CCW_CIO; | ||
383 | break; | ||
384 | case IPL_TYPE_FCP: | ||
385 | if (diag308_set_works) | ||
386 | reipl_method = IPL_METHOD_FCP_RW_DIAG; | ||
387 | else if (MACHINE_IS_VM) | ||
388 | reipl_method = IPL_METHOD_FCP_RO_VM; | ||
389 | else | ||
390 | reipl_method = IPL_METHOD_FCP_RO_DIAG; | ||
391 | break; | ||
392 | default: | ||
393 | reipl_method = IPL_METHOD_NONE; | ||
394 | } | ||
395 | reipl_type = type; | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static ssize_t reipl_type_show(struct subsystem *subsys, char *page) | ||
400 | { | ||
401 | return sprintf(page, "%s\n", ipl_type_str(reipl_type)); | ||
402 | } | ||
403 | |||
404 | static ssize_t reipl_type_store(struct subsystem *subsys, const char *buf, | ||
405 | size_t len) | ||
406 | { | ||
407 | int rc = -EINVAL; | ||
408 | |||
409 | if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0) | ||
410 | rc = reipl_set_type(IPL_TYPE_CCW); | ||
411 | else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) | ||
412 | rc = reipl_set_type(IPL_TYPE_FCP); | ||
413 | return (rc != 0) ? rc : len; | ||
414 | } | ||
415 | |||
416 | static struct subsys_attribute reipl_type_attr = | ||
417 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); | ||
418 | |||
419 | static decl_subsys(reipl, NULL, NULL); | ||
420 | |||
421 | /* | ||
422 | * dump section | ||
423 | */ | ||
424 | |||
425 | /* FCP dump device attributes */ | ||
426 | |||
427 | DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n", | ||
428 | dump_block_fcp->ipl_info.fcp.wwpn); | ||
429 | DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n", | ||
430 | dump_block_fcp->ipl_info.fcp.lun); | ||
431 | DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n", | ||
432 | dump_block_fcp->ipl_info.fcp.bootprog); | ||
433 | DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", | ||
434 | dump_block_fcp->ipl_info.fcp.br_lba); | ||
435 | DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
436 | dump_block_fcp->ipl_info.fcp.devno); | ||
437 | |||
438 | static struct attribute *dump_fcp_attrs[] = { | ||
439 | &sys_dump_fcp_device_attr.attr, | ||
440 | &sys_dump_fcp_wwpn_attr.attr, | ||
441 | &sys_dump_fcp_lun_attr.attr, | ||
442 | &sys_dump_fcp_bootprog_attr.attr, | ||
443 | &sys_dump_fcp_br_lba_attr.attr, | ||
444 | NULL, | ||
445 | }; | ||
446 | |||
447 | static struct attribute_group dump_fcp_attr_group = { | ||
448 | .name = IPL_FCP_STR, | ||
449 | .attrs = dump_fcp_attrs, | ||
450 | }; | ||
451 | |||
452 | /* CCW dump device attributes */ | ||
453 | |||
454 | DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
455 | dump_block_ccw->ipl_info.ccw.devno); | ||
456 | |||
457 | static struct attribute *dump_ccw_attrs[] = { | ||
458 | &sys_dump_ccw_device_attr.attr, | ||
459 | NULL, | ||
460 | }; | ||
461 | |||
462 | static struct attribute_group dump_ccw_attr_group = { | ||
463 | .name = IPL_CCW_STR, | ||
464 | .attrs = dump_ccw_attrs, | ||
465 | }; | ||
466 | |||
467 | /* dump type */ | ||
468 | |||
469 | static int dump_set_type(enum ipl_type type) | ||
470 | { | ||
471 | if (!(dump_capabilities & type)) | ||
472 | return -EINVAL; | ||
473 | switch(type) { | ||
474 | case IPL_TYPE_CCW: | ||
475 | if (MACHINE_IS_VM) | ||
476 | dump_method = IPL_METHOD_CCW_VM; | ||
477 | else | ||
478 | dump_method = IPL_METHOD_CCW_CIO; | ||
479 | break; | ||
480 | case IPL_TYPE_FCP: | ||
481 | dump_method = IPL_METHOD_FCP_RW_DIAG; | ||
482 | break; | ||
483 | default: | ||
484 | dump_method = IPL_METHOD_NONE; | ||
485 | } | ||
486 | dump_type = type; | ||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | static ssize_t dump_type_show(struct subsystem *subsys, char *page) | ||
491 | { | ||
492 | return sprintf(page, "%s\n", ipl_type_str(dump_type)); | ||
493 | } | ||
494 | |||
495 | static ssize_t dump_type_store(struct subsystem *subsys, const char *buf, | ||
496 | size_t len) | ||
497 | { | ||
498 | int rc = -EINVAL; | ||
499 | |||
500 | if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0) | ||
501 | rc = dump_set_type(IPL_TYPE_NONE); | ||
502 | else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0) | ||
503 | rc = dump_set_type(IPL_TYPE_CCW); | ||
504 | else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) | ||
505 | rc = dump_set_type(IPL_TYPE_FCP); | ||
506 | return (rc != 0) ? rc : len; | ||
507 | } | ||
508 | |||
509 | static struct subsys_attribute dump_type_attr = | ||
510 | __ATTR(dump_type, 0644, dump_type_show, dump_type_store); | ||
511 | |||
512 | static decl_subsys(dump, NULL, NULL); | ||
513 | |||
514 | #ifdef CONFIG_SMP | ||
515 | static void dump_smp_stop_all(void) | ||
516 | { | ||
517 | int cpu; | ||
518 | preempt_disable(); | ||
519 | for_each_online_cpu(cpu) { | ||
520 | if (cpu == smp_processor_id()) | ||
521 | continue; | ||
522 | while (signal_processor(cpu, sigp_stop) == sigp_busy) | ||
523 | udelay(10); | ||
524 | } | ||
525 | preempt_enable(); | ||
526 | } | ||
527 | #else | ||
528 | #define dump_smp_stop_all() do { } while (0) | ||
529 | #endif | ||
530 | |||
531 | /* | ||
532 | * Shutdown actions section | ||
533 | */ | ||
534 | |||
535 | static decl_subsys(shutdown_actions, NULL, NULL); | ||
536 | |||
537 | /* on panic */ | ||
538 | |||
539 | static ssize_t on_panic_show(struct subsystem *subsys, char *page) | ||
540 | { | ||
541 | return sprintf(page, "%s\n", shutdown_action_str(on_panic_action)); | ||
542 | } | ||
543 | |||
544 | static ssize_t on_panic_store(struct subsystem *subsys, const char *buf, | ||
545 | size_t len) | ||
546 | { | ||
547 | if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0) | ||
548 | on_panic_action = SHUTDOWN_REIPL; | ||
549 | else if (strncmp(buf, SHUTDOWN_DUMP_STR, | ||
550 | strlen(SHUTDOWN_DUMP_STR)) == 0) | ||
551 | on_panic_action = SHUTDOWN_DUMP; | ||
552 | else if (strncmp(buf, SHUTDOWN_STOP_STR, | ||
553 | strlen(SHUTDOWN_STOP_STR)) == 0) | ||
554 | on_panic_action = SHUTDOWN_STOP; | ||
555 | else | ||
556 | return -EINVAL; | ||
557 | |||
558 | return len; | ||
559 | } | ||
560 | |||
561 | static struct subsys_attribute on_panic_attr = | ||
562 | __ATTR(on_panic, 0644, on_panic_show, on_panic_store); | ||
563 | |||
564 | static void print_fcp_block(struct ipl_parameter_block *fcp_block) | ||
565 | { | ||
566 | printk(KERN_EMERG "wwpn: %016llx\n", | ||
567 | (unsigned long long)fcp_block->ipl_info.fcp.wwpn); | ||
568 | printk(KERN_EMERG "lun: %016llx\n", | ||
569 | (unsigned long long)fcp_block->ipl_info.fcp.lun); | ||
570 | printk(KERN_EMERG "bootprog: %lld\n", | ||
571 | (unsigned long long)fcp_block->ipl_info.fcp.bootprog); | ||
572 | printk(KERN_EMERG "br_lba: %lld\n", | ||
573 | (unsigned long long)fcp_block->ipl_info.fcp.br_lba); | ||
574 | printk(KERN_EMERG "device: %llx\n", | ||
575 | (unsigned long long)fcp_block->ipl_info.fcp.devno); | ||
576 | printk(KERN_EMERG "opt: %x\n", fcp_block->ipl_info.fcp.opt); | ||
577 | } | ||
578 | |||
579 | void do_reipl(void) | ||
580 | { | ||
581 | struct ccw_dev_id devid; | ||
582 | static char buf[100]; | ||
583 | |||
584 | switch (reipl_type) { | ||
585 | case IPL_TYPE_CCW: | ||
586 | printk(KERN_EMERG "reboot on ccw device: 0.0.%04x\n", | ||
587 | reipl_block_ccw->ipl_info.ccw.devno); | ||
588 | break; | ||
589 | case IPL_TYPE_FCP: | ||
590 | printk(KERN_EMERG "reboot on fcp device:\n"); | ||
591 | print_fcp_block(reipl_block_fcp); | ||
592 | break; | ||
593 | default: | ||
594 | break; | ||
595 | } | ||
596 | |||
597 | switch (reipl_method) { | ||
598 | case IPL_METHOD_CCW_CIO: | ||
599 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; | ||
600 | devid.ssid = 0; | ||
601 | reipl_ccw_dev(&devid); | ||
602 | break; | ||
603 | case IPL_METHOD_CCW_VM: | ||
604 | sprintf(buf, "IPL %X", reipl_block_ccw->ipl_info.ccw.devno); | ||
605 | cpcmd(buf, NULL, 0, NULL); | ||
606 | break; | ||
607 | case IPL_METHOD_CCW_DIAG: | ||
608 | diag308(DIAG308_SET, reipl_block_ccw); | ||
609 | diag308(DIAG308_IPL, NULL); | ||
610 | break; | ||
611 | case IPL_METHOD_FCP_RW_DIAG: | ||
612 | diag308(DIAG308_SET, reipl_block_fcp); | ||
613 | diag308(DIAG308_IPL, NULL); | ||
614 | break; | ||
615 | case IPL_METHOD_FCP_RO_DIAG: | ||
616 | diag308(DIAG308_IPL, NULL); | ||
617 | break; | ||
618 | case IPL_METHOD_FCP_RO_VM: | ||
619 | cpcmd("IPL", NULL, 0, NULL); | ||
620 | break; | ||
621 | case IPL_METHOD_NONE: | ||
622 | default: | ||
623 | if (MACHINE_IS_VM) | ||
624 | cpcmd("IPL", NULL, 0, NULL); | ||
625 | diag308(DIAG308_IPL, NULL); | ||
626 | break; | ||
627 | } | ||
628 | panic("reipl failed!\n"); | ||
629 | } | ||
630 | |||
631 | static void do_dump(void) | ||
632 | { | ||
633 | struct ccw_dev_id devid; | ||
634 | static char buf[100]; | ||
635 | |||
636 | switch (dump_type) { | ||
637 | case IPL_TYPE_CCW: | ||
638 | printk(KERN_EMERG "Automatic dump on ccw device: 0.0.%04x\n", | ||
639 | dump_block_ccw->ipl_info.ccw.devno); | ||
640 | break; | ||
641 | case IPL_TYPE_FCP: | ||
642 | printk(KERN_EMERG "Automatic dump on fcp device:\n"); | ||
643 | print_fcp_block(dump_block_fcp); | ||
644 | break; | ||
645 | default: | ||
646 | return; | ||
647 | } | ||
648 | |||
649 | switch (dump_method) { | ||
650 | case IPL_METHOD_CCW_CIO: | ||
651 | dump_smp_stop_all(); | ||
652 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | ||
653 | devid.ssid = 0; | ||
654 | reipl_ccw_dev(&devid); | ||
655 | break; | ||
656 | case IPL_METHOD_CCW_VM: | ||
657 | dump_smp_stop_all(); | ||
658 | sprintf(buf, "STORE STATUS"); | ||
659 | cpcmd(buf, NULL, 0, NULL); | ||
660 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | ||
661 | cpcmd(buf, NULL, 0, NULL); | ||
662 | break; | ||
663 | case IPL_METHOD_CCW_DIAG: | ||
664 | diag308(DIAG308_SET, dump_block_ccw); | ||
665 | diag308(DIAG308_DUMP, NULL); | ||
666 | break; | ||
667 | case IPL_METHOD_FCP_RW_DIAG: | ||
668 | diag308(DIAG308_SET, dump_block_fcp); | ||
669 | diag308(DIAG308_DUMP, NULL); | ||
670 | break; | ||
671 | case IPL_METHOD_NONE: | ||
672 | default: | ||
673 | return; | ||
674 | } | ||
675 | printk(KERN_EMERG "Dump failed!\n"); | ||
676 | } | ||
677 | |||
678 | /* init functions */ | ||
679 | |||
680 | static int __init ipl_register_fcp_files(void) | ||
681 | { | ||
682 | int rc; | ||
683 | |||
684 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | ||
685 | &ipl_fcp_attr_group); | ||
686 | if (rc) | ||
687 | goto out; | ||
688 | rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj, | ||
689 | &ipl_parameter_attr); | ||
690 | if (rc) | ||
691 | goto out_ipl_parm; | ||
692 | rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj, | ||
693 | &ipl_scp_data_attr); | ||
694 | if (!rc) | ||
695 | goto out; | ||
696 | |||
697 | sysfs_remove_bin_file(&ipl_subsys.kset.kobj, &ipl_parameter_attr); | ||
698 | |||
699 | out_ipl_parm: | ||
700 | sysfs_remove_group(&ipl_subsys.kset.kobj, &ipl_fcp_attr_group); | ||
701 | out: | ||
702 | return rc; | ||
703 | } | ||
704 | |||
705 | static int __init ipl_init(void) | ||
706 | { | ||
707 | int rc; | ||
708 | |||
709 | rc = firmware_register(&ipl_subsys); | ||
710 | if (rc) | ||
711 | return rc; | ||
712 | switch (ipl_get_type()) { | ||
713 | case IPL_TYPE_CCW: | ||
714 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | ||
715 | &ipl_ccw_attr_group); | ||
716 | break; | ||
717 | case IPL_TYPE_FCP: | ||
718 | rc = ipl_register_fcp_files(); | ||
719 | break; | ||
720 | default: | ||
721 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | ||
722 | &ipl_unknown_attr_group); | ||
723 | break; | ||
724 | } | ||
725 | if (rc) | ||
726 | firmware_unregister(&ipl_subsys); | ||
727 | return rc; | ||
728 | } | ||
729 | |||
730 | static void __init reipl_probe(void) | ||
731 | { | ||
732 | void *buffer; | ||
733 | |||
734 | buffer = (void *) get_zeroed_page(GFP_KERNEL); | ||
735 | if (!buffer) | ||
736 | return; | ||
737 | if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) | ||
738 | diag308_set_works = 1; | ||
739 | free_page((unsigned long)buffer); | ||
740 | } | ||
741 | |||
742 | static int __init reipl_ccw_init(void) | ||
743 | { | ||
744 | int rc; | ||
745 | |||
746 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); | ||
747 | if (!reipl_block_ccw) | ||
748 | return -ENOMEM; | ||
749 | rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_ccw_attr_group); | ||
750 | if (rc) { | ||
751 | free_page((unsigned long)reipl_block_ccw); | ||
752 | return rc; | ||
753 | } | ||
754 | reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN; | ||
755 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | ||
756 | reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw); | ||
757 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; | ||
758 | if (ipl_get_type() == IPL_TYPE_CCW) | ||
759 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; | ||
760 | reipl_capabilities |= IPL_TYPE_CCW; | ||
761 | return 0; | ||
762 | } | ||
763 | |||
764 | static int __init reipl_fcp_init(void) | ||
765 | { | ||
766 | int rc; | ||
767 | |||
768 | if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP)) | ||
769 | return 0; | ||
770 | if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP)) | ||
771 | make_attrs_ro(reipl_fcp_attrs); | ||
772 | |||
773 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); | ||
774 | if (!reipl_block_fcp) | ||
775 | return -ENOMEM; | ||
776 | rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_fcp_attr_group); | ||
777 | if (rc) { | ||
778 | free_page((unsigned long)reipl_block_fcp); | ||
779 | return rc; | ||
780 | } | ||
781 | if (ipl_get_type() == IPL_TYPE_FCP) { | ||
782 | memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); | ||
783 | } else { | ||
784 | reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; | ||
785 | reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; | ||
786 | reipl_block_fcp->hdr.blk0_len = | ||
787 | sizeof(reipl_block_fcp->ipl_info.fcp); | ||
788 | reipl_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP; | ||
789 | reipl_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_IPL; | ||
790 | } | ||
791 | reipl_capabilities |= IPL_TYPE_FCP; | ||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | static int __init reipl_init(void) | ||
796 | { | ||
797 | int rc; | ||
798 | |||
799 | rc = firmware_register(&reipl_subsys); | ||
800 | if (rc) | ||
801 | return rc; | ||
802 | rc = subsys_create_file(&reipl_subsys, &reipl_type_attr); | ||
803 | if (rc) { | ||
804 | firmware_unregister(&reipl_subsys); | ||
805 | return rc; | ||
806 | } | ||
807 | rc = reipl_ccw_init(); | ||
808 | if (rc) | ||
809 | return rc; | ||
810 | rc = reipl_fcp_init(); | ||
811 | if (rc) | ||
812 | return rc; | ||
813 | rc = reipl_set_type(ipl_get_type()); | ||
814 | if (rc) | ||
815 | return rc; | ||
816 | return 0; | ||
817 | } | ||
818 | |||
819 | static int __init dump_ccw_init(void) | ||
820 | { | ||
821 | int rc; | ||
822 | |||
823 | dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); | ||
824 | if (!dump_block_ccw) | ||
825 | return -ENOMEM; | ||
826 | rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_ccw_attr_group); | ||
827 | if (rc) { | ||
828 | free_page((unsigned long)dump_block_ccw); | ||
829 | return rc; | ||
830 | } | ||
831 | dump_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN; | ||
832 | dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | ||
833 | dump_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw); | ||
834 | dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; | ||
835 | dump_capabilities |= IPL_TYPE_CCW; | ||
836 | return 0; | ||
837 | } | ||
838 | |||
839 | extern char s390_readinfo_sccb[]; | ||
840 | |||
841 | static int __init dump_fcp_init(void) | ||
842 | { | ||
843 | int rc; | ||
844 | |||
845 | if(!(s390_readinfo_sccb[91] & 0x2)) | ||
846 | return 0; /* LDIPL DUMP is not installed */ | ||
847 | if (!diag308_set_works) | ||
848 | return 0; | ||
849 | dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); | ||
850 | if (!dump_block_fcp) | ||
851 | return -ENOMEM; | ||
852 | rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_fcp_attr_group); | ||
853 | if (rc) { | ||
854 | free_page((unsigned long)dump_block_fcp); | ||
855 | return rc; | ||
856 | } | ||
857 | dump_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; | ||
858 | dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; | ||
859 | dump_block_fcp->hdr.blk0_len = sizeof(dump_block_fcp->ipl_info.fcp); | ||
860 | dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP; | ||
861 | dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP; | ||
862 | dump_capabilities |= IPL_TYPE_FCP; | ||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | #define SHUTDOWN_ON_PANIC_PRIO 0 | ||
867 | |||
868 | static int shutdown_on_panic_notify(struct notifier_block *self, | ||
869 | unsigned long event, void *data) | ||
870 | { | ||
871 | if (on_panic_action == SHUTDOWN_DUMP) | ||
872 | do_dump(); | ||
873 | else if (on_panic_action == SHUTDOWN_REIPL) | ||
874 | do_reipl(); | ||
875 | return NOTIFY_OK; | ||
876 | } | ||
877 | |||
878 | static struct notifier_block shutdown_on_panic_nb = { | ||
879 | .notifier_call = shutdown_on_panic_notify, | ||
880 | .priority = SHUTDOWN_ON_PANIC_PRIO | ||
881 | }; | ||
882 | |||
883 | static int __init dump_init(void) | ||
884 | { | ||
885 | int rc; | ||
886 | |||
887 | rc = firmware_register(&dump_subsys); | ||
888 | if (rc) | ||
889 | return rc; | ||
890 | rc = subsys_create_file(&dump_subsys, &dump_type_attr); | ||
891 | if (rc) { | ||
892 | firmware_unregister(&dump_subsys); | ||
893 | return rc; | ||
894 | } | ||
895 | rc = dump_ccw_init(); | ||
896 | if (rc) | ||
897 | return rc; | ||
898 | rc = dump_fcp_init(); | ||
899 | if (rc) | ||
900 | return rc; | ||
901 | dump_set_type(IPL_TYPE_NONE); | ||
902 | return 0; | ||
903 | } | ||
904 | |||
905 | static int __init shutdown_actions_init(void) | ||
906 | { | ||
907 | int rc; | ||
908 | |||
909 | rc = firmware_register(&shutdown_actions_subsys); | ||
910 | if (rc) | ||
911 | return rc; | ||
912 | rc = subsys_create_file(&shutdown_actions_subsys, &on_panic_attr); | ||
913 | if (rc) { | ||
914 | firmware_unregister(&shutdown_actions_subsys); | ||
915 | return rc; | ||
916 | } | ||
917 | atomic_notifier_chain_register(&panic_notifier_list, | ||
918 | &shutdown_on_panic_nb); | ||
919 | return 0; | ||
920 | } | ||
921 | |||
922 | static int __init s390_ipl_init(void) | ||
923 | { | ||
924 | int rc; | ||
925 | |||
926 | reipl_probe(); | ||
927 | rc = ipl_init(); | ||
928 | if (rc) | ||
929 | return rc; | ||
930 | rc = reipl_init(); | ||
931 | if (rc) | ||
932 | return rc; | ||
933 | rc = dump_init(); | ||
934 | if (rc) | ||
935 | return rc; | ||
936 | rc = shutdown_actions_init(); | ||
937 | if (rc) | ||
938 | return rc; | ||
939 | return 0; | ||
940 | } | ||
941 | |||
942 | __initcall(s390_ipl_init); | ||
diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S index 658e5ac484f9..4562cdbce8eb 100644 --- a/arch/s390/kernel/reipl.S +++ b/arch/s390/kernel/reipl.S | |||
@@ -8,13 +8,30 @@ | |||
8 | 8 | ||
9 | #include <asm/lowcore.h> | 9 | #include <asm/lowcore.h> |
10 | 10 | ||
11 | .globl do_reipl | 11 | .globl do_reipl_asm |
12 | do_reipl: basr %r13,0 | 12 | do_reipl_asm: basr %r13,0 |
13 | .Lpg0: lpsw .Lnewpsw-.Lpg0(%r13) | 13 | .Lpg0: lpsw .Lnewpsw-.Lpg0(%r13) |
14 | .Lpg1: lctl %c6,%c6,.Lall-.Lpg0(%r13) | 14 | |
15 | stctl %c0,%c0,.Lctlsave-.Lpg0(%r13) | 15 | # switch off lowcore protection |
16 | ni .Lctlsave-.Lpg0(%r13),0xef | 16 | |
17 | lctl %c0,%c0,.Lctlsave-.Lpg0(%r13) | 17 | .Lpg1: stctl %c0,%c0,.Lctlsave1-.Lpg0(%r13) |
18 | stctl %c0,%c0,.Lctlsave2-.Lpg0(%r13) | ||
19 | ni .Lctlsave1-.Lpg0(%r13),0xef | ||
20 | lctl %c0,%c0,.Lctlsave1-.Lpg0(%r13) | ||
21 | |||
22 | # do store status of all registers | ||
23 | |||
24 | stm %r0,%r15,__LC_GPREGS_SAVE_AREA | ||
25 | stctl %c0,%c15,__LC_CREGS_SAVE_AREA | ||
26 | mvc __LC_CREGS_SAVE_AREA(4),.Lctlsave2-.Lpg0(%r13) | ||
27 | stam %a0,%a15,__LC_AREGS_SAVE_AREA | ||
28 | stpx __LC_PREFIX_SAVE_AREA | ||
29 | stckc .Lclkcmp-.Lpg0(%r13) | ||
30 | mvc __LC_CLOCK_COMP_SAVE_AREA(8),.Lclkcmp-.Lpg0(%r13) | ||
31 | stpt __LC_CPU_TIMER_SAVE_AREA | ||
32 | st %r13, __LC_PSW_SAVE_AREA+4 | ||
33 | |||
34 | lctl %c6,%c6,.Lall-.Lpg0(%r13) | ||
18 | lr %r1,%r2 | 35 | lr %r1,%r2 |
19 | mvc __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13) | 36 | mvc __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13) |
20 | stsch .Lschib-.Lpg0(%r13) | 37 | stsch .Lschib-.Lpg0(%r13) |
@@ -46,9 +63,11 @@ do_reipl: basr %r13,0 | |||
46 | .Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13) | 63 | .Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13) |
47 | lpsw .Ldispsw-.Lpg0(%r13) | 64 | lpsw .Ldispsw-.Lpg0(%r13) |
48 | .align 8 | 65 | .align 8 |
66 | .Lclkcmp: .quad 0x0000000000000000 | ||
49 | .Lall: .long 0xff000000 | 67 | .Lall: .long 0xff000000 |
50 | .Lnull: .long 0x00000000 | 68 | .Lnull: .long 0x00000000 |
51 | .Lctlsave: .long 0x00000000 | 69 | .Lctlsave1: .long 0x00000000 |
70 | .Lctlsave2: .long 0x00000000 | ||
52 | .align 8 | 71 | .align 8 |
53 | .Lnewpsw: .long 0x00080000,0x80000000+.Lpg1 | 72 | .Lnewpsw: .long 0x00080000,0x80000000+.Lpg1 |
54 | .Lpcnew: .long 0x00080000,0x80000000+.Lecs | 73 | .Lpcnew: .long 0x00080000,0x80000000+.Lecs |
diff --git a/arch/s390/kernel/reipl64.S b/arch/s390/kernel/reipl64.S index 4d090d60f3ef..95bd1e234f63 100644 --- a/arch/s390/kernel/reipl64.S +++ b/arch/s390/kernel/reipl64.S | |||
@@ -8,13 +8,30 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <asm/lowcore.h> | 10 | #include <asm/lowcore.h> |
11 | .globl do_reipl | 11 | .globl do_reipl_asm |
12 | do_reipl: basr %r13,0 | 12 | do_reipl_asm: basr %r13,0 |
13 | .Lpg0: lpswe .Lnewpsw-.Lpg0(%r13) | 13 | |
14 | # do store status of all registers | ||
15 | |||
16 | .Lpg0: stg %r1,.Lregsave-.Lpg0(%r13) | ||
17 | lghi %r1,0x1000 | ||
18 | stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-0x1000(%r1) | ||
19 | lg %r0,.Lregsave-.Lpg0(%r13) | ||
20 | stg %r0,__LC_GPREGS_SAVE_AREA-0x1000+8(%r1) | ||
21 | stctg %c0,%c15,__LC_CREGS_SAVE_AREA-0x1000(%r1) | ||
22 | stam %a0,%a15,__LC_AREGS_SAVE_AREA-0x1000(%r1) | ||
23 | stpx __LC_PREFIX_SAVE_AREA-0x1000(%r1) | ||
24 | stfpc __LC_FP_CREG_SAVE_AREA-0x1000(%r1) | ||
25 | stckc .Lclkcmp-.Lpg0(%r13) | ||
26 | mvc __LC_CLOCK_COMP_SAVE_AREA-0x1000(8,%r1),.Lclkcmp-.Lpg0(%r13) | ||
27 | stpt __LC_CPU_TIMER_SAVE_AREA-0x1000(%r1) | ||
28 | stg %r13, __LC_PSW_SAVE_AREA-0x1000+8(%r1) | ||
29 | |||
30 | lpswe .Lnewpsw-.Lpg0(%r13) | ||
14 | .Lpg1: lctlg %c6,%c6,.Lall-.Lpg0(%r13) | 31 | .Lpg1: lctlg %c6,%c6,.Lall-.Lpg0(%r13) |
15 | stctg %c0,%c0,.Lctlsave-.Lpg0(%r13) | 32 | stctg %c0,%c0,.Lregsave-.Lpg0(%r13) |
16 | ni .Lctlsave+4-.Lpg0(%r13),0xef | 33 | ni .Lregsave+4-.Lpg0(%r13),0xef |
17 | lctlg %c0,%c0,.Lctlsave-.Lpg0(%r13) | 34 | lctlg %c0,%c0,.Lregsave-.Lpg0(%r13) |
18 | lgr %r1,%r2 | 35 | lgr %r1,%r2 |
19 | mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13) | 36 | mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13) |
20 | stsch .Lschib-.Lpg0(%r13) | 37 | stsch .Lschib-.Lpg0(%r13) |
@@ -50,8 +67,9 @@ do_reipl: basr %r13,0 | |||
50 | st %r14,.Ldispsw+12-.Lpg0(%r13) | 67 | st %r14,.Ldispsw+12-.Lpg0(%r13) |
51 | lpswe .Ldispsw-.Lpg0(%r13) | 68 | lpswe .Ldispsw-.Lpg0(%r13) |
52 | .align 8 | 69 | .align 8 |
70 | .Lclkcmp: .quad 0x0000000000000000 | ||
53 | .Lall: .quad 0x00000000ff000000 | 71 | .Lall: .quad 0x00000000ff000000 |
54 | .Lctlsave: .quad 0x0000000000000000 | 72 | .Lregsave: .quad 0x0000000000000000 |
55 | .Lnull: .long 0x0000000000000000 | 73 | .Lnull: .long 0x0000000000000000 |
56 | .align 16 | 74 | .align 16 |
57 | /* | 75 | /* |
@@ -92,5 +110,3 @@ do_reipl: basr %r13,0 | |||
92 | .long 0x00000000,0x00000000 | 110 | .long 0x00000000,0x00000000 |
93 | .long 0x00000000,0x00000000 | 111 | .long 0x00000000,0x00000000 |
94 | 112 | ||
95 | |||
96 | |||
diff --git a/arch/s390/kernel/reipl_diag.c b/arch/s390/kernel/reipl_diag.c deleted file mode 100644 index 1f33951ba439..000000000000 --- a/arch/s390/kernel/reipl_diag.c +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | /* | ||
2 | * This file contains the implementation of the | ||
3 | * Linux re-IPL support | ||
4 | * | ||
5 | * (C) Copyright IBM Corp. 2005 | ||
6 | * | ||
7 | * Author(s): Volker Sameske (sameske@de.ibm.com) | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | |||
13 | static unsigned int reipl_diag_rc1; | ||
14 | static unsigned int reipl_diag_rc2; | ||
15 | |||
16 | /* | ||
17 | * re-IPL the system using the last used IPL parameters | ||
18 | */ | ||
19 | void reipl_diag(void) | ||
20 | { | ||
21 | asm volatile ( | ||
22 | " la %%r4,0\n" | ||
23 | " la %%r5,0\n" | ||
24 | " diag %%r4,%2,0x308\n" | ||
25 | "0:\n" | ||
26 | " st %%r4,%0\n" | ||
27 | " st %%r5,%1\n" | ||
28 | ".section __ex_table,\"a\"\n" | ||
29 | #ifdef CONFIG_64BIT | ||
30 | " .align 8\n" | ||
31 | " .quad 0b, 0b\n" | ||
32 | #else | ||
33 | " .align 4\n" | ||
34 | " .long 0b, 0b\n" | ||
35 | #endif | ||
36 | ".previous\n" | ||
37 | : "=m" (reipl_diag_rc1), "=m" (reipl_diag_rc2) | ||
38 | : "d" (3) : "cc", "4", "5" ); | ||
39 | } | ||
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 89051e8a5d8d..f2a9165ca4f8 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
@@ -285,16 +285,9 @@ void (*_machine_power_off)(void) = machine_power_off_smp; | |||
285 | /* | 285 | /* |
286 | * Reboot, halt and power_off routines for non SMP. | 286 | * Reboot, halt and power_off routines for non SMP. |
287 | */ | 287 | */ |
288 | extern void reipl(unsigned long devno); | ||
289 | extern void reipl_diag(void); | ||
290 | static void do_machine_restart_nonsmp(char * __unused) | 288 | static void do_machine_restart_nonsmp(char * __unused) |
291 | { | 289 | { |
292 | reipl_diag(); | 290 | do_reipl(); |
293 | |||
294 | if (MACHINE_IS_VM) | ||
295 | cpcmd ("IPL", NULL, 0, NULL); | ||
296 | else | ||
297 | reipl (0x10000 | S390_lowcore.ipl_device); | ||
298 | } | 291 | } |
299 | 292 | ||
300 | static void do_machine_halt_nonsmp(void) | 293 | static void do_machine_halt_nonsmp(void) |
@@ -755,214 +748,3 @@ struct seq_operations cpuinfo_op = { | |||
755 | .show = show_cpuinfo, | 748 | .show = show_cpuinfo, |
756 | }; | 749 | }; |
757 | 750 | ||
758 | #define DEFINE_IPL_ATTR(_name, _format, _value) \ | ||
759 | static ssize_t ipl_##_name##_show(struct subsystem *subsys, \ | ||
760 | char *page) \ | ||
761 | { \ | ||
762 | return sprintf(page, _format, _value); \ | ||
763 | } \ | ||
764 | static struct subsys_attribute ipl_##_name##_attr = \ | ||
765 | __ATTR(_name, S_IRUGO, ipl_##_name##_show, NULL); | ||
766 | |||
767 | DEFINE_IPL_ATTR(wwpn, "0x%016llx\n", (unsigned long long) | ||
768 | IPL_PARMBLOCK_START->fcp.wwpn); | ||
769 | DEFINE_IPL_ATTR(lun, "0x%016llx\n", (unsigned long long) | ||
770 | IPL_PARMBLOCK_START->fcp.lun); | ||
771 | DEFINE_IPL_ATTR(bootprog, "%lld\n", (unsigned long long) | ||
772 | IPL_PARMBLOCK_START->fcp.bootprog); | ||
773 | DEFINE_IPL_ATTR(br_lba, "%lld\n", (unsigned long long) | ||
774 | IPL_PARMBLOCK_START->fcp.br_lba); | ||
775 | |||
776 | enum ipl_type_type { | ||
777 | ipl_type_unknown, | ||
778 | ipl_type_ccw, | ||
779 | ipl_type_fcp, | ||
780 | }; | ||
781 | |||
782 | static enum ipl_type_type | ||
783 | get_ipl_type(void) | ||
784 | { | ||
785 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; | ||
786 | |||
787 | if (!IPL_DEVNO_VALID) | ||
788 | return ipl_type_unknown; | ||
789 | if (!IPL_PARMBLOCK_VALID) | ||
790 | return ipl_type_ccw; | ||
791 | if (ipl->hdr.header.version > IPL_MAX_SUPPORTED_VERSION) | ||
792 | return ipl_type_unknown; | ||
793 | if (ipl->fcp.pbt != IPL_TYPE_FCP) | ||
794 | return ipl_type_unknown; | ||
795 | return ipl_type_fcp; | ||
796 | } | ||
797 | |||
798 | static ssize_t | ||
799 | ipl_type_show(struct subsystem *subsys, char *page) | ||
800 | { | ||
801 | switch (get_ipl_type()) { | ||
802 | case ipl_type_ccw: | ||
803 | return sprintf(page, "ccw\n"); | ||
804 | case ipl_type_fcp: | ||
805 | return sprintf(page, "fcp\n"); | ||
806 | default: | ||
807 | return sprintf(page, "unknown\n"); | ||
808 | } | ||
809 | } | ||
810 | |||
811 | static struct subsys_attribute ipl_type_attr = __ATTR_RO(ipl_type); | ||
812 | |||
813 | static ssize_t | ||
814 | ipl_device_show(struct subsystem *subsys, char *page) | ||
815 | { | ||
816 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; | ||
817 | |||
818 | switch (get_ipl_type()) { | ||
819 | case ipl_type_ccw: | ||
820 | return sprintf(page, "0.0.%04x\n", ipl_devno); | ||
821 | case ipl_type_fcp: | ||
822 | return sprintf(page, "0.0.%04x\n", ipl->fcp.devno); | ||
823 | default: | ||
824 | return 0; | ||
825 | } | ||
826 | } | ||
827 | |||
828 | static struct subsys_attribute ipl_device_attr = | ||
829 | __ATTR(device, S_IRUGO, ipl_device_show, NULL); | ||
830 | |||
831 | static struct attribute *ipl_fcp_attrs[] = { | ||
832 | &ipl_type_attr.attr, | ||
833 | &ipl_device_attr.attr, | ||
834 | &ipl_wwpn_attr.attr, | ||
835 | &ipl_lun_attr.attr, | ||
836 | &ipl_bootprog_attr.attr, | ||
837 | &ipl_br_lba_attr.attr, | ||
838 | NULL, | ||
839 | }; | ||
840 | |||
841 | static struct attribute_group ipl_fcp_attr_group = { | ||
842 | .attrs = ipl_fcp_attrs, | ||
843 | }; | ||
844 | |||
845 | static struct attribute *ipl_ccw_attrs[] = { | ||
846 | &ipl_type_attr.attr, | ||
847 | &ipl_device_attr.attr, | ||
848 | NULL, | ||
849 | }; | ||
850 | |||
851 | static struct attribute_group ipl_ccw_attr_group = { | ||
852 | .attrs = ipl_ccw_attrs, | ||
853 | }; | ||
854 | |||
855 | static struct attribute *ipl_unknown_attrs[] = { | ||
856 | &ipl_type_attr.attr, | ||
857 | NULL, | ||
858 | }; | ||
859 | |||
860 | static struct attribute_group ipl_unknown_attr_group = { | ||
861 | .attrs = ipl_unknown_attrs, | ||
862 | }; | ||
863 | |||
864 | static ssize_t | ||
865 | ipl_parameter_read(struct kobject *kobj, char *buf, loff_t off, size_t count) | ||
866 | { | ||
867 | unsigned int size = IPL_PARMBLOCK_SIZE; | ||
868 | |||
869 | if (off > size) | ||
870 | return 0; | ||
871 | if (off + count > size) | ||
872 | count = size - off; | ||
873 | |||
874 | memcpy(buf, (void *) IPL_PARMBLOCK_START + off, count); | ||
875 | return count; | ||
876 | } | ||
877 | |||
878 | static struct bin_attribute ipl_parameter_attr = { | ||
879 | .attr = { | ||
880 | .name = "binary_parameter", | ||
881 | .mode = S_IRUGO, | ||
882 | .owner = THIS_MODULE, | ||
883 | }, | ||
884 | .size = PAGE_SIZE, | ||
885 | .read = &ipl_parameter_read, | ||
886 | }; | ||
887 | |||
888 | static ssize_t | ||
889 | ipl_scp_data_read(struct kobject *kobj, char *buf, loff_t off, size_t count) | ||
890 | { | ||
891 | unsigned int size = IPL_PARMBLOCK_START->fcp.scp_data_len; | ||
892 | void *scp_data = &IPL_PARMBLOCK_START->fcp.scp_data; | ||
893 | |||
894 | if (off > size) | ||
895 | return 0; | ||
896 | if (off + count > size) | ||
897 | count = size - off; | ||
898 | |||
899 | memcpy(buf, scp_data + off, count); | ||
900 | return count; | ||
901 | } | ||
902 | |||
903 | static struct bin_attribute ipl_scp_data_attr = { | ||
904 | .attr = { | ||
905 | .name = "scp_data", | ||
906 | .mode = S_IRUGO, | ||
907 | .owner = THIS_MODULE, | ||
908 | }, | ||
909 | .size = PAGE_SIZE, | ||
910 | .read = &ipl_scp_data_read, | ||
911 | }; | ||
912 | |||
913 | static decl_subsys(ipl, NULL, NULL); | ||
914 | |||
915 | static int ipl_register_fcp_files(void) | ||
916 | { | ||
917 | int rc; | ||
918 | |||
919 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | ||
920 | &ipl_fcp_attr_group); | ||
921 | if (rc) | ||
922 | goto out; | ||
923 | rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj, | ||
924 | &ipl_parameter_attr); | ||
925 | if (rc) | ||
926 | goto out_ipl_parm; | ||
927 | rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj, | ||
928 | &ipl_scp_data_attr); | ||
929 | if (!rc) | ||
930 | goto out; | ||
931 | |||
932 | sysfs_remove_bin_file(&ipl_subsys.kset.kobj, &ipl_parameter_attr); | ||
933 | |||
934 | out_ipl_parm: | ||
935 | sysfs_remove_group(&ipl_subsys.kset.kobj, &ipl_fcp_attr_group); | ||
936 | out: | ||
937 | return rc; | ||
938 | } | ||
939 | |||
940 | static int __init | ||
941 | ipl_device_sysfs_register(void) { | ||
942 | int rc; | ||
943 | |||
944 | rc = firmware_register(&ipl_subsys); | ||
945 | if (rc) | ||
946 | goto out; | ||
947 | |||
948 | switch (get_ipl_type()) { | ||
949 | case ipl_type_ccw: | ||
950 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | ||
951 | &ipl_ccw_attr_group); | ||
952 | break; | ||
953 | case ipl_type_fcp: | ||
954 | rc = ipl_register_fcp_files(); | ||
955 | break; | ||
956 | default: | ||
957 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | ||
958 | &ipl_unknown_attr_group); | ||
959 | break; | ||
960 | } | ||
961 | |||
962 | if (rc) | ||
963 | firmware_unregister(&ipl_subsys); | ||
964 | out: | ||
965 | return rc; | ||
966 | } | ||
967 | |||
968 | __initcall(ipl_device_sysfs_register); | ||
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 8e03219eea76..b2e6f4c8d382 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c | |||
@@ -59,9 +59,6 @@ static struct task_struct *current_set[NR_CPUS]; | |||
59 | extern char vmhalt_cmd[]; | 59 | extern char vmhalt_cmd[]; |
60 | extern char vmpoff_cmd[]; | 60 | extern char vmpoff_cmd[]; |
61 | 61 | ||
62 | extern void reipl(unsigned long devno); | ||
63 | extern void reipl_diag(void); | ||
64 | |||
65 | static void smp_ext_bitcall(int, ec_bit_sig); | 62 | static void smp_ext_bitcall(int, ec_bit_sig); |
66 | static void smp_ext_bitcall_others(ec_bit_sig); | 63 | static void smp_ext_bitcall_others(ec_bit_sig); |
67 | 64 | ||
@@ -279,12 +276,7 @@ static void do_machine_restart(void * __unused) | |||
279 | * interrupted by an external interrupt and s390irq | 276 | * interrupted by an external interrupt and s390irq |
280 | * locks are always held disabled). | 277 | * locks are always held disabled). |
281 | */ | 278 | */ |
282 | reipl_diag(); | 279 | do_reipl(); |
283 | |||
284 | if (MACHINE_IS_VM) | ||
285 | cpcmd ("IPL", NULL, 0, NULL); | ||
286 | else | ||
287 | reipl (0x10000 | S390_lowcore.ipl_device); | ||
288 | } | 280 | } |
289 | 281 | ||
290 | void machine_restart_smp(char * __unused) | 282 | void machine_restart_smp(char * __unused) |