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\n"
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;
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
diag310_memtop_stride(unsigned long arg)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
diag310_memtop_len(unsigned long arg)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
diag310_memtop_buf(unsigned long arg)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