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