1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ipl/reipl/dump support for Linux on s390. |
4 | * |
5 | * Copyright IBM Corp. 2005, 2012 |
6 | * Author(s): Michael Holzheu <holzheu@de.ibm.com> |
7 | * Volker Sameske <sameske@de.ibm.com> |
8 | */ |
9 | |
10 | #include <linux/types.h> |
11 | #include <linux/export.h> |
12 | #include <linux/init.h> |
13 | #include <linux/device.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/kstrtox.h> |
16 | #include <linux/panic_notifier.h> |
17 | #include <linux/reboot.h> |
18 | #include <linux/ctype.h> |
19 | #include <linux/fs.h> |
20 | #include <linux/gfp.h> |
21 | #include <linux/crash_dump.h> |
22 | #include <linux/debug_locks.h> |
23 | #include <asm/asm-extable.h> |
24 | #include <asm/diag.h> |
25 | #include <asm/ipl.h> |
26 | #include <asm/smp.h> |
27 | #include <asm/setup.h> |
28 | #include <asm/cpcmd.h> |
29 | #include <asm/ebcdic.h> |
30 | #include <asm/sclp.h> |
31 | #include <asm/checksum.h> |
32 | #include <asm/debug.h> |
33 | #include <asm/abs_lowcore.h> |
34 | #include <asm/os_info.h> |
35 | #include <asm/sections.h> |
36 | #include <asm/boot_data.h> |
37 | #include "entry.h" |
38 | |
39 | #define IPL_PARM_BLOCK_VERSION 0 |
40 | |
41 | #define IPL_UNKNOWN_STR "unknown" |
42 | #define IPL_CCW_STR "ccw" |
43 | #define IPL_ECKD_STR "eckd" |
44 | #define IPL_ECKD_DUMP_STR "eckd_dump" |
45 | #define IPL_FCP_STR "fcp" |
46 | #define IPL_FCP_DUMP_STR "fcp_dump" |
47 | #define IPL_NVME_STR "nvme" |
48 | #define IPL_NVME_DUMP_STR "nvme_dump" |
49 | #define IPL_NSS_STR "nss" |
50 | |
51 | #define DUMP_CCW_STR "ccw" |
52 | #define DUMP_ECKD_STR "eckd" |
53 | #define DUMP_FCP_STR "fcp" |
54 | #define DUMP_NVME_STR "nvme" |
55 | #define DUMP_NONE_STR "none" |
56 | |
57 | /* |
58 | * Four shutdown trigger types are supported: |
59 | * - panic |
60 | * - halt |
61 | * - power off |
62 | * - reipl |
63 | * - restart |
64 | */ |
65 | #define ON_PANIC_STR "on_panic" |
66 | #define ON_HALT_STR "on_halt" |
67 | #define ON_POFF_STR "on_poff" |
68 | #define ON_REIPL_STR "on_reboot" |
69 | #define ON_RESTART_STR "on_restart" |
70 | |
71 | struct shutdown_action; |
72 | struct shutdown_trigger { |
73 | char *name; |
74 | struct shutdown_action *action; |
75 | }; |
76 | |
77 | /* |
78 | * The following shutdown action types are supported: |
79 | */ |
80 | #define SHUTDOWN_ACTION_IPL_STR "ipl" |
81 | #define SHUTDOWN_ACTION_REIPL_STR "reipl" |
82 | #define SHUTDOWN_ACTION_DUMP_STR "dump" |
83 | #define SHUTDOWN_ACTION_VMCMD_STR "vmcmd" |
84 | #define SHUTDOWN_ACTION_STOP_STR "stop" |
85 | #define SHUTDOWN_ACTION_DUMP_REIPL_STR "dump_reipl" |
86 | |
87 | struct shutdown_action { |
88 | char *name; |
89 | void (*fn) (struct shutdown_trigger *trigger); |
90 | int (*init) (void); |
91 | int init_rc; |
92 | }; |
93 | |
94 | static char *ipl_type_str(enum ipl_type type) |
95 | { |
96 | switch (type) { |
97 | case IPL_TYPE_CCW: |
98 | return IPL_CCW_STR; |
99 | case IPL_TYPE_ECKD: |
100 | return IPL_ECKD_STR; |
101 | case IPL_TYPE_ECKD_DUMP: |
102 | return IPL_ECKD_DUMP_STR; |
103 | case IPL_TYPE_FCP: |
104 | return IPL_FCP_STR; |
105 | case IPL_TYPE_FCP_DUMP: |
106 | return IPL_FCP_DUMP_STR; |
107 | case IPL_TYPE_NSS: |
108 | return IPL_NSS_STR; |
109 | case IPL_TYPE_NVME: |
110 | return IPL_NVME_STR; |
111 | case IPL_TYPE_NVME_DUMP: |
112 | return IPL_NVME_DUMP_STR; |
113 | case IPL_TYPE_UNKNOWN: |
114 | default: |
115 | return IPL_UNKNOWN_STR; |
116 | } |
117 | } |
118 | |
119 | enum dump_type { |
120 | DUMP_TYPE_NONE = 1, |
121 | DUMP_TYPE_CCW = 2, |
122 | DUMP_TYPE_FCP = 4, |
123 | DUMP_TYPE_NVME = 8, |
124 | DUMP_TYPE_ECKD = 16, |
125 | }; |
126 | |
127 | static char *dump_type_str(enum dump_type type) |
128 | { |
129 | switch (type) { |
130 | case DUMP_TYPE_NONE: |
131 | return DUMP_NONE_STR; |
132 | case DUMP_TYPE_CCW: |
133 | return DUMP_CCW_STR; |
134 | case DUMP_TYPE_ECKD: |
135 | return DUMP_ECKD_STR; |
136 | case DUMP_TYPE_FCP: |
137 | return DUMP_FCP_STR; |
138 | case DUMP_TYPE_NVME: |
139 | return DUMP_NVME_STR; |
140 | default: |
141 | return NULL; |
142 | } |
143 | } |
144 | |
145 | int __bootdata_preserved(ipl_block_valid); |
146 | struct ipl_parameter_block __bootdata_preserved(ipl_block); |
147 | int __bootdata_preserved(ipl_secure_flag); |
148 | |
149 | unsigned long __bootdata_preserved(ipl_cert_list_addr); |
150 | unsigned long __bootdata_preserved(ipl_cert_list_size); |
151 | |
152 | unsigned long __bootdata(early_ipl_comp_list_addr); |
153 | unsigned long __bootdata(early_ipl_comp_list_size); |
154 | |
155 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; |
156 | |
157 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; |
158 | static struct ipl_parameter_block *reipl_block_fcp; |
159 | static struct ipl_parameter_block *reipl_block_nvme; |
160 | static struct ipl_parameter_block *reipl_block_ccw; |
161 | static struct ipl_parameter_block *reipl_block_eckd; |
162 | static struct ipl_parameter_block *reipl_block_nss; |
163 | static struct ipl_parameter_block *reipl_block_actual; |
164 | |
165 | static int dump_capabilities = DUMP_TYPE_NONE; |
166 | static enum dump_type dump_type = DUMP_TYPE_NONE; |
167 | static struct ipl_parameter_block *dump_block_fcp; |
168 | static struct ipl_parameter_block *dump_block_nvme; |
169 | static struct ipl_parameter_block *dump_block_ccw; |
170 | static struct ipl_parameter_block *dump_block_eckd; |
171 | |
172 | static struct sclp_ipl_info sclp_ipl_info; |
173 | |
174 | static bool reipl_nvme_clear; |
175 | static bool reipl_fcp_clear; |
176 | static bool reipl_ccw_clear; |
177 | static bool reipl_eckd_clear; |
178 | |
179 | static unsigned long os_info_flags; |
180 | |
181 | static inline int __diag308(unsigned long subcode, unsigned long addr) |
182 | { |
183 | union register_pair r1; |
184 | |
185 | r1.even = addr; |
186 | r1.odd = 0; |
187 | asm volatile( |
188 | " diag %[r1],%[subcode],0x308\n" |
189 | "0: nopr %%r7\n" |
190 | EX_TABLE(0b,0b) |
191 | : [r1] "+&d" (r1.pair) |
192 | : [subcode] "d" (subcode) |
193 | : "cc" , "memory" ); |
194 | return r1.odd; |
195 | } |
196 | |
197 | int diag308(unsigned long subcode, void *addr) |
198 | { |
199 | diag_stat_inc(DIAG_STAT_X308); |
200 | return __diag308(subcode, addr: addr ? virt_to_phys(address: addr) : 0); |
201 | } |
202 | EXPORT_SYMBOL_GPL(diag308); |
203 | |
204 | /* SYSFS */ |
205 | |
206 | #define IPL_ATTR_SHOW_FN(_prefix, _name, _format, args...) \ |
207 | static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \ |
208 | struct kobj_attribute *attr, \ |
209 | char *page) \ |
210 | { \ |
211 | return scnprintf(page, PAGE_SIZE, _format, ##args); \ |
212 | } |
213 | |
214 | #define IPL_ATTR_CCW_STORE_FN(_prefix, _name, _ipl_blk) \ |
215 | static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ |
216 | struct kobj_attribute *attr, \ |
217 | const char *buf, size_t len) \ |
218 | { \ |
219 | unsigned long long ssid, devno; \ |
220 | \ |
221 | if (sscanf(buf, "0.%llx.%llx\n", &ssid, &devno) != 2) \ |
222 | return -EINVAL; \ |
223 | \ |
224 | if (ssid > __MAX_SSID || devno > __MAX_SUBCHANNEL) \ |
225 | return -EINVAL; \ |
226 | \ |
227 | _ipl_blk.ssid = ssid; \ |
228 | _ipl_blk.devno = devno; \ |
229 | return len; \ |
230 | } |
231 | |
232 | #define DEFINE_IPL_CCW_ATTR_RW(_prefix, _name, _ipl_blk) \ |
233 | IPL_ATTR_SHOW_FN(_prefix, _name, "0.%x.%04x\n", \ |
234 | _ipl_blk.ssid, _ipl_blk.devno); \ |
235 | IPL_ATTR_CCW_STORE_FN(_prefix, _name, _ipl_blk); \ |
236 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
237 | __ATTR(_name, 0644, \ |
238 | sys_##_prefix##_##_name##_show, \ |
239 | sys_##_prefix##_##_name##_store) \ |
240 | |
241 | #define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \ |
242 | IPL_ATTR_SHOW_FN(_prefix, _name, _format, _value) \ |
243 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
244 | __ATTR(_name, 0444, sys_##_prefix##_##_name##_show, NULL) |
245 | |
246 | #define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \ |
247 | IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, (unsigned long long) _value) \ |
248 | static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ |
249 | struct kobj_attribute *attr, \ |
250 | const char *buf, size_t len) \ |
251 | { \ |
252 | unsigned long long value; \ |
253 | if (sscanf(buf, _fmt_in, &value) != 1) \ |
254 | return -EINVAL; \ |
255 | _value = value; \ |
256 | return len; \ |
257 | } \ |
258 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
259 | __ATTR(_name, 0644, \ |
260 | sys_##_prefix##_##_name##_show, \ |
261 | sys_##_prefix##_##_name##_store) |
262 | |
263 | #define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\ |
264 | IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, _value) \ |
265 | static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ |
266 | struct kobj_attribute *attr, \ |
267 | const char *buf, size_t len) \ |
268 | { \ |
269 | strscpy(_value, buf, sizeof(_value)); \ |
270 | strim(_value); \ |
271 | return len; \ |
272 | } \ |
273 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
274 | __ATTR(_name, 0644, \ |
275 | sys_##_prefix##_##_name##_show, \ |
276 | sys_##_prefix##_##_name##_store) |
277 | |
278 | /* |
279 | * ipl section |
280 | */ |
281 | |
282 | static __init enum ipl_type get_ipl_type(void) |
283 | { |
284 | if (!ipl_block_valid) |
285 | return IPL_TYPE_UNKNOWN; |
286 | |
287 | switch (ipl_block.pb0_hdr.pbt) { |
288 | case IPL_PBT_CCW: |
289 | return IPL_TYPE_CCW; |
290 | case IPL_PBT_FCP: |
291 | if (ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) |
292 | return IPL_TYPE_FCP_DUMP; |
293 | else |
294 | return IPL_TYPE_FCP; |
295 | case IPL_PBT_NVME: |
296 | if (ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP) |
297 | return IPL_TYPE_NVME_DUMP; |
298 | else |
299 | return IPL_TYPE_NVME; |
300 | case IPL_PBT_ECKD: |
301 | if (ipl_block.eckd.opt == IPL_PB0_ECKD_OPT_DUMP) |
302 | return IPL_TYPE_ECKD_DUMP; |
303 | else |
304 | return IPL_TYPE_ECKD; |
305 | } |
306 | return IPL_TYPE_UNKNOWN; |
307 | } |
308 | |
309 | struct ipl_info ipl_info; |
310 | EXPORT_SYMBOL_GPL(ipl_info); |
311 | |
312 | static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, |
313 | char *page) |
314 | { |
315 | return sprintf(buf: page, fmt: "%s\n" , ipl_type_str(type: ipl_info.type)); |
316 | } |
317 | |
318 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); |
319 | |
320 | static ssize_t ipl_secure_show(struct kobject *kobj, |
321 | struct kobj_attribute *attr, char *page) |
322 | { |
323 | return sprintf(buf: page, fmt: "%i\n" , !!ipl_secure_flag); |
324 | } |
325 | |
326 | static struct kobj_attribute sys_ipl_secure_attr = |
327 | __ATTR(secure, 0444, ipl_secure_show, NULL); |
328 | |
329 | static ssize_t ipl_has_secure_show(struct kobject *kobj, |
330 | struct kobj_attribute *attr, char *page) |
331 | { |
332 | return sprintf(buf: page, fmt: "%i\n" , !!sclp.has_sipl); |
333 | } |
334 | |
335 | static struct kobj_attribute sys_ipl_has_secure_attr = |
336 | __ATTR(has_secure, 0444, ipl_has_secure_show, NULL); |
337 | |
338 | static ssize_t ipl_vm_parm_show(struct kobject *kobj, |
339 | struct kobj_attribute *attr, char *page) |
340 | { |
341 | char parm[DIAG308_VMPARM_SIZE + 1] = {}; |
342 | |
343 | if (ipl_block_valid && (ipl_block.pb0_hdr.pbt == IPL_PBT_CCW)) |
344 | ipl_block_get_ascii_vmparm(parm, sizeof(parm), &ipl_block); |
345 | return sprintf(buf: page, fmt: "%s\n" , parm); |
346 | } |
347 | |
348 | static struct kobj_attribute sys_ipl_vm_parm_attr = |
349 | __ATTR(parm, 0444, ipl_vm_parm_show, NULL); |
350 | |
351 | static ssize_t sys_ipl_device_show(struct kobject *kobj, |
352 | struct kobj_attribute *attr, char *page) |
353 | { |
354 | switch (ipl_info.type) { |
355 | case IPL_TYPE_CCW: |
356 | return sprintf(page, "0.%x.%04x\n" , ipl_block.ccw.ssid, |
357 | ipl_block.ccw.devno); |
358 | case IPL_TYPE_ECKD: |
359 | case IPL_TYPE_ECKD_DUMP: |
360 | return sprintf(page, "0.%x.%04x\n" , ipl_block.eckd.ssid, |
361 | ipl_block.eckd.devno); |
362 | case IPL_TYPE_FCP: |
363 | case IPL_TYPE_FCP_DUMP: |
364 | return sprintf(page, "0.0.%04x\n" , ipl_block.fcp.devno); |
365 | case IPL_TYPE_NVME: |
366 | case IPL_TYPE_NVME_DUMP: |
367 | return sprintf(page, "%08ux\n" , ipl_block.nvme.fid); |
368 | default: |
369 | return 0; |
370 | } |
371 | } |
372 | |
373 | static struct kobj_attribute sys_ipl_device_attr = |
374 | __ATTR(device, 0444, sys_ipl_device_show, NULL); |
375 | |
376 | static ssize_t ipl_parameter_read(struct file *filp, struct kobject *kobj, |
377 | struct bin_attribute *attr, char *buf, |
378 | loff_t off, size_t count) |
379 | { |
380 | return memory_read_from_buffer(buf, count, &off, &ipl_block, |
381 | ipl_block.hdr.len); |
382 | } |
383 | static struct bin_attribute ipl_parameter_attr = |
384 | __BIN_ATTR(binary_parameter, 0444, ipl_parameter_read, NULL, |
385 | PAGE_SIZE); |
386 | |
387 | static ssize_t ipl_scp_data_read(struct file *filp, struct kobject *kobj, |
388 | struct bin_attribute *attr, char *buf, |
389 | loff_t off, size_t count) |
390 | { |
391 | unsigned int size = ipl_block.fcp.scp_data_len; |
392 | void *scp_data = &ipl_block.fcp.scp_data; |
393 | |
394 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: scp_data, available: size); |
395 | } |
396 | |
397 | static ssize_t ipl_nvme_scp_data_read(struct file *filp, struct kobject *kobj, |
398 | struct bin_attribute *attr, char *buf, |
399 | loff_t off, size_t count) |
400 | { |
401 | unsigned int size = ipl_block.nvme.scp_data_len; |
402 | void *scp_data = &ipl_block.nvme.scp_data; |
403 | |
404 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: scp_data, available: size); |
405 | } |
406 | |
407 | static ssize_t ipl_eckd_scp_data_read(struct file *filp, struct kobject *kobj, |
408 | struct bin_attribute *attr, char *buf, |
409 | loff_t off, size_t count) |
410 | { |
411 | unsigned int size = ipl_block.eckd.scp_data_len; |
412 | void *scp_data = &ipl_block.eckd.scp_data; |
413 | |
414 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: scp_data, available: size); |
415 | } |
416 | |
417 | static struct bin_attribute ipl_scp_data_attr = |
418 | __BIN_ATTR(scp_data, 0444, ipl_scp_data_read, NULL, PAGE_SIZE); |
419 | |
420 | static struct bin_attribute ipl_nvme_scp_data_attr = |
421 | __BIN_ATTR(scp_data, 0444, ipl_nvme_scp_data_read, NULL, PAGE_SIZE); |
422 | |
423 | static struct bin_attribute ipl_eckd_scp_data_attr = |
424 | __BIN_ATTR(scp_data, 0444, ipl_eckd_scp_data_read, NULL, PAGE_SIZE); |
425 | |
426 | static struct bin_attribute *ipl_fcp_bin_attrs[] = { |
427 | &ipl_parameter_attr, |
428 | &ipl_scp_data_attr, |
429 | NULL, |
430 | }; |
431 | |
432 | static struct bin_attribute *ipl_nvme_bin_attrs[] = { |
433 | &ipl_parameter_attr, |
434 | &ipl_nvme_scp_data_attr, |
435 | NULL, |
436 | }; |
437 | |
438 | static struct bin_attribute *ipl_eckd_bin_attrs[] = { |
439 | &ipl_parameter_attr, |
440 | &ipl_eckd_scp_data_attr, |
441 | NULL, |
442 | }; |
443 | |
444 | /* FCP ipl device attributes */ |
445 | |
446 | DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n" , |
447 | (unsigned long long)ipl_block.fcp.wwpn); |
448 | DEFINE_IPL_ATTR_RO(ipl_fcp, lun, "0x%016llx\n" , |
449 | (unsigned long long)ipl_block.fcp.lun); |
450 | DEFINE_IPL_ATTR_RO(ipl_fcp, bootprog, "%lld\n" , |
451 | (unsigned long long)ipl_block.fcp.bootprog); |
452 | DEFINE_IPL_ATTR_RO(ipl_fcp, br_lba, "%lld\n" , |
453 | (unsigned long long)ipl_block.fcp.br_lba); |
454 | |
455 | /* NVMe ipl device attributes */ |
456 | DEFINE_IPL_ATTR_RO(ipl_nvme, fid, "0x%08llx\n" , |
457 | (unsigned long long)ipl_block.nvme.fid); |
458 | DEFINE_IPL_ATTR_RO(ipl_nvme, nsid, "0x%08llx\n" , |
459 | (unsigned long long)ipl_block.nvme.nsid); |
460 | DEFINE_IPL_ATTR_RO(ipl_nvme, bootprog, "%lld\n" , |
461 | (unsigned long long)ipl_block.nvme.bootprog); |
462 | DEFINE_IPL_ATTR_RO(ipl_nvme, br_lba, "%lld\n" , |
463 | (unsigned long long)ipl_block.nvme.br_lba); |
464 | |
465 | /* ECKD ipl device attributes */ |
466 | DEFINE_IPL_ATTR_RO(ipl_eckd, bootprog, "%lld\n" , |
467 | (unsigned long long)ipl_block.eckd.bootprog); |
468 | |
469 | #define IPL_ATTR_BR_CHR_SHOW_FN(_name, _ipb) \ |
470 | static ssize_t eckd_##_name##_br_chr_show(struct kobject *kobj, \ |
471 | struct kobj_attribute *attr, \ |
472 | char *buf) \ |
473 | { \ |
474 | struct ipl_pb0_eckd *ipb = &(_ipb); \ |
475 | \ |
476 | if (!ipb->br_chr.cyl && \ |
477 | !ipb->br_chr.head && \ |
478 | !ipb->br_chr.record) \ |
479 | return sprintf(buf, "auto\n"); \ |
480 | \ |
481 | return sprintf(buf, "0x%x,0x%x,0x%x\n", \ |
482 | ipb->br_chr.cyl, \ |
483 | ipb->br_chr.head, \ |
484 | ipb->br_chr.record); \ |
485 | } |
486 | |
487 | #define IPL_ATTR_BR_CHR_STORE_FN(_name, _ipb) \ |
488 | static ssize_t eckd_##_name##_br_chr_store(struct kobject *kobj, \ |
489 | struct kobj_attribute *attr, \ |
490 | const char *buf, size_t len) \ |
491 | { \ |
492 | struct ipl_pb0_eckd *ipb = &(_ipb); \ |
493 | unsigned long args[3] = { 0 }; \ |
494 | char *p, *p1, *tmp = NULL; \ |
495 | int i, rc; \ |
496 | \ |
497 | if (!strncmp(buf, "auto", 4)) \ |
498 | goto out; \ |
499 | \ |
500 | tmp = kstrdup(buf, GFP_KERNEL); \ |
501 | p = tmp; \ |
502 | for (i = 0; i < 3; i++) { \ |
503 | p1 = strsep(&p, ", "); \ |
504 | if (!p1) { \ |
505 | rc = -EINVAL; \ |
506 | goto err; \ |
507 | } \ |
508 | rc = kstrtoul(p1, 0, args + i); \ |
509 | if (rc) \ |
510 | goto err; \ |
511 | } \ |
512 | \ |
513 | rc = -EINVAL; \ |
514 | if (i != 3) \ |
515 | goto err; \ |
516 | \ |
517 | if ((args[0] || args[1]) && !args[2]) \ |
518 | goto err; \ |
519 | \ |
520 | if (args[0] > UINT_MAX || args[1] > 255 || args[2] > 255) \ |
521 | goto err; \ |
522 | \ |
523 | out: \ |
524 | ipb->br_chr.cyl = args[0]; \ |
525 | ipb->br_chr.head = args[1]; \ |
526 | ipb->br_chr.record = args[2]; \ |
527 | rc = len; \ |
528 | err: \ |
529 | kfree(tmp); \ |
530 | return rc; \ |
531 | } |
532 | |
533 | IPL_ATTR_BR_CHR_SHOW_FN(ipl, ipl_block.eckd); |
534 | static struct kobj_attribute sys_ipl_eckd_br_chr_attr = |
535 | __ATTR(br_chr, 0644, eckd_ipl_br_chr_show, NULL); |
536 | |
537 | IPL_ATTR_BR_CHR_SHOW_FN(reipl, reipl_block_eckd->eckd); |
538 | IPL_ATTR_BR_CHR_STORE_FN(reipl, reipl_block_eckd->eckd); |
539 | |
540 | static struct kobj_attribute sys_reipl_eckd_br_chr_attr = |
541 | __ATTR(br_chr, 0644, eckd_reipl_br_chr_show, eckd_reipl_br_chr_store); |
542 | |
543 | static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj, |
544 | struct kobj_attribute *attr, char *page) |
545 | { |
546 | char loadparm[LOADPARM_LEN + 1] = {}; |
547 | |
548 | if (!sclp_ipl_info.is_valid) |
549 | return sprintf(buf: page, fmt: "#unknown#\n" ); |
550 | memcpy(loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN); |
551 | EBCASC(loadparm, LOADPARM_LEN); |
552 | strim(loadparm); |
553 | return sprintf(buf: page, fmt: "%s\n" , loadparm); |
554 | } |
555 | |
556 | static struct kobj_attribute sys_ipl_ccw_loadparm_attr = |
557 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); |
558 | |
559 | static struct attribute *ipl_fcp_attrs[] = { |
560 | &sys_ipl_device_attr.attr, |
561 | &sys_ipl_fcp_wwpn_attr.attr, |
562 | &sys_ipl_fcp_lun_attr.attr, |
563 | &sys_ipl_fcp_bootprog_attr.attr, |
564 | &sys_ipl_fcp_br_lba_attr.attr, |
565 | &sys_ipl_ccw_loadparm_attr.attr, |
566 | NULL, |
567 | }; |
568 | |
569 | static struct attribute_group ipl_fcp_attr_group = { |
570 | .attrs = ipl_fcp_attrs, |
571 | .bin_attrs = ipl_fcp_bin_attrs, |
572 | }; |
573 | |
574 | static struct attribute *ipl_nvme_attrs[] = { |
575 | &sys_ipl_nvme_fid_attr.attr, |
576 | &sys_ipl_nvme_nsid_attr.attr, |
577 | &sys_ipl_nvme_bootprog_attr.attr, |
578 | &sys_ipl_nvme_br_lba_attr.attr, |
579 | &sys_ipl_ccw_loadparm_attr.attr, |
580 | NULL, |
581 | }; |
582 | |
583 | static struct attribute_group ipl_nvme_attr_group = { |
584 | .attrs = ipl_nvme_attrs, |
585 | .bin_attrs = ipl_nvme_bin_attrs, |
586 | }; |
587 | |
588 | static struct attribute *ipl_eckd_attrs[] = { |
589 | &sys_ipl_eckd_bootprog_attr.attr, |
590 | &sys_ipl_eckd_br_chr_attr.attr, |
591 | &sys_ipl_ccw_loadparm_attr.attr, |
592 | &sys_ipl_device_attr.attr, |
593 | NULL, |
594 | }; |
595 | |
596 | static struct attribute_group ipl_eckd_attr_group = { |
597 | .attrs = ipl_eckd_attrs, |
598 | .bin_attrs = ipl_eckd_bin_attrs, |
599 | }; |
600 | |
601 | /* CCW ipl device attributes */ |
602 | |
603 | static struct attribute *ipl_ccw_attrs_vm[] = { |
604 | &sys_ipl_device_attr.attr, |
605 | &sys_ipl_ccw_loadparm_attr.attr, |
606 | &sys_ipl_vm_parm_attr.attr, |
607 | NULL, |
608 | }; |
609 | |
610 | static struct attribute *ipl_ccw_attrs_lpar[] = { |
611 | &sys_ipl_device_attr.attr, |
612 | &sys_ipl_ccw_loadparm_attr.attr, |
613 | NULL, |
614 | }; |
615 | |
616 | static struct attribute_group ipl_ccw_attr_group_vm = { |
617 | .attrs = ipl_ccw_attrs_vm, |
618 | }; |
619 | |
620 | static struct attribute_group ipl_ccw_attr_group_lpar = { |
621 | .attrs = ipl_ccw_attrs_lpar |
622 | }; |
623 | |
624 | static struct attribute *ipl_common_attrs[] = { |
625 | &sys_ipl_type_attr.attr, |
626 | &sys_ipl_secure_attr.attr, |
627 | &sys_ipl_has_secure_attr.attr, |
628 | NULL, |
629 | }; |
630 | |
631 | static struct attribute_group ipl_common_attr_group = { |
632 | .attrs = ipl_common_attrs, |
633 | }; |
634 | |
635 | static struct kset *ipl_kset; |
636 | |
637 | static void __ipl_run(void *unused) |
638 | { |
639 | diag308(DIAG308_LOAD_CLEAR, NULL); |
640 | } |
641 | |
642 | static void ipl_run(struct shutdown_trigger *trigger) |
643 | { |
644 | smp_call_ipl_cpu(__ipl_run, NULL); |
645 | } |
646 | |
647 | static int __init ipl_init(void) |
648 | { |
649 | int rc; |
650 | |
651 | ipl_kset = kset_create_and_add(name: "ipl" , NULL, parent_kobj: firmware_kobj); |
652 | if (!ipl_kset) { |
653 | rc = -ENOMEM; |
654 | goto out; |
655 | } |
656 | rc = sysfs_create_group(kobj: &ipl_kset->kobj, grp: &ipl_common_attr_group); |
657 | if (rc) |
658 | goto out; |
659 | switch (ipl_info.type) { |
660 | case IPL_TYPE_CCW: |
661 | if (MACHINE_IS_VM) |
662 | rc = sysfs_create_group(kobj: &ipl_kset->kobj, |
663 | grp: &ipl_ccw_attr_group_vm); |
664 | else |
665 | rc = sysfs_create_group(kobj: &ipl_kset->kobj, |
666 | grp: &ipl_ccw_attr_group_lpar); |
667 | break; |
668 | case IPL_TYPE_ECKD: |
669 | case IPL_TYPE_ECKD_DUMP: |
670 | rc = sysfs_create_group(kobj: &ipl_kset->kobj, grp: &ipl_eckd_attr_group); |
671 | break; |
672 | case IPL_TYPE_FCP: |
673 | case IPL_TYPE_FCP_DUMP: |
674 | rc = sysfs_create_group(kobj: &ipl_kset->kobj, grp: &ipl_fcp_attr_group); |
675 | break; |
676 | case IPL_TYPE_NVME: |
677 | case IPL_TYPE_NVME_DUMP: |
678 | rc = sysfs_create_group(kobj: &ipl_kset->kobj, grp: &ipl_nvme_attr_group); |
679 | break; |
680 | default: |
681 | break; |
682 | } |
683 | out: |
684 | if (rc) |
685 | panic(fmt: "ipl_init failed: rc = %i\n" , rc); |
686 | |
687 | return 0; |
688 | } |
689 | |
690 | static struct shutdown_action __refdata ipl_action = { |
691 | .name = SHUTDOWN_ACTION_IPL_STR, |
692 | .fn = ipl_run, |
693 | .init = ipl_init, |
694 | }; |
695 | |
696 | /* |
697 | * reipl shutdown action: Reboot Linux on shutdown. |
698 | */ |
699 | |
700 | /* VM IPL PARM attributes */ |
701 | static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb, |
702 | char *page) |
703 | { |
704 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; |
705 | |
706 | ipl_block_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb); |
707 | return sprintf(buf: page, fmt: "%s\n" , vmparm); |
708 | } |
709 | |
710 | static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb, |
711 | size_t vmparm_max, |
712 | const char *buf, size_t len) |
713 | { |
714 | int i, ip_len; |
715 | |
716 | /* ignore trailing newline */ |
717 | ip_len = len; |
718 | if ((len > 0) && (buf[len - 1] == '\n')) |
719 | ip_len--; |
720 | |
721 | if (ip_len > vmparm_max) |
722 | return -EINVAL; |
723 | |
724 | /* parm is used to store kernel options, check for common chars */ |
725 | for (i = 0; i < ip_len; i++) |
726 | if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i]))) |
727 | return -EINVAL; |
728 | |
729 | memset(ipb->ccw.vm_parm, 0, DIAG308_VMPARM_SIZE); |
730 | ipb->ccw.vm_parm_len = ip_len; |
731 | if (ip_len > 0) { |
732 | ipb->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_VP; |
733 | memcpy(ipb->ccw.vm_parm, buf, ip_len); |
734 | ASCEBC(ipb->ccw.vm_parm, ip_len); |
735 | } else { |
736 | ipb->ccw.vm_flags &= ~IPL_PB0_CCW_VM_FLAG_VP; |
737 | } |
738 | |
739 | return len; |
740 | } |
741 | |
742 | /* NSS wrapper */ |
743 | static ssize_t reipl_nss_vmparm_show(struct kobject *kobj, |
744 | struct kobj_attribute *attr, char *page) |
745 | { |
746 | return reipl_generic_vmparm_show(ipb: reipl_block_nss, page); |
747 | } |
748 | |
749 | static ssize_t reipl_nss_vmparm_store(struct kobject *kobj, |
750 | struct kobj_attribute *attr, |
751 | const char *buf, size_t len) |
752 | { |
753 | return reipl_generic_vmparm_store(ipb: reipl_block_nss, vmparm_max: 56, buf, len); |
754 | } |
755 | |
756 | /* CCW wrapper */ |
757 | static ssize_t reipl_ccw_vmparm_show(struct kobject *kobj, |
758 | struct kobj_attribute *attr, char *page) |
759 | { |
760 | return reipl_generic_vmparm_show(ipb: reipl_block_ccw, page); |
761 | } |
762 | |
763 | static ssize_t reipl_ccw_vmparm_store(struct kobject *kobj, |
764 | struct kobj_attribute *attr, |
765 | const char *buf, size_t len) |
766 | { |
767 | return reipl_generic_vmparm_store(ipb: reipl_block_ccw, vmparm_max: 64, buf, len); |
768 | } |
769 | |
770 | static struct kobj_attribute sys_reipl_nss_vmparm_attr = |
771 | __ATTR(parm, 0644, reipl_nss_vmparm_show, |
772 | reipl_nss_vmparm_store); |
773 | static struct kobj_attribute sys_reipl_ccw_vmparm_attr = |
774 | __ATTR(parm, 0644, reipl_ccw_vmparm_show, |
775 | reipl_ccw_vmparm_store); |
776 | |
777 | /* FCP reipl device attributes */ |
778 | |
779 | static ssize_t reipl_fcp_scpdata_read(struct file *filp, struct kobject *kobj, |
780 | struct bin_attribute *attr, |
781 | char *buf, loff_t off, size_t count) |
782 | { |
783 | size_t size = reipl_block_fcp->fcp.scp_data_len; |
784 | void *scp_data = reipl_block_fcp->fcp.scp_data; |
785 | |
786 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: scp_data, available: size); |
787 | } |
788 | |
789 | static ssize_t reipl_fcp_scpdata_write(struct file *filp, struct kobject *kobj, |
790 | struct bin_attribute *attr, |
791 | char *buf, loff_t off, size_t count) |
792 | { |
793 | size_t scpdata_len = count; |
794 | size_t padding; |
795 | |
796 | |
797 | if (off) |
798 | return -EINVAL; |
799 | |
800 | memcpy(reipl_block_fcp->fcp.scp_data, buf, count); |
801 | if (scpdata_len % 8) { |
802 | padding = 8 - (scpdata_len % 8); |
803 | memset(reipl_block_fcp->fcp.scp_data + scpdata_len, |
804 | 0, padding); |
805 | scpdata_len += padding; |
806 | } |
807 | |
808 | reipl_block_fcp->hdr.len = IPL_BP_FCP_LEN + scpdata_len; |
809 | reipl_block_fcp->fcp.len = IPL_BP0_FCP_LEN + scpdata_len; |
810 | reipl_block_fcp->fcp.scp_data_len = scpdata_len; |
811 | |
812 | return count; |
813 | } |
814 | static struct bin_attribute sys_reipl_fcp_scp_data_attr = |
815 | __BIN_ATTR(scp_data, 0644, reipl_fcp_scpdata_read, |
816 | reipl_fcp_scpdata_write, DIAG308_SCPDATA_SIZE); |
817 | |
818 | static struct bin_attribute *reipl_fcp_bin_attrs[] = { |
819 | &sys_reipl_fcp_scp_data_attr, |
820 | NULL, |
821 | }; |
822 | |
823 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n" , "%llx\n" , |
824 | reipl_block_fcp->fcp.wwpn); |
825 | DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n" , "%llx\n" , |
826 | reipl_block_fcp->fcp.lun); |
827 | DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n" , "%lld\n" , |
828 | reipl_block_fcp->fcp.bootprog); |
829 | DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n" , "%lld\n" , |
830 | reipl_block_fcp->fcp.br_lba); |
831 | DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n" , "0.0.%llx\n" , |
832 | reipl_block_fcp->fcp.devno); |
833 | |
834 | static void reipl_get_ascii_loadparm(char *loadparm, |
835 | struct ipl_parameter_block *ibp) |
836 | { |
837 | memcpy(loadparm, ibp->common.loadparm, LOADPARM_LEN); |
838 | EBCASC(loadparm, LOADPARM_LEN); |
839 | loadparm[LOADPARM_LEN] = 0; |
840 | strim(loadparm); |
841 | } |
842 | |
843 | static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb, |
844 | char *page) |
845 | { |
846 | char buf[LOADPARM_LEN + 1]; |
847 | |
848 | reipl_get_ascii_loadparm(loadparm: buf, ibp: ipb); |
849 | return sprintf(buf: page, fmt: "%s\n" , buf); |
850 | } |
851 | |
852 | static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, |
853 | const char *buf, size_t len) |
854 | { |
855 | int i, lp_len; |
856 | |
857 | /* ignore trailing newline */ |
858 | lp_len = len; |
859 | if ((len > 0) && (buf[len - 1] == '\n')) |
860 | lp_len--; |
861 | /* loadparm can have max 8 characters and must not start with a blank */ |
862 | if ((lp_len > LOADPARM_LEN) || ((lp_len > 0) && (buf[0] == ' '))) |
863 | return -EINVAL; |
864 | /* loadparm can only contain "a-z,A-Z,0-9,SP,." */ |
865 | for (i = 0; i < lp_len; i++) { |
866 | if (isalpha(buf[i]) || isdigit(c: buf[i]) || (buf[i] == ' ') || |
867 | (buf[i] == '.')) |
868 | continue; |
869 | return -EINVAL; |
870 | } |
871 | /* initialize loadparm with blanks */ |
872 | memset(ipb->common.loadparm, ' ', LOADPARM_LEN); |
873 | /* copy and convert to ebcdic */ |
874 | memcpy(ipb->common.loadparm, buf, lp_len); |
875 | ASCEBC(ipb->common.loadparm, LOADPARM_LEN); |
876 | ipb->common.flags |= IPL_PB0_FLAG_LOADPARM; |
877 | return len; |
878 | } |
879 | |
880 | #define DEFINE_GENERIC_LOADPARM(name) \ |
881 | static ssize_t reipl_##name##_loadparm_show(struct kobject *kobj, \ |
882 | struct kobj_attribute *attr, char *page) \ |
883 | { \ |
884 | return reipl_generic_loadparm_show(reipl_block_##name, page); \ |
885 | } \ |
886 | static ssize_t reipl_##name##_loadparm_store(struct kobject *kobj, \ |
887 | struct kobj_attribute *attr, \ |
888 | const char *buf, size_t len) \ |
889 | { \ |
890 | return reipl_generic_loadparm_store(reipl_block_##name, buf, len); \ |
891 | } \ |
892 | static struct kobj_attribute sys_reipl_##name##_loadparm_attr = \ |
893 | __ATTR(loadparm, 0644, reipl_##name##_loadparm_show, \ |
894 | reipl_##name##_loadparm_store) |
895 | |
896 | DEFINE_GENERIC_LOADPARM(fcp); |
897 | DEFINE_GENERIC_LOADPARM(nvme); |
898 | DEFINE_GENERIC_LOADPARM(ccw); |
899 | DEFINE_GENERIC_LOADPARM(nss); |
900 | DEFINE_GENERIC_LOADPARM(eckd); |
901 | |
902 | static ssize_t reipl_fcp_clear_show(struct kobject *kobj, |
903 | struct kobj_attribute *attr, char *page) |
904 | { |
905 | return sprintf(buf: page, fmt: "%u\n" , reipl_fcp_clear); |
906 | } |
907 | |
908 | static ssize_t reipl_fcp_clear_store(struct kobject *kobj, |
909 | struct kobj_attribute *attr, |
910 | const char *buf, size_t len) |
911 | { |
912 | if (kstrtobool(s: buf, res: &reipl_fcp_clear) < 0) |
913 | return -EINVAL; |
914 | return len; |
915 | } |
916 | |
917 | static struct attribute *reipl_fcp_attrs[] = { |
918 | &sys_reipl_fcp_device_attr.attr, |
919 | &sys_reipl_fcp_wwpn_attr.attr, |
920 | &sys_reipl_fcp_lun_attr.attr, |
921 | &sys_reipl_fcp_bootprog_attr.attr, |
922 | &sys_reipl_fcp_br_lba_attr.attr, |
923 | &sys_reipl_fcp_loadparm_attr.attr, |
924 | NULL, |
925 | }; |
926 | |
927 | static struct attribute_group reipl_fcp_attr_group = { |
928 | .attrs = reipl_fcp_attrs, |
929 | .bin_attrs = reipl_fcp_bin_attrs, |
930 | }; |
931 | |
932 | static struct kobj_attribute sys_reipl_fcp_clear_attr = |
933 | __ATTR(clear, 0644, reipl_fcp_clear_show, reipl_fcp_clear_store); |
934 | |
935 | /* NVME reipl device attributes */ |
936 | |
937 | static ssize_t reipl_nvme_scpdata_read(struct file *filp, struct kobject *kobj, |
938 | struct bin_attribute *attr, |
939 | char *buf, loff_t off, size_t count) |
940 | { |
941 | size_t size = reipl_block_nvme->nvme.scp_data_len; |
942 | void *scp_data = reipl_block_nvme->nvme.scp_data; |
943 | |
944 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: scp_data, available: size); |
945 | } |
946 | |
947 | static ssize_t reipl_nvme_scpdata_write(struct file *filp, struct kobject *kobj, |
948 | struct bin_attribute *attr, |
949 | char *buf, loff_t off, size_t count) |
950 | { |
951 | size_t scpdata_len = count; |
952 | size_t padding; |
953 | |
954 | if (off) |
955 | return -EINVAL; |
956 | |
957 | memcpy(reipl_block_nvme->nvme.scp_data, buf, count); |
958 | if (scpdata_len % 8) { |
959 | padding = 8 - (scpdata_len % 8); |
960 | memset(reipl_block_nvme->nvme.scp_data + scpdata_len, |
961 | 0, padding); |
962 | scpdata_len += padding; |
963 | } |
964 | |
965 | reipl_block_nvme->hdr.len = IPL_BP_FCP_LEN + scpdata_len; |
966 | reipl_block_nvme->nvme.len = IPL_BP0_FCP_LEN + scpdata_len; |
967 | reipl_block_nvme->nvme.scp_data_len = scpdata_len; |
968 | |
969 | return count; |
970 | } |
971 | |
972 | static struct bin_attribute sys_reipl_nvme_scp_data_attr = |
973 | __BIN_ATTR(scp_data, 0644, reipl_nvme_scpdata_read, |
974 | reipl_nvme_scpdata_write, DIAG308_SCPDATA_SIZE); |
975 | |
976 | static struct bin_attribute *reipl_nvme_bin_attrs[] = { |
977 | &sys_reipl_nvme_scp_data_attr, |
978 | NULL, |
979 | }; |
980 | |
981 | DEFINE_IPL_ATTR_RW(reipl_nvme, fid, "0x%08llx\n" , "%llx\n" , |
982 | reipl_block_nvme->nvme.fid); |
983 | DEFINE_IPL_ATTR_RW(reipl_nvme, nsid, "0x%08llx\n" , "%llx\n" , |
984 | reipl_block_nvme->nvme.nsid); |
985 | DEFINE_IPL_ATTR_RW(reipl_nvme, bootprog, "%lld\n" , "%lld\n" , |
986 | reipl_block_nvme->nvme.bootprog); |
987 | DEFINE_IPL_ATTR_RW(reipl_nvme, br_lba, "%lld\n" , "%lld\n" , |
988 | reipl_block_nvme->nvme.br_lba); |
989 | |
990 | static struct attribute *reipl_nvme_attrs[] = { |
991 | &sys_reipl_nvme_fid_attr.attr, |
992 | &sys_reipl_nvme_nsid_attr.attr, |
993 | &sys_reipl_nvme_bootprog_attr.attr, |
994 | &sys_reipl_nvme_br_lba_attr.attr, |
995 | &sys_reipl_nvme_loadparm_attr.attr, |
996 | NULL, |
997 | }; |
998 | |
999 | static struct attribute_group reipl_nvme_attr_group = { |
1000 | .attrs = reipl_nvme_attrs, |
1001 | .bin_attrs = reipl_nvme_bin_attrs |
1002 | }; |
1003 | |
1004 | static ssize_t reipl_nvme_clear_show(struct kobject *kobj, |
1005 | struct kobj_attribute *attr, char *page) |
1006 | { |
1007 | return sprintf(buf: page, fmt: "%u\n" , reipl_nvme_clear); |
1008 | } |
1009 | |
1010 | static ssize_t reipl_nvme_clear_store(struct kobject *kobj, |
1011 | struct kobj_attribute *attr, |
1012 | const char *buf, size_t len) |
1013 | { |
1014 | if (kstrtobool(s: buf, res: &reipl_nvme_clear) < 0) |
1015 | return -EINVAL; |
1016 | return len; |
1017 | } |
1018 | |
1019 | static struct kobj_attribute sys_reipl_nvme_clear_attr = |
1020 | __ATTR(clear, 0644, reipl_nvme_clear_show, reipl_nvme_clear_store); |
1021 | |
1022 | /* CCW reipl device attributes */ |
1023 | DEFINE_IPL_CCW_ATTR_RW(reipl_ccw, device, reipl_block_ccw->ccw); |
1024 | |
1025 | static ssize_t reipl_ccw_clear_show(struct kobject *kobj, |
1026 | struct kobj_attribute *attr, char *page) |
1027 | { |
1028 | return sprintf(buf: page, fmt: "%u\n" , reipl_ccw_clear); |
1029 | } |
1030 | |
1031 | static ssize_t reipl_ccw_clear_store(struct kobject *kobj, |
1032 | struct kobj_attribute *attr, |
1033 | const char *buf, size_t len) |
1034 | { |
1035 | if (kstrtobool(s: buf, res: &reipl_ccw_clear) < 0) |
1036 | return -EINVAL; |
1037 | return len; |
1038 | } |
1039 | |
1040 | static struct kobj_attribute sys_reipl_ccw_clear_attr = |
1041 | __ATTR(clear, 0644, reipl_ccw_clear_show, reipl_ccw_clear_store); |
1042 | |
1043 | static struct attribute *reipl_ccw_attrs_vm[] = { |
1044 | &sys_reipl_ccw_device_attr.attr, |
1045 | &sys_reipl_ccw_loadparm_attr.attr, |
1046 | &sys_reipl_ccw_vmparm_attr.attr, |
1047 | &sys_reipl_ccw_clear_attr.attr, |
1048 | NULL, |
1049 | }; |
1050 | |
1051 | static struct attribute *reipl_ccw_attrs_lpar[] = { |
1052 | &sys_reipl_ccw_device_attr.attr, |
1053 | &sys_reipl_ccw_loadparm_attr.attr, |
1054 | &sys_reipl_ccw_clear_attr.attr, |
1055 | NULL, |
1056 | }; |
1057 | |
1058 | static struct attribute_group reipl_ccw_attr_group_vm = { |
1059 | .name = IPL_CCW_STR, |
1060 | .attrs = reipl_ccw_attrs_vm, |
1061 | }; |
1062 | |
1063 | static struct attribute_group reipl_ccw_attr_group_lpar = { |
1064 | .name = IPL_CCW_STR, |
1065 | .attrs = reipl_ccw_attrs_lpar, |
1066 | }; |
1067 | |
1068 | /* ECKD reipl device attributes */ |
1069 | |
1070 | static ssize_t reipl_eckd_scpdata_read(struct file *filp, struct kobject *kobj, |
1071 | struct bin_attribute *attr, |
1072 | char *buf, loff_t off, size_t count) |
1073 | { |
1074 | size_t size = reipl_block_eckd->eckd.scp_data_len; |
1075 | void *scp_data = reipl_block_eckd->eckd.scp_data; |
1076 | |
1077 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: scp_data, available: size); |
1078 | } |
1079 | |
1080 | static ssize_t reipl_eckd_scpdata_write(struct file *filp, struct kobject *kobj, |
1081 | struct bin_attribute *attr, |
1082 | char *buf, loff_t off, size_t count) |
1083 | { |
1084 | size_t scpdata_len = count; |
1085 | size_t padding; |
1086 | |
1087 | if (off) |
1088 | return -EINVAL; |
1089 | |
1090 | memcpy(reipl_block_eckd->eckd.scp_data, buf, count); |
1091 | if (scpdata_len % 8) { |
1092 | padding = 8 - (scpdata_len % 8); |
1093 | memset(reipl_block_eckd->eckd.scp_data + scpdata_len, |
1094 | 0, padding); |
1095 | scpdata_len += padding; |
1096 | } |
1097 | |
1098 | reipl_block_eckd->hdr.len = IPL_BP_ECKD_LEN + scpdata_len; |
1099 | reipl_block_eckd->eckd.len = IPL_BP0_ECKD_LEN + scpdata_len; |
1100 | reipl_block_eckd->eckd.scp_data_len = scpdata_len; |
1101 | |
1102 | return count; |
1103 | } |
1104 | |
1105 | static struct bin_attribute sys_reipl_eckd_scp_data_attr = |
1106 | __BIN_ATTR(scp_data, 0644, reipl_eckd_scpdata_read, |
1107 | reipl_eckd_scpdata_write, DIAG308_SCPDATA_SIZE); |
1108 | |
1109 | static struct bin_attribute *reipl_eckd_bin_attrs[] = { |
1110 | &sys_reipl_eckd_scp_data_attr, |
1111 | NULL, |
1112 | }; |
1113 | |
1114 | DEFINE_IPL_CCW_ATTR_RW(reipl_eckd, device, reipl_block_eckd->eckd); |
1115 | DEFINE_IPL_ATTR_RW(reipl_eckd, bootprog, "%lld\n" , "%lld\n" , |
1116 | reipl_block_eckd->eckd.bootprog); |
1117 | |
1118 | static struct attribute *reipl_eckd_attrs[] = { |
1119 | &sys_reipl_eckd_device_attr.attr, |
1120 | &sys_reipl_eckd_bootprog_attr.attr, |
1121 | &sys_reipl_eckd_br_chr_attr.attr, |
1122 | &sys_reipl_eckd_loadparm_attr.attr, |
1123 | NULL, |
1124 | }; |
1125 | |
1126 | static struct attribute_group reipl_eckd_attr_group = { |
1127 | .attrs = reipl_eckd_attrs, |
1128 | .bin_attrs = reipl_eckd_bin_attrs |
1129 | }; |
1130 | |
1131 | static ssize_t reipl_eckd_clear_show(struct kobject *kobj, |
1132 | struct kobj_attribute *attr, char *page) |
1133 | { |
1134 | return sprintf(buf: page, fmt: "%u\n" , reipl_eckd_clear); |
1135 | } |
1136 | |
1137 | static ssize_t reipl_eckd_clear_store(struct kobject *kobj, |
1138 | struct kobj_attribute *attr, |
1139 | const char *buf, size_t len) |
1140 | { |
1141 | if (kstrtobool(s: buf, res: &reipl_eckd_clear) < 0) |
1142 | return -EINVAL; |
1143 | return len; |
1144 | } |
1145 | |
1146 | static struct kobj_attribute sys_reipl_eckd_clear_attr = |
1147 | __ATTR(clear, 0644, reipl_eckd_clear_show, reipl_eckd_clear_store); |
1148 | |
1149 | /* NSS reipl device attributes */ |
1150 | static void reipl_get_ascii_nss_name(char *dst, |
1151 | struct ipl_parameter_block *ipb) |
1152 | { |
1153 | memcpy(dst, ipb->ccw.nss_name, NSS_NAME_SIZE); |
1154 | EBCASC(dst, NSS_NAME_SIZE); |
1155 | dst[NSS_NAME_SIZE] = 0; |
1156 | } |
1157 | |
1158 | static ssize_t reipl_nss_name_show(struct kobject *kobj, |
1159 | struct kobj_attribute *attr, char *page) |
1160 | { |
1161 | char nss_name[NSS_NAME_SIZE + 1] = {}; |
1162 | |
1163 | reipl_get_ascii_nss_name(dst: nss_name, ipb: reipl_block_nss); |
1164 | return sprintf(buf: page, fmt: "%s\n" , nss_name); |
1165 | } |
1166 | |
1167 | static ssize_t reipl_nss_name_store(struct kobject *kobj, |
1168 | struct kobj_attribute *attr, |
1169 | const char *buf, size_t len) |
1170 | { |
1171 | int nss_len; |
1172 | |
1173 | /* ignore trailing newline */ |
1174 | nss_len = len; |
1175 | if ((len > 0) && (buf[len - 1] == '\n')) |
1176 | nss_len--; |
1177 | |
1178 | if (nss_len > NSS_NAME_SIZE) |
1179 | return -EINVAL; |
1180 | |
1181 | memset(reipl_block_nss->ccw.nss_name, 0x40, NSS_NAME_SIZE); |
1182 | if (nss_len > 0) { |
1183 | reipl_block_nss->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_NSS; |
1184 | memcpy(reipl_block_nss->ccw.nss_name, buf, nss_len); |
1185 | ASCEBC(reipl_block_nss->ccw.nss_name, nss_len); |
1186 | EBC_TOUPPER(reipl_block_nss->ccw.nss_name, nss_len); |
1187 | } else { |
1188 | reipl_block_nss->ccw.vm_flags &= ~IPL_PB0_CCW_VM_FLAG_NSS; |
1189 | } |
1190 | |
1191 | return len; |
1192 | } |
1193 | |
1194 | static struct kobj_attribute sys_reipl_nss_name_attr = |
1195 | __ATTR(name, 0644, reipl_nss_name_show, |
1196 | reipl_nss_name_store); |
1197 | |
1198 | static struct attribute *reipl_nss_attrs[] = { |
1199 | &sys_reipl_nss_name_attr.attr, |
1200 | &sys_reipl_nss_loadparm_attr.attr, |
1201 | &sys_reipl_nss_vmparm_attr.attr, |
1202 | NULL, |
1203 | }; |
1204 | |
1205 | static struct attribute_group reipl_nss_attr_group = { |
1206 | .name = IPL_NSS_STR, |
1207 | .attrs = reipl_nss_attrs, |
1208 | }; |
1209 | |
1210 | void set_os_info_reipl_block(void) |
1211 | { |
1212 | os_info_entry_add(OS_INFO_REIPL_BLOCK, reipl_block_actual, |
1213 | reipl_block_actual->hdr.len); |
1214 | } |
1215 | |
1216 | /* reipl type */ |
1217 | |
1218 | static int reipl_set_type(enum ipl_type type) |
1219 | { |
1220 | if (!(reipl_capabilities & type)) |
1221 | return -EINVAL; |
1222 | |
1223 | switch(type) { |
1224 | case IPL_TYPE_CCW: |
1225 | reipl_block_actual = reipl_block_ccw; |
1226 | break; |
1227 | case IPL_TYPE_ECKD: |
1228 | reipl_block_actual = reipl_block_eckd; |
1229 | break; |
1230 | case IPL_TYPE_FCP: |
1231 | reipl_block_actual = reipl_block_fcp; |
1232 | break; |
1233 | case IPL_TYPE_NVME: |
1234 | reipl_block_actual = reipl_block_nvme; |
1235 | break; |
1236 | case IPL_TYPE_NSS: |
1237 | reipl_block_actual = reipl_block_nss; |
1238 | break; |
1239 | default: |
1240 | break; |
1241 | } |
1242 | reipl_type = type; |
1243 | return 0; |
1244 | } |
1245 | |
1246 | static ssize_t reipl_type_show(struct kobject *kobj, |
1247 | struct kobj_attribute *attr, char *page) |
1248 | { |
1249 | return sprintf(buf: page, fmt: "%s\n" , ipl_type_str(type: reipl_type)); |
1250 | } |
1251 | |
1252 | static ssize_t reipl_type_store(struct kobject *kobj, |
1253 | struct kobj_attribute *attr, |
1254 | const char *buf, size_t len) |
1255 | { |
1256 | int rc = -EINVAL; |
1257 | |
1258 | if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0) |
1259 | rc = reipl_set_type(IPL_TYPE_CCW); |
1260 | else if (strncmp(buf, IPL_ECKD_STR, strlen(IPL_ECKD_STR)) == 0) |
1261 | rc = reipl_set_type(IPL_TYPE_ECKD); |
1262 | else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) |
1263 | rc = reipl_set_type(IPL_TYPE_FCP); |
1264 | else if (strncmp(buf, IPL_NVME_STR, strlen(IPL_NVME_STR)) == 0) |
1265 | rc = reipl_set_type(IPL_TYPE_NVME); |
1266 | else if (strncmp(buf, IPL_NSS_STR, strlen(IPL_NSS_STR)) == 0) |
1267 | rc = reipl_set_type(IPL_TYPE_NSS); |
1268 | return (rc != 0) ? rc : len; |
1269 | } |
1270 | |
1271 | static struct kobj_attribute reipl_type_attr = |
1272 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); |
1273 | |
1274 | static struct kset *reipl_kset; |
1275 | static struct kset *reipl_fcp_kset; |
1276 | static struct kset *reipl_nvme_kset; |
1277 | static struct kset *reipl_eckd_kset; |
1278 | |
1279 | static void __reipl_run(void *unused) |
1280 | { |
1281 | switch (reipl_type) { |
1282 | case IPL_TYPE_CCW: |
1283 | diag308(DIAG308_SET, reipl_block_ccw); |
1284 | if (reipl_ccw_clear) |
1285 | diag308(DIAG308_LOAD_CLEAR, NULL); |
1286 | else |
1287 | diag308(DIAG308_LOAD_NORMAL_DUMP, NULL); |
1288 | break; |
1289 | case IPL_TYPE_ECKD: |
1290 | diag308(DIAG308_SET, reipl_block_eckd); |
1291 | if (reipl_eckd_clear) |
1292 | diag308(DIAG308_LOAD_CLEAR, NULL); |
1293 | else |
1294 | diag308(DIAG308_LOAD_NORMAL, NULL); |
1295 | break; |
1296 | case IPL_TYPE_FCP: |
1297 | diag308(DIAG308_SET, reipl_block_fcp); |
1298 | if (reipl_fcp_clear) |
1299 | diag308(DIAG308_LOAD_CLEAR, NULL); |
1300 | else |
1301 | diag308(DIAG308_LOAD_NORMAL, NULL); |
1302 | break; |
1303 | case IPL_TYPE_NVME: |
1304 | diag308(DIAG308_SET, reipl_block_nvme); |
1305 | if (reipl_nvme_clear) |
1306 | diag308(DIAG308_LOAD_CLEAR, NULL); |
1307 | else |
1308 | diag308(DIAG308_LOAD_NORMAL, NULL); |
1309 | break; |
1310 | case IPL_TYPE_NSS: |
1311 | diag308(DIAG308_SET, reipl_block_nss); |
1312 | diag308(DIAG308_LOAD_CLEAR, NULL); |
1313 | break; |
1314 | case IPL_TYPE_UNKNOWN: |
1315 | diag308(DIAG308_LOAD_CLEAR, NULL); |
1316 | break; |
1317 | case IPL_TYPE_FCP_DUMP: |
1318 | case IPL_TYPE_NVME_DUMP: |
1319 | case IPL_TYPE_ECKD_DUMP: |
1320 | break; |
1321 | } |
1322 | disabled_wait(); |
1323 | } |
1324 | |
1325 | static void reipl_run(struct shutdown_trigger *trigger) |
1326 | { |
1327 | smp_call_ipl_cpu(__reipl_run, NULL); |
1328 | } |
1329 | |
1330 | static void reipl_block_ccw_init(struct ipl_parameter_block *ipb) |
1331 | { |
1332 | ipb->hdr.len = IPL_BP_CCW_LEN; |
1333 | ipb->hdr.version = IPL_PARM_BLOCK_VERSION; |
1334 | ipb->pb0_hdr.len = IPL_BP0_CCW_LEN; |
1335 | ipb->pb0_hdr.pbt = IPL_PBT_CCW; |
1336 | } |
1337 | |
1338 | static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb) |
1339 | { |
1340 | /* LOADPARM */ |
1341 | /* check if read scp info worked and set loadparm */ |
1342 | if (sclp_ipl_info.is_valid) |
1343 | memcpy(ipb->ccw.loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN); |
1344 | else |
1345 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ |
1346 | memset(ipb->ccw.loadparm, 0x40, LOADPARM_LEN); |
1347 | ipb->ccw.flags = IPL_PB0_FLAG_LOADPARM; |
1348 | |
1349 | /* VM PARM */ |
1350 | if (MACHINE_IS_VM && ipl_block_valid && |
1351 | (ipl_block.ccw.vm_flags & IPL_PB0_CCW_VM_FLAG_VP)) { |
1352 | |
1353 | ipb->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_VP; |
1354 | ipb->ccw.vm_parm_len = ipl_block.ccw.vm_parm_len; |
1355 | memcpy(ipb->ccw.vm_parm, |
1356 | ipl_block.ccw.vm_parm, DIAG308_VMPARM_SIZE); |
1357 | } |
1358 | } |
1359 | |
1360 | static int __init reipl_nss_init(void) |
1361 | { |
1362 | int rc; |
1363 | |
1364 | if (!MACHINE_IS_VM) |
1365 | return 0; |
1366 | |
1367 | reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL); |
1368 | if (!reipl_block_nss) |
1369 | return -ENOMEM; |
1370 | |
1371 | rc = sysfs_create_group(kobj: &reipl_kset->kobj, grp: &reipl_nss_attr_group); |
1372 | if (rc) |
1373 | return rc; |
1374 | |
1375 | reipl_block_ccw_init(ipb: reipl_block_nss); |
1376 | reipl_capabilities |= IPL_TYPE_NSS; |
1377 | return 0; |
1378 | } |
1379 | |
1380 | static int __init reipl_ccw_init(void) |
1381 | { |
1382 | int rc; |
1383 | |
1384 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); |
1385 | if (!reipl_block_ccw) |
1386 | return -ENOMEM; |
1387 | |
1388 | rc = sysfs_create_group(&reipl_kset->kobj, |
1389 | MACHINE_IS_VM ? &reipl_ccw_attr_group_vm |
1390 | : &reipl_ccw_attr_group_lpar); |
1391 | if (rc) |
1392 | return rc; |
1393 | |
1394 | reipl_block_ccw_init(ipb: reipl_block_ccw); |
1395 | if (ipl_info.type == IPL_TYPE_CCW) { |
1396 | reipl_block_ccw->ccw.ssid = ipl_block.ccw.ssid; |
1397 | reipl_block_ccw->ccw.devno = ipl_block.ccw.devno; |
1398 | reipl_block_ccw_fill_parms(ipb: reipl_block_ccw); |
1399 | } |
1400 | |
1401 | reipl_capabilities |= IPL_TYPE_CCW; |
1402 | return 0; |
1403 | } |
1404 | |
1405 | static int __init reipl_fcp_init(void) |
1406 | { |
1407 | int rc; |
1408 | |
1409 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
1410 | if (!reipl_block_fcp) |
1411 | return -ENOMEM; |
1412 | |
1413 | /* sysfs: create fcp kset for mixing attr group and bin attrs */ |
1414 | reipl_fcp_kset = kset_create_and_add(IPL_FCP_STR, NULL, |
1415 | parent_kobj: &reipl_kset->kobj); |
1416 | if (!reipl_fcp_kset) { |
1417 | free_page((unsigned long) reipl_block_fcp); |
1418 | return -ENOMEM; |
1419 | } |
1420 | |
1421 | rc = sysfs_create_group(kobj: &reipl_fcp_kset->kobj, grp: &reipl_fcp_attr_group); |
1422 | if (rc) |
1423 | goto out1; |
1424 | |
1425 | if (test_facility(141)) { |
1426 | rc = sysfs_create_file(kobj: &reipl_fcp_kset->kobj, |
1427 | attr: &sys_reipl_fcp_clear_attr.attr); |
1428 | if (rc) |
1429 | goto out2; |
1430 | } else { |
1431 | reipl_fcp_clear = true; |
1432 | } |
1433 | |
1434 | if (ipl_info.type == IPL_TYPE_FCP) { |
1435 | memcpy(reipl_block_fcp, &ipl_block, sizeof(ipl_block)); |
1436 | /* |
1437 | * Fix loadparm: There are systems where the (SCSI) LOADPARM |
1438 | * is invalid in the SCSI IPL parameter block, so take it |
1439 | * always from sclp_ipl_info. |
1440 | */ |
1441 | memcpy(reipl_block_fcp->fcp.loadparm, sclp_ipl_info.loadparm, |
1442 | LOADPARM_LEN); |
1443 | } else { |
1444 | reipl_block_fcp->hdr.len = IPL_BP_FCP_LEN; |
1445 | reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; |
1446 | reipl_block_fcp->fcp.len = IPL_BP0_FCP_LEN; |
1447 | reipl_block_fcp->fcp.pbt = IPL_PBT_FCP; |
1448 | reipl_block_fcp->fcp.opt = IPL_PB0_FCP_OPT_IPL; |
1449 | } |
1450 | reipl_capabilities |= IPL_TYPE_FCP; |
1451 | return 0; |
1452 | |
1453 | out2: |
1454 | sysfs_remove_group(kobj: &reipl_fcp_kset->kobj, grp: &reipl_fcp_attr_group); |
1455 | out1: |
1456 | kset_unregister(kset: reipl_fcp_kset); |
1457 | free_page((unsigned long) reipl_block_fcp); |
1458 | return rc; |
1459 | } |
1460 | |
1461 | static int __init reipl_nvme_init(void) |
1462 | { |
1463 | int rc; |
1464 | |
1465 | reipl_block_nvme = (void *) get_zeroed_page(GFP_KERNEL); |
1466 | if (!reipl_block_nvme) |
1467 | return -ENOMEM; |
1468 | |
1469 | /* sysfs: create kset for mixing attr group and bin attrs */ |
1470 | reipl_nvme_kset = kset_create_and_add(IPL_NVME_STR, NULL, |
1471 | parent_kobj: &reipl_kset->kobj); |
1472 | if (!reipl_nvme_kset) { |
1473 | free_page((unsigned long) reipl_block_nvme); |
1474 | return -ENOMEM; |
1475 | } |
1476 | |
1477 | rc = sysfs_create_group(kobj: &reipl_nvme_kset->kobj, grp: &reipl_nvme_attr_group); |
1478 | if (rc) |
1479 | goto out1; |
1480 | |
1481 | if (test_facility(141)) { |
1482 | rc = sysfs_create_file(kobj: &reipl_nvme_kset->kobj, |
1483 | attr: &sys_reipl_nvme_clear_attr.attr); |
1484 | if (rc) |
1485 | goto out2; |
1486 | } else { |
1487 | reipl_nvme_clear = true; |
1488 | } |
1489 | |
1490 | if (ipl_info.type == IPL_TYPE_NVME) { |
1491 | memcpy(reipl_block_nvme, &ipl_block, sizeof(ipl_block)); |
1492 | /* |
1493 | * Fix loadparm: There are systems where the (SCSI) LOADPARM |
1494 | * is invalid in the IPL parameter block, so take it |
1495 | * always from sclp_ipl_info. |
1496 | */ |
1497 | memcpy(reipl_block_nvme->nvme.loadparm, sclp_ipl_info.loadparm, |
1498 | LOADPARM_LEN); |
1499 | } else { |
1500 | reipl_block_nvme->hdr.len = IPL_BP_NVME_LEN; |
1501 | reipl_block_nvme->hdr.version = IPL_PARM_BLOCK_VERSION; |
1502 | reipl_block_nvme->nvme.len = IPL_BP0_NVME_LEN; |
1503 | reipl_block_nvme->nvme.pbt = IPL_PBT_NVME; |
1504 | reipl_block_nvme->nvme.opt = IPL_PB0_NVME_OPT_IPL; |
1505 | } |
1506 | reipl_capabilities |= IPL_TYPE_NVME; |
1507 | return 0; |
1508 | |
1509 | out2: |
1510 | sysfs_remove_group(kobj: &reipl_nvme_kset->kobj, grp: &reipl_nvme_attr_group); |
1511 | out1: |
1512 | kset_unregister(kset: reipl_nvme_kset); |
1513 | free_page((unsigned long) reipl_block_nvme); |
1514 | return rc; |
1515 | } |
1516 | |
1517 | static int __init reipl_eckd_init(void) |
1518 | { |
1519 | int rc; |
1520 | |
1521 | if (!sclp.has_sipl_eckd) |
1522 | return 0; |
1523 | |
1524 | reipl_block_eckd = (void *)get_zeroed_page(GFP_KERNEL); |
1525 | if (!reipl_block_eckd) |
1526 | return -ENOMEM; |
1527 | |
1528 | /* sysfs: create kset for mixing attr group and bin attrs */ |
1529 | reipl_eckd_kset = kset_create_and_add(IPL_ECKD_STR, NULL, |
1530 | parent_kobj: &reipl_kset->kobj); |
1531 | if (!reipl_eckd_kset) { |
1532 | free_page((unsigned long)reipl_block_eckd); |
1533 | return -ENOMEM; |
1534 | } |
1535 | |
1536 | rc = sysfs_create_group(kobj: &reipl_eckd_kset->kobj, grp: &reipl_eckd_attr_group); |
1537 | if (rc) |
1538 | goto out1; |
1539 | |
1540 | if (test_facility(141)) { |
1541 | rc = sysfs_create_file(kobj: &reipl_eckd_kset->kobj, |
1542 | attr: &sys_reipl_eckd_clear_attr.attr); |
1543 | if (rc) |
1544 | goto out2; |
1545 | } else { |
1546 | reipl_eckd_clear = true; |
1547 | } |
1548 | |
1549 | if (ipl_info.type == IPL_TYPE_ECKD) { |
1550 | memcpy(reipl_block_eckd, &ipl_block, sizeof(ipl_block)); |
1551 | } else { |
1552 | reipl_block_eckd->hdr.len = IPL_BP_ECKD_LEN; |
1553 | reipl_block_eckd->hdr.version = IPL_PARM_BLOCK_VERSION; |
1554 | reipl_block_eckd->eckd.len = IPL_BP0_ECKD_LEN; |
1555 | reipl_block_eckd->eckd.pbt = IPL_PBT_ECKD; |
1556 | reipl_block_eckd->eckd.opt = IPL_PB0_ECKD_OPT_IPL; |
1557 | } |
1558 | reipl_capabilities |= IPL_TYPE_ECKD; |
1559 | return 0; |
1560 | |
1561 | out2: |
1562 | sysfs_remove_group(kobj: &reipl_eckd_kset->kobj, grp: &reipl_eckd_attr_group); |
1563 | out1: |
1564 | kset_unregister(kset: reipl_eckd_kset); |
1565 | free_page((unsigned long)reipl_block_eckd); |
1566 | return rc; |
1567 | } |
1568 | |
1569 | static int __init reipl_type_init(void) |
1570 | { |
1571 | enum ipl_type reipl_type = ipl_info.type; |
1572 | struct ipl_parameter_block *reipl_block; |
1573 | unsigned long size; |
1574 | |
1575 | reipl_block = os_info_old_entry(OS_INFO_REIPL_BLOCK, &size); |
1576 | if (!reipl_block) |
1577 | goto out; |
1578 | /* |
1579 | * If we have an OS info reipl block, this will be used |
1580 | */ |
1581 | if (reipl_block->pb0_hdr.pbt == IPL_PBT_FCP) { |
1582 | memcpy(reipl_block_fcp, reipl_block, size); |
1583 | reipl_type = IPL_TYPE_FCP; |
1584 | } else if (reipl_block->pb0_hdr.pbt == IPL_PBT_NVME) { |
1585 | memcpy(reipl_block_nvme, reipl_block, size); |
1586 | reipl_type = IPL_TYPE_NVME; |
1587 | } else if (reipl_block->pb0_hdr.pbt == IPL_PBT_CCW) { |
1588 | memcpy(reipl_block_ccw, reipl_block, size); |
1589 | reipl_type = IPL_TYPE_CCW; |
1590 | } else if (reipl_block->pb0_hdr.pbt == IPL_PBT_ECKD) { |
1591 | memcpy(reipl_block_eckd, reipl_block, size); |
1592 | reipl_type = IPL_TYPE_ECKD; |
1593 | } |
1594 | out: |
1595 | return reipl_set_type(type: reipl_type); |
1596 | } |
1597 | |
1598 | static int __init reipl_init(void) |
1599 | { |
1600 | int rc; |
1601 | |
1602 | reipl_kset = kset_create_and_add(name: "reipl" , NULL, parent_kobj: firmware_kobj); |
1603 | if (!reipl_kset) |
1604 | return -ENOMEM; |
1605 | rc = sysfs_create_file(kobj: &reipl_kset->kobj, attr: &reipl_type_attr.attr); |
1606 | if (rc) { |
1607 | kset_unregister(kset: reipl_kset); |
1608 | return rc; |
1609 | } |
1610 | rc = reipl_ccw_init(); |
1611 | if (rc) |
1612 | return rc; |
1613 | rc = reipl_eckd_init(); |
1614 | if (rc) |
1615 | return rc; |
1616 | rc = reipl_fcp_init(); |
1617 | if (rc) |
1618 | return rc; |
1619 | rc = reipl_nvme_init(); |
1620 | if (rc) |
1621 | return rc; |
1622 | rc = reipl_nss_init(); |
1623 | if (rc) |
1624 | return rc; |
1625 | return reipl_type_init(); |
1626 | } |
1627 | |
1628 | static struct shutdown_action __refdata reipl_action = { |
1629 | .name = SHUTDOWN_ACTION_REIPL_STR, |
1630 | .fn = reipl_run, |
1631 | .init = reipl_init, |
1632 | }; |
1633 | |
1634 | /* |
1635 | * dump shutdown action: Dump Linux on shutdown. |
1636 | */ |
1637 | |
1638 | /* FCP dump device attributes */ |
1639 | |
1640 | DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n" , "%llx\n" , |
1641 | dump_block_fcp->fcp.wwpn); |
1642 | DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n" , "%llx\n" , |
1643 | dump_block_fcp->fcp.lun); |
1644 | DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n" , "%lld\n" , |
1645 | dump_block_fcp->fcp.bootprog); |
1646 | DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n" , "%lld\n" , |
1647 | dump_block_fcp->fcp.br_lba); |
1648 | DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n" , "0.0.%llx\n" , |
1649 | dump_block_fcp->fcp.devno); |
1650 | |
1651 | static struct attribute *dump_fcp_attrs[] = { |
1652 | &sys_dump_fcp_device_attr.attr, |
1653 | &sys_dump_fcp_wwpn_attr.attr, |
1654 | &sys_dump_fcp_lun_attr.attr, |
1655 | &sys_dump_fcp_bootprog_attr.attr, |
1656 | &sys_dump_fcp_br_lba_attr.attr, |
1657 | NULL, |
1658 | }; |
1659 | |
1660 | static struct attribute_group dump_fcp_attr_group = { |
1661 | .name = IPL_FCP_STR, |
1662 | .attrs = dump_fcp_attrs, |
1663 | }; |
1664 | |
1665 | /* NVME dump device attributes */ |
1666 | DEFINE_IPL_ATTR_RW(dump_nvme, fid, "0x%08llx\n" , "%llx\n" , |
1667 | dump_block_nvme->nvme.fid); |
1668 | DEFINE_IPL_ATTR_RW(dump_nvme, nsid, "0x%08llx\n" , "%llx\n" , |
1669 | dump_block_nvme->nvme.nsid); |
1670 | DEFINE_IPL_ATTR_RW(dump_nvme, bootprog, "%lld\n" , "%llx\n" , |
1671 | dump_block_nvme->nvme.bootprog); |
1672 | DEFINE_IPL_ATTR_RW(dump_nvme, br_lba, "%lld\n" , "%llx\n" , |
1673 | dump_block_nvme->nvme.br_lba); |
1674 | |
1675 | static struct attribute *dump_nvme_attrs[] = { |
1676 | &sys_dump_nvme_fid_attr.attr, |
1677 | &sys_dump_nvme_nsid_attr.attr, |
1678 | &sys_dump_nvme_bootprog_attr.attr, |
1679 | &sys_dump_nvme_br_lba_attr.attr, |
1680 | NULL, |
1681 | }; |
1682 | |
1683 | static struct attribute_group dump_nvme_attr_group = { |
1684 | .name = IPL_NVME_STR, |
1685 | .attrs = dump_nvme_attrs, |
1686 | }; |
1687 | |
1688 | /* ECKD dump device attributes */ |
1689 | DEFINE_IPL_CCW_ATTR_RW(dump_eckd, device, dump_block_eckd->eckd); |
1690 | DEFINE_IPL_ATTR_RW(dump_eckd, bootprog, "%lld\n" , "%llx\n" , |
1691 | dump_block_eckd->eckd.bootprog); |
1692 | |
1693 | IPL_ATTR_BR_CHR_SHOW_FN(dump, dump_block_eckd->eckd); |
1694 | IPL_ATTR_BR_CHR_STORE_FN(dump, dump_block_eckd->eckd); |
1695 | |
1696 | static struct kobj_attribute sys_dump_eckd_br_chr_attr = |
1697 | __ATTR(br_chr, 0644, eckd_dump_br_chr_show, eckd_dump_br_chr_store); |
1698 | |
1699 | static struct attribute *dump_eckd_attrs[] = { |
1700 | &sys_dump_eckd_device_attr.attr, |
1701 | &sys_dump_eckd_bootprog_attr.attr, |
1702 | &sys_dump_eckd_br_chr_attr.attr, |
1703 | NULL, |
1704 | }; |
1705 | |
1706 | static struct attribute_group dump_eckd_attr_group = { |
1707 | .name = IPL_ECKD_STR, |
1708 | .attrs = dump_eckd_attrs, |
1709 | }; |
1710 | |
1711 | /* CCW dump device attributes */ |
1712 | DEFINE_IPL_CCW_ATTR_RW(dump_ccw, device, dump_block_ccw->ccw); |
1713 | |
1714 | static struct attribute *dump_ccw_attrs[] = { |
1715 | &sys_dump_ccw_device_attr.attr, |
1716 | NULL, |
1717 | }; |
1718 | |
1719 | static struct attribute_group dump_ccw_attr_group = { |
1720 | .name = IPL_CCW_STR, |
1721 | .attrs = dump_ccw_attrs, |
1722 | }; |
1723 | |
1724 | /* dump type */ |
1725 | |
1726 | static int dump_set_type(enum dump_type type) |
1727 | { |
1728 | if (!(dump_capabilities & type)) |
1729 | return -EINVAL; |
1730 | dump_type = type; |
1731 | return 0; |
1732 | } |
1733 | |
1734 | static ssize_t dump_type_show(struct kobject *kobj, |
1735 | struct kobj_attribute *attr, char *page) |
1736 | { |
1737 | return sprintf(buf: page, fmt: "%s\n" , dump_type_str(type: dump_type)); |
1738 | } |
1739 | |
1740 | static ssize_t dump_type_store(struct kobject *kobj, |
1741 | struct kobj_attribute *attr, |
1742 | const char *buf, size_t len) |
1743 | { |
1744 | int rc = -EINVAL; |
1745 | |
1746 | if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) |
1747 | rc = dump_set_type(type: DUMP_TYPE_NONE); |
1748 | else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) |
1749 | rc = dump_set_type(type: DUMP_TYPE_CCW); |
1750 | else if (strncmp(buf, DUMP_ECKD_STR, strlen(DUMP_ECKD_STR)) == 0) |
1751 | rc = dump_set_type(type: DUMP_TYPE_ECKD); |
1752 | else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) |
1753 | rc = dump_set_type(type: DUMP_TYPE_FCP); |
1754 | else if (strncmp(buf, DUMP_NVME_STR, strlen(DUMP_NVME_STR)) == 0) |
1755 | rc = dump_set_type(type: DUMP_TYPE_NVME); |
1756 | return (rc != 0) ? rc : len; |
1757 | } |
1758 | |
1759 | static struct kobj_attribute dump_type_attr = |
1760 | __ATTR(dump_type, 0644, dump_type_show, dump_type_store); |
1761 | |
1762 | static struct kset *dump_kset; |
1763 | |
1764 | static void diag308_dump(void *dump_block) |
1765 | { |
1766 | diag308(DIAG308_SET, dump_block); |
1767 | while (1) { |
1768 | if (diag308(DIAG308_LOAD_NORMAL_DUMP, NULL) != 0x302) |
1769 | break; |
1770 | udelay(USEC_PER_SEC); |
1771 | } |
1772 | } |
1773 | |
1774 | static void __dump_run(void *unused) |
1775 | { |
1776 | switch (dump_type) { |
1777 | case DUMP_TYPE_CCW: |
1778 | diag308_dump(dump_block: dump_block_ccw); |
1779 | break; |
1780 | case DUMP_TYPE_ECKD: |
1781 | diag308_dump(dump_block: dump_block_eckd); |
1782 | break; |
1783 | case DUMP_TYPE_FCP: |
1784 | diag308_dump(dump_block: dump_block_fcp); |
1785 | break; |
1786 | case DUMP_TYPE_NVME: |
1787 | diag308_dump(dump_block: dump_block_nvme); |
1788 | break; |
1789 | default: |
1790 | break; |
1791 | } |
1792 | } |
1793 | |
1794 | static void dump_run(struct shutdown_trigger *trigger) |
1795 | { |
1796 | if (dump_type == DUMP_TYPE_NONE) |
1797 | return; |
1798 | smp_send_stop(); |
1799 | smp_call_ipl_cpu(__dump_run, NULL); |
1800 | } |
1801 | |
1802 | static int __init dump_ccw_init(void) |
1803 | { |
1804 | int rc; |
1805 | |
1806 | dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); |
1807 | if (!dump_block_ccw) |
1808 | return -ENOMEM; |
1809 | rc = sysfs_create_group(kobj: &dump_kset->kobj, grp: &dump_ccw_attr_group); |
1810 | if (rc) { |
1811 | free_page((unsigned long)dump_block_ccw); |
1812 | return rc; |
1813 | } |
1814 | dump_block_ccw->hdr.len = IPL_BP_CCW_LEN; |
1815 | dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; |
1816 | dump_block_ccw->ccw.len = IPL_BP0_CCW_LEN; |
1817 | dump_block_ccw->ccw.pbt = IPL_PBT_CCW; |
1818 | dump_capabilities |= DUMP_TYPE_CCW; |
1819 | return 0; |
1820 | } |
1821 | |
1822 | static int __init dump_fcp_init(void) |
1823 | { |
1824 | int rc; |
1825 | |
1826 | if (!sclp_ipl_info.has_dump) |
1827 | return 0; /* LDIPL DUMP is not installed */ |
1828 | dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
1829 | if (!dump_block_fcp) |
1830 | return -ENOMEM; |
1831 | rc = sysfs_create_group(kobj: &dump_kset->kobj, grp: &dump_fcp_attr_group); |
1832 | if (rc) { |
1833 | free_page((unsigned long)dump_block_fcp); |
1834 | return rc; |
1835 | } |
1836 | dump_block_fcp->hdr.len = IPL_BP_FCP_LEN; |
1837 | dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; |
1838 | dump_block_fcp->fcp.len = IPL_BP0_FCP_LEN; |
1839 | dump_block_fcp->fcp.pbt = IPL_PBT_FCP; |
1840 | dump_block_fcp->fcp.opt = IPL_PB0_FCP_OPT_DUMP; |
1841 | dump_capabilities |= DUMP_TYPE_FCP; |
1842 | return 0; |
1843 | } |
1844 | |
1845 | static int __init dump_nvme_init(void) |
1846 | { |
1847 | int rc; |
1848 | |
1849 | if (!sclp_ipl_info.has_dump) |
1850 | return 0; /* LDIPL DUMP is not installed */ |
1851 | dump_block_nvme = (void *) get_zeroed_page(GFP_KERNEL); |
1852 | if (!dump_block_nvme) |
1853 | return -ENOMEM; |
1854 | rc = sysfs_create_group(kobj: &dump_kset->kobj, grp: &dump_nvme_attr_group); |
1855 | if (rc) { |
1856 | free_page((unsigned long)dump_block_nvme); |
1857 | return rc; |
1858 | } |
1859 | dump_block_nvme->hdr.len = IPL_BP_NVME_LEN; |
1860 | dump_block_nvme->hdr.version = IPL_PARM_BLOCK_VERSION; |
1861 | dump_block_nvme->fcp.len = IPL_BP0_NVME_LEN; |
1862 | dump_block_nvme->fcp.pbt = IPL_PBT_NVME; |
1863 | dump_block_nvme->fcp.opt = IPL_PB0_NVME_OPT_DUMP; |
1864 | dump_capabilities |= DUMP_TYPE_NVME; |
1865 | return 0; |
1866 | } |
1867 | |
1868 | static int __init dump_eckd_init(void) |
1869 | { |
1870 | int rc; |
1871 | |
1872 | if (!sclp_ipl_info.has_dump || !sclp.has_sipl_eckd) |
1873 | return 0; /* LDIPL DUMP is not installed */ |
1874 | dump_block_eckd = (void *)get_zeroed_page(GFP_KERNEL); |
1875 | if (!dump_block_eckd) |
1876 | return -ENOMEM; |
1877 | rc = sysfs_create_group(kobj: &dump_kset->kobj, grp: &dump_eckd_attr_group); |
1878 | if (rc) { |
1879 | free_page((unsigned long)dump_block_eckd); |
1880 | return rc; |
1881 | } |
1882 | dump_block_eckd->hdr.len = IPL_BP_ECKD_LEN; |
1883 | dump_block_eckd->hdr.version = IPL_PARM_BLOCK_VERSION; |
1884 | dump_block_eckd->eckd.len = IPL_BP0_ECKD_LEN; |
1885 | dump_block_eckd->eckd.pbt = IPL_PBT_ECKD; |
1886 | dump_block_eckd->eckd.opt = IPL_PB0_ECKD_OPT_DUMP; |
1887 | dump_capabilities |= DUMP_TYPE_ECKD; |
1888 | return 0; |
1889 | } |
1890 | |
1891 | static int __init dump_init(void) |
1892 | { |
1893 | int rc; |
1894 | |
1895 | dump_kset = kset_create_and_add(name: "dump" , NULL, parent_kobj: firmware_kobj); |
1896 | if (!dump_kset) |
1897 | return -ENOMEM; |
1898 | rc = sysfs_create_file(kobj: &dump_kset->kobj, attr: &dump_type_attr.attr); |
1899 | if (rc) { |
1900 | kset_unregister(kset: dump_kset); |
1901 | return rc; |
1902 | } |
1903 | rc = dump_ccw_init(); |
1904 | if (rc) |
1905 | return rc; |
1906 | rc = dump_eckd_init(); |
1907 | if (rc) |
1908 | return rc; |
1909 | rc = dump_fcp_init(); |
1910 | if (rc) |
1911 | return rc; |
1912 | rc = dump_nvme_init(); |
1913 | if (rc) |
1914 | return rc; |
1915 | dump_set_type(type: DUMP_TYPE_NONE); |
1916 | return 0; |
1917 | } |
1918 | |
1919 | static struct shutdown_action __refdata dump_action = { |
1920 | .name = SHUTDOWN_ACTION_DUMP_STR, |
1921 | .fn = dump_run, |
1922 | .init = dump_init, |
1923 | }; |
1924 | |
1925 | static void dump_reipl_run(struct shutdown_trigger *trigger) |
1926 | { |
1927 | struct lowcore *abs_lc; |
1928 | unsigned int csum; |
1929 | |
1930 | /* |
1931 | * Set REIPL_CLEAR flag in os_info flags entry indicating |
1932 | * 'clear' sysfs attribute has been set on the panicked system |
1933 | * for specified reipl type. |
1934 | * Always set for IPL_TYPE_NSS and IPL_TYPE_UNKNOWN. |
1935 | */ |
1936 | if ((reipl_type == IPL_TYPE_CCW && reipl_ccw_clear) || |
1937 | (reipl_type == IPL_TYPE_ECKD && reipl_eckd_clear) || |
1938 | (reipl_type == IPL_TYPE_FCP && reipl_fcp_clear) || |
1939 | (reipl_type == IPL_TYPE_NVME && reipl_nvme_clear) || |
1940 | reipl_type == IPL_TYPE_NSS || |
1941 | reipl_type == IPL_TYPE_UNKNOWN) |
1942 | os_info_flags |= OS_INFO_FLAG_REIPL_CLEAR; |
1943 | os_info_entry_add(OS_INFO_FLAGS_ENTRY, &os_info_flags, sizeof(os_info_flags)); |
1944 | csum = (__force unsigned int)cksm(reipl_block_actual, reipl_block_actual->hdr.len, 0); |
1945 | abs_lc = get_abs_lowcore(); |
1946 | abs_lc->ipib = __pa(reipl_block_actual); |
1947 | abs_lc->ipib_checksum = csum; |
1948 | put_abs_lowcore(abs_lc); |
1949 | dump_run(trigger); |
1950 | } |
1951 | |
1952 | static struct shutdown_action __refdata dump_reipl_action = { |
1953 | .name = SHUTDOWN_ACTION_DUMP_REIPL_STR, |
1954 | .fn = dump_reipl_run, |
1955 | }; |
1956 | |
1957 | /* |
1958 | * vmcmd shutdown action: Trigger vm command on shutdown. |
1959 | */ |
1960 | |
1961 | static char vmcmd_on_reboot[128]; |
1962 | static char vmcmd_on_panic[128]; |
1963 | static char vmcmd_on_halt[128]; |
1964 | static char vmcmd_on_poff[128]; |
1965 | static char vmcmd_on_restart[128]; |
1966 | |
1967 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n" , "%s\n" , vmcmd_on_reboot); |
1968 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n" , "%s\n" , vmcmd_on_panic); |
1969 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n" , "%s\n" , vmcmd_on_halt); |
1970 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n" , "%s\n" , vmcmd_on_poff); |
1971 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_restart, "%s\n" , "%s\n" , vmcmd_on_restart); |
1972 | |
1973 | static struct attribute *vmcmd_attrs[] = { |
1974 | &sys_vmcmd_on_reboot_attr.attr, |
1975 | &sys_vmcmd_on_panic_attr.attr, |
1976 | &sys_vmcmd_on_halt_attr.attr, |
1977 | &sys_vmcmd_on_poff_attr.attr, |
1978 | &sys_vmcmd_on_restart_attr.attr, |
1979 | NULL, |
1980 | }; |
1981 | |
1982 | static struct attribute_group vmcmd_attr_group = { |
1983 | .attrs = vmcmd_attrs, |
1984 | }; |
1985 | |
1986 | static struct kset *vmcmd_kset; |
1987 | |
1988 | static void vmcmd_run(struct shutdown_trigger *trigger) |
1989 | { |
1990 | char *cmd; |
1991 | |
1992 | if (strcmp(trigger->name, ON_REIPL_STR) == 0) |
1993 | cmd = vmcmd_on_reboot; |
1994 | else if (strcmp(trigger->name, ON_PANIC_STR) == 0) |
1995 | cmd = vmcmd_on_panic; |
1996 | else if (strcmp(trigger->name, ON_HALT_STR) == 0) |
1997 | cmd = vmcmd_on_halt; |
1998 | else if (strcmp(trigger->name, ON_POFF_STR) == 0) |
1999 | cmd = vmcmd_on_poff; |
2000 | else if (strcmp(trigger->name, ON_RESTART_STR) == 0) |
2001 | cmd = vmcmd_on_restart; |
2002 | else |
2003 | return; |
2004 | |
2005 | if (strlen(cmd) == 0) |
2006 | return; |
2007 | __cpcmd(cmd, NULL, 0, NULL); |
2008 | } |
2009 | |
2010 | static int vmcmd_init(void) |
2011 | { |
2012 | if (!MACHINE_IS_VM) |
2013 | return -EOPNOTSUPP; |
2014 | vmcmd_kset = kset_create_and_add(name: "vmcmd" , NULL, parent_kobj: firmware_kobj); |
2015 | if (!vmcmd_kset) |
2016 | return -ENOMEM; |
2017 | return sysfs_create_group(kobj: &vmcmd_kset->kobj, grp: &vmcmd_attr_group); |
2018 | } |
2019 | |
2020 | static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, |
2021 | vmcmd_run, vmcmd_init}; |
2022 | |
2023 | /* |
2024 | * stop shutdown action: Stop Linux on shutdown. |
2025 | */ |
2026 | |
2027 | static void stop_run(struct shutdown_trigger *trigger) |
2028 | { |
2029 | if (strcmp(trigger->name, ON_PANIC_STR) == 0 || |
2030 | strcmp(trigger->name, ON_RESTART_STR) == 0) |
2031 | disabled_wait(); |
2032 | smp_stop_cpu(); |
2033 | } |
2034 | |
2035 | static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, |
2036 | stop_run, NULL}; |
2037 | |
2038 | /* action list */ |
2039 | |
2040 | static struct shutdown_action *shutdown_actions_list[] = { |
2041 | &ipl_action, &reipl_action, &dump_reipl_action, &dump_action, |
2042 | &vmcmd_action, &stop_action}; |
2043 | #define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *)) |
2044 | |
2045 | /* |
2046 | * Trigger section |
2047 | */ |
2048 | |
2049 | static struct kset *shutdown_actions_kset; |
2050 | |
2051 | static int set_trigger(const char *buf, struct shutdown_trigger *trigger, |
2052 | size_t len) |
2053 | { |
2054 | int i; |
2055 | |
2056 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { |
2057 | if (sysfs_streq(s1: buf, s2: shutdown_actions_list[i]->name)) { |
2058 | if (shutdown_actions_list[i]->init_rc) { |
2059 | return shutdown_actions_list[i]->init_rc; |
2060 | } else { |
2061 | trigger->action = shutdown_actions_list[i]; |
2062 | return len; |
2063 | } |
2064 | } |
2065 | } |
2066 | return -EINVAL; |
2067 | } |
2068 | |
2069 | /* on reipl */ |
2070 | |
2071 | static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR, |
2072 | &reipl_action}; |
2073 | |
2074 | static ssize_t on_reboot_show(struct kobject *kobj, |
2075 | struct kobj_attribute *attr, char *page) |
2076 | { |
2077 | return sprintf(buf: page, fmt: "%s\n" , on_reboot_trigger.action->name); |
2078 | } |
2079 | |
2080 | static ssize_t on_reboot_store(struct kobject *kobj, |
2081 | struct kobj_attribute *attr, |
2082 | const char *buf, size_t len) |
2083 | { |
2084 | return set_trigger(buf, trigger: &on_reboot_trigger, len); |
2085 | } |
2086 | static struct kobj_attribute on_reboot_attr = __ATTR_RW(on_reboot); |
2087 | |
2088 | static void do_machine_restart(char *__unused) |
2089 | { |
2090 | smp_send_stop(); |
2091 | on_reboot_trigger.action->fn(&on_reboot_trigger); |
2092 | reipl_run(NULL); |
2093 | } |
2094 | void (*_machine_restart)(char *command) = do_machine_restart; |
2095 | |
2096 | /* on panic */ |
2097 | |
2098 | static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action}; |
2099 | |
2100 | static ssize_t on_panic_show(struct kobject *kobj, |
2101 | struct kobj_attribute *attr, char *page) |
2102 | { |
2103 | return sprintf(buf: page, fmt: "%s\n" , on_panic_trigger.action->name); |
2104 | } |
2105 | |
2106 | static ssize_t on_panic_store(struct kobject *kobj, |
2107 | struct kobj_attribute *attr, |
2108 | const char *buf, size_t len) |
2109 | { |
2110 | return set_trigger(buf, trigger: &on_panic_trigger, len); |
2111 | } |
2112 | static struct kobj_attribute on_panic_attr = __ATTR_RW(on_panic); |
2113 | |
2114 | static void do_panic(void) |
2115 | { |
2116 | lgr_info_log(); |
2117 | on_panic_trigger.action->fn(&on_panic_trigger); |
2118 | stop_run(trigger: &on_panic_trigger); |
2119 | } |
2120 | |
2121 | /* on restart */ |
2122 | |
2123 | static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR, |
2124 | &stop_action}; |
2125 | |
2126 | static ssize_t on_restart_show(struct kobject *kobj, |
2127 | struct kobj_attribute *attr, char *page) |
2128 | { |
2129 | return sprintf(buf: page, fmt: "%s\n" , on_restart_trigger.action->name); |
2130 | } |
2131 | |
2132 | static ssize_t on_restart_store(struct kobject *kobj, |
2133 | struct kobj_attribute *attr, |
2134 | const char *buf, size_t len) |
2135 | { |
2136 | return set_trigger(buf, trigger: &on_restart_trigger, len); |
2137 | } |
2138 | static struct kobj_attribute on_restart_attr = __ATTR_RW(on_restart); |
2139 | |
2140 | static void __do_restart(void *ignore) |
2141 | { |
2142 | smp_send_stop(); |
2143 | #ifdef CONFIG_CRASH_DUMP |
2144 | crash_kexec(NULL); |
2145 | #endif |
2146 | on_restart_trigger.action->fn(&on_restart_trigger); |
2147 | stop_run(trigger: &on_restart_trigger); |
2148 | } |
2149 | |
2150 | void do_restart(void *arg) |
2151 | { |
2152 | tracing_off(); |
2153 | debug_locks_off(); |
2154 | lgr_info_log(); |
2155 | smp_call_online_cpu(__do_restart, arg); |
2156 | } |
2157 | |
2158 | /* on halt */ |
2159 | |
2160 | static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; |
2161 | |
2162 | static ssize_t on_halt_show(struct kobject *kobj, |
2163 | struct kobj_attribute *attr, char *page) |
2164 | { |
2165 | return sprintf(buf: page, fmt: "%s\n" , on_halt_trigger.action->name); |
2166 | } |
2167 | |
2168 | static ssize_t on_halt_store(struct kobject *kobj, |
2169 | struct kobj_attribute *attr, |
2170 | const char *buf, size_t len) |
2171 | { |
2172 | return set_trigger(buf, trigger: &on_halt_trigger, len); |
2173 | } |
2174 | static struct kobj_attribute on_halt_attr = __ATTR_RW(on_halt); |
2175 | |
2176 | static void do_machine_halt(void) |
2177 | { |
2178 | smp_send_stop(); |
2179 | on_halt_trigger.action->fn(&on_halt_trigger); |
2180 | stop_run(trigger: &on_halt_trigger); |
2181 | } |
2182 | void (*_machine_halt)(void) = do_machine_halt; |
2183 | |
2184 | /* on power off */ |
2185 | |
2186 | static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action}; |
2187 | |
2188 | static ssize_t on_poff_show(struct kobject *kobj, |
2189 | struct kobj_attribute *attr, char *page) |
2190 | { |
2191 | return sprintf(buf: page, fmt: "%s\n" , on_poff_trigger.action->name); |
2192 | } |
2193 | |
2194 | static ssize_t on_poff_store(struct kobject *kobj, |
2195 | struct kobj_attribute *attr, |
2196 | const char *buf, size_t len) |
2197 | { |
2198 | return set_trigger(buf, trigger: &on_poff_trigger, len); |
2199 | } |
2200 | static struct kobj_attribute on_poff_attr = __ATTR_RW(on_poff); |
2201 | |
2202 | static void do_machine_power_off(void) |
2203 | { |
2204 | smp_send_stop(); |
2205 | on_poff_trigger.action->fn(&on_poff_trigger); |
2206 | stop_run(trigger: &on_poff_trigger); |
2207 | } |
2208 | void (*_machine_power_off)(void) = do_machine_power_off; |
2209 | |
2210 | static struct attribute *shutdown_action_attrs[] = { |
2211 | &on_restart_attr.attr, |
2212 | &on_reboot_attr.attr, |
2213 | &on_panic_attr.attr, |
2214 | &on_halt_attr.attr, |
2215 | &on_poff_attr.attr, |
2216 | NULL, |
2217 | }; |
2218 | |
2219 | static struct attribute_group shutdown_action_attr_group = { |
2220 | .attrs = shutdown_action_attrs, |
2221 | }; |
2222 | |
2223 | static void __init shutdown_triggers_init(void) |
2224 | { |
2225 | shutdown_actions_kset = kset_create_and_add(name: "shutdown_actions" , NULL, |
2226 | parent_kobj: firmware_kobj); |
2227 | if (!shutdown_actions_kset) |
2228 | goto fail; |
2229 | if (sysfs_create_group(kobj: &shutdown_actions_kset->kobj, |
2230 | grp: &shutdown_action_attr_group)) |
2231 | goto fail; |
2232 | return; |
2233 | fail: |
2234 | panic(fmt: "shutdown_triggers_init failed\n" ); |
2235 | } |
2236 | |
2237 | static void __init shutdown_actions_init(void) |
2238 | { |
2239 | int i; |
2240 | |
2241 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { |
2242 | if (!shutdown_actions_list[i]->init) |
2243 | continue; |
2244 | shutdown_actions_list[i]->init_rc = |
2245 | shutdown_actions_list[i]->init(); |
2246 | } |
2247 | } |
2248 | |
2249 | static int __init s390_ipl_init(void) |
2250 | { |
2251 | char str[8] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}; |
2252 | |
2253 | sclp_early_get_ipl_info(&sclp_ipl_info); |
2254 | /* |
2255 | * Fix loadparm: There are systems where the (SCSI) LOADPARM |
2256 | * returned by read SCP info is invalid (contains EBCDIC blanks) |
2257 | * when the system has been booted via diag308. In that case we use |
2258 | * the value from diag308, if available. |
2259 | * |
2260 | * There are also systems where diag308 store does not work in |
2261 | * case the system is booted from HMC. Fortunately in this case |
2262 | * READ SCP info provides the correct value. |
2263 | */ |
2264 | if (memcmp(sclp_ipl_info.loadparm, str, sizeof(str)) == 0 && ipl_block_valid) |
2265 | memcpy(sclp_ipl_info.loadparm, ipl_block.ccw.loadparm, LOADPARM_LEN); |
2266 | shutdown_actions_init(); |
2267 | shutdown_triggers_init(); |
2268 | return 0; |
2269 | } |
2270 | |
2271 | __initcall(s390_ipl_init); |
2272 | |
2273 | static void __init strncpy_skip_quote(char *dst, char *src, int n) |
2274 | { |
2275 | int sx, dx; |
2276 | |
2277 | dx = 0; |
2278 | for (sx = 0; src[sx] != 0; sx++) { |
2279 | if (src[sx] == '"') |
2280 | continue; |
2281 | dst[dx++] = src[sx]; |
2282 | if (dx >= n) |
2283 | break; |
2284 | } |
2285 | } |
2286 | |
2287 | static int __init vmcmd_on_reboot_setup(char *str) |
2288 | { |
2289 | if (!MACHINE_IS_VM) |
2290 | return 1; |
2291 | strncpy_skip_quote(dst: vmcmd_on_reboot, src: str, n: 127); |
2292 | vmcmd_on_reboot[127] = 0; |
2293 | on_reboot_trigger.action = &vmcmd_action; |
2294 | return 1; |
2295 | } |
2296 | __setup("vmreboot=" , vmcmd_on_reboot_setup); |
2297 | |
2298 | static int __init vmcmd_on_panic_setup(char *str) |
2299 | { |
2300 | if (!MACHINE_IS_VM) |
2301 | return 1; |
2302 | strncpy_skip_quote(dst: vmcmd_on_panic, src: str, n: 127); |
2303 | vmcmd_on_panic[127] = 0; |
2304 | on_panic_trigger.action = &vmcmd_action; |
2305 | return 1; |
2306 | } |
2307 | __setup("vmpanic=" , vmcmd_on_panic_setup); |
2308 | |
2309 | static int __init vmcmd_on_halt_setup(char *str) |
2310 | { |
2311 | if (!MACHINE_IS_VM) |
2312 | return 1; |
2313 | strncpy_skip_quote(dst: vmcmd_on_halt, src: str, n: 127); |
2314 | vmcmd_on_halt[127] = 0; |
2315 | on_halt_trigger.action = &vmcmd_action; |
2316 | return 1; |
2317 | } |
2318 | __setup("vmhalt=" , vmcmd_on_halt_setup); |
2319 | |
2320 | static int __init vmcmd_on_poff_setup(char *str) |
2321 | { |
2322 | if (!MACHINE_IS_VM) |
2323 | return 1; |
2324 | strncpy_skip_quote(dst: vmcmd_on_poff, src: str, n: 127); |
2325 | vmcmd_on_poff[127] = 0; |
2326 | on_poff_trigger.action = &vmcmd_action; |
2327 | return 1; |
2328 | } |
2329 | __setup("vmpoff=" , vmcmd_on_poff_setup); |
2330 | |
2331 | static int on_panic_notify(struct notifier_block *self, |
2332 | unsigned long event, void *data) |
2333 | { |
2334 | do_panic(); |
2335 | return NOTIFY_OK; |
2336 | } |
2337 | |
2338 | static struct notifier_block on_panic_nb = { |
2339 | .notifier_call = on_panic_notify, |
2340 | .priority = INT_MIN, |
2341 | }; |
2342 | |
2343 | void __init setup_ipl(void) |
2344 | { |
2345 | BUILD_BUG_ON(sizeof(struct ipl_parameter_block) != PAGE_SIZE); |
2346 | |
2347 | ipl_info.type = get_ipl_type(); |
2348 | switch (ipl_info.type) { |
2349 | case IPL_TYPE_CCW: |
2350 | ipl_info.data.ccw.dev_id.ssid = ipl_block.ccw.ssid; |
2351 | ipl_info.data.ccw.dev_id.devno = ipl_block.ccw.devno; |
2352 | break; |
2353 | case IPL_TYPE_ECKD: |
2354 | case IPL_TYPE_ECKD_DUMP: |
2355 | ipl_info.data.eckd.dev_id.ssid = ipl_block.eckd.ssid; |
2356 | ipl_info.data.eckd.dev_id.devno = ipl_block.eckd.devno; |
2357 | break; |
2358 | case IPL_TYPE_FCP: |
2359 | case IPL_TYPE_FCP_DUMP: |
2360 | ipl_info.data.fcp.dev_id.ssid = 0; |
2361 | ipl_info.data.fcp.dev_id.devno = ipl_block.fcp.devno; |
2362 | ipl_info.data.fcp.wwpn = ipl_block.fcp.wwpn; |
2363 | ipl_info.data.fcp.lun = ipl_block.fcp.lun; |
2364 | break; |
2365 | case IPL_TYPE_NVME: |
2366 | case IPL_TYPE_NVME_DUMP: |
2367 | ipl_info.data.nvme.fid = ipl_block.nvme.fid; |
2368 | ipl_info.data.nvme.nsid = ipl_block.nvme.nsid; |
2369 | break; |
2370 | case IPL_TYPE_NSS: |
2371 | case IPL_TYPE_UNKNOWN: |
2372 | /* We have no info to copy */ |
2373 | break; |
2374 | } |
2375 | atomic_notifier_chain_register(nh: &panic_notifier_list, nb: &on_panic_nb); |
2376 | } |
2377 | |
2378 | void s390_reset_system(void) |
2379 | { |
2380 | /* Disable prefixing */ |
2381 | set_prefix(0); |
2382 | |
2383 | /* Disable lowcore protection */ |
2384 | local_ctl_clear_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT); |
2385 | diag_amode31_ops.diag308_reset(); |
2386 | } |
2387 | |
2388 | #ifdef CONFIG_KEXEC_FILE |
2389 | |
2390 | int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf, |
2391 | unsigned char flags, unsigned short cert) |
2392 | { |
2393 | struct ipl_report_component *comp; |
2394 | |
2395 | comp = vzalloc(sizeof(*comp)); |
2396 | if (!comp) |
2397 | return -ENOMEM; |
2398 | list_add_tail(new: &comp->list, head: &report->components); |
2399 | |
2400 | comp->entry.addr = kbuf->mem; |
2401 | comp->entry.len = kbuf->memsz; |
2402 | comp->entry.flags = flags; |
2403 | comp->entry.certificate_index = cert; |
2404 | |
2405 | report->size += sizeof(comp->entry); |
2406 | |
2407 | return 0; |
2408 | } |
2409 | |
2410 | int ipl_report_add_certificate(struct ipl_report *report, void *key, |
2411 | unsigned long addr, unsigned long len) |
2412 | { |
2413 | struct ipl_report_certificate *cert; |
2414 | |
2415 | cert = vzalloc(sizeof(*cert)); |
2416 | if (!cert) |
2417 | return -ENOMEM; |
2418 | list_add_tail(new: &cert->list, head: &report->certificates); |
2419 | |
2420 | cert->entry.addr = addr; |
2421 | cert->entry.len = len; |
2422 | cert->key = key; |
2423 | |
2424 | report->size += sizeof(cert->entry); |
2425 | report->size += cert->entry.len; |
2426 | |
2427 | return 0; |
2428 | } |
2429 | |
2430 | struct ipl_report *ipl_report_init(struct ipl_parameter_block *ipib) |
2431 | { |
2432 | struct ipl_report *report; |
2433 | |
2434 | report = vzalloc(sizeof(*report)); |
2435 | if (!report) |
2436 | return ERR_PTR(error: -ENOMEM); |
2437 | |
2438 | report->ipib = ipib; |
2439 | INIT_LIST_HEAD(list: &report->components); |
2440 | INIT_LIST_HEAD(list: &report->certificates); |
2441 | |
2442 | report->size = ALIGN(ipib->hdr.len, 8); |
2443 | report->size += sizeof(struct ipl_rl_hdr); |
2444 | report->size += sizeof(struct ipl_rb_components); |
2445 | report->size += sizeof(struct ipl_rb_certificates); |
2446 | |
2447 | return report; |
2448 | } |
2449 | |
2450 | void *ipl_report_finish(struct ipl_report *report) |
2451 | { |
2452 | struct ipl_report_certificate *cert; |
2453 | struct ipl_report_component *comp; |
2454 | struct ipl_rb_certificates *certs; |
2455 | struct ipl_parameter_block *ipib; |
2456 | struct ipl_rb_components *comps; |
2457 | struct ipl_rl_hdr *rl_hdr; |
2458 | void *buf, *ptr; |
2459 | |
2460 | buf = vzalloc(size: report->size); |
2461 | if (!buf) |
2462 | goto out; |
2463 | ptr = buf; |
2464 | |
2465 | memcpy(ptr, report->ipib, report->ipib->hdr.len); |
2466 | ipib = ptr; |
2467 | if (ipl_secure_flag) |
2468 | ipib->hdr.flags |= IPL_PL_FLAG_SIPL; |
2469 | ipib->hdr.flags |= IPL_PL_FLAG_IPLSR; |
2470 | ptr += report->ipib->hdr.len; |
2471 | ptr = PTR_ALIGN(ptr, 8); |
2472 | |
2473 | rl_hdr = ptr; |
2474 | ptr += sizeof(*rl_hdr); |
2475 | |
2476 | comps = ptr; |
2477 | comps->rbt = IPL_RBT_COMPONENTS; |
2478 | ptr += sizeof(*comps); |
2479 | list_for_each_entry(comp, &report->components, list) { |
2480 | memcpy(ptr, &comp->entry, sizeof(comp->entry)); |
2481 | ptr += sizeof(comp->entry); |
2482 | } |
2483 | comps->len = ptr - (void *)comps; |
2484 | |
2485 | certs = ptr; |
2486 | certs->rbt = IPL_RBT_CERTIFICATES; |
2487 | ptr += sizeof(*certs); |
2488 | list_for_each_entry(cert, &report->certificates, list) { |
2489 | memcpy(ptr, &cert->entry, sizeof(cert->entry)); |
2490 | ptr += sizeof(cert->entry); |
2491 | } |
2492 | certs->len = ptr - (void *)certs; |
2493 | rl_hdr->len = ptr - (void *)rl_hdr; |
2494 | |
2495 | list_for_each_entry(cert, &report->certificates, list) { |
2496 | memcpy(ptr, cert->key, cert->entry.len); |
2497 | ptr += cert->entry.len; |
2498 | } |
2499 | |
2500 | BUG_ON(ptr > buf + report->size); |
2501 | out: |
2502 | return buf; |
2503 | } |
2504 | |
2505 | int ipl_report_free(struct ipl_report *report) |
2506 | { |
2507 | struct ipl_report_component *comp, *ncomp; |
2508 | struct ipl_report_certificate *cert, *ncert; |
2509 | |
2510 | list_for_each_entry_safe(comp, ncomp, &report->components, list) |
2511 | vfree(addr: comp); |
2512 | |
2513 | list_for_each_entry_safe(cert, ncert, &report->certificates, list) |
2514 | vfree(addr: cert); |
2515 | |
2516 | vfree(addr: report); |
2517 | |
2518 | return 0; |
2519 | } |
2520 | |
2521 | #endif |
2522 | |