File Index
Files and directories
Sign in
FILE

diag310.c

File preview with syntax highlighting.

linux-6.19.14/arch/s390/kernel/diag/diag310.c C
  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
 20enum diag310_sc {
 21	DIAG310_SUBC_0 = 0,
 22	DIAG310_SUBC_1 = 1,
 23	DIAG310_SUBC_4 = 4,
 24	DIAG310_SUBC_5 = 5
 25};
 26
 27enum 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
 38union diag310_response {
 39	u64 response;
 40	struct {
 41		u64 result	: 32;
 42		u64		: 16;
 43		u64 rc		: 16;
 44	};
 45};
 46
 47union diag310_req_subcode {
 48	u64 subcode;
 49	struct {
 50		u64		: 48;
 51		u64 st		: 8;
 52		u64 sc		: 8;
 53	};
 54};
 55
 56union diag310_req_size {
 57	u64 size;
 58	struct {
 59		u64 page_count	: 32;
 60		u64		: 32;
 61	};
 62};
 63
 64static 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
 76static 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
 88static 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
 99static 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
110static 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
129static 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
150static 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
173static 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
190static 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
209long 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
226long 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
245long 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;
273out:
274	vfree(buf);
275	return rc;
276}
~/ Read-only file preview.