0001
0002
0003
0004
0005
0006
0007
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:
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 }