Commit | Line | Data |
---|---|---|
a17ae4c3 | 1 | // SPDX-License-Identifier: GPL-2.0 |
ff6b8ea6 | 2 | /* |
ff6b8ea6 MH |
3 | * ipl/reipl/dump support for Linux on s390. |
4 | * | |
a53c8fab | 5 | * Copyright IBM Corp. 2005, 2012 |
ff6b8ea6 | 6 | * Author(s): Michael Holzheu <holzheu@de.ibm.com> |
ff6b8ea6 MH |
7 | * Volker Sameske <sameske@de.ibm.com> |
8 | */ | |
9 | ||
10 | #include <linux/types.h> | |
3994a52b PG |
11 | #include <linux/export.h> |
12 | #include <linux/init.h> | |
ff6b8ea6 MH |
13 | #include <linux/device.h> |
14 | #include <linux/delay.h> | |
d9b25bdf | 15 | #include <linux/kstrtox.h> |
f39650de | 16 | #include <linux/panic_notifier.h> |
ff6b8ea6 | 17 | #include <linux/reboot.h> |
03a4d208 | 18 | #include <linux/ctype.h> |
0788fea4 | 19 | #include <linux/fs.h> |
5a0e3ad6 | 20 | #include <linux/gfp.h> |
60a0c68d | 21 | #include <linux/crash_dump.h> |
3ab121ab | 22 | #include <linux/debug_locks.h> |
d09a307f | 23 | #include <asm/asm-extable.h> |
1ec2772e | 24 | #include <asm/diag.h> |
46b05d26 | 25 | #include <asm/ipl.h> |
ff6b8ea6 MH |
26 | #include <asm/smp.h> |
27 | #include <asm/setup.h> | |
28 | #include <asm/cpcmd.h> | |
03a4d208 | 29 | #include <asm/ebcdic.h> |
ab14de6c | 30 | #include <asm/sclp.h> |
159d1ff8 | 31 | #include <asm/checksum.h> |
3ab121ab | 32 | #include <asm/debug.h> |
4df29d2b | 33 | #include <asm/abs_lowcore.h> |
4857d4bb | 34 | #include <asm/os_info.h> |
49698745 VG |
35 | #include <asm/sections.h> |
36 | #include <asm/boot_data.h> | |
638ad34a | 37 | #include "entry.h" |
ff6b8ea6 MH |
38 | |
39 | #define IPL_PARM_BLOCK_VERSION 0 | |
03a4d208 | 40 | |
411ed322 MH |
41 | #define IPL_UNKNOWN_STR "unknown" |
42 | #define IPL_CCW_STR "ccw" | |
87fd22e0 | 43 | #define IPL_ECKD_STR "eckd" |
e2d2a296 | 44 | #define IPL_ECKD_DUMP_STR "eckd_dump" |
411ed322 MH |
45 | #define IPL_FCP_STR "fcp" |
46 | #define IPL_FCP_DUMP_STR "fcp_dump" | |
3737e8ee | 47 | #define IPL_NVME_STR "nvme" |
d70e38cb | 48 | #define IPL_NVME_DUMP_STR "nvme_dump" |
411ed322 | 49 | #define IPL_NSS_STR "nss" |
615b04b3 | 50 | |
99ca4e58 | 51 | #define DUMP_CCW_STR "ccw" |
e2d2a296 | 52 | #define DUMP_ECKD_STR "eckd" |
99ca4e58 | 53 | #define DUMP_FCP_STR "fcp" |
d70e38cb | 54 | #define DUMP_NVME_STR "nvme" |
99ca4e58 MH |
55 | #define DUMP_NONE_STR "none" |
56 | ||
57 | /* | |
58 | * Four shutdown trigger types are supported: | |
59 | * - panic | |
60 | * - halt | |
61 | * - power off | |
62 | * - reipl | |
7dd6b334 | 63 | * - restart |
99ca4e58 MH |
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" | |
7dd6b334 | 69 | #define ON_RESTART_STR "on_restart" |
99ca4e58 MH |
70 | |
71 | struct shutdown_action; | |
72 | struct shutdown_trigger { | |
73 | char *name; | |
74 | struct shutdown_action *action; | |
75 | }; | |
76 | ||
77 | /* | |
099b7651 | 78 | * The following shutdown action types are supported: |
99ca4e58 MH |
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" | |
099b7651 | 85 | #define SHUTDOWN_ACTION_DUMP_REIPL_STR "dump_reipl" |
99ca4e58 MH |
86 | |
87 | struct shutdown_action { | |
88 | char *name; | |
89 | void (*fn) (struct shutdown_trigger *trigger); | |
90 | int (*init) (void); | |
81088819 | 91 | int init_rc; |
99ca4e58 MH |
92 | }; |
93 | ||
ff6b8ea6 MH |
94 | static char *ipl_type_str(enum ipl_type type) |
95 | { | |
96 | switch (type) { | |
ff6b8ea6 MH |
97 | case IPL_TYPE_CCW: |
98 | return IPL_CCW_STR; | |
87fd22e0 SS |
99 | case IPL_TYPE_ECKD: |
100 | return IPL_ECKD_STR; | |
e2d2a296 SS |
101 | case IPL_TYPE_ECKD_DUMP: |
102 | return IPL_ECKD_DUMP_STR; | |
ff6b8ea6 MH |
103 | case IPL_TYPE_FCP: |
104 | return IPL_FCP_STR; | |
411ed322 MH |
105 | case IPL_TYPE_FCP_DUMP: |
106 | return IPL_FCP_DUMP_STR; | |
fe355b7f HY |
107 | case IPL_TYPE_NSS: |
108 | return IPL_NSS_STR; | |
3737e8ee JH |
109 | case IPL_TYPE_NVME: |
110 | return IPL_NVME_STR; | |
d70e38cb JH |
111 | case IPL_TYPE_NVME_DUMP: |
112 | return IPL_NVME_DUMP_STR; | |
ff6b8ea6 MH |
113 | case IPL_TYPE_UNKNOWN: |
114 | default: | |
115 | return IPL_UNKNOWN_STR; | |
116 | } | |
117 | } | |
118 | ||
411ed322 MH |
119 | enum dump_type { |
120 | DUMP_TYPE_NONE = 1, | |
121 | DUMP_TYPE_CCW = 2, | |
122 | DUMP_TYPE_FCP = 4, | |
d70e38cb | 123 | DUMP_TYPE_NVME = 8, |
e2d2a296 | 124 | DUMP_TYPE_ECKD = 16, |
411ed322 MH |
125 | }; |
126 | ||
411ed322 MH |
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; | |
e2d2a296 SS |
134 | case DUMP_TYPE_ECKD: |
135 | return DUMP_ECKD_STR; | |
411ed322 MH |
136 | case DUMP_TYPE_FCP: |
137 | return DUMP_FCP_STR; | |
d70e38cb JH |
138 | case DUMP_TYPE_NVME: |
139 | return DUMP_NVME_STR; | |
411ed322 MH |
140 | default: |
141 | return NULL; | |
142 | } | |
143 | } | |
144 | ||
1e941d39 VG |
145 | int __bootdata_preserved(ipl_block_valid); |
146 | struct ipl_parameter_block __bootdata_preserved(ipl_block); | |
9641b8cc MS |
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); | |
a0443fbb | 154 | |
ff6b8ea6 | 155 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; |
fe355b7f | 156 | |
ff6b8ea6 | 157 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; |
ff6b8ea6 | 158 | static struct ipl_parameter_block *reipl_block_fcp; |
23a457b8 | 159 | static struct ipl_parameter_block *reipl_block_nvme; |
ff6b8ea6 | 160 | static struct ipl_parameter_block *reipl_block_ccw; |
87fd22e0 | 161 | static struct ipl_parameter_block *reipl_block_eckd; |
a0443fbb | 162 | static struct ipl_parameter_block *reipl_block_nss; |
099b7651 | 163 | static struct ipl_parameter_block *reipl_block_actual; |
fe355b7f | 164 | |
411ed322 MH |
165 | static int dump_capabilities = DUMP_TYPE_NONE; |
166 | static enum dump_type dump_type = DUMP_TYPE_NONE; | |
ff6b8ea6 | 167 | static struct ipl_parameter_block *dump_block_fcp; |
d70e38cb | 168 | static struct ipl_parameter_block *dump_block_nvme; |
ff6b8ea6 | 169 | static struct ipl_parameter_block *dump_block_ccw; |
e2d2a296 | 170 | static struct ipl_parameter_block *dump_block_eckd; |
ff6b8ea6 | 171 | |
05dd2530 HC |
172 | static struct sclp_ipl_info sclp_ipl_info; |
173 | ||
5627b922 | 174 | static bool reipl_nvme_clear; |
1a2ae03b GS |
175 | static bool reipl_fcp_clear; |
176 | static bool reipl_ccw_clear; | |
87fd22e0 | 177 | static bool reipl_eckd_clear; |
1a2ae03b | 178 | |
31e9ccc6 MZ |
179 | static unsigned long os_info_flags; |
180 | ||
bac30ea9 | 181 | static inline int __diag308(unsigned long subcode, unsigned long addr) |
ff6b8ea6 | 182 | { |
5a4e0f58 | 183 | union register_pair r1; |
ff6b8ea6 | 184 | |
bac30ea9 | 185 | r1.even = addr; |
5a4e0f58 | 186 | r1.odd = 0; |
94c12cc7 | 187 | asm volatile( |
5a4e0f58 | 188 | " diag %[r1],%[subcode],0x308\n" |
6c22c986 | 189 | "0: nopr %%r7\n" |
94c12cc7 | 190 | EX_TABLE(0b,0b) |
5a4e0f58 HC |
191 | : [r1] "+&d" (r1.pair) |
192 | : [subcode] "d" (subcode) | |
193 | : "cc", "memory"); | |
194 | return r1.odd; | |
ff6b8ea6 | 195 | } |
1ec2772e MS |
196 | |
197 | int diag308(unsigned long subcode, void *addr) | |
198 | { | |
199 | diag_stat_inc(DIAG_STAT_X308); | |
bac30ea9 | 200 | return __diag308(subcode, addr ? virt_to_phys(addr) : 0); |
1ec2772e | 201 | } |
411ed322 | 202 | EXPORT_SYMBOL_GPL(diag308); |
ff6b8ea6 MH |
203 | |
204 | /* SYSFS */ | |
205 | ||
3be7ae63 | 206 | #define IPL_ATTR_SHOW_FN(_prefix, _name, _format, args...) \ |
9b949165 GKH |
207 | static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \ |
208 | struct kobj_attribute *attr, \ | |
ff6b8ea6 MH |
209 | char *page) \ |
210 | { \ | |
92fd3565 | 211 | return scnprintf(page, PAGE_SIZE, _format, ##args); \ |
3be7ae63 SO |
212 | } |
213 | ||
18e22a17 SO |
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 = \ | |
a70f7276 | 237 | __ATTR(_name, 0644, \ |
18e22a17 SO |
238 | sys_##_prefix##_##_name##_show, \ |
239 | sys_##_prefix##_##_name##_store) \ | |
240 | ||
3be7ae63 SO |
241 | #define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \ |
242 | IPL_ATTR_SHOW_FN(_prefix, _name, _format, _value) \ | |
9b949165 | 243 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
a70f7276 | 244 | __ATTR(_name, 0444, sys_##_prefix##_##_name##_show, NULL) |
ff6b8ea6 MH |
245 | |
246 | #define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \ | |
3be7ae63 | 247 | IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, (unsigned long long) _value) \ |
9b949165 GKH |
248 | static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ |
249 | struct kobj_attribute *attr, \ | |
ff6b8ea6 MH |
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 | } \ | |
9b949165 | 258 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
a70f7276 | 259 | __ATTR(_name, 0644, \ |
ff6b8ea6 | 260 | sys_##_prefix##_##_name##_show, \ |
3be7ae63 | 261 | sys_##_prefix##_##_name##_store) |
ff6b8ea6 | 262 | |
fe355b7f | 263 | #define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\ |
3be7ae63 | 264 | IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, _value) \ |
9b949165 GKH |
265 | static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ |
266 | struct kobj_attribute *attr, \ | |
fe355b7f HY |
267 | const char *buf, size_t len) \ |
268 | { \ | |
cfd01210 | 269 | strscpy(_value, buf, sizeof(_value)); \ |
1d802e24 | 270 | strim(_value); \ |
fe355b7f HY |
271 | return len; \ |
272 | } \ | |
9b949165 | 273 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
a70f7276 | 274 | __ATTR(_name, 0644, \ |
fe355b7f | 275 | sys_##_prefix##_##_name##_show, \ |
3be7ae63 | 276 | sys_##_prefix##_##_name##_store) |
fe355b7f | 277 | |
ff6b8ea6 MH |
278 | /* |
279 | * ipl section | |
280 | */ | |
281 | ||
411ed322 | 282 | static __init enum ipl_type get_ipl_type(void) |
ff6b8ea6 | 283 | { |
d08091ac | 284 | if (!ipl_block_valid) |
ff6b8ea6 | 285 | return IPL_TYPE_UNKNOWN; |
d08091ac | 286 | |
5f1207fb MS |
287 | switch (ipl_block.pb0_hdr.pbt) { |
288 | case IPL_PBT_CCW: | |
ff6b8ea6 | 289 | return IPL_TYPE_CCW; |
5f1207fb MS |
290 | case IPL_PBT_FCP: |
291 | if (ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) | |
d08091ac VG |
292 | return IPL_TYPE_FCP_DUMP; |
293 | else | |
294 | return IPL_TYPE_FCP; | |
3737e8ee | 295 | case IPL_PBT_NVME: |
d70e38cb JH |
296 | if (ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP) |
297 | return IPL_TYPE_NVME_DUMP; | |
298 | else | |
299 | return IPL_TYPE_NVME; | |
87fd22e0 | 300 | case IPL_PBT_ECKD: |
e2d2a296 SS |
301 | if (ipl_block.eckd.opt == IPL_PB0_ECKD_OPT_DUMP) |
302 | return IPL_TYPE_ECKD_DUMP; | |
303 | else | |
304 | return IPL_TYPE_ECKD; | |
d08091ac VG |
305 | } |
306 | return IPL_TYPE_UNKNOWN; | |
ff6b8ea6 MH |
307 | } |
308 | ||
411ed322 MH |
309 | struct ipl_info ipl_info; |
310 | EXPORT_SYMBOL_GPL(ipl_info); | |
311 | ||
9b949165 GKH |
312 | static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, |
313 | char *page) | |
ff6b8ea6 | 314 | { |
411ed322 | 315 | return sprintf(page, "%s\n", ipl_type_str(ipl_info.type)); |
ff6b8ea6 MH |
316 | } |
317 | ||
9b949165 | 318 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); |
ff6b8ea6 | 319 | |
9641b8cc MS |
320 | static ssize_t ipl_secure_show(struct kobject *kobj, |
321 | struct kobj_attribute *attr, char *page) | |
322 | { | |
323 | return sprintf(page, "%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 | ||
c9896acc PR |
329 | static ssize_t ipl_has_secure_show(struct kobject *kobj, |
330 | struct kobj_attribute *attr, char *page) | |
331 | { | |
1b2be207 | 332 | return sprintf(page, "%i\n", !!sclp.has_sipl); |
c9896acc PR |
333 | } |
334 | ||
335 | static struct kobj_attribute sys_ipl_has_secure_attr = | |
336 | __ATTR(has_secure, 0444, ipl_has_secure_show, NULL); | |
337 | ||
a0443fbb HB |
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 | ||
5f1207fb | 343 | if (ipl_block_valid && (ipl_block.pb0_hdr.pbt == IPL_PBT_CCW)) |
49698745 | 344 | ipl_block_get_ascii_vmparm(parm, sizeof(parm), &ipl_block); |
a0443fbb HB |
345 | return sprintf(page, "%s\n", parm); |
346 | } | |
347 | ||
348 | static struct kobj_attribute sys_ipl_vm_parm_attr = | |
a70f7276 | 349 | __ATTR(parm, 0444, ipl_vm_parm_show, NULL); |
a0443fbb | 350 | |
9b949165 GKH |
351 | static ssize_t sys_ipl_device_show(struct kobject *kobj, |
352 | struct kobj_attribute *attr, char *page) | |
ff6b8ea6 | 353 | { |
411ed322 | 354 | switch (ipl_info.type) { |
ff6b8ea6 | 355 | case IPL_TYPE_CCW: |
86c74d86 MS |
356 | return sprintf(page, "0.%x.%04x\n", ipl_block.ccw.ssid, |
357 | ipl_block.ccw.devno); | |
87fd22e0 | 358 | case IPL_TYPE_ECKD: |
e2d2a296 | 359 | case IPL_TYPE_ECKD_DUMP: |
87fd22e0 SS |
360 | return sprintf(page, "0.%x.%04x\n", ipl_block.eckd.ssid, |
361 | ipl_block.eckd.devno); | |
ff6b8ea6 | 362 | case IPL_TYPE_FCP: |
411ed322 | 363 | case IPL_TYPE_FCP_DUMP: |
86c74d86 | 364 | return sprintf(page, "0.0.%04x\n", ipl_block.fcp.devno); |
3737e8ee | 365 | case IPL_TYPE_NVME: |
d70e38cb | 366 | case IPL_TYPE_NVME_DUMP: |
3737e8ee | 367 | return sprintf(page, "%08ux\n", ipl_block.nvme.fid); |
ff6b8ea6 MH |
368 | default: |
369 | return 0; | |
370 | } | |
371 | } | |
372 | ||
9b949165 | 373 | static struct kobj_attribute sys_ipl_device_attr = |
a70f7276 | 374 | __ATTR(device, 0444, sys_ipl_device_show, NULL); |
ff6b8ea6 | 375 | |
2c3c8bea CW |
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) | |
ff6b8ea6 | 379 | { |
bdbfe185 VG |
380 | return memory_read_from_buffer(buf, count, &off, &ipl_block, |
381 | ipl_block.hdr.len); | |
ff6b8ea6 | 382 | } |
22d557ab | 383 | static struct bin_attribute ipl_parameter_attr = |
a70f7276 | 384 | __BIN_ATTR(binary_parameter, 0444, ipl_parameter_read, NULL, |
22d557ab | 385 | PAGE_SIZE); |
ff6b8ea6 | 386 | |
2c3c8bea CW |
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) | |
ff6b8ea6 | 390 | { |
86c74d86 MS |
391 | unsigned int size = ipl_block.fcp.scp_data_len; |
392 | void *scp_data = &ipl_block.fcp.scp_data; | |
ff6b8ea6 | 393 | |
0788fea4 | 394 | return memory_read_from_buffer(buf, count, &off, scp_data, size); |
ff6b8ea6 | 395 | } |
3737e8ee JH |
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(buf, count, &off, scp_data, size); | |
405 | } | |
406 | ||
87fd22e0 SS |
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(buf, count, &off, scp_data, size); | |
415 | } | |
416 | ||
22d557ab | 417 | static struct bin_attribute ipl_scp_data_attr = |
a70f7276 | 418 | __BIN_ATTR(scp_data, 0444, ipl_scp_data_read, NULL, PAGE_SIZE); |
ff6b8ea6 | 419 | |
3737e8ee | 420 | static struct bin_attribute ipl_nvme_scp_data_attr = |
a70f7276 | 421 | __BIN_ATTR(scp_data, 0444, ipl_nvme_scp_data_read, NULL, PAGE_SIZE); |
3737e8ee | 422 | |
87fd22e0 | 423 | static struct bin_attribute ipl_eckd_scp_data_attr = |
a70f7276 | 424 | __BIN_ATTR(scp_data, 0444, ipl_eckd_scp_data_read, NULL, PAGE_SIZE); |
87fd22e0 | 425 | |
22d557ab SO |
426 | static struct bin_attribute *ipl_fcp_bin_attrs[] = { |
427 | &ipl_parameter_attr, | |
428 | &ipl_scp_data_attr, | |
429 | NULL, | |
ff6b8ea6 MH |
430 | }; |
431 | ||
3737e8ee JH |
432 | static struct bin_attribute *ipl_nvme_bin_attrs[] = { |
433 | &ipl_parameter_attr, | |
434 | &ipl_nvme_scp_data_attr, | |
435 | NULL, | |
436 | }; | |
437 | ||
87fd22e0 SS |
438 | static struct bin_attribute *ipl_eckd_bin_attrs[] = { |
439 | &ipl_parameter_attr, | |
440 | &ipl_eckd_scp_data_attr, | |
441 | NULL, | |
442 | }; | |
443 | ||
ff6b8ea6 MH |
444 | /* FCP ipl device attributes */ |
445 | ||
bdbfe185 | 446 | DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n", |
86c74d86 | 447 | (unsigned long long)ipl_block.fcp.wwpn); |
bdbfe185 | 448 | DEFINE_IPL_ATTR_RO(ipl_fcp, lun, "0x%016llx\n", |
86c74d86 | 449 | (unsigned long long)ipl_block.fcp.lun); |
bdbfe185 | 450 | DEFINE_IPL_ATTR_RO(ipl_fcp, bootprog, "%lld\n", |
86c74d86 | 451 | (unsigned long long)ipl_block.fcp.bootprog); |
bdbfe185 | 452 | DEFINE_IPL_ATTR_RO(ipl_fcp, br_lba, "%lld\n", |
86c74d86 | 453 | (unsigned long long)ipl_block.fcp.br_lba); |
ff6b8ea6 | 454 | |
3737e8ee JH |
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 | ||
87fd22e0 SS |
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 = | |
a70f7276 | 535 | __ATTR(br_chr, 0644, eckd_ipl_br_chr_show, NULL); |
87fd22e0 SS |
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 = | |
a70f7276 | 541 | __ATTR(br_chr, 0644, eckd_reipl_br_chr_show, eckd_reipl_br_chr_store); |
87fd22e0 | 542 | |
9b949165 GKH |
543 | static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj, |
544 | struct kobj_attribute *attr, char *page) | |
03a4d208 MH |
545 | { |
546 | char loadparm[LOADPARM_LEN + 1] = {}; | |
547 | ||
05dd2530 | 548 | if (!sclp_ipl_info.is_valid) |
03a4d208 | 549 | return sprintf(page, "#unknown#\n"); |
05dd2530 | 550 | memcpy(loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN); |
03a4d208 | 551 | EBCASC(loadparm, LOADPARM_LEN); |
1d802e24 | 552 | strim(loadparm); |
03a4d208 MH |
553 | return sprintf(page, "%s\n", loadparm); |
554 | } | |
555 | ||
9b949165 | 556 | static struct kobj_attribute sys_ipl_ccw_loadparm_attr = |
03a4d208 MH |
557 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); |
558 | ||
69928601 | 559 | static struct attribute *ipl_fcp_attrs[] = { |
69928601 MH |
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, | |
22d557ab | 571 | .bin_attrs = ipl_fcp_bin_attrs, |
69928601 MH |
572 | }; |
573 | ||
3737e8ee | 574 | static struct attribute *ipl_nvme_attrs[] = { |
3737e8ee JH |
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, | |
3737e8ee JH |
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 | ||
87fd22e0 | 588 | static struct attribute *ipl_eckd_attrs[] = { |
87fd22e0 SS |
589 | &sys_ipl_eckd_bootprog_attr.attr, |
590 | &sys_ipl_eckd_br_chr_attr.attr, | |
6bb361d5 | 591 | &sys_ipl_ccw_loadparm_attr.attr, |
87fd22e0 | 592 | &sys_ipl_device_attr.attr, |
87fd22e0 SS |
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 | }; | |
3737e8ee | 600 | |
69928601 MH |
601 | /* CCW ipl device attributes */ |
602 | ||
a0443fbb | 603 | static struct attribute *ipl_ccw_attrs_vm[] = { |
a0443fbb HB |
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[] = { | |
ff6b8ea6 | 611 | &sys_ipl_device_attr.attr, |
03a4d208 | 612 | &sys_ipl_ccw_loadparm_attr.attr, |
ff6b8ea6 MH |
613 | NULL, |
614 | }; | |
615 | ||
a0443fbb HB |
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 | |
ff6b8ea6 MH |
622 | }; |
623 | ||
7645dcdd | 624 | static struct attribute *ipl_common_attrs[] = { |
ff6b8ea6 | 625 | &sys_ipl_type_attr.attr, |
ea5717cb SS |
626 | &sys_ipl_secure_attr.attr, |
627 | &sys_ipl_has_secure_attr.attr, | |
ff6b8ea6 MH |
628 | NULL, |
629 | }; | |
630 | ||
7645dcdd SS |
631 | static struct attribute_group ipl_common_attr_group = { |
632 | .attrs = ipl_common_attrs, | |
ff6b8ea6 MH |
633 | }; |
634 | ||
d91885be | 635 | static struct kset *ipl_kset; |
ff6b8ea6 | 636 | |
2c2df118 | 637 | static void __ipl_run(void *unused) |
99ca4e58 | 638 | { |
0599eead | 639 | diag308(DIAG308_LOAD_CLEAR, NULL); |
99ca4e58 MH |
640 | } |
641 | ||
2c2df118 HC |
642 | static void ipl_run(struct shutdown_trigger *trigger) |
643 | { | |
8b646bd7 | 644 | smp_call_ipl_cpu(__ipl_run, NULL); |
2c2df118 HC |
645 | } |
646 | ||
2bc89b5e | 647 | static int __init ipl_init(void) |
99ca4e58 MH |
648 | { |
649 | int rc; | |
650 | ||
651 | ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj); | |
652 | if (!ipl_kset) { | |
653 | rc = -ENOMEM; | |
654 | goto out; | |
655 | } | |
7645dcdd SS |
656 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_common_attr_group); |
657 | if (rc) | |
658 | goto out; | |
99ca4e58 MH |
659 | switch (ipl_info.type) { |
660 | case IPL_TYPE_CCW: | |
a0443fbb HB |
661 | if (MACHINE_IS_VM) |
662 | rc = sysfs_create_group(&ipl_kset->kobj, | |
663 | &ipl_ccw_attr_group_vm); | |
664 | else | |
665 | rc = sysfs_create_group(&ipl_kset->kobj, | |
666 | &ipl_ccw_attr_group_lpar); | |
99ca4e58 | 667 | break; |
87fd22e0 | 668 | case IPL_TYPE_ECKD: |
673752a8 | 669 | case IPL_TYPE_ECKD_DUMP: |
87fd22e0 SS |
670 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_eckd_attr_group); |
671 | break; | |
99ca4e58 MH |
672 | case IPL_TYPE_FCP: |
673 | case IPL_TYPE_FCP_DUMP: | |
22d557ab | 674 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group); |
99ca4e58 | 675 | break; |
3737e8ee | 676 | case IPL_TYPE_NVME: |
d70e38cb | 677 | case IPL_TYPE_NVME_DUMP: |
3737e8ee JH |
678 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_nvme_attr_group); |
679 | break; | |
99ca4e58 | 680 | default: |
99ca4e58 MH |
681 | break; |
682 | } | |
683 | out: | |
684 | if (rc) | |
685 | panic("ipl_init failed: rc = %i\n", rc); | |
686 | ||
687 | return 0; | |
688 | } | |
689 | ||
2bc89b5e HC |
690 | static struct shutdown_action __refdata ipl_action = { |
691 | .name = SHUTDOWN_ACTION_IPL_STR, | |
692 | .fn = ipl_run, | |
693 | .init = ipl_init, | |
694 | }; | |
99ca4e58 | 695 | |
ff6b8ea6 | 696 | /* |
99ca4e58 | 697 | * reipl shutdown action: Reboot Linux on shutdown. |
ff6b8ea6 MH |
698 | */ |
699 | ||
a0443fbb HB |
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 | ||
49698745 | 706 | ipl_block_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb); |
a0443fbb HB |
707 | return sprintf(page, "%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 | ||
86c74d86 MS |
729 | memset(ipb->ccw.vm_parm, 0, DIAG308_VMPARM_SIZE); |
730 | ipb->ccw.vm_parm_len = ip_len; | |
a0443fbb | 731 | if (ip_len > 0) { |
5f1207fb | 732 | ipb->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_VP; |
86c74d86 MS |
733 | memcpy(ipb->ccw.vm_parm, buf, ip_len); |
734 | ASCEBC(ipb->ccw.vm_parm, ip_len); | |
a0443fbb | 735 | } else { |
5f1207fb | 736 | ipb->ccw.vm_flags &= ~IPL_PB0_CCW_VM_FLAG_VP; |
a0443fbb HB |
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(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(reipl_block_nss, 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(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(reipl_block_ccw, 64, buf, len); | |
768 | } | |
769 | ||
770 | static struct kobj_attribute sys_reipl_nss_vmparm_attr = | |
a70f7276 SS |
771 | __ATTR(parm, 0644, reipl_nss_vmparm_show, |
772 | reipl_nss_vmparm_store); | |
a0443fbb | 773 | static struct kobj_attribute sys_reipl_ccw_vmparm_attr = |
a70f7276 SS |
774 | __ATTR(parm, 0644, reipl_ccw_vmparm_show, |
775 | reipl_ccw_vmparm_store); | |
a0443fbb | 776 | |
ff6b8ea6 MH |
777 | /* FCP reipl device attributes */ |
778 | ||
2c3c8bea | 779 | static ssize_t reipl_fcp_scpdata_read(struct file *filp, struct kobject *kobj, |
684d2fd4 HB |
780 | struct bin_attribute *attr, |
781 | char *buf, loff_t off, size_t count) | |
782 | { | |
86c74d86 MS |
783 | size_t size = reipl_block_fcp->fcp.scp_data_len; |
784 | void *scp_data = reipl_block_fcp->fcp.scp_data; | |
684d2fd4 HB |
785 | |
786 | return memory_read_from_buffer(buf, count, &off, scp_data, size); | |
787 | } | |
788 | ||
2c3c8bea | 789 | static ssize_t reipl_fcp_scpdata_write(struct file *filp, struct kobject *kobj, |
684d2fd4 HB |
790 | struct bin_attribute *attr, |
791 | char *buf, loff_t off, size_t count) | |
792 | { | |
e0bedada | 793 | size_t scpdata_len = count; |
684d2fd4 | 794 | size_t padding; |
684d2fd4 | 795 | |
684d2fd4 | 796 | |
e0bedada SO |
797 | if (off) |
798 | return -EINVAL; | |
684d2fd4 | 799 | |
86c74d86 | 800 | memcpy(reipl_block_fcp->fcp.scp_data, buf, count); |
684d2fd4 HB |
801 | if (scpdata_len % 8) { |
802 | padding = 8 - (scpdata_len % 8); | |
86c74d86 | 803 | memset(reipl_block_fcp->fcp.scp_data + scpdata_len, |
684d2fd4 HB |
804 | 0, padding); |
805 | scpdata_len += padding; | |
806 | } | |
807 | ||
5f1207fb MS |
808 | reipl_block_fcp->hdr.len = IPL_BP_FCP_LEN + scpdata_len; |
809 | reipl_block_fcp->fcp.len = IPL_BP0_FCP_LEN + scpdata_len; | |
86c74d86 | 810 | reipl_block_fcp->fcp.scp_data_len = scpdata_len; |
684d2fd4 HB |
811 | |
812 | return count; | |
813 | } | |
22d557ab | 814 | static struct bin_attribute sys_reipl_fcp_scp_data_attr = |
a70f7276 | 815 | __BIN_ATTR(scp_data, 0644, reipl_fcp_scpdata_read, |
e0bedada | 816 | reipl_fcp_scpdata_write, DIAG308_SCPDATA_SIZE); |
684d2fd4 | 817 | |
22d557ab SO |
818 | static struct bin_attribute *reipl_fcp_bin_attrs[] = { |
819 | &sys_reipl_fcp_scp_data_attr, | |
820 | NULL, | |
684d2fd4 HB |
821 | }; |
822 | ||
eda4ddf7 | 823 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%llx\n", |
86c74d86 | 824 | reipl_block_fcp->fcp.wwpn); |
eda4ddf7 | 825 | DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%llx\n", |
86c74d86 | 826 | reipl_block_fcp->fcp.lun); |
ff6b8ea6 | 827 | DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n", |
86c74d86 | 828 | reipl_block_fcp->fcp.bootprog); |
ff6b8ea6 | 829 | DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n", |
86c74d86 | 830 | reipl_block_fcp->fcp.br_lba); |
ff6b8ea6 | 831 | DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", |
86c74d86 | 832 | reipl_block_fcp->fcp.devno); |
ff6b8ea6 | 833 | |
a0443fbb HB |
834 | static void reipl_get_ascii_loadparm(char *loadparm, |
835 | struct ipl_parameter_block *ibp) | |
03a4d208 | 836 | { |
5f1207fb | 837 | memcpy(loadparm, ibp->common.loadparm, LOADPARM_LEN); |
03a4d208 MH |
838 | EBCASC(loadparm, LOADPARM_LEN); |
839 | loadparm[LOADPARM_LEN] = 0; | |
1d802e24 | 840 | strim(loadparm); |
03a4d208 MH |
841 | } |
842 | ||
a0443fbb HB |
843 | static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb, |
844 | char *page) | |
03a4d208 MH |
845 | { |
846 | char buf[LOADPARM_LEN + 1]; | |
847 | ||
a0443fbb | 848 | reipl_get_ascii_loadparm(buf, ipb); |
03a4d208 MH |
849 | return sprintf(page, "%s\n", buf); |
850 | } | |
851 | ||
a0443fbb HB |
852 | static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, |
853 | const char *buf, size_t len) | |
03a4d208 MH |
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(buf[i]) || (buf[i] == ' ') || | |
867 | (buf[i] == '.')) | |
868 | continue; | |
869 | return -EINVAL; | |
870 | } | |
871 | /* initialize loadparm with blanks */ | |
5f1207fb | 872 | memset(ipb->common.loadparm, ' ', LOADPARM_LEN); |
03a4d208 | 873 | /* copy and convert to ebcdic */ |
5f1207fb MS |
874 | memcpy(ipb->common.loadparm, buf, lp_len); |
875 | ASCEBC(ipb->common.loadparm, LOADPARM_LEN); | |
876 | ipb->common.flags |= IPL_PB0_FLAG_LOADPARM; | |
03a4d208 MH |
877 | return len; |
878 | } | |
879 | ||
c676aac6 SS |
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); | |
6bb361d5 | 900 | DEFINE_GENERIC_LOADPARM(eckd); |
69928601 | 901 | |
1a2ae03b GS |
902 | static ssize_t reipl_fcp_clear_show(struct kobject *kobj, |
903 | struct kobj_attribute *attr, char *page) | |
904 | { | |
905 | return sprintf(page, "%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 | { | |
d9b25bdf | 912 | if (kstrtobool(buf, &reipl_fcp_clear) < 0) |
1a2ae03b GS |
913 | return -EINVAL; |
914 | return len; | |
915 | } | |
916 | ||
69928601 MH |
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, | |
22d557ab | 929 | .bin_attrs = reipl_fcp_bin_attrs, |
69928601 MH |
930 | }; |
931 | ||
1a2ae03b GS |
932 | static struct kobj_attribute sys_reipl_fcp_clear_attr = |
933 | __ATTR(clear, 0644, reipl_fcp_clear_show, reipl_fcp_clear_store); | |
934 | ||
23a457b8 JH |
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(buf, count, &off, scp_data, 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 = | |
a70f7276 | 973 | __BIN_ATTR(scp_data, 0644, reipl_nvme_scpdata_read, |
23a457b8 JH |
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 | ||
23a457b8 JH |
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 | ||
5627b922 GS |
1004 | static ssize_t reipl_nvme_clear_show(struct kobject *kobj, |
1005 | struct kobj_attribute *attr, char *page) | |
1006 | { | |
1007 | return sprintf(page, "%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 | { | |
d9b25bdf | 1014 | if (kstrtobool(buf, &reipl_nvme_clear) < 0) |
5627b922 GS |
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 | ||
69928601 | 1022 | /* CCW reipl device attributes */ |
86c74d86 | 1023 | DEFINE_IPL_CCW_ATTR_RW(reipl_ccw, device, reipl_block_ccw->ccw); |
69928601 | 1024 | |
1a2ae03b GS |
1025 | static ssize_t reipl_ccw_clear_show(struct kobject *kobj, |
1026 | struct kobj_attribute *attr, char *page) | |
1027 | { | |
1028 | return sprintf(page, "%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 | { | |
d9b25bdf | 1035 | if (kstrtobool(buf, &reipl_ccw_clear) < 0) |
1a2ae03b GS |
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 | ||
a0443fbb HB |
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, | |
1a2ae03b | 1047 | &sys_reipl_ccw_clear_attr.attr, |
a0443fbb HB |
1048 | NULL, |
1049 | }; | |
03a4d208 | 1050 | |
a0443fbb | 1051 | static struct attribute *reipl_ccw_attrs_lpar[] = { |
ff6b8ea6 | 1052 | &sys_reipl_ccw_device_attr.attr, |
03a4d208 | 1053 | &sys_reipl_ccw_loadparm_attr.attr, |
1a2ae03b | 1054 | &sys_reipl_ccw_clear_attr.attr, |
ff6b8ea6 MH |
1055 | NULL, |
1056 | }; | |
1057 | ||
a0443fbb | 1058 | static struct attribute_group reipl_ccw_attr_group_vm = { |
ff6b8ea6 | 1059 | .name = IPL_CCW_STR, |
a0443fbb HB |
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, | |
ff6b8ea6 MH |
1066 | }; |
1067 | ||
87fd22e0 SS |
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(buf, count, &off, scp_data, 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 = | |
a70f7276 | 1106 | __BIN_ATTR(scp_data, 0644, reipl_eckd_scpdata_read, |
87fd22e0 SS |
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, | |
6bb361d5 | 1122 | &sys_reipl_eckd_loadparm_attr.attr, |
87fd22e0 SS |
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(page, "%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 | { | |
c3130944 | 1141 | if (kstrtobool(buf, &reipl_eckd_clear) < 0) |
87fd22e0 SS |
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); | |
fe355b7f HY |
1148 | |
1149 | /* NSS reipl device attributes */ | |
a0443fbb HB |
1150 | static void reipl_get_ascii_nss_name(char *dst, |
1151 | struct ipl_parameter_block *ipb) | |
1152 | { | |
86c74d86 | 1153 | memcpy(dst, ipb->ccw.nss_name, NSS_NAME_SIZE); |
a0443fbb HB |
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(nss_name, reipl_block_nss); | |
1164 | return sprintf(page, "%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--; | |
fe355b7f | 1177 | |
a0443fbb HB |
1178 | if (nss_len > NSS_NAME_SIZE) |
1179 | return -EINVAL; | |
1180 | ||
86c74d86 | 1181 | memset(reipl_block_nss->ccw.nss_name, 0x40, NSS_NAME_SIZE); |
a0443fbb | 1182 | if (nss_len > 0) { |
5f1207fb | 1183 | reipl_block_nss->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_NSS; |
86c74d86 MS |
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); | |
a0443fbb | 1187 | } else { |
5f1207fb | 1188 | reipl_block_nss->ccw.vm_flags &= ~IPL_PB0_CCW_VM_FLAG_NSS; |
a0443fbb HB |
1189 | } |
1190 | ||
1191 | return len; | |
1192 | } | |
1193 | ||
1194 | static struct kobj_attribute sys_reipl_nss_name_attr = | |
a70f7276 SS |
1195 | __ATTR(name, 0644, reipl_nss_name_show, |
1196 | reipl_nss_name_store); | |
a0443fbb | 1197 | |
fe355b7f HY |
1198 | static struct attribute *reipl_nss_attrs[] = { |
1199 | &sys_reipl_nss_name_attr.attr, | |
a0443fbb HB |
1200 | &sys_reipl_nss_loadparm_attr.attr, |
1201 | &sys_reipl_nss_vmparm_attr.attr, | |
fe355b7f HY |
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 | ||
3b967847 | 1210 | void set_os_info_reipl_block(void) |
4857d4bb | 1211 | { |
4857d4bb | 1212 | os_info_entry_add(OS_INFO_REIPL_BLOCK, reipl_block_actual, |
3b967847 | 1213 | reipl_block_actual->hdr.len); |
4857d4bb MH |
1214 | } |
1215 | ||
ff6b8ea6 MH |
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: | |
3b967847 | 1225 | reipl_block_actual = reipl_block_ccw; |
ff6b8ea6 | 1226 | break; |
87fd22e0 SS |
1227 | case IPL_TYPE_ECKD: |
1228 | reipl_block_actual = reipl_block_eckd; | |
1229 | break; | |
ff6b8ea6 | 1230 | case IPL_TYPE_FCP: |
3b967847 | 1231 | reipl_block_actual = reipl_block_fcp; |
411ed322 | 1232 | break; |
23a457b8 JH |
1233 | case IPL_TYPE_NVME: |
1234 | reipl_block_actual = reipl_block_nvme; | |
1235 | break; | |
fe355b7f | 1236 | case IPL_TYPE_NSS: |
3b967847 | 1237 | reipl_block_actual = reipl_block_nss; |
411ed322 | 1238 | break; |
ff6b8ea6 | 1239 | default: |
96c0cdbc | 1240 | break; |
ff6b8ea6 MH |
1241 | } |
1242 | reipl_type = type; | |
1243 | return 0; | |
1244 | } | |
1245 | ||
9b949165 GKH |
1246 | static ssize_t reipl_type_show(struct kobject *kobj, |
1247 | struct kobj_attribute *attr, char *page) | |
ff6b8ea6 MH |
1248 | { |
1249 | return sprintf(page, "%s\n", ipl_type_str(reipl_type)); | |
1250 | } | |
1251 | ||
9b949165 GKH |
1252 | static ssize_t reipl_type_store(struct kobject *kobj, |
1253 | struct kobj_attribute *attr, | |
1254 | const char *buf, size_t len) | |
ff6b8ea6 MH |
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); | |
87fd22e0 SS |
1260 | else if (strncmp(buf, IPL_ECKD_STR, strlen(IPL_ECKD_STR)) == 0) |
1261 | rc = reipl_set_type(IPL_TYPE_ECKD); | |
ff6b8ea6 MH |
1262 | else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) |
1263 | rc = reipl_set_type(IPL_TYPE_FCP); | |
23a457b8 JH |
1264 | else if (strncmp(buf, IPL_NVME_STR, strlen(IPL_NVME_STR)) == 0) |
1265 | rc = reipl_set_type(IPL_TYPE_NVME); | |
fe355b7f HY |
1266 | else if (strncmp(buf, IPL_NSS_STR, strlen(IPL_NSS_STR)) == 0) |
1267 | rc = reipl_set_type(IPL_TYPE_NSS); | |
ff6b8ea6 MH |
1268 | return (rc != 0) ? rc : len; |
1269 | } | |
1270 | ||
9b949165 | 1271 | static struct kobj_attribute reipl_type_attr = |
99ca4e58 | 1272 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); |
ff6b8ea6 | 1273 | |
d91885be | 1274 | static struct kset *reipl_kset; |
684d2fd4 | 1275 | static struct kset *reipl_fcp_kset; |
23a457b8 | 1276 | static struct kset *reipl_nvme_kset; |
87fd22e0 | 1277 | static struct kset *reipl_eckd_kset; |
ff6b8ea6 | 1278 | |
2c2df118 | 1279 | static void __reipl_run(void *unused) |
ff6b8ea6 | 1280 | { |
96c0cdbc VG |
1281 | switch (reipl_type) { |
1282 | case IPL_TYPE_CCW: | |
ff6b8ea6 | 1283 | diag308(DIAG308_SET, reipl_block_ccw); |
1a2ae03b GS |
1284 | if (reipl_ccw_clear) |
1285 | diag308(DIAG308_LOAD_CLEAR, NULL); | |
1286 | else | |
1287 | diag308(DIAG308_LOAD_NORMAL_DUMP, NULL); | |
ff6b8ea6 | 1288 | break; |
87fd22e0 SS |
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; | |
96c0cdbc | 1296 | case IPL_TYPE_FCP: |
ff6b8ea6 | 1297 | diag308(DIAG308_SET, reipl_block_fcp); |
1a2ae03b GS |
1298 | if (reipl_fcp_clear) |
1299 | diag308(DIAG308_LOAD_CLEAR, NULL); | |
1300 | else | |
1301 | diag308(DIAG308_LOAD_NORMAL, NULL); | |
ff6b8ea6 | 1302 | break; |
23a457b8 JH |
1303 | case IPL_TYPE_NVME: |
1304 | diag308(DIAG308_SET, reipl_block_nvme); | |
5627b922 GS |
1305 | if (reipl_nvme_clear) |
1306 | diag308(DIAG308_LOAD_CLEAR, NULL); | |
1307 | else | |
1308 | diag308(DIAG308_LOAD_NORMAL, NULL); | |
23a457b8 | 1309 | break; |
96c0cdbc | 1310 | case IPL_TYPE_NSS: |
a0443fbb | 1311 | diag308(DIAG308_SET, reipl_block_nss); |
0599eead | 1312 | diag308(DIAG308_LOAD_CLEAR, NULL); |
a0443fbb | 1313 | break; |
96c0cdbc | 1314 | case IPL_TYPE_UNKNOWN: |
0599eead | 1315 | diag308(DIAG308_LOAD_CLEAR, NULL); |
ff6b8ea6 | 1316 | break; |
96c0cdbc | 1317 | case IPL_TYPE_FCP_DUMP: |
d70e38cb | 1318 | case IPL_TYPE_NVME_DUMP: |
e2d2a296 | 1319 | case IPL_TYPE_ECKD_DUMP: |
411ed322 | 1320 | break; |
ff6b8ea6 | 1321 | } |
98587c2d | 1322 | disabled_wait(); |
ff6b8ea6 MH |
1323 | } |
1324 | ||
2c2df118 HC |
1325 | static void reipl_run(struct shutdown_trigger *trigger) |
1326 | { | |
8b646bd7 | 1327 | smp_call_ipl_cpu(__reipl_run, NULL); |
2c2df118 HC |
1328 | } |
1329 | ||
a0443fbb | 1330 | static void reipl_block_ccw_init(struct ipl_parameter_block *ipb) |
ff6b8ea6 | 1331 | { |
5f1207fb | 1332 | ipb->hdr.len = IPL_BP_CCW_LEN; |
a0443fbb | 1333 | ipb->hdr.version = IPL_PARM_BLOCK_VERSION; |
5f1207fb MS |
1334 | ipb->pb0_hdr.len = IPL_BP0_CCW_LEN; |
1335 | ipb->pb0_hdr.pbt = IPL_PBT_CCW; | |
a0443fbb | 1336 | } |
ff6b8ea6 | 1337 | |
a0443fbb HB |
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) | |
5f1207fb | 1343 | memcpy(ipb->ccw.loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN); |
a0443fbb HB |
1344 | else |
1345 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | |
5f1207fb MS |
1346 | memset(ipb->ccw.loadparm, 0x40, LOADPARM_LEN); |
1347 | ipb->ccw.flags = IPL_PB0_FLAG_LOADPARM; | |
a0443fbb HB |
1348 | |
1349 | /* VM PARM */ | |
a0832b3a | 1350 | if (MACHINE_IS_VM && ipl_block_valid && |
5f1207fb | 1351 | (ipl_block.ccw.vm_flags & IPL_PB0_CCW_VM_FLAG_VP)) { |
a0443fbb | 1352 | |
5f1207fb | 1353 | ipb->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_VP; |
86c74d86 MS |
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); | |
a0443fbb | 1357 | } |
ff6b8ea6 MH |
1358 | } |
1359 | ||
99ca4e58 | 1360 | static int __init reipl_nss_init(void) |
ff6b8ea6 MH |
1361 | { |
1362 | int rc; | |
1363 | ||
99ca4e58 MH |
1364 | if (!MACHINE_IS_VM) |
1365 | return 0; | |
a0443fbb HB |
1366 | |
1367 | reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL); | |
1368 | if (!reipl_block_nss) | |
1369 | return -ENOMEM; | |
1370 | ||
99ca4e58 | 1371 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); |
fe355b7f HY |
1372 | if (rc) |
1373 | return rc; | |
a0443fbb HB |
1374 | |
1375 | reipl_block_ccw_init(reipl_block_nss); | |
fe355b7f HY |
1376 | reipl_capabilities |= IPL_TYPE_NSS; |
1377 | return 0; | |
1378 | } | |
1379 | ||
ff6b8ea6 MH |
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; | |
a0443fbb | 1387 | |
d485235b VG |
1388 | rc = sysfs_create_group(&reipl_kset->kobj, |
1389 | MACHINE_IS_VM ? &reipl_ccw_attr_group_vm | |
1390 | : &reipl_ccw_attr_group_lpar); | |
a0443fbb HB |
1391 | if (rc) |
1392 | return rc; | |
1393 | ||
1394 | reipl_block_ccw_init(reipl_block_ccw); | |
1395 | if (ipl_info.type == IPL_TYPE_CCW) { | |
86c74d86 MS |
1396 | reipl_block_ccw->ccw.ssid = ipl_block.ccw.ssid; |
1397 | reipl_block_ccw->ccw.devno = ipl_block.ccw.devno; | |
a0443fbb HB |
1398 | reipl_block_ccw_fill_parms(reipl_block_ccw); |
1399 | } | |
1400 | ||
ff6b8ea6 MH |
1401 | reipl_capabilities |= IPL_TYPE_CCW; |
1402 | return 0; | |
1403 | } | |
1404 | ||
1405 | static int __init reipl_fcp_init(void) | |
1406 | { | |
1407 | int rc; | |
1408 | ||
ff6b8ea6 MH |
1409 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
1410 | if (!reipl_block_fcp) | |
1411 | return -ENOMEM; | |
684d2fd4 HB |
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 | &reipl_kset->kobj); | |
798620fb | 1416 | if (!reipl_fcp_kset) { |
684d2fd4 HB |
1417 | free_page((unsigned long) reipl_block_fcp); |
1418 | return -ENOMEM; | |
1419 | } | |
1420 | ||
1421 | rc = sysfs_create_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group); | |
1a2ae03b GS |
1422 | if (rc) |
1423 | goto out1; | |
1424 | ||
1425 | if (test_facility(141)) { | |
1426 | rc = sysfs_create_file(&reipl_fcp_kset->kobj, | |
1427 | &sys_reipl_fcp_clear_attr.attr); | |
1428 | if (rc) | |
1429 | goto out2; | |
5627b922 | 1430 | } else { |
1a2ae03b | 1431 | reipl_fcp_clear = true; |
5627b922 | 1432 | } |
684d2fd4 | 1433 | |
69928601 | 1434 | if (ipl_info.type == IPL_TYPE_FCP) { |
bdbfe185 | 1435 | memcpy(reipl_block_fcp, &ipl_block, sizeof(ipl_block)); |
69928601 MH |
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 | */ | |
5f1207fb | 1441 | memcpy(reipl_block_fcp->fcp.loadparm, sclp_ipl_info.loadparm, |
69928601 MH |
1442 | LOADPARM_LEN); |
1443 | } else { | |
5f1207fb | 1444 | reipl_block_fcp->hdr.len = IPL_BP_FCP_LEN; |
ff6b8ea6 | 1445 | reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; |
5f1207fb MS |
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; | |
ff6b8ea6 MH |
1449 | } |
1450 | reipl_capabilities |= IPL_TYPE_FCP; | |
1451 | return 0; | |
1a2ae03b GS |
1452 | |
1453 | out2: | |
1454 | sysfs_remove_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group); | |
1455 | out1: | |
1456 | kset_unregister(reipl_fcp_kset); | |
1457 | free_page((unsigned long) reipl_block_fcp); | |
1458 | return rc; | |
ff6b8ea6 MH |
1459 | } |
1460 | ||
23a457b8 JH |
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 | &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(&reipl_nvme_kset->kobj, &reipl_nvme_attr_group); | |
5627b922 GS |
1478 | if (rc) |
1479 | goto out1; | |
1480 | ||
1481 | if (test_facility(141)) { | |
1482 | rc = sysfs_create_file(&reipl_nvme_kset->kobj, | |
1483 | &sys_reipl_nvme_clear_attr.attr); | |
1484 | if (rc) | |
1485 | goto out2; | |
1486 | } else { | |
1487 | reipl_nvme_clear = true; | |
23a457b8 JH |
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; | |
5627b922 GS |
1508 | |
1509 | out2: | |
1510 | sysfs_remove_group(&reipl_nvme_kset->kobj, &reipl_nvme_attr_group); | |
1511 | out1: | |
1512 | kset_unregister(reipl_nvme_kset); | |
1513 | free_page((unsigned long) reipl_block_nvme); | |
1514 | return rc; | |
23a457b8 JH |
1515 | } |
1516 | ||
87fd22e0 SS |
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 | &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(&reipl_eckd_kset->kobj, &reipl_eckd_attr_group); | |
1537 | if (rc) | |
1538 | goto out1; | |
1539 | ||
1540 | if (test_facility(141)) { | |
1541 | rc = sysfs_create_file(&reipl_eckd_kset->kobj, | |
1542 | &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(&reipl_eckd_kset->kobj, &reipl_eckd_attr_group); | |
1563 | out1: | |
1564 | kset_unregister(reipl_eckd_kset); | |
1565 | free_page((unsigned long)reipl_block_eckd); | |
1566 | return rc; | |
1567 | } | |
1568 | ||
4857d4bb MH |
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 | */ | |
5f1207fb | 1581 | if (reipl_block->pb0_hdr.pbt == IPL_PBT_FCP) { |
4857d4bb MH |
1582 | memcpy(reipl_block_fcp, reipl_block, size); |
1583 | reipl_type = IPL_TYPE_FCP; | |
23a457b8 JH |
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; | |
5f1207fb | 1587 | } else if (reipl_block->pb0_hdr.pbt == IPL_PBT_CCW) { |
4857d4bb MH |
1588 | memcpy(reipl_block_ccw, reipl_block, size); |
1589 | reipl_type = IPL_TYPE_CCW; | |
87fd22e0 SS |
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; | |
4857d4bb MH |
1593 | } |
1594 | out: | |
1595 | return reipl_set_type(reipl_type); | |
1596 | } | |
1597 | ||
2bc89b5e | 1598 | static int __init reipl_init(void) |
ff6b8ea6 MH |
1599 | { |
1600 | int rc; | |
1601 | ||
f62ed9e3 | 1602 | reipl_kset = kset_create_and_add("reipl", NULL, firmware_kobj); |
d91885be GKH |
1603 | if (!reipl_kset) |
1604 | return -ENOMEM; | |
1605 | rc = sysfs_create_file(&reipl_kset->kobj, &reipl_type_attr.attr); | |
ff6b8ea6 | 1606 | if (rc) { |
d91885be | 1607 | kset_unregister(reipl_kset); |
ff6b8ea6 MH |
1608 | return rc; |
1609 | } | |
1610 | rc = reipl_ccw_init(); | |
87fd22e0 SS |
1611 | if (rc) |
1612 | return rc; | |
1613 | rc = reipl_eckd_init(); | |
ff6b8ea6 MH |
1614 | if (rc) |
1615 | return rc; | |
1616 | rc = reipl_fcp_init(); | |
23a457b8 JH |
1617 | if (rc) |
1618 | return rc; | |
1619 | rc = reipl_nvme_init(); | |
fe355b7f HY |
1620 | if (rc) |
1621 | return rc; | |
1622 | rc = reipl_nss_init(); | |
ff6b8ea6 MH |
1623 | if (rc) |
1624 | return rc; | |
4857d4bb | 1625 | return reipl_type_init(); |
ff6b8ea6 MH |
1626 | } |
1627 | ||
2bc89b5e HC |
1628 | static struct shutdown_action __refdata reipl_action = { |
1629 | .name = SHUTDOWN_ACTION_REIPL_STR, | |
1630 | .fn = reipl_run, | |
1631 | .init = reipl_init, | |
1632 | }; | |
99ca4e58 MH |
1633 | |
1634 | /* | |
1635 | * dump shutdown action: Dump Linux on shutdown. | |
1636 | */ | |
1637 | ||
1638 | /* FCP dump device attributes */ | |
1639 | ||
eda4ddf7 | 1640 | DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%llx\n", |
86c74d86 | 1641 | dump_block_fcp->fcp.wwpn); |
eda4ddf7 | 1642 | DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%llx\n", |
86c74d86 | 1643 | dump_block_fcp->fcp.lun); |
99ca4e58 | 1644 | DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n", |
86c74d86 | 1645 | dump_block_fcp->fcp.bootprog); |
99ca4e58 | 1646 | DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", |
86c74d86 | 1647 | dump_block_fcp->fcp.br_lba); |
99ca4e58 | 1648 | DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", |
86c74d86 | 1649 | dump_block_fcp->fcp.devno); |
99ca4e58 MH |
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 | ||
d70e38cb JH |
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 | ||
e2d2a296 SS |
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 = | |
a70f7276 | 1697 | __ATTR(br_chr, 0644, eckd_dump_br_chr_show, eckd_dump_br_chr_store); |
e2d2a296 SS |
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 | ||
99ca4e58 | 1711 | /* CCW dump device attributes */ |
86c74d86 | 1712 | DEFINE_IPL_CCW_ATTR_RW(dump_ccw, device, dump_block_ccw->ccw); |
99ca4e58 MH |
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; | |
99ca4e58 MH |
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(page, "%s\n", dump_type_str(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(DUMP_TYPE_NONE); | |
1748 | else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) | |
1749 | rc = dump_set_type(DUMP_TYPE_CCW); | |
e2d2a296 SS |
1750 | else if (strncmp(buf, DUMP_ECKD_STR, strlen(DUMP_ECKD_STR)) == 0) |
1751 | rc = dump_set_type(DUMP_TYPE_ECKD); | |
99ca4e58 MH |
1752 | else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) |
1753 | rc = dump_set_type(DUMP_TYPE_FCP); | |
d70e38cb JH |
1754 | else if (strncmp(buf, DUMP_NVME_STR, strlen(DUMP_NVME_STR)) == 0) |
1755 | rc = dump_set_type(DUMP_TYPE_NVME); | |
99ca4e58 MH |
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 | ||
0894b3ae MH |
1764 | static void diag308_dump(void *dump_block) |
1765 | { | |
1766 | diag308(DIAG308_SET, dump_block); | |
1767 | while (1) { | |
0599eead | 1768 | if (diag308(DIAG308_LOAD_NORMAL_DUMP, NULL) != 0x302) |
0894b3ae | 1769 | break; |
e0d62dcb | 1770 | udelay(USEC_PER_SEC); |
0894b3ae MH |
1771 | } |
1772 | } | |
1773 | ||
2c2df118 | 1774 | static void __dump_run(void *unused) |
99ca4e58 | 1775 | { |
96c0cdbc VG |
1776 | switch (dump_type) { |
1777 | case DUMP_TYPE_CCW: | |
0894b3ae | 1778 | diag308_dump(dump_block_ccw); |
99ca4e58 | 1779 | break; |
e2d2a296 SS |
1780 | case DUMP_TYPE_ECKD: |
1781 | diag308_dump(dump_block_eckd); | |
1782 | break; | |
96c0cdbc | 1783 | case DUMP_TYPE_FCP: |
0894b3ae | 1784 | diag308_dump(dump_block_fcp); |
99ca4e58 | 1785 | break; |
d70e38cb JH |
1786 | case DUMP_TYPE_NVME: |
1787 | diag308_dump(dump_block_nvme); | |
1788 | break; | |
2c2df118 HC |
1789 | default: |
1790 | break; | |
99ca4e58 | 1791 | } |
2c2df118 HC |
1792 | } |
1793 | ||
1794 | static void dump_run(struct shutdown_trigger *trigger) | |
1795 | { | |
96c0cdbc | 1796 | if (dump_type == DUMP_TYPE_NONE) |
2c2df118 HC |
1797 | return; |
1798 | smp_send_stop(); | |
8b646bd7 | 1799 | smp_call_ipl_cpu(__dump_run, NULL); |
99ca4e58 MH |
1800 | } |
1801 | ||
ff6b8ea6 MH |
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; | |
d91885be | 1809 | rc = sysfs_create_group(&dump_kset->kobj, &dump_ccw_attr_group); |
ff6b8ea6 MH |
1810 | if (rc) { |
1811 | free_page((unsigned long)dump_block_ccw); | |
1812 | return rc; | |
1813 | } | |
5f1207fb | 1814 | dump_block_ccw->hdr.len = IPL_BP_CCW_LEN; |
ff6b8ea6 | 1815 | dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; |
5f1207fb MS |
1816 | dump_block_ccw->ccw.len = IPL_BP0_CCW_LEN; |
1817 | dump_block_ccw->ccw.pbt = IPL_PBT_CCW; | |
411ed322 | 1818 | dump_capabilities |= DUMP_TYPE_CCW; |
ff6b8ea6 MH |
1819 | return 0; |
1820 | } | |
1821 | ||
ff6b8ea6 MH |
1822 | static int __init dump_fcp_init(void) |
1823 | { | |
1824 | int rc; | |
1825 | ||
05dd2530 | 1826 | if (!sclp_ipl_info.has_dump) |
ff6b8ea6 | 1827 | return 0; /* LDIPL DUMP is not installed */ |
ff6b8ea6 MH |
1828 | dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
1829 | if (!dump_block_fcp) | |
1830 | return -ENOMEM; | |
d91885be | 1831 | rc = sysfs_create_group(&dump_kset->kobj, &dump_fcp_attr_group); |
ff6b8ea6 MH |
1832 | if (rc) { |
1833 | free_page((unsigned long)dump_block_fcp); | |
1834 | return rc; | |
1835 | } | |
5f1207fb | 1836 | dump_block_fcp->hdr.len = IPL_BP_FCP_LEN; |
ff6b8ea6 | 1837 | dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION; |
5f1207fb MS |
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; | |
411ed322 | 1841 | dump_capabilities |= DUMP_TYPE_FCP; |
ff6b8ea6 MH |
1842 | return 0; |
1843 | } | |
1844 | ||
d70e38cb JH |
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(&dump_kset->kobj, &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 | ||
e2d2a296 SS |
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(&dump_kset->kobj, &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 | ||
2bc89b5e | 1891 | static int __init dump_init(void) |
ff6b8ea6 MH |
1892 | { |
1893 | int rc; | |
1894 | ||
f62ed9e3 | 1895 | dump_kset = kset_create_and_add("dump", NULL, firmware_kobj); |
d91885be GKH |
1896 | if (!dump_kset) |
1897 | return -ENOMEM; | |
99ca4e58 | 1898 | rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr); |
ff6b8ea6 | 1899 | if (rc) { |
d91885be | 1900 | kset_unregister(dump_kset); |
ff6b8ea6 MH |
1901 | return rc; |
1902 | } | |
1903 | rc = dump_ccw_init(); | |
e2d2a296 SS |
1904 | if (rc) |
1905 | return rc; | |
1906 | rc = dump_eckd_init(); | |
ff6b8ea6 MH |
1907 | if (rc) |
1908 | return rc; | |
1909 | rc = dump_fcp_init(); | |
d70e38cb JH |
1910 | if (rc) |
1911 | return rc; | |
1912 | rc = dump_nvme_init(); | |
ff6b8ea6 MH |
1913 | if (rc) |
1914 | return rc; | |
411ed322 | 1915 | dump_set_type(DUMP_TYPE_NONE); |
ff6b8ea6 MH |
1916 | return 0; |
1917 | } | |
1918 | ||
2bc89b5e HC |
1919 | static struct shutdown_action __refdata dump_action = { |
1920 | .name = SHUTDOWN_ACTION_DUMP_STR, | |
1921 | .fn = dump_run, | |
1922 | .init = dump_init, | |
1923 | }; | |
99ca4e58 | 1924 | |
099b7651 FM |
1925 | static void dump_reipl_run(struct shutdown_trigger *trigger) |
1926 | { | |
4df29d2b | 1927 | struct lowcore *abs_lc; |
fbe76568 HC |
1928 | unsigned int csum; |
1929 | ||
31e9ccc6 MZ |
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)); | |
90b3baa2 HC |
1944 | csum = (__force unsigned int) |
1945 | csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0); | |
2154e0b3 | 1946 | abs_lc = get_abs_lowcore(); |
2facd5d3 | 1947 | abs_lc->ipib = __pa(reipl_block_actual); |
4df29d2b | 1948 | abs_lc->ipib_checksum = csum; |
2154e0b3 | 1949 | put_abs_lowcore(abs_lc); |
099b7651 FM |
1950 | dump_run(trigger); |
1951 | } | |
1952 | ||
099b7651 FM |
1953 | static struct shutdown_action __refdata dump_reipl_action = { |
1954 | .name = SHUTDOWN_ACTION_DUMP_REIPL_STR, | |
1955 | .fn = dump_reipl_run, | |
099b7651 FM |
1956 | }; |
1957 | ||
99ca4e58 MH |
1958 | /* |
1959 | * vmcmd shutdown action: Trigger vm command on shutdown. | |
1960 | */ | |
1961 | ||
1962 | static char vmcmd_on_reboot[128]; | |
1963 | static char vmcmd_on_panic[128]; | |
1964 | static char vmcmd_on_halt[128]; | |
1965 | static char vmcmd_on_poff[128]; | |
7dd6b334 | 1966 | static char vmcmd_on_restart[128]; |
99ca4e58 MH |
1967 | |
1968 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot); | |
1969 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic); | |
1970 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt); | |
1971 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff); | |
7dd6b334 | 1972 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_restart, "%s\n", "%s\n", vmcmd_on_restart); |
99ca4e58 MH |
1973 | |
1974 | static struct attribute *vmcmd_attrs[] = { | |
1975 | &sys_vmcmd_on_reboot_attr.attr, | |
1976 | &sys_vmcmd_on_panic_attr.attr, | |
1977 | &sys_vmcmd_on_halt_attr.attr, | |
1978 | &sys_vmcmd_on_poff_attr.attr, | |
7dd6b334 | 1979 | &sys_vmcmd_on_restart_attr.attr, |
99ca4e58 MH |
1980 | NULL, |
1981 | }; | |
1982 | ||
1983 | static struct attribute_group vmcmd_attr_group = { | |
1984 | .attrs = vmcmd_attrs, | |
1985 | }; | |
1986 | ||
1987 | static struct kset *vmcmd_kset; | |
1988 | ||
1989 | static void vmcmd_run(struct shutdown_trigger *trigger) | |
ff6b8ea6 | 1990 | { |
8143adaf | 1991 | char *cmd; |
99ca4e58 MH |
1992 | |
1993 | if (strcmp(trigger->name, ON_REIPL_STR) == 0) | |
1994 | cmd = vmcmd_on_reboot; | |
1995 | else if (strcmp(trigger->name, ON_PANIC_STR) == 0) | |
1996 | cmd = vmcmd_on_panic; | |
1997 | else if (strcmp(trigger->name, ON_HALT_STR) == 0) | |
1998 | cmd = vmcmd_on_halt; | |
1999 | else if (strcmp(trigger->name, ON_POFF_STR) == 0) | |
2000 | cmd = vmcmd_on_poff; | |
7dd6b334 MH |
2001 | else if (strcmp(trigger->name, ON_RESTART_STR) == 0) |
2002 | cmd = vmcmd_on_restart; | |
99ca4e58 MH |
2003 | else |
2004 | return; | |
2005 | ||
2006 | if (strlen(cmd) == 0) | |
2007 | return; | |
8143adaf | 2008 | __cpcmd(cmd, NULL, 0, NULL); |
99ca4e58 MH |
2009 | } |
2010 | ||
2011 | static int vmcmd_init(void) | |
2012 | { | |
2013 | if (!MACHINE_IS_VM) | |
b8e660b8 | 2014 | return -EOPNOTSUPP; |
99ca4e58 MH |
2015 | vmcmd_kset = kset_create_and_add("vmcmd", NULL, firmware_kobj); |
2016 | if (!vmcmd_kset) | |
2017 | return -ENOMEM; | |
2018 | return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group); | |
2019 | } | |
2020 | ||
2021 | static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, | |
2022 | vmcmd_run, vmcmd_init}; | |
2023 | ||
2024 | /* | |
2025 | * stop shutdown action: Stop Linux on shutdown. | |
2026 | */ | |
2027 | ||
2028 | static void stop_run(struct shutdown_trigger *trigger) | |
2029 | { | |
e1202eda MH |
2030 | if (strcmp(trigger->name, ON_PANIC_STR) == 0 || |
2031 | strcmp(trigger->name, ON_RESTART_STR) == 0) | |
98587c2d | 2032 | disabled_wait(); |
8b646bd7 | 2033 | smp_stop_cpu(); |
99ca4e58 MH |
2034 | } |
2035 | ||
2036 | static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, | |
2037 | stop_run, NULL}; | |
2038 | ||
2039 | /* action list */ | |
2040 | ||
2041 | static struct shutdown_action *shutdown_actions_list[] = { | |
099b7651 FM |
2042 | &ipl_action, &reipl_action, &dump_reipl_action, &dump_action, |
2043 | &vmcmd_action, &stop_action}; | |
99ca4e58 MH |
2044 | #define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *)) |
2045 | ||
2046 | /* | |
2047 | * Trigger section | |
2048 | */ | |
2049 | ||
2050 | static struct kset *shutdown_actions_kset; | |
2051 | ||
2052 | static int set_trigger(const char *buf, struct shutdown_trigger *trigger, | |
2053 | size_t len) | |
2054 | { | |
2055 | int i; | |
099b7651 | 2056 | |
99ca4e58 | 2057 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { |
099b7651 | 2058 | if (sysfs_streq(buf, shutdown_actions_list[i]->name)) { |
81088819 FM |
2059 | if (shutdown_actions_list[i]->init_rc) { |
2060 | return shutdown_actions_list[i]->init_rc; | |
2061 | } else { | |
2062 | trigger->action = shutdown_actions_list[i]; | |
2063 | return len; | |
2064 | } | |
99ca4e58 MH |
2065 | } |
2066 | } | |
2067 | return -EINVAL; | |
2068 | } | |
2069 | ||
2070 | /* on reipl */ | |
2071 | ||
2072 | static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR, | |
2073 | &reipl_action}; | |
2074 | ||
2075 | static ssize_t on_reboot_show(struct kobject *kobj, | |
2076 | struct kobj_attribute *attr, char *page) | |
2077 | { | |
2078 | return sprintf(page, "%s\n", on_reboot_trigger.action->name); | |
2079 | } | |
2080 | ||
2081 | static ssize_t on_reboot_store(struct kobject *kobj, | |
2082 | struct kobj_attribute *attr, | |
2083 | const char *buf, size_t len) | |
2084 | { | |
2085 | return set_trigger(buf, &on_reboot_trigger, len); | |
2086 | } | |
0f024379 | 2087 | static struct kobj_attribute on_reboot_attr = __ATTR_RW(on_reboot); |
99ca4e58 MH |
2088 | |
2089 | static void do_machine_restart(char *__unused) | |
2090 | { | |
2091 | smp_send_stop(); | |
2092 | on_reboot_trigger.action->fn(&on_reboot_trigger); | |
2093 | reipl_run(NULL); | |
2094 | } | |
2095 | void (*_machine_restart)(char *command) = do_machine_restart; | |
2096 | ||
2097 | /* on panic */ | |
2098 | ||
2099 | static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action}; | |
2100 | ||
2101 | static ssize_t on_panic_show(struct kobject *kobj, | |
2102 | struct kobj_attribute *attr, char *page) | |
2103 | { | |
2104 | return sprintf(page, "%s\n", on_panic_trigger.action->name); | |
2105 | } | |
2106 | ||
2107 | static ssize_t on_panic_store(struct kobject *kobj, | |
2108 | struct kobj_attribute *attr, | |
2109 | const char *buf, size_t len) | |
2110 | { | |
2111 | return set_trigger(buf, &on_panic_trigger, len); | |
2112 | } | |
0f024379 | 2113 | static struct kobj_attribute on_panic_attr = __ATTR_RW(on_panic); |
99ca4e58 MH |
2114 | |
2115 | static void do_panic(void) | |
2116 | { | |
3ab121ab | 2117 | lgr_info_log(); |
99ca4e58 MH |
2118 | on_panic_trigger.action->fn(&on_panic_trigger); |
2119 | stop_run(&on_panic_trigger); | |
2120 | } | |
2121 | ||
7dd6b334 MH |
2122 | /* on restart */ |
2123 | ||
2124 | static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR, | |
e1202eda | 2125 | &stop_action}; |
7dd6b334 MH |
2126 | |
2127 | static ssize_t on_restart_show(struct kobject *kobj, | |
2128 | struct kobj_attribute *attr, char *page) | |
2129 | { | |
2130 | return sprintf(page, "%s\n", on_restart_trigger.action->name); | |
2131 | } | |
2132 | ||
2133 | static ssize_t on_restart_store(struct kobject *kobj, | |
2134 | struct kobj_attribute *attr, | |
2135 | const char *buf, size_t len) | |
2136 | { | |
2137 | return set_trigger(buf, &on_restart_trigger, len); | |
2138 | } | |
0f024379 | 2139 | static struct kobj_attribute on_restart_attr = __ATTR_RW(on_restart); |
7dd6b334 | 2140 | |
8b646bd7 | 2141 | static void __do_restart(void *ignore) |
7dd6b334 MH |
2142 | { |
2143 | smp_send_stop(); | |
60a0c68d MH |
2144 | #ifdef CONFIG_CRASH_DUMP |
2145 | crash_kexec(NULL); | |
2146 | #endif | |
7dd6b334 MH |
2147 | on_restart_trigger.action->fn(&on_restart_trigger); |
2148 | stop_run(&on_restart_trigger); | |
2149 | } | |
2150 | ||
b44913fc | 2151 | void do_restart(void *arg) |
8b646bd7 | 2152 | { |
3ab121ab MH |
2153 | tracing_off(); |
2154 | debug_locks_off(); | |
2155 | lgr_info_log(); | |
b44913fc | 2156 | smp_call_online_cpu(__do_restart, arg); |
8b646bd7 MS |
2157 | } |
2158 | ||
99ca4e58 MH |
2159 | /* on halt */ |
2160 | ||
2161 | static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; | |
2162 | ||
2163 | static ssize_t on_halt_show(struct kobject *kobj, | |
2164 | struct kobj_attribute *attr, char *page) | |
2165 | { | |
2166 | return sprintf(page, "%s\n", on_halt_trigger.action->name); | |
2167 | } | |
2168 | ||
2169 | static ssize_t on_halt_store(struct kobject *kobj, | |
2170 | struct kobj_attribute *attr, | |
2171 | const char *buf, size_t len) | |
2172 | { | |
2173 | return set_trigger(buf, &on_halt_trigger, len); | |
2174 | } | |
0f024379 | 2175 | static struct kobj_attribute on_halt_attr = __ATTR_RW(on_halt); |
99ca4e58 MH |
2176 | |
2177 | static void do_machine_halt(void) | |
2178 | { | |
2179 | smp_send_stop(); | |
2180 | on_halt_trigger.action->fn(&on_halt_trigger); | |
2181 | stop_run(&on_halt_trigger); | |
2182 | } | |
2183 | void (*_machine_halt)(void) = do_machine_halt; | |
2184 | ||
2185 | /* on power off */ | |
2186 | ||
2187 | static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action}; | |
2188 | ||
2189 | static ssize_t on_poff_show(struct kobject *kobj, | |
2190 | struct kobj_attribute *attr, char *page) | |
2191 | { | |
2192 | return sprintf(page, "%s\n", on_poff_trigger.action->name); | |
2193 | } | |
2194 | ||
2195 | static ssize_t on_poff_store(struct kobject *kobj, | |
2196 | struct kobj_attribute *attr, | |
2197 | const char *buf, size_t len) | |
2198 | { | |
2199 | return set_trigger(buf, &on_poff_trigger, len); | |
2200 | } | |
0f024379 | 2201 | static struct kobj_attribute on_poff_attr = __ATTR_RW(on_poff); |
99ca4e58 MH |
2202 | |
2203 | static void do_machine_power_off(void) | |
2204 | { | |
2205 | smp_send_stop(); | |
2206 | on_poff_trigger.action->fn(&on_poff_trigger); | |
2207 | stop_run(&on_poff_trigger); | |
2208 | } | |
2209 | void (*_machine_power_off)(void) = do_machine_power_off; | |
2210 | ||
0f024379 SO |
2211 | static struct attribute *shutdown_action_attrs[] = { |
2212 | &on_restart_attr.attr, | |
2213 | &on_reboot_attr.attr, | |
2214 | &on_panic_attr.attr, | |
2215 | &on_halt_attr.attr, | |
2216 | &on_poff_attr.attr, | |
2217 | NULL, | |
2218 | }; | |
2219 | ||
2220 | static struct attribute_group shutdown_action_attr_group = { | |
2221 | .attrs = shutdown_action_attrs, | |
2222 | }; | |
2223 | ||
99ca4e58 MH |
2224 | static void __init shutdown_triggers_init(void) |
2225 | { | |
d91885be | 2226 | shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, |
f62ed9e3 | 2227 | firmware_kobj); |
d91885be | 2228 | if (!shutdown_actions_kset) |
99ca4e58 | 2229 | goto fail; |
0f024379 SO |
2230 | if (sysfs_create_group(&shutdown_actions_kset->kobj, |
2231 | &shutdown_action_attr_group)) | |
7dd6b334 | 2232 | goto fail; |
99ca4e58 MH |
2233 | return; |
2234 | fail: | |
2235 | panic("shutdown_triggers_init failed\n"); | |
2236 | } | |
2237 | ||
2238 | static void __init shutdown_actions_init(void) | |
2239 | { | |
2240 | int i; | |
2241 | ||
2242 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { | |
2243 | if (!shutdown_actions_list[i]->init) | |
2244 | continue; | |
81088819 FM |
2245 | shutdown_actions_list[i]->init_rc = |
2246 | shutdown_actions_list[i]->init(); | |
ff6b8ea6 | 2247 | } |
ff6b8ea6 MH |
2248 | } |
2249 | ||
2250 | static int __init s390_ipl_init(void) | |
2251 | { | |
69928601 MH |
2252 | char str[8] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}; |
2253 | ||
d5ab7a34 | 2254 | sclp_early_get_ipl_info(&sclp_ipl_info); |
69928601 MH |
2255 | /* |
2256 | * Fix loadparm: There are systems where the (SCSI) LOADPARM | |
2257 | * returned by read SCP info is invalid (contains EBCDIC blanks) | |
2258 | * when the system has been booted via diag308. In that case we use | |
2259 | * the value from diag308, if available. | |
2260 | * | |
2261 | * There are also systems where diag308 store does not work in | |
2262 | * case the system is booted from HMC. Fortunately in this case | |
2263 | * READ SCP info provides the correct value. | |
2264 | */ | |
a0832b3a | 2265 | if (memcmp(sclp_ipl_info.loadparm, str, sizeof(str)) == 0 && ipl_block_valid) |
5f1207fb | 2266 | memcpy(sclp_ipl_info.loadparm, ipl_block.ccw.loadparm, LOADPARM_LEN); |
99ca4e58 MH |
2267 | shutdown_actions_init(); |
2268 | shutdown_triggers_init(); | |
ff6b8ea6 MH |
2269 | return 0; |
2270 | } | |
2271 | ||
2272 | __initcall(s390_ipl_init); | |
15e9b586 | 2273 | |
99ca4e58 MH |
2274 | static void __init strncpy_skip_quote(char *dst, char *src, int n) |
2275 | { | |
2276 | int sx, dx; | |
2277 | ||
2278 | dx = 0; | |
2279 | for (sx = 0; src[sx] != 0; sx++) { | |
2280 | if (src[sx] == '"') | |
2281 | continue; | |
2282 | dst[dx++] = src[sx]; | |
2283 | if (dx >= n) | |
2284 | break; | |
2285 | } | |
2286 | } | |
2287 | ||
2288 | static int __init vmcmd_on_reboot_setup(char *str) | |
2289 | { | |
2290 | if (!MACHINE_IS_VM) | |
2291 | return 1; | |
2292 | strncpy_skip_quote(vmcmd_on_reboot, str, 127); | |
2293 | vmcmd_on_reboot[127] = 0; | |
2294 | on_reboot_trigger.action = &vmcmd_action; | |
2295 | return 1; | |
2296 | } | |
2297 | __setup("vmreboot=", vmcmd_on_reboot_setup); | |
2298 | ||
2299 | static int __init vmcmd_on_panic_setup(char *str) | |
2300 | { | |
2301 | if (!MACHINE_IS_VM) | |
2302 | return 1; | |
2303 | strncpy_skip_quote(vmcmd_on_panic, str, 127); | |
2304 | vmcmd_on_panic[127] = 0; | |
2305 | on_panic_trigger.action = &vmcmd_action; | |
2306 | return 1; | |
2307 | } | |
2308 | __setup("vmpanic=", vmcmd_on_panic_setup); | |
2309 | ||
2310 | static int __init vmcmd_on_halt_setup(char *str) | |
2311 | { | |
2312 | if (!MACHINE_IS_VM) | |
2313 | return 1; | |
2314 | strncpy_skip_quote(vmcmd_on_halt, str, 127); | |
2315 | vmcmd_on_halt[127] = 0; | |
2316 | on_halt_trigger.action = &vmcmd_action; | |
2317 | return 1; | |
2318 | } | |
2319 | __setup("vmhalt=", vmcmd_on_halt_setup); | |
2320 | ||
2321 | static int __init vmcmd_on_poff_setup(char *str) | |
2322 | { | |
2323 | if (!MACHINE_IS_VM) | |
2324 | return 1; | |
2325 | strncpy_skip_quote(vmcmd_on_poff, str, 127); | |
2326 | vmcmd_on_poff[127] = 0; | |
2327 | on_poff_trigger.action = &vmcmd_action; | |
2328 | return 1; | |
2329 | } | |
2330 | __setup("vmpoff=", vmcmd_on_poff_setup); | |
2331 | ||
2332 | static int on_panic_notify(struct notifier_block *self, | |
2333 | unsigned long event, void *data) | |
2334 | { | |
2335 | do_panic(); | |
2336 | return NOTIFY_OK; | |
2337 | } | |
2338 | ||
2339 | static struct notifier_block on_panic_nb = { | |
2340 | .notifier_call = on_panic_notify, | |
7e9b580e | 2341 | .priority = INT_MIN, |
99ca4e58 MH |
2342 | }; |
2343 | ||
2344 | void __init setup_ipl(void) | |
2345 | { | |
bdbfe185 VG |
2346 | BUILD_BUG_ON(sizeof(struct ipl_parameter_block) != PAGE_SIZE); |
2347 | ||
99ca4e58 MH |
2348 | ipl_info.type = get_ipl_type(); |
2349 | switch (ipl_info.type) { | |
2350 | case IPL_TYPE_CCW: | |
86c74d86 MS |
2351 | ipl_info.data.ccw.dev_id.ssid = ipl_block.ccw.ssid; |
2352 | ipl_info.data.ccw.dev_id.devno = ipl_block.ccw.devno; | |
99ca4e58 | 2353 | break; |
87fd22e0 | 2354 | case IPL_TYPE_ECKD: |
e2d2a296 | 2355 | case IPL_TYPE_ECKD_DUMP: |
87fd22e0 SS |
2356 | ipl_info.data.eckd.dev_id.ssid = ipl_block.eckd.ssid; |
2357 | ipl_info.data.eckd.dev_id.devno = ipl_block.eckd.devno; | |
2358 | break; | |
99ca4e58 MH |
2359 | case IPL_TYPE_FCP: |
2360 | case IPL_TYPE_FCP_DUMP: | |
18e22a17 | 2361 | ipl_info.data.fcp.dev_id.ssid = 0; |
86c74d86 MS |
2362 | ipl_info.data.fcp.dev_id.devno = ipl_block.fcp.devno; |
2363 | ipl_info.data.fcp.wwpn = ipl_block.fcp.wwpn; | |
2364 | ipl_info.data.fcp.lun = ipl_block.fcp.lun; | |
99ca4e58 | 2365 | break; |
3737e8ee | 2366 | case IPL_TYPE_NVME: |
d70e38cb | 2367 | case IPL_TYPE_NVME_DUMP: |
3737e8ee JH |
2368 | ipl_info.data.nvme.fid = ipl_block.nvme.fid; |
2369 | ipl_info.data.nvme.nsid = ipl_block.nvme.nsid; | |
2370 | break; | |
99ca4e58 | 2371 | case IPL_TYPE_NSS: |
99ca4e58 | 2372 | case IPL_TYPE_UNKNOWN: |
99ca4e58 MH |
2373 | /* We have no info to copy */ |
2374 | break; | |
2375 | } | |
2376 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); | |
2377 | } | |
2378 | ||
1a36a39e | 2379 | void s390_reset_system(void) |
15e9b586 | 2380 | { |
15e9b586 HC |
2381 | /* Disable prefixing */ |
2382 | set_prefix(0); | |
2383 | ||
2384 | /* Disable lowcore protection */ | |
99441a38 | 2385 | local_ctl_clear_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT); |
c78d0c74 | 2386 | diag_amode31_ops.diag308_reset(); |
15e9b586 | 2387 | } |
937347ac MS |
2388 | |
2389 | #ifdef CONFIG_KEXEC_FILE | |
2390 | ||
2391 | int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf, | |
2392 | unsigned char flags, unsigned short cert) | |
2393 | { | |
2394 | struct ipl_report_component *comp; | |
2395 | ||
2396 | comp = vzalloc(sizeof(*comp)); | |
2397 | if (!comp) | |
2398 | return -ENOMEM; | |
2399 | list_add_tail(&comp->list, &report->components); | |
2400 | ||
2401 | comp->entry.addr = kbuf->mem; | |
2402 | comp->entry.len = kbuf->memsz; | |
2403 | comp->entry.flags = flags; | |
2404 | comp->entry.certificate_index = cert; | |
2405 | ||
2406 | report->size += sizeof(comp->entry); | |
2407 | ||
2408 | return 0; | |
2409 | } | |
2410 | ||
2411 | int ipl_report_add_certificate(struct ipl_report *report, void *key, | |
2412 | unsigned long addr, unsigned long len) | |
2413 | { | |
2414 | struct ipl_report_certificate *cert; | |
2415 | ||
2416 | cert = vzalloc(sizeof(*cert)); | |
2417 | if (!cert) | |
2418 | return -ENOMEM; | |
2419 | list_add_tail(&cert->list, &report->certificates); | |
2420 | ||
2421 | cert->entry.addr = addr; | |
2422 | cert->entry.len = len; | |
2423 | cert->key = key; | |
2424 | ||
2425 | report->size += sizeof(cert->entry); | |
2426 | report->size += cert->entry.len; | |
2427 | ||
2428 | return 0; | |
2429 | } | |
2430 | ||
2431 | struct ipl_report *ipl_report_init(struct ipl_parameter_block *ipib) | |
2432 | { | |
2433 | struct ipl_report *report; | |
2434 | ||
2435 | report = vzalloc(sizeof(*report)); | |
2436 | if (!report) | |
2437 | return ERR_PTR(-ENOMEM); | |
2438 | ||
2439 | report->ipib = ipib; | |
2440 | INIT_LIST_HEAD(&report->components); | |
2441 | INIT_LIST_HEAD(&report->certificates); | |
2442 | ||
2443 | report->size = ALIGN(ipib->hdr.len, 8); | |
2444 | report->size += sizeof(struct ipl_rl_hdr); | |
2445 | report->size += sizeof(struct ipl_rb_components); | |
2446 | report->size += sizeof(struct ipl_rb_certificates); | |
2447 | ||
2448 | return report; | |
2449 | } | |
2450 | ||
2451 | void *ipl_report_finish(struct ipl_report *report) | |
2452 | { | |
2453 | struct ipl_report_certificate *cert; | |
2454 | struct ipl_report_component *comp; | |
2455 | struct ipl_rb_certificates *certs; | |
2456 | struct ipl_parameter_block *ipib; | |
2457 | struct ipl_rb_components *comps; | |
2458 | struct ipl_rl_hdr *rl_hdr; | |
2459 | void *buf, *ptr; | |
2460 | ||
2461 | buf = vzalloc(report->size); | |
2462 | if (!buf) | |
20c76e24 | 2463 | goto out; |
937347ac MS |
2464 | ptr = buf; |
2465 | ||
2466 | memcpy(ptr, report->ipib, report->ipib->hdr.len); | |
2467 | ipib = ptr; | |
2468 | if (ipl_secure_flag) | |
2469 | ipib->hdr.flags |= IPL_PL_FLAG_SIPL; | |
2470 | ipib->hdr.flags |= IPL_PL_FLAG_IPLSR; | |
2471 | ptr += report->ipib->hdr.len; | |
2472 | ptr = PTR_ALIGN(ptr, 8); | |
2473 | ||
2474 | rl_hdr = ptr; | |
2475 | ptr += sizeof(*rl_hdr); | |
2476 | ||
2477 | comps = ptr; | |
2478 | comps->rbt = IPL_RBT_COMPONENTS; | |
2479 | ptr += sizeof(*comps); | |
2480 | list_for_each_entry(comp, &report->components, list) { | |
2481 | memcpy(ptr, &comp->entry, sizeof(comp->entry)); | |
2482 | ptr += sizeof(comp->entry); | |
2483 | } | |
2484 | comps->len = ptr - (void *)comps; | |
2485 | ||
2486 | certs = ptr; | |
2487 | certs->rbt = IPL_RBT_CERTIFICATES; | |
2488 | ptr += sizeof(*certs); | |
2489 | list_for_each_entry(cert, &report->certificates, list) { | |
2490 | memcpy(ptr, &cert->entry, sizeof(cert->entry)); | |
2491 | ptr += sizeof(cert->entry); | |
2492 | } | |
2493 | certs->len = ptr - (void *)certs; | |
2494 | rl_hdr->len = ptr - (void *)rl_hdr; | |
2495 | ||
2496 | list_for_each_entry(cert, &report->certificates, list) { | |
2497 | memcpy(ptr, cert->key, cert->entry.len); | |
2498 | ptr += cert->entry.len; | |
2499 | } | |
2500 | ||
2501 | BUG_ON(ptr > buf + report->size); | |
20c76e24 | 2502 | out: |
937347ac MS |
2503 | return buf; |
2504 | } | |
2505 | ||
2506 | int ipl_report_free(struct ipl_report *report) | |
2507 | { | |
2508 | struct ipl_report_component *comp, *ncomp; | |
2509 | struct ipl_report_certificate *cert, *ncert; | |
2510 | ||
2511 | list_for_each_entry_safe(comp, ncomp, &report->components, list) | |
2512 | vfree(comp); | |
2513 | ||
2514 | list_for_each_entry_safe(cert, ncert, &report->certificates, list) | |
2515 | vfree(cert); | |
2516 | ||
2517 | vfree(report); | |
2518 | ||
2519 | return 0; | |
2520 | } | |
2521 | ||
2522 | #endif |