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