s390/sclp: Add SCLP character device driver
authorMichael Holzheu <holzheu@linux.vnet.ibm.com>
Thu, 6 Jun 2013 07:52:08 +0000 (09:52 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 26 Jun 2013 19:10:13 +0000 (21:10 +0200)
Add a character misc device "sclp_ctl" that allows to run SCCBs
from user space using the SCLP_CTL_SCCB ioctl.

Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Documentation/ioctl/ioctl-number.txt
arch/s390/include/uapi/asm/Kbuild
arch/s390/include/uapi/asm/sclp_ctl.h [new file with mode: 0644]
drivers/s390/char/Makefile
drivers/s390/char/sclp.c
drivers/s390/char/sclp.h
drivers/s390/char/sclp_cmd.c
drivers/s390/char/sclp_ctl.c [new file with mode: 0644]

index 237acab169dd723b5a3f9ec3e40c79d55246f48a..2a5f0e14efa351a73ef1eb53c392ecef7c9b4f6b 100644 (file)
@@ -72,6 +72,7 @@ Code  Seq#(hex)       Include File            Comments
 0x06   all     linux/lp.h
 0x09   all     linux/raid/md_u.h
 0x10   00-0F   drivers/char/s390/vmcp.h
+0x10   10-1F   arch/s390/include/uapi/sclp_ctl.h
 0x12   all     linux/fs.h
                linux/blkpg.h
 0x1b   all     InfiniBand Subsystem    <http://infiniband.sourceforge.net/>
index 9ccd1905bdad45da5d0f1b18ffae312f59957a7b..6a9a9eb645f523ee203ae87f3c4395de18b578f7 100644 (file)
@@ -35,6 +35,7 @@ header-y += siginfo.h
 header-y += signal.h
 header-y += socket.h
 header-y += sockios.h
+header-y += sclp_ctl.h
 header-y += stat.h
 header-y += statfs.h
 header-y += swab.h
diff --git a/arch/s390/include/uapi/asm/sclp_ctl.h b/arch/s390/include/uapi/asm/sclp_ctl.h
new file mode 100644 (file)
index 0000000..f281861
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * IOCTL interface for SCLP
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#ifndef _ASM_SCLP_CTL_H
+#define _ASM_SCLP_CTL_H
+
+#include <linux/types.h>
+
+struct sclp_ctl_sccb {
+       __u32   cmdw;
+       __u64   sccb;
+} __attribute__((packed));
+
+#define SCLP_CTL_IOCTL_MAGIC 0x10
+
+#define SCLP_CTL_SCCB \
+       _IOWR(SCLP_CTL_IOCTL_MAGIC, 0x10, struct sclp_ctl_sccb)
+
+#endif
index f3c325207445513c5aca471d413845b6a90980d3..17821a026c9ca9b4ae6878bb57c04a56692850e4 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
-        sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o
+        sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o
 
 obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
index a77febeead1fdd4fea04eb56969d377d6a18caca..97319d692b047df98dd332ee248e9a64911a4a1f 100644 (file)
@@ -148,14 +148,19 @@ static int sclp_init(void);
 int
 sclp_service_call(sclp_cmdw_t command, void *sccb)
 {
-       int cc;
+       int cc = 4; /* Initialize for program check handling */
 
        asm volatile(
-               "       .insn   rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
-               "       ipm     %0\n"
-               "       srl     %0,28"
-               : "=&d" (cc) : "d" (command), "a" (__pa(sccb))
+               "0:     .insn   rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
+               "1:     ipm     %0\n"
+               "       srl     %0,28\n"
+               "2:\n"
+               EX_TABLE(0b, 2b)
+               EX_TABLE(1b, 2b)
+               : "+&d" (cc) : "d" (command), "a" (__pa(sccb))
                : "cc", "memory");
+       if (cc == 4)
+               return -EINVAL;
        if (cc == 3)
                return -EIO;
        if (cc == 2)
index e11383f5d3d2c2e3bb093397b39adec2191d872c..40d1406289ed2533f6bd4bdffda0b0e19a80a79e 100644 (file)
@@ -171,6 +171,7 @@ int sclp_remove_processed(struct sccb_header *sccb);
 int sclp_deactivate(void);
 int sclp_reactivate(void);
 int sclp_service_call(sclp_cmdw_t command, void *sccb);
+int sclp_sync_request(sclp_cmdw_t command, void *sccb);
 
 int sclp_sdias_init(void);
 void sclp_sdias_exit(void);
index bf07c3a188d4c0f5b694b51d84f053ab3b526838..657b0348579c93cd601c33286a696f8c1c39d549 100644 (file)
@@ -195,7 +195,7 @@ static void sclp_sync_callback(struct sclp_req *req, void *data)
        complete(completion);
 }
 
-static int do_sync_request(sclp_cmdw_t cmd, void *sccb)
+int sclp_sync_request(sclp_cmdw_t cmd, void *sccb)
 {
        struct completion completion;
        struct sclp_req *request;
@@ -270,7 +270,7 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)
        if (!sccb)
                return -ENOMEM;
        sccb->header.length = sizeof(*sccb);
-       rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
+       rc = sclp_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
        if (rc)
                goto out;
        if (sccb->header.response_code != 0x0010) {
@@ -304,7 +304,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd)
        if (!sccb)
                return -ENOMEM;
        sccb->header.length = sizeof(*sccb);
-       rc = do_sync_request(cmd, sccb);
+       rc = sclp_sync_request(cmd, sccb);
        if (rc)
                goto out;
        switch (sccb->header.response_code) {
@@ -374,7 +374,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
                return -ENOMEM;
        sccb->header.length = PAGE_SIZE;
        sccb->rn = rn;
-       rc = do_sync_request(cmd, sccb);
+       rc = sclp_sync_request(cmd, sccb);
        if (rc)
                goto out;
        switch (sccb->header.response_code) {
@@ -429,7 +429,7 @@ static int sclp_attach_storage(u8 id)
        if (!sccb)
                return -ENOMEM;
        sccb->header.length = PAGE_SIZE;
-       rc = do_sync_request(0x00080001 | id << 8, sccb);
+       rc = sclp_sync_request(0x00080001 | id << 8, sccb);
        if (rc)
                goto out;
        switch (sccb->header.response_code) {
@@ -627,7 +627,7 @@ static int __init sclp_detect_standby_memory(void)
        for (id = 0; id <= sclp_max_storage_id; id++) {
                memset(sccb, 0, PAGE_SIZE);
                sccb->header.length = PAGE_SIZE;
-               rc = do_sync_request(0x00040001 | id << 8, sccb);
+               rc = sclp_sync_request(0x00040001 | id << 8, sccb);
                if (rc)
                        goto out;
                switch (sccb->header.response_code) {
@@ -714,7 +714,7 @@ static int do_pci_configure(sclp_cmdw_t cmd, u32 fid)
        sccb->header.length = PAGE_SIZE;
        sccb->atype = SCLP_RECONFIG_PCI_ATPYE;
        sccb->aid = fid;
-       rc = do_sync_request(cmd, sccb);
+       rc = sclp_sync_request(cmd, sccb);
        if (rc)
                goto out;
        switch (sccb->header.response_code) {
@@ -771,7 +771,7 @@ static int do_chp_configure(sclp_cmdw_t cmd)
        if (!sccb)
                return -ENOMEM;
        sccb->header.length = sizeof(*sccb);
-       rc = do_sync_request(cmd, sccb);
+       rc = sclp_sync_request(cmd, sccb);
        if (rc)
                goto out;
        switch (sccb->header.response_code) {
@@ -846,7 +846,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info)
        if (!sccb)
                return -ENOMEM;
        sccb->header.length = sizeof(*sccb);
-       rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);
+       rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);
        if (rc)
                goto out;
        if (sccb->header.response_code != 0x0010) {
diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c
new file mode 100644 (file)
index 0000000..abe8ef1
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * IOCTL interface for SCLP
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <linux/compat.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/gfp.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/compat.h>
+#include <asm/compat.h>
+#include <asm/sclp_ctl.h>
+#include <asm/sclp.h>
+
+#include "sclp.h"
+
+/*
+ * Supported command words
+ */
+static unsigned int sclp_ctl_sccb_wlist[] = {
+       0x00400002,
+       0x00410002,
+};
+
+/*
+ * Check if command word is supported
+ */
+static int sclp_ctl_cmdw_supported(unsigned int cmdw)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) {
+               if (cmdw == sclp_ctl_sccb_wlist[i])
+                       return 1;
+       }
+       return 0;
+}
+
+static void __user *u64_to_uptr(u64 value)
+{
+       if (is_compat_task())
+               return compat_ptr(value);
+       else
+               return (void __user *)(unsigned long)value;
+}
+
+/*
+ * Start SCLP request
+ */
+static int sclp_ctl_ioctl_sccb(void __user *user_area)
+{
+       struct sclp_ctl_sccb ctl_sccb;
+       struct sccb_header *sccb;
+       int rc;
+
+       if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
+               return -EFAULT;
+       if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
+               return -EOPNOTSUPP;
+       sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+       if (!sccb)
+               return -ENOMEM;
+       if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) {
+               rc = -EFAULT;
+               goto out_free;
+       }
+       if (sccb->length > PAGE_SIZE || sccb->length < 8)
+               return -EINVAL;
+       if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) {
+               rc = -EFAULT;
+               goto out_free;
+       }
+       rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
+       if (rc)
+               goto out_free;
+       if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
+               rc = -EFAULT;
+out_free:
+       free_page((unsigned long) sccb);
+       return rc;
+}
+
+/*
+ * SCLP SCCB ioctl function
+ */
+static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
+                          unsigned long arg)
+{
+       void __user *argp;
+
+       if (is_compat_task())
+               argp = compat_ptr(arg);
+       else
+               argp = (void __user *) arg;
+       switch (cmd) {
+       case SCLP_CTL_SCCB:
+               return sclp_ctl_ioctl_sccb(argp);
+       default: /* unknown ioctl number */
+               return -ENOTTY;
+       }
+}
+
+/*
+ * File operations
+ */
+static const struct file_operations sclp_ctl_fops = {
+       .owner = THIS_MODULE,
+       .open = nonseekable_open,
+       .unlocked_ioctl = sclp_ctl_ioctl,
+       .compat_ioctl = sclp_ctl_ioctl,
+       .llseek = no_llseek,
+};
+
+/*
+ * Misc device definition
+ */
+static struct miscdevice sclp_ctl_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "sclp",
+       .fops = &sclp_ctl_fops,
+};
+
+/*
+ * Register sclp_ctl misc device
+ */
+static int __init sclp_ctl_init(void)
+{
+       return misc_register(&sclp_ctl_device);
+}
+module_init(sclp_ctl_init);
+
+/*
+ * Deregister sclp_ctl misc device
+ */
+static void __exit sclp_ctl_exit(void)
+{
+       misc_deregister(&sclp_ctl_device);
+}
+module_exit(sclp_ctl_exit);