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