xref: /linux/arch/s390/kernel/diag/diag310.c (revision fab183d632628381b466a41479489541ac0e29a0)
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