linux/arch/s390/hypfs/hypfs_sprp.c
<<
>>
Prefs
   1/*
   2 *    Hypervisor filesystem for Linux on s390.
   3 *    Set Partition-Resource Parameter interface.
   4 *
   5 *    Copyright IBM Corp. 2013
   6 *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
   7 */
   8
   9#include <linux/compat.h>
  10#include <linux/errno.h>
  11#include <linux/gfp.h>
  12#include <linux/string.h>
  13#include <linux/types.h>
  14#include <linux/uaccess.h>
  15#include <asm/compat.h>
  16#include <asm/diag.h>
  17#include <asm/sclp.h>
  18#include "hypfs.h"
  19
  20#define DIAG304_SET_WEIGHTS     0
  21#define DIAG304_QUERY_PRP       1
  22#define DIAG304_SET_CAPPING     2
  23
  24#define DIAG304_CMD_MAX         2
  25
  26static inline unsigned long __hypfs_sprp_diag304(void *data, unsigned long cmd)
  27{
  28        register unsigned long _data asm("2") = (unsigned long) data;
  29        register unsigned long _rc asm("3");
  30        register unsigned long _cmd asm("4") = cmd;
  31
  32        asm volatile("diag %1,%2,0x304\n"
  33                     : "=d" (_rc) : "d" (_data), "d" (_cmd) : "memory");
  34
  35        return _rc;
  36}
  37
  38static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd)
  39{
  40        diag_stat_inc(DIAG_STAT_X304);
  41        return __hypfs_sprp_diag304(data, cmd);
  42}
  43
  44static void hypfs_sprp_free(const void *data)
  45{
  46        free_page((unsigned long) data);
  47}
  48
  49static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size)
  50{
  51        unsigned long rc;
  52        void *data;
  53
  54        data = (void *) get_zeroed_page(GFP_KERNEL);
  55        if (!data)
  56                return -ENOMEM;
  57        rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP);
  58        if (rc != 1) {
  59                *data_ptr = *free_ptr = NULL;
  60                *size = 0;
  61                free_page((unsigned long) data);
  62                return -EIO;
  63        }
  64        *data_ptr = *free_ptr = data;
  65        *size = PAGE_SIZE;
  66        return 0;
  67}
  68
  69static int __hypfs_sprp_ioctl(void __user *user_area)
  70{
  71        struct hypfs_diag304 diag304;
  72        unsigned long cmd;
  73        void __user *udata;
  74        void *data;
  75        int rc;
  76
  77        if (copy_from_user(&diag304, user_area, sizeof(diag304)))
  78                return -EFAULT;
  79        if ((diag304.args[0] >> 8) != 0 || diag304.args[1] > DIAG304_CMD_MAX)
  80                return -EINVAL;
  81
  82        data = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  83        if (!data)
  84                return -ENOMEM;
  85
  86        udata = (void __user *)(unsigned long) diag304.data;
  87        if (diag304.args[1] == DIAG304_SET_WEIGHTS ||
  88            diag304.args[1] == DIAG304_SET_CAPPING)
  89                if (copy_from_user(data, udata, PAGE_SIZE)) {
  90                        rc = -EFAULT;
  91                        goto out;
  92                }
  93
  94        cmd = *(unsigned long *) &diag304.args[0];
  95        diag304.rc = hypfs_sprp_diag304(data, cmd);
  96
  97        if (diag304.args[1] == DIAG304_QUERY_PRP)
  98                if (copy_to_user(udata, data, PAGE_SIZE)) {
  99                        rc = -EFAULT;
 100                        goto out;
 101                }
 102
 103        rc = copy_to_user(user_area, &diag304, sizeof(diag304)) ? -EFAULT : 0;
 104out:
 105        free_page((unsigned long) data);
 106        return rc;
 107}
 108
 109static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
 110                               unsigned long arg)
 111{
 112        void __user *argp;
 113
 114        if (!capable(CAP_SYS_ADMIN))
 115                return -EACCES;
 116        if (is_compat_task())
 117                argp = compat_ptr(arg);
 118        else
 119                argp = (void __user *) arg;
 120        switch (cmd) {
 121        case HYPFS_DIAG304:
 122                return __hypfs_sprp_ioctl(argp);
 123        default: /* unknown ioctl number */
 124                return -ENOTTY;
 125        }
 126        return 0;
 127}
 128
 129static struct hypfs_dbfs_file hypfs_sprp_file = {
 130        .name           = "diag_304",
 131        .data_create    = hypfs_sprp_create,
 132        .data_free      = hypfs_sprp_free,
 133        .unlocked_ioctl = hypfs_sprp_ioctl,
 134};
 135
 136int hypfs_sprp_init(void)
 137{
 138        if (!sclp.has_sprp)
 139                return 0;
 140        return hypfs_dbfs_create_file(&hypfs_sprp_file);
 141}
 142
 143void hypfs_sprp_exit(void)
 144{
 145        if (!sclp.has_sprp)
 146                return;
 147        hypfs_dbfs_remove_file(&hypfs_sprp_file);
 148}
 149