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
diag310(unsigned long subcode,unsigned long size,void * addr)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"
70 : [rp] "+d" (rp.pair)
71 : [subcode] "d" (subcode)
72 : "memory");
73 return rp.odd;
74 }
75
diag310_result_to_errno(unsigned int result)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
diag310_get_subcode_mask(unsigned long * mask)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
diag310_get_memtop_stride(unsigned long * stride)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
diag310_get_memtop_size(unsigned long * pages,unsigned long level)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
diag310_store_topology_map(void * buf,unsigned long pages,unsigned long level)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
diag310_check_features(void)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
memtop_get_stride_len(unsigned long * res)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
memtop_get_page_count(unsigned long * res,unsigned long level)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, idx;
194 int rc;
195
196 if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN)
197 return -EINVAL;
198 idx = array_index_nospec(level - 1, ARRAY_SIZE(memtop_pages));
199 pages = READ_ONCE(memtop_pages[idx]);
200 if (!pages) {
201 rc = diag310_get_memtop_size(&pages, level);
202 if (rc)
203 return rc;
204 WRITE_ONCE(memtop_pages[idx], pages);
205 }
206 *res = pages;
207 return 0;
208 }
209
diag310_memtop_stride(unsigned long arg)210 long diag310_memtop_stride(unsigned long arg)
211 {
212 size_t __user *argp = (void __user *)arg;
213 unsigned long stride;
214 int rc;
215
216 rc = diag310_check_features();
217 if (rc)
218 return rc;
219 rc = memtop_get_stride_len(&stride);
220 if (rc)
221 return rc;
222 if (put_user(stride, argp))
223 return -EFAULT;
224 return 0;
225 }
226
diag310_memtop_len(unsigned long arg)227 long diag310_memtop_len(unsigned long arg)
228 {
229 size_t __user *argp = (void __user *)arg;
230 unsigned long pages, level;
231 int rc;
232
233 rc = diag310_check_features();
234 if (rc)
235 return rc;
236 if (get_user(level, argp))
237 return -EFAULT;
238 rc = memtop_get_page_count(&pages, level);
239 if (rc)
240 return rc;
241 if (put_user(pages * PAGE_SIZE, argp))
242 return -EFAULT;
243 return 0;
244 }
245
diag310_memtop_buf(unsigned long arg)246 long diag310_memtop_buf(unsigned long arg)
247 {
248 struct diag310_memtop __user *udata = (struct diag310_memtop __user *)arg;
249 unsigned long level, pages, data_size;
250 u64 address;
251 void *buf;
252 int rc;
253
254 rc = diag310_check_features();
255 if (rc)
256 return rc;
257 if (get_user(level, &udata->nesting_lvl))
258 return -EFAULT;
259 if (get_user(address, &udata->address))
260 return -EFAULT;
261 rc = memtop_get_page_count(&pages, level);
262 if (rc)
263 return rc;
264 data_size = pages * PAGE_SIZE;
265 buf = __vmalloc_node(data_size, PAGE_SIZE, GFP_KERNEL | __GFP_ZERO | __GFP_ACCOUNT,
266 NUMA_NO_NODE, __builtin_return_address(0));
267 if (!buf)
268 return -ENOMEM;
269 rc = diag310_store_topology_map(buf, pages, level);
270 if (rc)
271 goto out;
272 if (copy_to_user((void __user *)address, buf, data_size))
273 rc = -EFAULT;
274 out:
275 vfree(buf);
276 return rc;
277 }
278