// SPDX-License-Identifier: GPL-2.0 /* * CIO inject interface * * Copyright IBM Corp. 2021 * Author(s): Vineeth Vijayan <vneethv@linux.ibm.com> */ #define KMSG_COMPONENT "cio" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/mm.h> #include <linux/debugfs.h> #include <asm/chpid.h> #include "cio_inject.h" #include "cio_debug.h" static DEFINE_SPINLOCK(crw_inject_lock); DEFINE_STATIC_KEY_FALSE(cio_inject_enabled); static struct crw *crw_inject_data; /** * crw_inject : Initiate the artificial CRW inject * @crw: The data which needs to be injected as new CRW. * * The CRW handler is called, which will use the provided artificial * data instead of the CRW from the underlying hardware. * * Return: 0 on success */ static int crw_inject(struct crw *crw) { int rc = 0; struct crw *copy; unsigned long flags; copy = kmemdup(crw, sizeof(*crw), GFP_KERNEL); if (!copy) return -ENOMEM; spin_lock_irqsave(&crw_inject_lock, flags); if (crw_inject_data) { kfree(copy); rc = -EBUSY; } else { crw_inject_data = copy; } spin_unlock_irqrestore(&crw_inject_lock, flags); if (!rc) crw_handle_channel_report(); return rc; } /** * stcrw_get_injected: Copy the artificial CRW data to CRW struct. * @crw: The target CRW pointer. * * Retrieve an injected CRW data. Return 0 on success, 1 if no * injected-CRW is available. The function reproduces the return * code of the actual STCRW function. */ int stcrw_get_injected(struct crw *crw) { int rc = 1; unsigned long flags; spin_lock_irqsave(&crw_inject_lock, flags); if (crw_inject_data) { memcpy(crw, crw_inject_data, sizeof(*crw)); kfree(crw_inject_data); crw_inject_data = NULL; rc = 0; } spin_unlock_irqrestore(&crw_inject_lock, flags); return rc; } /* The debugfs write handler for crw_inject nodes operation */ static ssize_t crw_inject_write(struct file *file, const char __user *buf, size_t lbuf, loff_t *ppos) { u32 slct, oflw, chn, rsc, anc, erc, rsid; struct crw crw; char *buffer; int rc; if (!static_branch_likely(&cio_inject_enabled)) { pr_warn("CIO inject is not enabled - ignoring CRW inject\n"); return -EINVAL; } buffer = vmemdup_user(buf, lbuf); if (IS_ERR(buffer)) return -ENOMEM; rc = sscanf(buffer, "%x %x %x %x %x %x %x", &slct, &oflw, &chn, &rsc, &anc, &erc, &rsid); kvfree(buffer); if (rc != 7) { pr_warn("crw_inject: Invalid format (need <solicited> <overflow> <chaining> <rsc> <ancillary> <erc> <rsid>)\n"); return -EINVAL; } memset(&crw, 0, sizeof(crw)); crw.slct = slct; crw.oflw = oflw; crw.chn = chn; crw.rsc = rsc; crw.anc = anc; crw.erc = erc; crw.rsid = rsid; rc = crw_inject(&crw); if (rc) return rc; return lbuf; } /* Debugfs write handler for inject_enable node*/ static ssize_t enable_inject_write(struct file *file, const char __user *buf, size_t lbuf, loff_t *ppos) { unsigned long en = 0; int rc; rc = kstrtoul_from_user(buf, lbuf, 10, &en); if (rc) return rc; switch (en) { case 0: static_branch_disable(&cio_inject_enabled); break; case 1: static_branch_enable(&cio_inject_enabled); break; } return lbuf; } static const struct file_operations crw_fops = { .owner = THIS_MODULE, .write = crw_inject_write, }; static const struct file_operations cio_en_fops = { .owner = THIS_MODULE, .write = enable_inject_write, }; static int __init cio_inject_init(void) { /* enable_inject node enables the static branching */ debugfs_create_file("enable_inject", 0200, cio_debugfs_dir, NULL, &cio_en_fops); debugfs_create_file("crw_inject", 0200, cio_debugfs_dir, NULL, &crw_fops); return 0; } device_initcall(cio_inject_init);