Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/kernel/diag/diag310.c
26489 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Request memory topology information via diag0x310.
4
*
5
* Copyright IBM Corp. 2025
6
*/
7
8
#include <linux/kernel.h>
9
#include <linux/types.h>
10
#include <linux/uaccess.h>
11
#include <linux/vmalloc.h>
12
#include <asm/diag.h>
13
#include <asm/sclp.h>
14
#include <uapi/asm/diag.h>
15
#include "diag_ioctl.h"
16
17
#define DIAG310_LEVELMIN 1
18
#define DIAG310_LEVELMAX 6
19
20
enum diag310_sc {
21
DIAG310_SUBC_0 = 0,
22
DIAG310_SUBC_1 = 1,
23
DIAG310_SUBC_4 = 4,
24
DIAG310_SUBC_5 = 5
25
};
26
27
enum diag310_retcode {
28
DIAG310_RET_SUCCESS = 0x0001,
29
DIAG310_RET_BUSY = 0x0101,
30
DIAG310_RET_OPNOTSUPP = 0x0102,
31
DIAG310_RET_SC4_INVAL = 0x0401,
32
DIAG310_RET_SC4_NODATA = 0x0402,
33
DIAG310_RET_SC5_INVAL = 0x0501,
34
DIAG310_RET_SC5_NODATA = 0x0502,
35
DIAG310_RET_SC5_ESIZE = 0x0503
36
};
37
38
union diag310_response {
39
u64 response;
40
struct {
41
u64 result : 32;
42
u64 : 16;
43
u64 rc : 16;
44
};
45
};
46
47
union diag310_req_subcode {
48
u64 subcode;
49
struct {
50
u64 : 48;
51
u64 st : 8;
52
u64 sc : 8;
53
};
54
};
55
56
union diag310_req_size {
57
u64 size;
58
struct {
59
u64 page_count : 32;
60
u64 : 32;
61
};
62
};
63
64
static inline unsigned long diag310(unsigned long subcode, unsigned long size, void *addr)
65
{
66
union register_pair rp = { .even = (unsigned long)addr, .odd = size };
67
68
diag_stat_inc(DIAG_STAT_X310);
69
asm volatile("diag %[rp],%[subcode],0x310\n"
70
: [rp] "+d" (rp.pair)
71
: [subcode] "d" (subcode)
72
: "memory");
73
return rp.odd;
74
}
75
76
static int diag310_result_to_errno(unsigned int result)
77
{
78
switch (result) {
79
case DIAG310_RET_BUSY:
80
return -EBUSY;
81
case DIAG310_RET_OPNOTSUPP:
82
return -EOPNOTSUPP;
83
default:
84
return -EINVAL;
85
}
86
}
87
88
static int diag310_get_subcode_mask(unsigned long *mask)
89
{
90
union diag310_response res;
91
92
res.response = diag310(DIAG310_SUBC_0, 0, NULL);
93
if (res.rc != DIAG310_RET_SUCCESS)
94
return diag310_result_to_errno(res.rc);
95
*mask = res.response;
96
return 0;
97
}
98
99
static int diag310_get_memtop_stride(unsigned long *stride)
100
{
101
union diag310_response res;
102
103
res.response = diag310(DIAG310_SUBC_1, 0, NULL);
104
if (res.rc != DIAG310_RET_SUCCESS)
105
return diag310_result_to_errno(res.rc);
106
*stride = res.result;
107
return 0;
108
}
109
110
static int diag310_get_memtop_size(unsigned long *pages, unsigned long level)
111
{
112
union diag310_req_subcode req = { .sc = DIAG310_SUBC_4, .st = level };
113
union diag310_response res;
114
115
res.response = diag310(req.subcode, 0, NULL);
116
switch (res.rc) {
117
case DIAG310_RET_SUCCESS:
118
*pages = res.result;
119
return 0;
120
case DIAG310_RET_SC4_NODATA:
121
return -ENODATA;
122
case DIAG310_RET_SC4_INVAL:
123
return -EINVAL;
124
default:
125
return diag310_result_to_errno(res.rc);
126
}
127
}
128
129
static int diag310_store_topology_map(void *buf, unsigned long pages, unsigned long level)
130
{
131
union diag310_req_subcode req_sc = { .sc = DIAG310_SUBC_5, .st = level };
132
union diag310_req_size req_size = { .page_count = pages };
133
union diag310_response res;
134
135
res.response = diag310(req_sc.subcode, req_size.size, buf);
136
switch (res.rc) {
137
case DIAG310_RET_SUCCESS:
138
return 0;
139
case DIAG310_RET_SC5_NODATA:
140
return -ENODATA;
141
case DIAG310_RET_SC5_ESIZE:
142
return -EOVERFLOW;
143
case DIAG310_RET_SC5_INVAL:
144
return -EINVAL;
145
default:
146
return diag310_result_to_errno(res.rc);
147
}
148
}
149
150
static int diag310_check_features(void)
151
{
152
static int features_available;
153
unsigned long mask;
154
int rc;
155
156
if (READ_ONCE(features_available))
157
return 0;
158
if (!sclp.has_diag310)
159
return -EOPNOTSUPP;
160
rc = diag310_get_subcode_mask(&mask);
161
if (rc)
162
return rc;
163
if (!test_bit_inv(DIAG310_SUBC_1, &mask))
164
return -EOPNOTSUPP;
165
if (!test_bit_inv(DIAG310_SUBC_4, &mask))
166
return -EOPNOTSUPP;
167
if (!test_bit_inv(DIAG310_SUBC_5, &mask))
168
return -EOPNOTSUPP;
169
WRITE_ONCE(features_available, 1);
170
return 0;
171
}
172
173
static int memtop_get_stride_len(unsigned long *res)
174
{
175
static unsigned long memtop_stride;
176
unsigned long stride;
177
int rc;
178
179
stride = READ_ONCE(memtop_stride);
180
if (!stride) {
181
rc = diag310_get_memtop_stride(&stride);
182
if (rc)
183
return rc;
184
WRITE_ONCE(memtop_stride, stride);
185
}
186
*res = stride;
187
return 0;
188
}
189
190
static int memtop_get_page_count(unsigned long *res, unsigned long level)
191
{
192
static unsigned long memtop_pages[DIAG310_LEVELMAX];
193
unsigned long pages;
194
int rc;
195
196
if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN)
197
return -EINVAL;
198
pages = READ_ONCE(memtop_pages[level - 1]);
199
if (!pages) {
200
rc = diag310_get_memtop_size(&pages, level);
201
if (rc)
202
return rc;
203
WRITE_ONCE(memtop_pages[level - 1], pages);
204
}
205
*res = pages;
206
return 0;
207
}
208
209
long diag310_memtop_stride(unsigned long arg)
210
{
211
size_t __user *argp = (void __user *)arg;
212
unsigned long stride;
213
int rc;
214
215
rc = diag310_check_features();
216
if (rc)
217
return rc;
218
rc = memtop_get_stride_len(&stride);
219
if (rc)
220
return rc;
221
if (put_user(stride, argp))
222
return -EFAULT;
223
return 0;
224
}
225
226
long diag310_memtop_len(unsigned long arg)
227
{
228
size_t __user *argp = (void __user *)arg;
229
unsigned long pages, level;
230
int rc;
231
232
rc = diag310_check_features();
233
if (rc)
234
return rc;
235
if (get_user(level, argp))
236
return -EFAULT;
237
rc = memtop_get_page_count(&pages, level);
238
if (rc)
239
return rc;
240
if (put_user(pages * PAGE_SIZE, argp))
241
return -EFAULT;
242
return 0;
243
}
244
245
long diag310_memtop_buf(unsigned long arg)
246
{
247
struct diag310_memtop __user *udata = (struct diag310_memtop __user *)arg;
248
unsigned long level, pages, data_size;
249
u64 address;
250
void *buf;
251
int rc;
252
253
rc = diag310_check_features();
254
if (rc)
255
return rc;
256
if (get_user(level, &udata->nesting_lvl))
257
return -EFAULT;
258
if (get_user(address, &udata->address))
259
return -EFAULT;
260
rc = memtop_get_page_count(&pages, level);
261
if (rc)
262
return rc;
263
data_size = pages * PAGE_SIZE;
264
buf = __vmalloc_node(data_size, PAGE_SIZE, GFP_KERNEL | __GFP_ZERO | __GFP_ACCOUNT,
265
NUMA_NO_NODE, __builtin_return_address(0));
266
if (!buf)
267
return -ENOMEM;
268
rc = diag310_store_topology_map(buf, pages, level);
269
if (rc)
270
goto out;
271
if (copy_to_user((void __user *)address, buf, data_size))
272
rc = -EFAULT;
273
out:
274
vfree(buf);
275
return rc;
276
}
277
278