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