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