blob: 3ad3d538e43276bad63ac6f5a4d6e808307c8c35 [file] [log] [blame]
Michael Holzheu411ed322007-04-27 16:01:49 +02001/*
2 * zcore module to export memory content and register sets for creating system
3 * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same
4 * dump format as s390 standalone dumps.
5 *
6 * For more information please refer to Documentation/s390/zfcpdump.txt
7 *
Heiko Carstensa53c8fa2012-07-20 11:15:04 +02008 * Copyright IBM Corp. 2003, 2008
Michael Holzheu411ed322007-04-27 16:01:49 +02009 * Author(s): Michael Holzheu
10 */
11
Michael Holzheu17159dc62008-12-25 13:39:51 +010012#define KMSG_COMPONENT "zdump"
13#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
14
Michael Holzheu411ed322007-04-27 16:01:49 +020015#include <linux/init.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090016#include <linux/slab.h>
Michael Holzheu411ed322007-04-27 16:01:49 +020017#include <linux/miscdevice.h>
Michael Holzheu411ed322007-04-27 16:01:49 +020018#include <linux/debugfs.h>
Heiko Carstens3948a102011-10-30 15:16:55 +010019#include <linux/module.h>
Philipp Hachtmann50be6342014-01-29 18:16:01 +010020#include <linux/memblock.h>
21
Heiko Carstenscbb870c2010-02-26 22:37:43 +010022#include <asm/asm-offsets.h>
Michael Holzheu411ed322007-04-27 16:01:49 +020023#include <asm/ipl.h>
24#include <asm/sclp.h>
25#include <asm/setup.h>
Michael Holzheu411ed322007-04-27 16:01:49 +020026#include <asm/uaccess.h>
27#include <asm/debug.h>
28#include <asm/processor.h>
29#include <asm/irqflags.h>
Frank Munzert159d1ff2009-03-26 15:24:45 +010030#include <asm/checksum.h>
Martin Schwidefskybbfed512015-10-15 11:14:19 +020031#include <asm/os_info.h>
Michael Holzheua62bc072014-10-06 17:57:43 +020032#include <asm/switch_to.h>
Heiko Carstens763968e2007-05-10 15:45:46 +020033#include "sclp.h"
Michael Holzheu411ed322007-04-27 16:01:49 +020034
35#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
Michael Holzheu411ed322007-04-27 16:01:49 +020036
Michael Holzheu6f79d332013-09-11 14:24:54 -070037#define TO_USER 1
38#define TO_KERNEL 0
Frank Munzert12e0c952008-07-17 17:16:40 +020039#define CHUNK_INFO_SIZE 34 /* 2 16-byte char, each followed by blank */
Michael Holzheu411ed322007-04-27 16:01:49 +020040
41enum arch_id {
42 ARCH_S390 = 0,
43 ARCH_S390X = 1,
44};
45
Frank Munzert099b7652009-03-26 15:23:43 +010046struct ipib_info {
47 unsigned long ipib;
48 u32 checksum;
49} __attribute__((packed));
50
Michael Holzheu411ed322007-04-27 16:01:49 +020051static struct debug_info *zcore_dbf;
52static int hsa_available;
53static struct dentry *zcore_dir;
Frank Munzert12e0c952008-07-17 17:16:40 +020054static struct dentry *zcore_memmap_file;
Frank Munzert099b7652009-03-26 15:23:43 +010055static struct dentry *zcore_reipl_file;
Michael Holzheub4b3d122013-01-21 18:37:41 +010056static struct dentry *zcore_hsa_file;
Frank Munzert099b7652009-03-26 15:23:43 +010057static struct ipl_parameter_block *ipl_block;
Michael Holzheu411ed322007-04-27 16:01:49 +020058
59/*
60 * Copy memory from HSA to kernel or user memory (not reentrant):
61 *
62 * @dest: Kernel or user buffer 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 * @mode: Either TO_KERNEL or TO_USER
66 */
Michael Holzheu6f79d332013-09-11 14:24:54 -070067int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
Michael Holzheu411ed322007-04-27 16:01:49 +020068{
69 int offs, blk_num;
70 static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
71
Michael Holzheub4b3d122013-01-21 18:37:41 +010072 if (!hsa_available)
73 return -ENODATA;
Michael Holzheu411ed322007-04-27 16:01:49 +020074 if (count == 0)
75 return 0;
76
77 /* copy first block */
78 offs = 0;
79 if ((src % PAGE_SIZE) != 0) {
80 blk_num = src / PAGE_SIZE + 2;
81 if (sclp_sdias_copy(buf, blk_num, 1)) {
82 TRACE("sclp_sdias_copy() failed\n");
83 return -EIO;
84 }
85 offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count);
86 if (mode == TO_USER) {
87 if (copy_to_user((__force __user void*) dest,
88 buf + (src % PAGE_SIZE), offs))
89 return -EFAULT;
90 } else
91 memcpy(dest, buf + (src % PAGE_SIZE), offs);
92 }
93 if (offs == count)
94 goto out;
95
96 /* copy middle */
97 for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) {
98 blk_num = (src + offs) / PAGE_SIZE + 2;
99 if (sclp_sdias_copy(buf, blk_num, 1)) {
100 TRACE("sclp_sdias_copy() failed\n");
101 return -EIO;
102 }
103 if (mode == TO_USER) {
104 if (copy_to_user((__force __user void*) dest + offs,
105 buf, PAGE_SIZE))
106 return -EFAULT;
107 } else
108 memcpy(dest + offs, buf, PAGE_SIZE);
109 }
110 if (offs == count)
111 goto out;
112
113 /* copy last block */
114 blk_num = (src + offs) / PAGE_SIZE + 2;
115 if (sclp_sdias_copy(buf, blk_num, 1)) {
116 TRACE("sclp_sdias_copy() failed\n");
117 return -EIO;
118 }
119 if (mode == TO_USER) {
120 if (copy_to_user((__force __user void*) dest + offs, buf,
Michael Holzheu241fd9b2013-04-19 18:03:02 +0200121 count - offs))
Michael Holzheu411ed322007-04-27 16:01:49 +0200122 return -EFAULT;
123 } else
124 memcpy(dest + offs, buf, count - offs);
125out:
126 return 0;
127}
128
129static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
130{
131 return memcpy_hsa((void __force *) dest, src, count, TO_USER);
132}
133
134static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
135{
136 return memcpy_hsa(dest, src, count, TO_KERNEL);
137}
138
Martin Schwidefskyffa52d02015-10-28 09:47:58 +0100139static int __init init_cpu_info(void)
Michael Holzheu411ed322007-04-27 16:01:49 +0200140{
Michael Holzheua62bc072014-10-06 17:57:43 +0200141 struct save_area_ext *sa_ext;
Michael Holzheu411ed322007-04-27 16:01:49 +0200142
143 /* get info for boot cpu from lowcore, stored in the HSA */
144
Michael Holzheu1592a8e2015-05-26 19:05:23 +0200145 sa_ext = dump_save_areas.areas[0];
Michael Holzheua62bc072014-10-06 17:57:43 +0200146 if (!sa_ext)
Michael Holzheu411ed322007-04-27 16:01:49 +0200147 return -ENOMEM;
Martin Schwidefskyffa52d02015-10-28 09:47:58 +0100148 if (memcpy_hsa_kernel(&sa_ext->sa, SAVE_AREA_BASE,
149 sizeof(struct save_area)) < 0) {
Michael Holzheu2a062ab2008-07-14 09:59:38 +0200150 TRACE("could not copy from HSA\n");
Michael Holzheu411ed322007-04-27 16:01:49 +0200151 return -EIO;
152 }
Martin Schwidefskyffa52d02015-10-28 09:47:58 +0100153 if (MACHINE_HAS_VX)
154 save_vx_regs_safe(sa_ext->vx_regs);
Michael Holzheu411ed322007-04-27 16:01:49 +0200155 return 0;
156}
157
158/*
Michael Holzheub4b3d122013-01-21 18:37:41 +0100159 * Release the HSA
160 */
161static void release_hsa(void)
162{
163 diag308(DIAG308_REL_HSA, NULL);
164 hsa_available = 0;
165}
166
Frank Munzert12e0c952008-07-17 17:16:40 +0200167static ssize_t zcore_memmap_read(struct file *filp, char __user *buf,
168 size_t count, loff_t *ppos)
169{
170 return simple_read_from_buffer(buf, count, ppos, filp->private_data,
Philipp Hachtmann50be6342014-01-29 18:16:01 +0100171 memblock.memory.cnt * CHUNK_INFO_SIZE);
Frank Munzert12e0c952008-07-17 17:16:40 +0200172}
173
174static int zcore_memmap_open(struct inode *inode, struct file *filp)
175{
Philipp Hachtmann50be6342014-01-29 18:16:01 +0100176 struct memblock_region *reg;
Frank Munzert12e0c952008-07-17 17:16:40 +0200177 char *buf;
Philipp Hachtmann50be6342014-01-29 18:16:01 +0100178 int i = 0;
Frank Munzert12e0c952008-07-17 17:16:40 +0200179
Philipp Hachtmann50be6342014-01-29 18:16:01 +0100180 buf = kzalloc(memblock.memory.cnt * CHUNK_INFO_SIZE, GFP_KERNEL);
Frank Munzert12e0c952008-07-17 17:16:40 +0200181 if (!buf) {
Frank Munzert12e0c952008-07-17 17:16:40 +0200182 return -ENOMEM;
183 }
Philipp Hachtmann50be6342014-01-29 18:16:01 +0100184 for_each_memblock(memory, reg) {
185 sprintf(buf + (i++ * CHUNK_INFO_SIZE), "%016llx %016llx ",
186 (unsigned long long) reg->base,
187 (unsigned long long) reg->size);
Frank Munzert12e0c952008-07-17 17:16:40 +0200188 }
Frank Munzert12e0c952008-07-17 17:16:40 +0200189 filp->private_data = buf;
Martin Schwidefsky58ea91c2010-05-17 10:00:07 +0200190 return nonseekable_open(inode, filp);
Frank Munzert12e0c952008-07-17 17:16:40 +0200191}
192
193static int zcore_memmap_release(struct inode *inode, struct file *filp)
194{
195 kfree(filp->private_data);
196 return 0;
197}
198
199static const struct file_operations zcore_memmap_fops = {
200 .owner = THIS_MODULE,
201 .read = zcore_memmap_read,
202 .open = zcore_memmap_open,
203 .release = zcore_memmap_release,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200204 .llseek = no_llseek,
Frank Munzert12e0c952008-07-17 17:16:40 +0200205};
206
Frank Munzert099b7652009-03-26 15:23:43 +0100207static ssize_t zcore_reipl_write(struct file *filp, const char __user *buf,
208 size_t count, loff_t *ppos)
209{
210 if (ipl_block) {
211 diag308(DIAG308_SET, ipl_block);
212 diag308(DIAG308_IPL, NULL);
213 }
214 return count;
215}
216
217static int zcore_reipl_open(struct inode *inode, struct file *filp)
218{
Martin Schwidefsky58ea91c2010-05-17 10:00:07 +0200219 return nonseekable_open(inode, filp);
Frank Munzert099b7652009-03-26 15:23:43 +0100220}
221
222static int zcore_reipl_release(struct inode *inode, struct file *filp)
223{
224 return 0;
225}
226
227static const struct file_operations zcore_reipl_fops = {
228 .owner = THIS_MODULE,
229 .write = zcore_reipl_write,
230 .open = zcore_reipl_open,
231 .release = zcore_reipl_release,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200232 .llseek = no_llseek,
Frank Munzert099b7652009-03-26 15:23:43 +0100233};
234
Michael Holzheub4b3d122013-01-21 18:37:41 +0100235static ssize_t zcore_hsa_read(struct file *filp, char __user *buf,
236 size_t count, loff_t *ppos)
237{
238 static char str[18];
239
240 if (hsa_available)
David Hildenbrand37c5f6c2015-05-06 13:18:59 +0200241 snprintf(str, sizeof(str), "%lx\n", sclp.hsa_size);
Michael Holzheub4b3d122013-01-21 18:37:41 +0100242 else
243 snprintf(str, sizeof(str), "0\n");
244 return simple_read_from_buffer(buf, count, ppos, str, strlen(str));
245}
246
247static ssize_t zcore_hsa_write(struct file *filp, const char __user *buf,
248 size_t count, loff_t *ppos)
249{
250 char value;
251
252 if (*ppos != 0)
253 return -EPIPE;
254 if (copy_from_user(&value, buf, 1))
255 return -EFAULT;
256 if (value != '0')
257 return -EINVAL;
258 release_hsa();
259 return count;
260}
261
262static const struct file_operations zcore_hsa_fops = {
263 .owner = THIS_MODULE,
264 .write = zcore_hsa_write,
265 .read = zcore_hsa_read,
266 .open = nonseekable_open,
267 .llseek = no_llseek,
268};
269
Michael Holzheu411ed322007-04-27 16:01:49 +0200270static int __init check_sdias(void)
271{
David Hildenbrand37c5f6c2015-05-06 13:18:59 +0200272 if (!sclp.hsa_size) {
Michael Holzheu2a062ab2008-07-14 09:59:38 +0200273 TRACE("Could not determine HSA size\n");
Michael Holzheue657d8f2013-11-13 10:38:27 +0100274 return -ENODEV;
Michael Holzheu411ed322007-04-27 16:01:49 +0200275 }
276 return 0;
277}
278
Frank Munzert099b7652009-03-26 15:23:43 +0100279/*
280 * Provide IPL parameter information block from either HSA or memory
281 * for future reipl
282 */
283static int __init zcore_reipl_init(void)
284{
285 struct ipib_info ipib_info;
286 int rc;
287
288 rc = memcpy_hsa_kernel(&ipib_info, __LC_DUMP_REIPL, sizeof(ipib_info));
289 if (rc)
290 return rc;
291 if (ipib_info.ipib == 0)
292 return 0;
293 ipl_block = (void *) __get_free_page(GFP_KERNEL);
294 if (!ipl_block)
295 return -ENOMEM;
David Hildenbrand37c5f6c2015-05-06 13:18:59 +0200296 if (ipib_info.ipib < sclp.hsa_size)
Frank Munzert099b7652009-03-26 15:23:43 +0100297 rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
298 else
Michael Holzheu92fe3132010-03-24 11:49:50 +0100299 rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE);
Michael Holzheu76ef9642010-04-22 17:17:07 +0200300 if (rc || csum_partial(ipl_block, ipl_block->hdr.len, 0) !=
Frank Munzert159d1ff2009-03-26 15:24:45 +0100301 ipib_info.checksum) {
Frank Munzert099b7652009-03-26 15:23:43 +0100302 TRACE("Checksum does not match\n");
303 free_page((unsigned long) ipl_block);
304 ipl_block = NULL;
305 }
306 return 0;
307}
308
Michael Holzheu411ed322007-04-27 16:01:49 +0200309static int __init zcore_init(void)
310{
311 unsigned char arch;
312 int rc;
313
314 if (ipl_info.type != IPL_TYPE_FCP_DUMP)
315 return -ENODATA;
Michael Holzheu3f25dc42011-11-14 11:19:05 +0100316 if (OLDMEM_BASE)
317 return -ENODATA;
Michael Holzheu411ed322007-04-27 16:01:49 +0200318
319 zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));
320 debug_register_view(zcore_dbf, &debug_sprintf_view);
321 debug_set_level(zcore_dbf, 6);
322
323 TRACE("devno: %x\n", ipl_info.data.fcp.dev_id.devno);
324 TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn);
325 TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun);
326
Heiko Carstens763968e2007-05-10 15:45:46 +0200327 rc = sclp_sdias_init();
Michael Holzheu411ed322007-04-27 16:01:49 +0200328 if (rc)
329 goto fail;
330
331 rc = check_sdias();
Michael Holzheu2a062ab2008-07-14 09:59:38 +0200332 if (rc)
Michael Holzheu411ed322007-04-27 16:01:49 +0200333 goto fail;
Michael Holzheub4b3d122013-01-21 18:37:41 +0100334 hsa_available = 1;
Michael Holzheu411ed322007-04-27 16:01:49 +0200335
336 rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
Michael Holzheu2a062ab2008-07-14 09:59:38 +0200337 if (rc)
Michael Holzheu411ed322007-04-27 16:01:49 +0200338 goto fail;
Michael Holzheu411ed322007-04-27 16:01:49 +0200339
Heiko Carstensf64ca212010-02-26 22:37:32 +0100340 if (arch == ARCH_S390) {
341 pr_alert("The 64-bit dump tool cannot be used for a "
342 "32-bit system\n");
343 rc = -EINVAL;
344 goto fail;
345 }
Michael Holzheu411ed322007-04-27 16:01:49 +0200346
Martin Schwidefskyffa52d02015-10-28 09:47:58 +0100347 pr_alert("DETECTED 'S390X (64 bit) OS'\n");
348 rc = init_cpu_info();
Michael Holzheu2a062ab2008-07-14 09:59:38 +0200349 if (rc)
Michael Holzheu411ed322007-04-27 16:01:49 +0200350 goto fail;
Michael Holzheu411ed322007-04-27 16:01:49 +0200351
Frank Munzert099b7652009-03-26 15:23:43 +0100352 rc = zcore_reipl_init();
353 if (rc)
354 goto fail;
355
Michael Holzheu411ed322007-04-27 16:01:49 +0200356 zcore_dir = debugfs_create_dir("zcore" , NULL);
357 if (!zcore_dir) {
358 rc = -ENOMEM;
359 goto fail;
360 }
Frank Munzert12e0c952008-07-17 17:16:40 +0200361 zcore_memmap_file = debugfs_create_file("memmap", S_IRUSR, zcore_dir,
362 NULL, &zcore_memmap_fops);
363 if (!zcore_memmap_file) {
364 rc = -ENOMEM;
Martin Schwidefskyffa52d02015-10-28 09:47:58 +0100365 goto fail_dir;
Michael Holzheu411ed322007-04-27 16:01:49 +0200366 }
Frank Munzert099b7652009-03-26 15:23:43 +0100367 zcore_reipl_file = debugfs_create_file("reipl", S_IRUSR, zcore_dir,
368 NULL, &zcore_reipl_fops);
369 if (!zcore_reipl_file) {
370 rc = -ENOMEM;
371 goto fail_memmap_file;
372 }
Michael Holzheub4b3d122013-01-21 18:37:41 +0100373 zcore_hsa_file = debugfs_create_file("hsa", S_IRUSR|S_IWUSR, zcore_dir,
374 NULL, &zcore_hsa_fops);
375 if (!zcore_hsa_file) {
376 rc = -ENOMEM;
377 goto fail_reipl_file;
378 }
Michael Holzheu411ed322007-04-27 16:01:49 +0200379 return 0;
380
Michael Holzheub4b3d122013-01-21 18:37:41 +0100381fail_reipl_file:
382 debugfs_remove(zcore_reipl_file);
Frank Munzert099b7652009-03-26 15:23:43 +0100383fail_memmap_file:
384 debugfs_remove(zcore_memmap_file);
Frank Munzert12e0c952008-07-17 17:16:40 +0200385fail_dir:
386 debugfs_remove(zcore_dir);
Michael Holzheu411ed322007-04-27 16:01:49 +0200387fail:
388 diag308(DIAG308_REL_HSA, NULL);
389 return rc;
390}
391
Michael Holzheu411ed322007-04-27 16:01:49 +0200392static void __exit zcore_exit(void)
393{
394 debug_unregister(zcore_dbf);
Heiko Carstens763968e2007-05-10 15:45:46 +0200395 sclp_sdias_exit();
Frank Munzert099b7652009-03-26 15:23:43 +0100396 free_page((unsigned long) ipl_block);
Michael Holzheub4b3d122013-01-21 18:37:41 +0100397 debugfs_remove(zcore_hsa_file);
Frank Munzert099b7652009-03-26 15:23:43 +0100398 debugfs_remove(zcore_reipl_file);
399 debugfs_remove(zcore_memmap_file);
Frank Munzert099b7652009-03-26 15:23:43 +0100400 debugfs_remove(zcore_dir);
Michael Holzheu411ed322007-04-27 16:01:49 +0200401 diag308(DIAG308_REL_HSA, NULL);
402}
403
Frank Munzert099b7652009-03-26 15:23:43 +0100404MODULE_AUTHOR("Copyright IBM Corp. 2003,2008");
Michael Holzheu411ed322007-04-27 16:01:49 +0200405MODULE_DESCRIPTION("zcore module for zfcpdump support");
406MODULE_LICENSE("GPL");
407
408subsys_initcall(zcore_init);
409module_exit(zcore_exit);