File Index
Files and directories
Sign in
FILE

papr-indices.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/pseries/papr-indices.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3#define pr_fmt(fmt) "papr-indices: " fmt
  4
  5#include <linux/build_bug.h>
  6#include <linux/file.h>
  7#include <linux/fs.h>
  8#include <linux/init.h>
  9#include <linux/lockdep.h>
 10#include <linux/kernel.h>
 11#include <linux/miscdevice.h>
 12#include <linux/signal.h>
 13#include <linux/slab.h>
 14#include <linux/string.h>
 15#include <linux/string_helpers.h>
 16#include <linux/uaccess.h>
 17#include <asm/machdep.h>
 18#include <asm/rtas-work-area.h>
 19#include <asm/rtas.h>
 20#include <uapi/asm/papr-indices.h>
 21#include "papr-rtas-common.h"
 22
 23/*
 24 * Function-specific return values for ibm,set-dynamic-indicator and
 25 * ibm,get-dynamic-sensor-state RTAS calls.
 26 * PAPR+ v2.13 7.3.18 and 7.3.19.
 27 */
 28#define RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR	-3
 29
 30/**
 31 * struct rtas_get_indices_params - Parameters (in and out) for
 32 *                                      ibm,get-indices.
 33 * @is_sensor:	In: Caller-provided whether sensor or indicator.
 34 * @indice_type:In: Caller-provided indice (sensor or indicator) token
 35 * @work_area:	In: Caller-provided work area buffer for results.
 36 * @next:	In: Sequence number. Out: Next sequence number.
 37 * @status:	Out: RTAS call status.
 38 */
 39struct rtas_get_indices_params {
 40	u8 is_sensor;
 41	u32 indice_type;
 42	struct rtas_work_area *work_area;
 43	u32 next;
 44	s32 status;
 45};
 46
 47/*
 48 * rtas_ibm_get_indices() - Call ibm,get-indices to fill a work area buffer.
 49 * @params: See &struct rtas_ibm_get_indices_params.
 50 *
 51 * Calls ibm,get-indices until it errors or successfully deposits data
 52 * into the supplied work area. Handles RTAS retry statuses. Maps RTAS
 53 * error statuses to reasonable errno values.
 54 *
 55 * The caller is expected to invoke rtas_ibm_get_indices() multiple times
 56 * to retrieve all indices data for the provided indice type. Only one
 57 * sequence should be in progress at any time; starting a new sequence
 58 * will disrupt any sequence already in progress. Serialization of
 59 * indices retrieval sequences is the responsibility of the caller.
 60 *
 61 * The caller should inspect @params.status to determine whether more
 62 * calls are needed to complete the sequence.
 63 *
 64 * Context: May sleep.
 65 * Return: -ve on error, 0 otherwise.
 66 */
 67static int rtas_ibm_get_indices(struct rtas_get_indices_params *params)
 68{
 69	struct rtas_work_area *work_area = params->work_area;
 70	const s32 token = rtas_function_token(RTAS_FN_IBM_GET_INDICES);
 71	u32 rets;
 72	s32 fwrc;
 73	int ret;
 74
 75	if (token == RTAS_UNKNOWN_SERVICE)
 76		return -ENOENT;
 77
 78	lockdep_assert_held(&rtas_ibm_get_indices_lock);
 79
 80	do {
 81		fwrc = rtas_call(token, 5, 2, &rets, params->is_sensor,
 82				params->indice_type,
 83				rtas_work_area_phys(work_area),
 84				rtas_work_area_size(work_area),
 85				params->next);
 86	} while (rtas_busy_delay(fwrc));
 87
 88	switch (fwrc) {
 89	case RTAS_HARDWARE_ERROR:
 90		ret = -EIO;
 91		break;
 92	case RTAS_INVALID_PARAMETER: /* Indicator type is not supported */
 93		ret = -EINVAL;
 94		break;
 95	case RTAS_SEQ_START_OVER:
 96		ret = -EAGAIN;
 97		pr_info_ratelimited("Indices changed during retrieval, retrying\n");
 98		params->next = 1;
 99		break;
100	case RTAS_SEQ_MORE_DATA:
101		params->next = rets;
102		ret = 0;
103		break;
104	case RTAS_SEQ_COMPLETE:
105		params->next = 0;
106		ret = 0;
107		break;
108	default:
109		ret = -EIO;
110		pr_err_ratelimited("unexpected ibm,get-indices status %d\n", fwrc);
111		break;
112	}
113
114	params->status = fwrc;
115	return ret;
116}
117
118/*
119 * Internal indices sequence APIs. A sequence is a series of calls to
120 * ibm,get-indices for a given location code. The sequence ends when
121 * an error is encountered or all indices for the input has been
122 * returned.
123 */
124
125/*
126 * indices_sequence_begin() - Begin a indices retrieval sequence.
127 *
128 * Context: May sleep.
129 */
130static void indices_sequence_begin(struct papr_rtas_sequence *seq)
131{
132	struct rtas_get_indices_params  *param;
133
134	param = (struct rtas_get_indices_params *)seq->params;
135	/*
136	 * We could allocate the work area before acquiring the
137	 * function lock, but that would allow concurrent requests to
138	 * exhaust the limited work area pool for no benefit. So
139	 * allocate the work area under the lock.
140	 */
141	mutex_lock(&rtas_ibm_get_indices_lock);
142	param->work_area = rtas_work_area_alloc(RTAS_GET_INDICES_BUF_SIZE);
143	param->next = 1;
144	param->status = 0;
145}
146
147/*
148 * indices_sequence_end() - Finalize a indices retrieval sequence.
149 *
150 * Releases resources obtained by indices_sequence_begin().
151 */
152static void indices_sequence_end(struct papr_rtas_sequence *seq)
153{
154	struct rtas_get_indices_params *param;
155
156	param =  (struct rtas_get_indices_params *)seq->params;
157	rtas_work_area_free(param->work_area);
158	mutex_unlock(&rtas_ibm_get_indices_lock);
159}
160
161/*
162 * Work function to be passed to papr_rtas_blob_generate().
163 *
164 * ibm,get-indices RTAS call fills the work area with the certain
165 * format but does not return the bytes written in the buffer. So
166 * instead of kernel parsing this work area to determine the buffer
167 * length, copy the complete work area (RTAS_GET_INDICES_BUF_SIZE)
168 * to the blob and let the user space to obtain the data.
169 * Means RTAS_GET_INDICES_BUF_SIZE data will be returned for each
170 * read().
171 */
172
173static const char *indices_sequence_fill_work_area(struct papr_rtas_sequence *seq,
174						size_t *len)
175{
176	struct rtas_get_indices_params *p;
177	bool init_state;
178
179	p = (struct rtas_get_indices_params *)seq->params;
180	init_state = (p->next == 1) ? true : false;
181
182	if (papr_rtas_sequence_should_stop(seq, p->status, init_state))
183		return NULL;
184	if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_indices(p)))
185		return NULL;
186
187	*len = RTAS_GET_INDICES_BUF_SIZE;
188	return rtas_work_area_raw_buf(p->work_area);
189}
190
191/*
192 * papr_indices_handle_read - returns indices blob data to the user space
193 *
194 * ibm,get-indices RTAS call fills the work area with the certian
195 * format but does not return the bytes written in the buffer and
196 * copied RTAS_GET_INDICES_BUF_SIZE data to the blob for each RTAS
197 * call. So send RTAS_GET_INDICES_BUF_SIZE buffer to the user space
198 * for each read().
199 */
200static ssize_t papr_indices_handle_read(struct file *file,
201		char __user *buf, size_t size, loff_t *off)
202{
203	const struct papr_rtas_blob *blob = file->private_data;
204
205	/* we should not instantiate a handle without any data attached. */
206	if (!papr_rtas_blob_has_data(blob)) {
207		pr_err_once("handle without data\n");
208		return -EIO;
209	}
210
211	if (size < RTAS_GET_INDICES_BUF_SIZE) {
212		pr_err_once("Invalid buffer length %ld, expect %d\n",
213				size, RTAS_GET_INDICES_BUF_SIZE);
214		return -EINVAL;
215	} else if (size > RTAS_GET_INDICES_BUF_SIZE)
216		size = RTAS_GET_INDICES_BUF_SIZE;
217
218	return simple_read_from_buffer(buf, size, off, blob->data, blob->len);
219}
220
221static const struct file_operations papr_indices_handle_ops = {
222	.read = papr_indices_handle_read,
223	.llseek = papr_rtas_common_handle_seek,
224	.release = papr_rtas_common_handle_release,
225};
226
227/*
228 * papr_indices_create_handle() - Create a fd-based handle for reading
229 *                                indices data
230 * @ubuf: Input parameters to RTAS call such as whether sensor or indicator
231 *        and indice type in user memory
232 *
233 * Handler for PAPR_INDICES_IOC_GET ioctl command. Validates @ubuf
234 * and instantiates an immutable indices "blob" for it. The blob is
235 * attached to a file descriptor for reading by user space. The memory
236 * backing the blob is freed when the file is released.
237 *
238 * The entire requested indices is retrieved by this call and all
239 * necessary RTAS interactions are performed before returning the fd
240 * to user space. This keeps the read handler simple and ensures that
241 * the kernel can prevent interleaving of ibm,get-indices call sequences.
242 *
243 * Return: The installed fd number if successful, -ve errno otherwise.
244 */
245static long papr_indices_create_handle(struct papr_indices_io_block __user *ubuf)
246{
247	struct papr_rtas_sequence seq = {};
248	struct rtas_get_indices_params params = {};
249	int fd;
250
251	if (get_user(params.is_sensor, &ubuf->indices.is_sensor))
252		return -EFAULT;
253
254	if (get_user(params.indice_type, &ubuf->indices.indice_type))
255		return -EFAULT;
256
257	seq = (struct papr_rtas_sequence) {
258		.begin = indices_sequence_begin,
259		.end = indices_sequence_end,
260		.work = indices_sequence_fill_work_area,
261	};
262
263	seq.params = &params;
264	fd = papr_rtas_setup_file_interface(&seq,
265			&papr_indices_handle_ops, "[papr-indices]");
266
267	return fd;
268}
269
270/*
271 * Create work area with the input parameters. This function is used
272 * for both ibm,set-dynamic-indicator and ibm,get-dynamic-sensor-state
273 * RTAS Calls.
274 */
275static struct rtas_work_area *
276papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user *ubuf,
277				struct papr_indices_io_block *kbuf)
278{
279	struct rtas_work_area *work_area;
280	u32 length;
281	__be32 len_be;
282
283	if (copy_from_user(kbuf, ubuf, sizeof(*kbuf)))
284		return ERR_PTR(-EFAULT);
285
286
287	if (!string_is_terminated(kbuf->dynamic_param.location_code_str,
288			ARRAY_SIZE(kbuf->dynamic_param.location_code_str)))
289		return ERR_PTR(-EINVAL);
290
291	/*
292	 * The input data in the work area should be as follows:
293	 * - 32-bit integer length of the location code string,
294	 *   including NULL.
295	 * - Location code string, NULL terminated, identifying the
296	 *   token (sensor or indicator).
297	 * PAPR 2.13 - R1–7.3.18–5 ibm,set-dynamic-indicator
298	 *           - R1–7.3.19–5 ibm,get-dynamic-sensor-state
299	 */
300	/*
301	 * Length that user space passed should also include NULL
302	 * terminator.
303	 */
304	length = strlen(kbuf->dynamic_param.location_code_str) + 1;
305	if (length > LOC_CODE_SIZE)
306		return ERR_PTR(-EINVAL);
307
308	len_be = cpu_to_be32(length);
309
310	work_area = rtas_work_area_alloc(LOC_CODE_SIZE + sizeof(u32));
311	memcpy(rtas_work_area_raw_buf(work_area), &len_be, sizeof(u32));
312	memcpy((rtas_work_area_raw_buf(work_area) + sizeof(u32)),
313			&kbuf->dynamic_param.location_code_str, length);
314
315	return work_area;
316}
317
318/**
319 * papr_dynamic_indicator_ioc_set - ibm,set-dynamic-indicator RTAS Call
320 * PAPR 2.13 7.3.18
321 *
322 * @ubuf: Input parameters to RTAS call such as indicator token and
323 *        new state.
324 *
325 * Returns success or -errno.
326 */
327static long papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user *ubuf)
328{
329	struct papr_indices_io_block kbuf;
330	struct rtas_work_area *work_area;
331	s32 fwrc, token, ret;
332
333	token = rtas_function_token(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR);
334	if (token == RTAS_UNKNOWN_SERVICE)
335		return -ENOENT;
336
337	mutex_lock(&rtas_ibm_set_dynamic_indicator_lock);
338	work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
339	if (IS_ERR(work_area)) {
340		ret = PTR_ERR(work_area);
341		goto out;
342	}
343
344	do {
345		fwrc = rtas_call(token, 3, 1, NULL,
346				kbuf.dynamic_param.token,
347				kbuf.dynamic_param.state,
348				rtas_work_area_phys(work_area));
349	} while (rtas_busy_delay(fwrc));
350
351	rtas_work_area_free(work_area);
352
353	switch (fwrc) {
354	case RTAS_SUCCESS:
355		ret = 0;
356		break;
357	case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR:	/* No such indicator */
358		ret = -EOPNOTSUPP;
359		break;
360	default:
361		pr_err("unexpected ibm,set-dynamic-indicator result %d\n",
362			fwrc);
363		fallthrough;
364	case RTAS_HARDWARE_ERROR:	/* Hardware/platform error */
365		ret = -EIO;
366		break;
367	}
368
369out:
370	mutex_unlock(&rtas_ibm_set_dynamic_indicator_lock);
371	return ret;
372}
373
374/**
375 * papr_dynamic_sensor_ioc_get - ibm,get-dynamic-sensor-state RTAS Call
376 * PAPR 2.13 7.3.19
377 *
378 * @ubuf: Input parameters to RTAS call such as sensor token
379 *        Copies the state in user space buffer.
380 *
381 *
382 * Returns success or -errno.
383 */
384
385static long papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user *ubuf)
386{
387	struct papr_indices_io_block kbuf;
388	struct rtas_work_area *work_area;
389	s32 fwrc, token, ret;
390	u32 rets;
391
392	token = rtas_function_token(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE);
393	if (token == RTAS_UNKNOWN_SERVICE)
394		return -ENOENT;
395
396	mutex_lock(&rtas_ibm_get_dynamic_sensor_state_lock);
397	work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
398	if (IS_ERR(work_area)) {
399		ret = PTR_ERR(work_area);
400		goto out;
401	}
402
403	do {
404		fwrc = rtas_call(token, 2, 2, &rets,
405				kbuf.dynamic_param.token,
406				rtas_work_area_phys(work_area));
407	} while (rtas_busy_delay(fwrc));
408
409	rtas_work_area_free(work_area);
410
411	switch (fwrc) {
412	case RTAS_SUCCESS:
413		if (put_user(rets, &ubuf->dynamic_param.state))
414			ret = -EFAULT;
415		else
416			ret = 0;
417		break;
418	case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR:	/* No such indicator */
419		ret = -EOPNOTSUPP;
420		break;
421	default:
422		pr_err("unexpected ibm,get-dynamic-sensor result %d\n",
423				fwrc);
424		fallthrough;
425	case RTAS_HARDWARE_ERROR:	/* Hardware/platform error */
426		ret = -EIO;
427		break;
428	}
429
430out:
431	mutex_unlock(&rtas_ibm_get_dynamic_sensor_state_lock);
432	return ret;
433}
434
435/*
436 * Top-level ioctl handler for /dev/papr-indices.
437 */
438static long papr_indices_dev_ioctl(struct file *filp, unsigned int ioctl,
439				unsigned long arg)
440{
441	void __user *argp = (__force void __user *)arg;
442	long ret;
443
444	switch (ioctl) {
445	case PAPR_INDICES_IOC_GET:
446		ret = papr_indices_create_handle(argp);
447		break;
448	case PAPR_DYNAMIC_SENSOR_IOC_GET:
449		ret = papr_dynamic_sensor_ioc_get(argp);
450		break;
451	case PAPR_DYNAMIC_INDICATOR_IOC_SET:
452		if (filp->f_mode & FMODE_WRITE)
453			ret = papr_dynamic_indicator_ioc_set(argp);
454		else
455			ret = -EBADF;
456		break;
457	default:
458		ret = -ENOIOCTLCMD;
459		break;
460	}
461
462	return ret;
463}
464
465static const struct file_operations papr_indices_ops = {
466	.unlocked_ioctl = papr_indices_dev_ioctl,
467};
468
469static struct miscdevice papr_indices_dev = {
470	.minor = MISC_DYNAMIC_MINOR,
471	.name = "papr-indices",
472	.fops = &papr_indices_ops,
473};
474
475static __init int papr_indices_init(void)
476{
477	if (!rtas_function_implemented(RTAS_FN_IBM_GET_INDICES))
478		return -ENODEV;
479
480	if (!rtas_function_implemented(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR))
481		return -ENODEV;
482
483	if (!rtas_function_implemented(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE))
484		return -ENODEV;
485
486	return misc_register(&papr_indices_dev);
487}
488machine_device_initcall(pseries, papr_indices_init);
~/ Read-only file preview.