Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/hypfs/hypfs_sprp.c
26425 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/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
26
static inline unsigned long __hypfs_sprp_diag304(void *data, unsigned long cmd)
27
{
28
union register_pair r1 = { .even = virt_to_phys(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
37
static 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
43
static void hypfs_sprp_free(const void *data)
44
{
45
free_page((unsigned long) data);
46
}
47
48
static 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
68
static 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);
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;
106
out:
107
kfree(diag304);
108
free_page((unsigned long) data);
109
return rc;
110
}
111
112
static 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
132
static 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
139
void hypfs_sprp_init(void)
140
{
141
if (!sclp.has_sprp)
142
return;
143
hypfs_dbfs_create_file(&hypfs_sprp_file);
144
}
145
146
void hypfs_sprp_exit(void)
147
{
148
if (!sclp.has_sprp)
149
return;
150
hypfs_dbfs_remove_file(&hypfs_sprp_file);
151
}
152
153