cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hypfs_sprp.c (3364B)


      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}