Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *    Hypervisor filesystem for Linux on s390.
0004  *    Set Partition-Resource Parameter interface.
0005  *
0006  *    Copyright IBM Corp. 2013
0007  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
0008  */
0009 
0010 #include <linux/compat.h>
0011 #include <linux/errno.h>
0012 #include <linux/gfp.h>
0013 #include <linux/string.h>
0014 #include <linux/types.h>
0015 #include <linux/uaccess.h>
0016 #include <asm/diag.h>
0017 #include <asm/sclp.h>
0018 #include "hypfs.h"
0019 
0020 #define DIAG304_SET_WEIGHTS 0
0021 #define DIAG304_QUERY_PRP   1
0022 #define DIAG304_SET_CAPPING 2
0023 
0024 #define DIAG304_CMD_MAX     2
0025 
0026 static inline unsigned long __hypfs_sprp_diag304(void *data, unsigned long cmd)
0027 {
0028     union register_pair r1 = { .even = (unsigned long)data, };
0029 
0030     asm volatile("diag %[r1],%[r3],0x304\n"
0031              : [r1] "+&d" (r1.pair)
0032              : [r3] "d" (cmd)
0033              : "memory");
0034     return r1.odd;
0035 }
0036 
0037 static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd)
0038 {
0039     diag_stat_inc(DIAG_STAT_X304);
0040     return __hypfs_sprp_diag304(data, cmd);
0041 }
0042 
0043 static void hypfs_sprp_free(const void *data)
0044 {
0045     free_page((unsigned long) data);
0046 }
0047 
0048 static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size)
0049 {
0050     unsigned long rc;
0051     void *data;
0052 
0053     data = (void *) get_zeroed_page(GFP_KERNEL);
0054     if (!data)
0055         return -ENOMEM;
0056     rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP);
0057     if (rc != 1) {
0058         *data_ptr = *free_ptr = NULL;
0059         *size = 0;
0060         free_page((unsigned long) data);
0061         return -EIO;
0062     }
0063     *data_ptr = *free_ptr = data;
0064     *size = PAGE_SIZE;
0065     return 0;
0066 }
0067 
0068 static int __hypfs_sprp_ioctl(void __user *user_area)
0069 {
0070     struct hypfs_diag304 *diag304;
0071     unsigned long cmd;
0072     void __user *udata;
0073     void *data;
0074     int rc;
0075 
0076     rc = -ENOMEM;
0077     data = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
0078     diag304 = kzalloc(sizeof(*diag304), GFP_KERNEL);
0079     if (!data || !diag304)
0080         goto out;
0081 
0082     rc = -EFAULT;
0083     if (copy_from_user(diag304, user_area, sizeof(*diag304)))
0084         goto out;
0085     rc = -EINVAL;
0086     if ((diag304->args[0] >> 8) != 0 || diag304->args[1] > DIAG304_CMD_MAX)
0087         goto out;
0088 
0089     rc = -EFAULT;
0090     udata = (void __user *)(unsigned long) diag304->data;
0091     if (diag304->args[1] == DIAG304_SET_WEIGHTS ||
0092         diag304->args[1] == DIAG304_SET_CAPPING)
0093         if (copy_from_user(data, udata, PAGE_SIZE))
0094             goto out;
0095 
0096     cmd = *(unsigned long *) &diag304->args[0];
0097     diag304->rc = hypfs_sprp_diag304(data, cmd);
0098 
0099     if (diag304->args[1] == DIAG304_QUERY_PRP)
0100         if (copy_to_user(udata, data, PAGE_SIZE)) {
0101             rc = -EFAULT;
0102             goto out;
0103         }
0104 
0105     rc = copy_to_user(user_area, diag304, sizeof(*diag304)) ? -EFAULT : 0;
0106 out:
0107     kfree(diag304);
0108     free_page((unsigned long) data);
0109     return rc;
0110 }
0111 
0112 static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
0113                    unsigned long arg)
0114 {
0115     void __user *argp;
0116 
0117     if (!capable(CAP_SYS_ADMIN))
0118         return -EACCES;
0119     if (is_compat_task())
0120         argp = compat_ptr(arg);
0121     else
0122         argp = (void __user *) arg;
0123     switch (cmd) {
0124     case HYPFS_DIAG304:
0125         return __hypfs_sprp_ioctl(argp);
0126     default: /* unknown ioctl number */
0127         return -ENOTTY;
0128     }
0129     return 0;
0130 }
0131 
0132 static struct hypfs_dbfs_file hypfs_sprp_file = {
0133     .name       = "diag_304",
0134     .data_create    = hypfs_sprp_create,
0135     .data_free  = hypfs_sprp_free,
0136     .unlocked_ioctl = hypfs_sprp_ioctl,
0137 };
0138 
0139 void hypfs_sprp_init(void)
0140 {
0141     if (!sclp.has_sprp)
0142         return;
0143     hypfs_dbfs_create_file(&hypfs_sprp_file);
0144 }
0145 
0146 void hypfs_sprp_exit(void)
0147 {
0148     if (!sclp.has_sprp)
0149         return;
0150     hypfs_dbfs_remove_file(&hypfs_sprp_file);
0151 }