Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/hypfs/hypfs_sprp.c
51805 views
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 <[email protected]>
8
*/
9
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/diag.h>
16
#include <asm/sclp.h>
17
#include "hypfs.h"
18
19
#define DIAG304_SET_WEIGHTS 0
20
#define DIAG304_QUERY_PRP 1
21
#define DIAG304_SET_CAPPING 2
22
23
#define DIAG304_CMD_MAX 2
24
25
static inline unsigned long __hypfs_sprp_diag304(void *data, unsigned long cmd)
26
{
27
union register_pair r1 = { .even = virt_to_phys(data), };
28
29
asm volatile("diag %[r1],%[r3],0x304"
30
: [r1] "+&d" (r1.pair)
31
: [r3] "d" (cmd)
32
: "memory");
33
return r1.odd;
34
}
35
36
static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd)
37
{
38
diag_stat_inc(DIAG_STAT_X304);
39
return __hypfs_sprp_diag304(data, cmd);
40
}
41
42
static void hypfs_sprp_free(const void *data)
43
{
44
free_page((unsigned long) data);
45
}
46
47
static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size)
48
{
49
unsigned long rc;
50
void *data;
51
52
data = (void *) get_zeroed_page(GFP_KERNEL);
53
if (!data)
54
return -ENOMEM;
55
rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP);
56
if (rc != 1) {
57
*data_ptr = *free_ptr = NULL;
58
*size = 0;
59
free_page((unsigned long) data);
60
return -EIO;
61
}
62
*data_ptr = *free_ptr = data;
63
*size = PAGE_SIZE;
64
return 0;
65
}
66
67
static int __hypfs_sprp_ioctl(void __user *user_area)
68
{
69
struct hypfs_diag304 *diag304;
70
unsigned long cmd;
71
void __user *udata;
72
void *data;
73
int rc;
74
75
rc = -ENOMEM;
76
data = (void *)get_zeroed_page(GFP_KERNEL);
77
diag304 = kzalloc(sizeof(*diag304), GFP_KERNEL);
78
if (!data || !diag304)
79
goto out;
80
81
rc = -EFAULT;
82
if (copy_from_user(diag304, user_area, sizeof(*diag304)))
83
goto out;
84
rc = -EINVAL;
85
if ((diag304->args[0] >> 8) != 0 || diag304->args[1] > DIAG304_CMD_MAX)
86
goto out;
87
88
rc = -EFAULT;
89
udata = (void __user *)(unsigned long) diag304->data;
90
if (diag304->args[1] == DIAG304_SET_WEIGHTS ||
91
diag304->args[1] == DIAG304_SET_CAPPING)
92
if (copy_from_user(data, udata, PAGE_SIZE))
93
goto out;
94
95
cmd = *(unsigned long *) &diag304->args[0];
96
diag304->rc = hypfs_sprp_diag304(data, cmd);
97
98
if (diag304->args[1] == DIAG304_QUERY_PRP)
99
if (copy_to_user(udata, data, PAGE_SIZE)) {
100
rc = -EFAULT;
101
goto out;
102
}
103
104
rc = copy_to_user(user_area, diag304, sizeof(*diag304)) ? -EFAULT : 0;
105
out:
106
kfree(diag304);
107
free_page((unsigned long) data);
108
return rc;
109
}
110
111
static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
112
unsigned long arg)
113
{
114
void __user *argp;
115
116
if (!capable(CAP_SYS_ADMIN))
117
return -EACCES;
118
argp = (void __user *)arg;
119
switch (cmd) {
120
case HYPFS_DIAG304:
121
return __hypfs_sprp_ioctl(argp);
122
default: /* unknown ioctl number */
123
return -ENOTTY;
124
}
125
return 0;
126
}
127
128
static struct hypfs_dbfs_file hypfs_sprp_file = {
129
.name = "diag_304",
130
.data_create = hypfs_sprp_create,
131
.data_free = hypfs_sprp_free,
132
.unlocked_ioctl = hypfs_sprp_ioctl,
133
};
134
135
void hypfs_sprp_init(void)
136
{
137
if (!sclp.has_sprp)
138
return;
139
hypfs_dbfs_create_file(&hypfs_sprp_file);
140
}
141
142
void hypfs_sprp_exit(void)
143
{
144
if (!sclp.has_sprp)
145
return;
146
hypfs_dbfs_remove_file(&hypfs_sprp_file);
147
}
148
149