1 | // SPDX-License-Identifier: GPL-1.0+ |
2 | /* |
3 | * zcore module to export memory content and register sets for creating system |
4 | * dumps on SCSI/NVMe disks (zfcp/nvme dump). |
5 | * |
6 | * For more information please refer to Documentation/arch/s390/zfcpdump.rst |
7 | * |
8 | * Copyright IBM Corp. 2003, 2008 |
9 | * Author(s): Michael Holzheu |
10 | */ |
11 | |
12 | #define KMSG_COMPONENT "zdump" |
13 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
14 | |
15 | #include <linux/init.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/debugfs.h> |
18 | #include <linux/panic_notifier.h> |
19 | #include <linux/reboot.h> |
20 | #include <linux/uio.h> |
21 | |
22 | #include <asm/asm-offsets.h> |
23 | #include <asm/ipl.h> |
24 | #include <asm/sclp.h> |
25 | #include <asm/setup.h> |
26 | #include <linux/uaccess.h> |
27 | #include <asm/debug.h> |
28 | #include <asm/processor.h> |
29 | #include <asm/irqflags.h> |
30 | #include <asm/checksum.h> |
31 | #include <asm/os_info.h> |
32 | #include <asm/switch_to.h> |
33 | #include <asm/maccess.h> |
34 | #include "sclp.h" |
35 | |
36 | #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) |
37 | |
38 | enum arch_id { |
39 | ARCH_S390 = 0, |
40 | ARCH_S390X = 1, |
41 | }; |
42 | |
43 | struct ipib_info { |
44 | unsigned long ipib; |
45 | u32 checksum; |
46 | } __attribute__((packed)); |
47 | |
48 | static struct debug_info *zcore_dbf; |
49 | static int hsa_available; |
50 | static struct dentry *zcore_dir; |
51 | static struct dentry *zcore_reipl_file; |
52 | static struct dentry *zcore_hsa_file; |
53 | static struct ipl_parameter_block *zcore_ipl_block; |
54 | static unsigned long os_info_flags; |
55 | |
56 | static DEFINE_MUTEX(hsa_buf_mutex); |
57 | static char hsa_buf[PAGE_SIZE] __aligned(PAGE_SIZE); |
58 | |
59 | /* |
60 | * Copy memory from HSA to iterator (not reentrant): |
61 | * |
62 | * @iter: Iterator where memory should be copied to |
63 | * @src: Start address within HSA where data should be copied |
64 | * @count: Size of buffer, which should be copied |
65 | */ |
66 | size_t memcpy_hsa_iter(struct iov_iter *iter, unsigned long src, size_t count) |
67 | { |
68 | size_t bytes, copied, res = 0; |
69 | unsigned long offset; |
70 | |
71 | if (!hsa_available) |
72 | return 0; |
73 | |
74 | mutex_lock(&hsa_buf_mutex); |
75 | while (count) { |
76 | if (sclp_sdias_copy(hsa_buf, src / PAGE_SIZE + 2, 1)) { |
77 | TRACE("sclp_sdias_copy() failed\n" ); |
78 | break; |
79 | } |
80 | offset = src % PAGE_SIZE; |
81 | bytes = min(PAGE_SIZE - offset, count); |
82 | copied = copy_to_iter(addr: hsa_buf + offset, bytes, i: iter); |
83 | count -= copied; |
84 | src += copied; |
85 | res += copied; |
86 | if (copied < bytes) |
87 | break; |
88 | } |
89 | mutex_unlock(lock: &hsa_buf_mutex); |
90 | return res; |
91 | } |
92 | |
93 | /* |
94 | * Copy memory from HSA to kernel memory (not reentrant): |
95 | * |
96 | * @dest: Kernel or user buffer where memory should be copied to |
97 | * @src: Start address within HSA where data should be copied |
98 | * @count: Size of buffer, which should be copied |
99 | */ |
100 | static inline int memcpy_hsa_kernel(void *dst, unsigned long src, size_t count) |
101 | { |
102 | struct iov_iter iter; |
103 | struct kvec kvec; |
104 | |
105 | kvec.iov_base = dst; |
106 | kvec.iov_len = count; |
107 | iov_iter_kvec(i: &iter, ITER_DEST, kvec: &kvec, nr_segs: 1, count); |
108 | if (memcpy_hsa_iter(iter: &iter, src, count) < count) |
109 | return -EIO; |
110 | return 0; |
111 | } |
112 | |
113 | static int __init init_cpu_info(void) |
114 | { |
115 | struct save_area *sa; |
116 | |
117 | /* get info for boot cpu from lowcore, stored in the HSA */ |
118 | sa = save_area_boot_cpu(); |
119 | if (!sa) |
120 | return -ENOMEM; |
121 | if (memcpy_hsa_kernel(dst: hsa_buf, src: __LC_FPREGS_SAVE_AREA, count: 512) < 0) { |
122 | TRACE("could not copy from HSA\n" ); |
123 | return -EIO; |
124 | } |
125 | save_area_add_regs(sa, hsa_buf); /* vx registers are saved in smp.c */ |
126 | return 0; |
127 | } |
128 | |
129 | /* |
130 | * Release the HSA |
131 | */ |
132 | static void release_hsa(void) |
133 | { |
134 | diag308(DIAG308_REL_HSA, NULL); |
135 | hsa_available = 0; |
136 | } |
137 | |
138 | static ssize_t zcore_reipl_write(struct file *filp, const char __user *buf, |
139 | size_t count, loff_t *ppos) |
140 | { |
141 | if (zcore_ipl_block) { |
142 | diag308(DIAG308_SET, zcore_ipl_block); |
143 | if (os_info_flags & OS_INFO_FLAG_REIPL_CLEAR) |
144 | diag308(DIAG308_LOAD_CLEAR, NULL); |
145 | /* Use special diag308 subcode for CCW normal ipl */ |
146 | if (zcore_ipl_block->pb0_hdr.pbt == IPL_PBT_CCW) |
147 | diag308(DIAG308_LOAD_NORMAL_DUMP, NULL); |
148 | else |
149 | diag308(DIAG308_LOAD_NORMAL, NULL); |
150 | } |
151 | return count; |
152 | } |
153 | |
154 | static int zcore_reipl_open(struct inode *inode, struct file *filp) |
155 | { |
156 | return stream_open(inode, filp); |
157 | } |
158 | |
159 | static int zcore_reipl_release(struct inode *inode, struct file *filp) |
160 | { |
161 | return 0; |
162 | } |
163 | |
164 | static const struct file_operations zcore_reipl_fops = { |
165 | .owner = THIS_MODULE, |
166 | .write = zcore_reipl_write, |
167 | .open = zcore_reipl_open, |
168 | .release = zcore_reipl_release, |
169 | .llseek = no_llseek, |
170 | }; |
171 | |
172 | static ssize_t zcore_hsa_read(struct file *filp, char __user *buf, |
173 | size_t count, loff_t *ppos) |
174 | { |
175 | static char str[18]; |
176 | |
177 | if (hsa_available) |
178 | snprintf(buf: str, size: sizeof(str), fmt: "%lx\n" , sclp.hsa_size); |
179 | else |
180 | snprintf(buf: str, size: sizeof(str), fmt: "0\n" ); |
181 | return simple_read_from_buffer(to: buf, count, ppos, from: str, strlen(str)); |
182 | } |
183 | |
184 | static ssize_t zcore_hsa_write(struct file *filp, const char __user *buf, |
185 | size_t count, loff_t *ppos) |
186 | { |
187 | char value; |
188 | |
189 | if (*ppos != 0) |
190 | return -EPIPE; |
191 | if (copy_from_user(to: &value, from: buf, n: 1)) |
192 | return -EFAULT; |
193 | if (value != '0') |
194 | return -EINVAL; |
195 | release_hsa(); |
196 | return count; |
197 | } |
198 | |
199 | static const struct file_operations zcore_hsa_fops = { |
200 | .owner = THIS_MODULE, |
201 | .write = zcore_hsa_write, |
202 | .read = zcore_hsa_read, |
203 | .open = nonseekable_open, |
204 | .llseek = no_llseek, |
205 | }; |
206 | |
207 | static int __init check_sdias(void) |
208 | { |
209 | if (!sclp.hsa_size) { |
210 | TRACE("Could not determine HSA size\n" ); |
211 | return -ENODEV; |
212 | } |
213 | return 0; |
214 | } |
215 | |
216 | /* |
217 | * Provide IPL parameter information block from either HSA or memory |
218 | * for future reipl |
219 | */ |
220 | static int __init zcore_reipl_init(void) |
221 | { |
222 | struct os_info_entry *entry; |
223 | struct ipib_info ipib_info; |
224 | unsigned long os_info_addr; |
225 | struct os_info *os_info; |
226 | int rc; |
227 | |
228 | rc = memcpy_hsa_kernel(dst: &ipib_info, src: __LC_DUMP_REIPL, count: sizeof(ipib_info)); |
229 | if (rc) |
230 | return rc; |
231 | if (ipib_info.ipib == 0) |
232 | return 0; |
233 | zcore_ipl_block = (void *) __get_free_page(GFP_KERNEL); |
234 | if (!zcore_ipl_block) |
235 | return -ENOMEM; |
236 | if (ipib_info.ipib < sclp.hsa_size) |
237 | rc = memcpy_hsa_kernel(dst: zcore_ipl_block, src: ipib_info.ipib, |
238 | PAGE_SIZE); |
239 | else |
240 | rc = memcpy_real(zcore_ipl_block, ipib_info.ipib, PAGE_SIZE); |
241 | if (rc || (__force u32)csum_partial(buff: zcore_ipl_block, len: zcore_ipl_block->hdr.len, sum: 0) != |
242 | ipib_info.checksum) { |
243 | TRACE("Checksum does not match\n" ); |
244 | free_page((unsigned long) zcore_ipl_block); |
245 | zcore_ipl_block = NULL; |
246 | } |
247 | /* |
248 | * Read the bit-flags field from os_info flags entry. |
249 | * Return zero even for os_info read or entry checksum errors in order |
250 | * to continue dump processing, considering that os_info could be |
251 | * corrupted on the panicked system. |
252 | */ |
253 | os_info = (void *)__get_free_page(GFP_KERNEL); |
254 | if (!os_info) |
255 | return -ENOMEM; |
256 | rc = memcpy_hsa_kernel(dst: &os_info_addr, src: __LC_OS_INFO, count: sizeof(os_info_addr)); |
257 | if (rc) |
258 | goto out; |
259 | if (os_info_addr < sclp.hsa_size) |
260 | rc = memcpy_hsa_kernel(dst: os_info, src: os_info_addr, PAGE_SIZE); |
261 | else |
262 | rc = memcpy_real(os_info, os_info_addr, PAGE_SIZE); |
263 | if (rc || os_info_csum(os_info) != os_info->csum) |
264 | goto out; |
265 | entry = &os_info->entry[OS_INFO_FLAGS_ENTRY]; |
266 | if (entry->addr && entry->size) { |
267 | if (entry->addr < sclp.hsa_size) |
268 | rc = memcpy_hsa_kernel(dst: &os_info_flags, src: entry->addr, count: sizeof(os_info_flags)); |
269 | else |
270 | rc = memcpy_real(&os_info_flags, entry->addr, sizeof(os_info_flags)); |
271 | if (rc || (__force u32)csum_partial(buff: &os_info_flags, len: entry->size, sum: 0) != entry->csum) |
272 | os_info_flags = 0; |
273 | } |
274 | out: |
275 | free_page((unsigned long)os_info); |
276 | return 0; |
277 | } |
278 | |
279 | static int zcore_reboot_and_on_panic_handler(struct notifier_block *self, |
280 | unsigned long event, |
281 | void *data) |
282 | { |
283 | if (hsa_available) |
284 | release_hsa(); |
285 | |
286 | return NOTIFY_OK; |
287 | } |
288 | |
289 | static struct notifier_block zcore_reboot_notifier = { |
290 | .notifier_call = zcore_reboot_and_on_panic_handler, |
291 | /* we need to be notified before reipl and kdump */ |
292 | .priority = INT_MAX, |
293 | }; |
294 | |
295 | static struct notifier_block zcore_on_panic_notifier = { |
296 | .notifier_call = zcore_reboot_and_on_panic_handler, |
297 | /* we need to be notified before reipl and kdump */ |
298 | .priority = INT_MAX, |
299 | }; |
300 | |
301 | static int __init zcore_init(void) |
302 | { |
303 | unsigned char arch; |
304 | int rc; |
305 | |
306 | if (!is_ipl_type_dump()) |
307 | return -ENODATA; |
308 | if (oldmem_data.start) |
309 | return -ENODATA; |
310 | |
311 | zcore_dbf = debug_register("zcore" , 4, 1, 4 * sizeof(long)); |
312 | debug_register_view(zcore_dbf, &debug_sprintf_view); |
313 | debug_set_level(zcore_dbf, 6); |
314 | |
315 | if (ipl_info.type == IPL_TYPE_FCP_DUMP) { |
316 | TRACE("type: fcp\n" ); |
317 | TRACE("devno: %x\n" , ipl_info.data.fcp.dev_id.devno); |
318 | TRACE("wwpn: %llx\n" , (unsigned long long) ipl_info.data.fcp.wwpn); |
319 | TRACE("lun: %llx\n" , (unsigned long long) ipl_info.data.fcp.lun); |
320 | } else if (ipl_info.type == IPL_TYPE_NVME_DUMP) { |
321 | TRACE("type: nvme\n" ); |
322 | TRACE("fid: %x\n" , ipl_info.data.nvme.fid); |
323 | TRACE("nsid: %x\n" , ipl_info.data.nvme.nsid); |
324 | } else if (ipl_info.type == IPL_TYPE_ECKD_DUMP) { |
325 | TRACE("type: eckd\n" ); |
326 | TRACE("devno: %x\n" , ipl_info.data.eckd.dev_id.devno); |
327 | TRACE("ssid: %x\n" , ipl_info.data.eckd.dev_id.ssid); |
328 | } |
329 | |
330 | rc = sclp_sdias_init(); |
331 | if (rc) |
332 | goto fail; |
333 | |
334 | rc = check_sdias(); |
335 | if (rc) |
336 | goto fail; |
337 | hsa_available = 1; |
338 | |
339 | rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1); |
340 | if (rc) |
341 | goto fail; |
342 | |
343 | if (arch == ARCH_S390) { |
344 | pr_alert("The 64-bit dump tool cannot be used for a " |
345 | "32-bit system\n" ); |
346 | rc = -EINVAL; |
347 | goto fail; |
348 | } |
349 | |
350 | pr_alert("The dump process started for a 64-bit operating system\n" ); |
351 | rc = init_cpu_info(); |
352 | if (rc) |
353 | goto fail; |
354 | |
355 | rc = zcore_reipl_init(); |
356 | if (rc) |
357 | goto fail; |
358 | |
359 | zcore_dir = debugfs_create_dir(name: "zcore" , NULL); |
360 | zcore_reipl_file = debugfs_create_file(name: "reipl" , S_IRUSR, parent: zcore_dir, |
361 | NULL, fops: &zcore_reipl_fops); |
362 | zcore_hsa_file = debugfs_create_file(name: "hsa" , S_IRUSR|S_IWUSR, parent: zcore_dir, |
363 | NULL, fops: &zcore_hsa_fops); |
364 | |
365 | register_reboot_notifier(&zcore_reboot_notifier); |
366 | atomic_notifier_chain_register(nh: &panic_notifier_list, nb: &zcore_on_panic_notifier); |
367 | |
368 | return 0; |
369 | fail: |
370 | diag308(DIAG308_REL_HSA, NULL); |
371 | return rc; |
372 | } |
373 | subsys_initcall(zcore_init); |
374 | |