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