xref: /linux/arch/powerpc/platforms/pseries/papr-indices.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
16e9aec54SHaren Myneni // SPDX-License-Identifier: GPL-2.0-only
26e9aec54SHaren Myneni 
36e9aec54SHaren Myneni #define pr_fmt(fmt) "papr-indices: " fmt
46e9aec54SHaren Myneni 
56e9aec54SHaren Myneni #include <linux/build_bug.h>
66e9aec54SHaren Myneni #include <linux/file.h>
76e9aec54SHaren Myneni #include <linux/fs.h>
86e9aec54SHaren Myneni #include <linux/init.h>
96e9aec54SHaren Myneni #include <linux/lockdep.h>
106e9aec54SHaren Myneni #include <linux/kernel.h>
116e9aec54SHaren Myneni #include <linux/miscdevice.h>
126e9aec54SHaren Myneni #include <linux/signal.h>
136e9aec54SHaren Myneni #include <linux/slab.h>
146e9aec54SHaren Myneni #include <linux/string.h>
156e9aec54SHaren Myneni #include <linux/string_helpers.h>
166e9aec54SHaren Myneni #include <linux/uaccess.h>
176e9aec54SHaren Myneni #include <asm/machdep.h>
186e9aec54SHaren Myneni #include <asm/rtas-work-area.h>
196e9aec54SHaren Myneni #include <asm/rtas.h>
206e9aec54SHaren Myneni #include <uapi/asm/papr-indices.h>
216e9aec54SHaren Myneni #include "papr-rtas-common.h"
226e9aec54SHaren Myneni 
23f60a3796SHaren Myneni /*
24f60a3796SHaren Myneni  * Function-specific return values for ibm,set-dynamic-indicator and
25f60a3796SHaren Myneni  * ibm,get-dynamic-sensor-state RTAS calls.
26f60a3796SHaren Myneni  * PAPR+ v2.13 7.3.18 and 7.3.19.
27f60a3796SHaren Myneni  */
28f60a3796SHaren Myneni #define RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR	-3
29f60a3796SHaren Myneni 
306e9aec54SHaren Myneni /**
316e9aec54SHaren Myneni  * struct rtas_get_indices_params - Parameters (in and out) for
326e9aec54SHaren Myneni  *                                      ibm,get-indices.
336e9aec54SHaren Myneni  * @is_sensor:	In: Caller-provided whether sensor or indicator.
346e9aec54SHaren Myneni  * @indice_type:In: Caller-provided indice (sensor or indicator) token
356e9aec54SHaren Myneni  * @work_area:	In: Caller-provided work area buffer for results.
366e9aec54SHaren Myneni  * @next:	In: Sequence number. Out: Next sequence number.
376e9aec54SHaren Myneni  * @status:	Out: RTAS call status.
386e9aec54SHaren Myneni  */
396e9aec54SHaren Myneni struct rtas_get_indices_params {
406e9aec54SHaren Myneni 	u8 is_sensor;
416e9aec54SHaren Myneni 	u32 indice_type;
426e9aec54SHaren Myneni 	struct rtas_work_area *work_area;
436e9aec54SHaren Myneni 	u32 next;
446e9aec54SHaren Myneni 	s32 status;
456e9aec54SHaren Myneni };
466e9aec54SHaren Myneni 
476e9aec54SHaren Myneni /*
486e9aec54SHaren Myneni  * rtas_ibm_get_indices() - Call ibm,get-indices to fill a work area buffer.
496e9aec54SHaren Myneni  * @params: See &struct rtas_ibm_get_indices_params.
506e9aec54SHaren Myneni  *
516e9aec54SHaren Myneni  * Calls ibm,get-indices until it errors or successfully deposits data
526e9aec54SHaren Myneni  * into the supplied work area. Handles RTAS retry statuses. Maps RTAS
536e9aec54SHaren Myneni  * error statuses to reasonable errno values.
546e9aec54SHaren Myneni  *
556e9aec54SHaren Myneni  * The caller is expected to invoke rtas_ibm_get_indices() multiple times
566e9aec54SHaren Myneni  * to retrieve all indices data for the provided indice type. Only one
576e9aec54SHaren Myneni  * sequence should be in progress at any time; starting a new sequence
586e9aec54SHaren Myneni  * will disrupt any sequence already in progress. Serialization of
596e9aec54SHaren Myneni  * indices retrieval sequences is the responsibility of the caller.
606e9aec54SHaren Myneni  *
616e9aec54SHaren Myneni  * The caller should inspect @params.status to determine whether more
626e9aec54SHaren Myneni  * calls are needed to complete the sequence.
636e9aec54SHaren Myneni  *
646e9aec54SHaren Myneni  * Context: May sleep.
656e9aec54SHaren Myneni  * Return: -ve on error, 0 otherwise.
666e9aec54SHaren Myneni  */
rtas_ibm_get_indices(struct rtas_get_indices_params * params)676e9aec54SHaren Myneni static int rtas_ibm_get_indices(struct rtas_get_indices_params *params)
686e9aec54SHaren Myneni {
696e9aec54SHaren Myneni 	struct rtas_work_area *work_area = params->work_area;
706e9aec54SHaren Myneni 	const s32 token = rtas_function_token(RTAS_FN_IBM_GET_INDICES);
716e9aec54SHaren Myneni 	u32 rets;
726e9aec54SHaren Myneni 	s32 fwrc;
736e9aec54SHaren Myneni 	int ret;
746e9aec54SHaren Myneni 
756e9aec54SHaren Myneni 	if (token == RTAS_UNKNOWN_SERVICE)
766e9aec54SHaren Myneni 		return -ENOENT;
776e9aec54SHaren Myneni 
786e9aec54SHaren Myneni 	lockdep_assert_held(&rtas_ibm_get_indices_lock);
796e9aec54SHaren Myneni 
806e9aec54SHaren Myneni 	do {
816e9aec54SHaren Myneni 		fwrc = rtas_call(token, 5, 2, &rets, params->is_sensor,
826e9aec54SHaren Myneni 				params->indice_type,
836e9aec54SHaren Myneni 				rtas_work_area_phys(work_area),
846e9aec54SHaren Myneni 				rtas_work_area_size(work_area),
856e9aec54SHaren Myneni 				params->next);
866e9aec54SHaren Myneni 	} while (rtas_busy_delay(fwrc));
876e9aec54SHaren Myneni 
886e9aec54SHaren Myneni 	switch (fwrc) {
896e9aec54SHaren Myneni 	case RTAS_HARDWARE_ERROR:
906e9aec54SHaren Myneni 		ret = -EIO;
916e9aec54SHaren Myneni 		break;
926e9aec54SHaren Myneni 	case RTAS_INVALID_PARAMETER: /* Indicator type is not supported */
936e9aec54SHaren Myneni 		ret = -EINVAL;
946e9aec54SHaren Myneni 		break;
956e9aec54SHaren Myneni 	case RTAS_SEQ_START_OVER:
966e9aec54SHaren Myneni 		ret = -EAGAIN;
976e9aec54SHaren Myneni 		pr_info_ratelimited("Indices changed during retrieval, retrying\n");
986e9aec54SHaren Myneni 		params->next = 1;
996e9aec54SHaren Myneni 		break;
1006e9aec54SHaren Myneni 	case RTAS_SEQ_MORE_DATA:
1016e9aec54SHaren Myneni 		params->next = rets;
1026e9aec54SHaren Myneni 		ret = 0;
1036e9aec54SHaren Myneni 		break;
1046e9aec54SHaren Myneni 	case RTAS_SEQ_COMPLETE:
1056e9aec54SHaren Myneni 		params->next = 0;
1066e9aec54SHaren Myneni 		ret = 0;
1076e9aec54SHaren Myneni 		break;
1086e9aec54SHaren Myneni 	default:
1096e9aec54SHaren Myneni 		ret = -EIO;
1106e9aec54SHaren Myneni 		pr_err_ratelimited("unexpected ibm,get-indices status %d\n", fwrc);
1116e9aec54SHaren Myneni 		break;
1126e9aec54SHaren Myneni 	}
1136e9aec54SHaren Myneni 
1146e9aec54SHaren Myneni 	params->status = fwrc;
1156e9aec54SHaren Myneni 	return ret;
1166e9aec54SHaren Myneni }
1176e9aec54SHaren Myneni 
1186e9aec54SHaren Myneni /*
1196e9aec54SHaren Myneni  * Internal indices sequence APIs. A sequence is a series of calls to
1206e9aec54SHaren Myneni  * ibm,get-indices for a given location code. The sequence ends when
1216e9aec54SHaren Myneni  * an error is encountered or all indices for the input has been
1226e9aec54SHaren Myneni  * returned.
1236e9aec54SHaren Myneni  */
1246e9aec54SHaren Myneni 
1256e9aec54SHaren Myneni /*
1266e9aec54SHaren Myneni  * indices_sequence_begin() - Begin a indices retrieval sequence.
1276e9aec54SHaren Myneni  *
1286e9aec54SHaren Myneni  * Context: May sleep.
1296e9aec54SHaren Myneni  */
indices_sequence_begin(struct papr_rtas_sequence * seq)1306e9aec54SHaren Myneni static void indices_sequence_begin(struct papr_rtas_sequence *seq)
1316e9aec54SHaren Myneni {
1326e9aec54SHaren Myneni 	struct rtas_get_indices_params  *param;
1336e9aec54SHaren Myneni 
1346e9aec54SHaren Myneni 	param = (struct rtas_get_indices_params *)seq->params;
1356e9aec54SHaren Myneni 	/*
1366e9aec54SHaren Myneni 	 * We could allocate the work area before acquiring the
1376e9aec54SHaren Myneni 	 * function lock, but that would allow concurrent requests to
1386e9aec54SHaren Myneni 	 * exhaust the limited work area pool for no benefit. So
1396e9aec54SHaren Myneni 	 * allocate the work area under the lock.
1406e9aec54SHaren Myneni 	 */
1416e9aec54SHaren Myneni 	mutex_lock(&rtas_ibm_get_indices_lock);
1426e9aec54SHaren Myneni 	param->work_area = rtas_work_area_alloc(RTAS_GET_INDICES_BUF_SIZE);
1436e9aec54SHaren Myneni 	param->next = 1;
1446e9aec54SHaren Myneni 	param->status = 0;
1456e9aec54SHaren Myneni }
1466e9aec54SHaren Myneni 
1476e9aec54SHaren Myneni /*
1486e9aec54SHaren Myneni  * indices_sequence_end() - Finalize a indices retrieval sequence.
1496e9aec54SHaren Myneni  *
1506e9aec54SHaren Myneni  * Releases resources obtained by indices_sequence_begin().
1516e9aec54SHaren Myneni  */
indices_sequence_end(struct papr_rtas_sequence * seq)1526e9aec54SHaren Myneni static void indices_sequence_end(struct papr_rtas_sequence *seq)
1536e9aec54SHaren Myneni {
1546e9aec54SHaren Myneni 	struct rtas_get_indices_params *param;
1556e9aec54SHaren Myneni 
1566e9aec54SHaren Myneni 	param =  (struct rtas_get_indices_params *)seq->params;
1576e9aec54SHaren Myneni 	rtas_work_area_free(param->work_area);
1586e9aec54SHaren Myneni 	mutex_unlock(&rtas_ibm_get_indices_lock);
1596e9aec54SHaren Myneni }
1606e9aec54SHaren Myneni 
1616e9aec54SHaren Myneni /*
1626e9aec54SHaren Myneni  * Work function to be passed to papr_rtas_blob_generate().
1636e9aec54SHaren Myneni  *
1646e9aec54SHaren Myneni  * ibm,get-indices RTAS call fills the work area with the certain
1656e9aec54SHaren Myneni  * format but does not return the bytes written in the buffer. So
1666e9aec54SHaren Myneni  * instead of kernel parsing this work area to determine the buffer
1676e9aec54SHaren Myneni  * length, copy the complete work area (RTAS_GET_INDICES_BUF_SIZE)
1686e9aec54SHaren Myneni  * to the blob and let the user space to obtain the data.
1696e9aec54SHaren Myneni  * Means RTAS_GET_INDICES_BUF_SIZE data will be returned for each
1706e9aec54SHaren Myneni  * read().
1716e9aec54SHaren Myneni  */
1726e9aec54SHaren Myneni 
indices_sequence_fill_work_area(struct papr_rtas_sequence * seq,size_t * len)1736e9aec54SHaren Myneni static const char *indices_sequence_fill_work_area(struct papr_rtas_sequence *seq,
1746e9aec54SHaren Myneni 						size_t *len)
1756e9aec54SHaren Myneni {
1766e9aec54SHaren Myneni 	struct rtas_get_indices_params *p;
1776e9aec54SHaren Myneni 	bool init_state;
1786e9aec54SHaren Myneni 
1796e9aec54SHaren Myneni 	p = (struct rtas_get_indices_params *)seq->params;
1806e9aec54SHaren Myneni 	init_state = (p->next == 1) ? true : false;
1816e9aec54SHaren Myneni 
1826e9aec54SHaren Myneni 	if (papr_rtas_sequence_should_stop(seq, p->status, init_state))
1836e9aec54SHaren Myneni 		return NULL;
1846e9aec54SHaren Myneni 	if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_indices(p)))
1856e9aec54SHaren Myneni 		return NULL;
1866e9aec54SHaren Myneni 
1876e9aec54SHaren Myneni 	*len = RTAS_GET_INDICES_BUF_SIZE;
1886e9aec54SHaren Myneni 	return rtas_work_area_raw_buf(p->work_area);
1896e9aec54SHaren Myneni }
1906e9aec54SHaren Myneni 
1916e9aec54SHaren Myneni /*
1926e9aec54SHaren Myneni  * papr_indices_handle_read - returns indices blob data to the user space
1936e9aec54SHaren Myneni  *
1946e9aec54SHaren Myneni  * ibm,get-indices RTAS call fills the work area with the certian
1956e9aec54SHaren Myneni  * format but does not return the bytes written in the buffer and
1966e9aec54SHaren Myneni  * copied RTAS_GET_INDICES_BUF_SIZE data to the blob for each RTAS
1976e9aec54SHaren Myneni  * call. So send RTAS_GET_INDICES_BUF_SIZE buffer to the user space
1986e9aec54SHaren Myneni  * for each read().
1996e9aec54SHaren Myneni  */
papr_indices_handle_read(struct file * file,char __user * buf,size_t size,loff_t * off)2006e9aec54SHaren Myneni static ssize_t papr_indices_handle_read(struct file *file,
2016e9aec54SHaren Myneni 		char __user *buf, size_t size, loff_t *off)
2026e9aec54SHaren Myneni {
2036e9aec54SHaren Myneni 	const struct papr_rtas_blob *blob = file->private_data;
2046e9aec54SHaren Myneni 
2056e9aec54SHaren Myneni 	/* we should not instantiate a handle without any data attached. */
2066e9aec54SHaren Myneni 	if (!papr_rtas_blob_has_data(blob)) {
2076e9aec54SHaren Myneni 		pr_err_once("handle without data\n");
2086e9aec54SHaren Myneni 		return -EIO;
2096e9aec54SHaren Myneni 	}
2106e9aec54SHaren Myneni 
2116e9aec54SHaren Myneni 	if (size < RTAS_GET_INDICES_BUF_SIZE) {
2126e9aec54SHaren Myneni 		pr_err_once("Invalid buffer length %ld, expect %d\n",
2136e9aec54SHaren Myneni 				size, RTAS_GET_INDICES_BUF_SIZE);
2146e9aec54SHaren Myneni 		return -EINVAL;
2156e9aec54SHaren Myneni 	} else if (size > RTAS_GET_INDICES_BUF_SIZE)
2166e9aec54SHaren Myneni 		size = RTAS_GET_INDICES_BUF_SIZE;
2176e9aec54SHaren Myneni 
2186e9aec54SHaren Myneni 	return simple_read_from_buffer(buf, size, off, blob->data, blob->len);
2196e9aec54SHaren Myneni }
2206e9aec54SHaren Myneni 
2216e9aec54SHaren Myneni static const struct file_operations papr_indices_handle_ops = {
2226e9aec54SHaren Myneni 	.read = papr_indices_handle_read,
2236e9aec54SHaren Myneni 	.llseek = papr_rtas_common_handle_seek,
2246e9aec54SHaren Myneni 	.release = papr_rtas_common_handle_release,
2256e9aec54SHaren Myneni };
2266e9aec54SHaren Myneni 
2276e9aec54SHaren Myneni /*
2286e9aec54SHaren Myneni  * papr_indices_create_handle() - Create a fd-based handle for reading
2296e9aec54SHaren Myneni  *                                indices data
2306e9aec54SHaren Myneni  * @ubuf: Input parameters to RTAS call such as whether sensor or indicator
2316e9aec54SHaren Myneni  *        and indice type in user memory
2326e9aec54SHaren Myneni  *
2336e9aec54SHaren Myneni  * Handler for PAPR_INDICES_IOC_GET ioctl command. Validates @ubuf
2346e9aec54SHaren Myneni  * and instantiates an immutable indices "blob" for it. The blob is
2356e9aec54SHaren Myneni  * attached to a file descriptor for reading by user space. The memory
2366e9aec54SHaren Myneni  * backing the blob is freed when the file is released.
2376e9aec54SHaren Myneni  *
2386e9aec54SHaren Myneni  * The entire requested indices is retrieved by this call and all
2396e9aec54SHaren Myneni  * necessary RTAS interactions are performed before returning the fd
2406e9aec54SHaren Myneni  * to user space. This keeps the read handler simple and ensures that
2416e9aec54SHaren Myneni  * the kernel can prevent interleaving of ibm,get-indices call sequences.
2426e9aec54SHaren Myneni  *
2436e9aec54SHaren Myneni  * Return: The installed fd number if successful, -ve errno otherwise.
2446e9aec54SHaren Myneni  */
papr_indices_create_handle(struct papr_indices_io_block __user * ubuf)2456e9aec54SHaren Myneni static long papr_indices_create_handle(struct papr_indices_io_block __user *ubuf)
2466e9aec54SHaren Myneni {
2476e9aec54SHaren Myneni 	struct papr_rtas_sequence seq = {};
2486e9aec54SHaren Myneni 	struct rtas_get_indices_params params = {};
2496e9aec54SHaren Myneni 	int fd;
2506e9aec54SHaren Myneni 
2516e9aec54SHaren Myneni 	if (get_user(params.is_sensor, &ubuf->indices.is_sensor))
2526e9aec54SHaren Myneni 		return -EFAULT;
2536e9aec54SHaren Myneni 
2546e9aec54SHaren Myneni 	if (get_user(params.indice_type, &ubuf->indices.indice_type))
2556e9aec54SHaren Myneni 		return -EFAULT;
2566e9aec54SHaren Myneni 
2576e9aec54SHaren Myneni 	seq = (struct papr_rtas_sequence) {
2586e9aec54SHaren Myneni 		.begin = indices_sequence_begin,
2596e9aec54SHaren Myneni 		.end = indices_sequence_end,
2606e9aec54SHaren Myneni 		.work = indices_sequence_fill_work_area,
2616e9aec54SHaren Myneni 	};
2626e9aec54SHaren Myneni 
2636e9aec54SHaren Myneni 	seq.params = &params;
2646e9aec54SHaren Myneni 	fd = papr_rtas_setup_file_interface(&seq,
2656e9aec54SHaren Myneni 			&papr_indices_handle_ops, "[papr-indices]");
2666e9aec54SHaren Myneni 
2676e9aec54SHaren Myneni 	return fd;
2686e9aec54SHaren Myneni }
2696e9aec54SHaren Myneni 
2706e9aec54SHaren Myneni /*
271f60a3796SHaren Myneni  * Create work area with the input parameters. This function is used
272f60a3796SHaren Myneni  * for both ibm,set-dynamic-indicator and ibm,get-dynamic-sensor-state
273f60a3796SHaren Myneni  * RTAS Calls.
274f60a3796SHaren Myneni  */
275f60a3796SHaren Myneni static struct rtas_work_area *
papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user * ubuf,struct papr_indices_io_block * kbuf)276f60a3796SHaren Myneni papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user *ubuf,
277f60a3796SHaren Myneni 				struct papr_indices_io_block *kbuf)
278f60a3796SHaren Myneni {
279f60a3796SHaren Myneni 	struct rtas_work_area *work_area;
280f60a3796SHaren Myneni 	u32 length;
281f60a3796SHaren Myneni 	__be32 len_be;
282f60a3796SHaren Myneni 
283f60a3796SHaren Myneni 	if (copy_from_user(kbuf, ubuf, sizeof(*kbuf)))
284f60a3796SHaren Myneni 		return ERR_PTR(-EFAULT);
285f60a3796SHaren Myneni 
286f60a3796SHaren Myneni 
287f60a3796SHaren Myneni 	if (!string_is_terminated(kbuf->dynamic_param.location_code_str,
288f60a3796SHaren Myneni 			ARRAY_SIZE(kbuf->dynamic_param.location_code_str)))
289f60a3796SHaren Myneni 		return ERR_PTR(-EINVAL);
290f60a3796SHaren Myneni 
291f60a3796SHaren Myneni 	/*
292f60a3796SHaren Myneni 	 * The input data in the work area should be as follows:
293f60a3796SHaren Myneni 	 * - 32-bit integer length of the location code string,
294f60a3796SHaren Myneni 	 *   including NULL.
295f60a3796SHaren Myneni 	 * - Location code string, NULL terminated, identifying the
296f60a3796SHaren Myneni 	 *   token (sensor or indicator).
297f60a3796SHaren Myneni 	 * PAPR 2.13 - R1–7.3.18–5 ibm,set-dynamic-indicator
298f60a3796SHaren Myneni 	 *           - R1–7.3.19–5 ibm,get-dynamic-sensor-state
299f60a3796SHaren Myneni 	 */
300f60a3796SHaren Myneni 	/*
301f60a3796SHaren Myneni 	 * Length that user space passed should also include NULL
302f60a3796SHaren Myneni 	 * terminator.
303f60a3796SHaren Myneni 	 */
304f60a3796SHaren Myneni 	length = strlen(kbuf->dynamic_param.location_code_str) + 1;
305f60a3796SHaren Myneni 	if (length > LOC_CODE_SIZE)
306f60a3796SHaren Myneni 		return ERR_PTR(-EINVAL);
307f60a3796SHaren Myneni 
308f60a3796SHaren Myneni 	len_be = cpu_to_be32(length);
309f60a3796SHaren Myneni 
310f60a3796SHaren Myneni 	work_area = rtas_work_area_alloc(LOC_CODE_SIZE + sizeof(u32));
311f60a3796SHaren Myneni 	memcpy(rtas_work_area_raw_buf(work_area), &len_be, sizeof(u32));
312f60a3796SHaren Myneni 	memcpy((rtas_work_area_raw_buf(work_area) + sizeof(u32)),
313f60a3796SHaren Myneni 			&kbuf->dynamic_param.location_code_str, length);
314f60a3796SHaren Myneni 
315f60a3796SHaren Myneni 	return work_area;
316f60a3796SHaren Myneni }
317f60a3796SHaren Myneni 
318f60a3796SHaren Myneni /**
319f60a3796SHaren Myneni  * papr_dynamic_indicator_ioc_set - ibm,set-dynamic-indicator RTAS Call
320f60a3796SHaren Myneni  * PAPR 2.13 7.3.18
321f60a3796SHaren Myneni  *
322f60a3796SHaren Myneni  * @ubuf: Input parameters to RTAS call such as indicator token and
323f60a3796SHaren Myneni  *        new state.
324f60a3796SHaren Myneni  *
325f60a3796SHaren Myneni  * Returns success or -errno.
326f60a3796SHaren Myneni  */
papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user * ubuf)327f60a3796SHaren Myneni static long papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user *ubuf)
328f60a3796SHaren Myneni {
329f60a3796SHaren Myneni 	struct papr_indices_io_block kbuf;
330f60a3796SHaren Myneni 	struct rtas_work_area *work_area;
331f60a3796SHaren Myneni 	s32 fwrc, token, ret;
332f60a3796SHaren Myneni 
333f60a3796SHaren Myneni 	token = rtas_function_token(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR);
334f60a3796SHaren Myneni 	if (token == RTAS_UNKNOWN_SERVICE)
335f60a3796SHaren Myneni 		return -ENOENT;
336f60a3796SHaren Myneni 
337f60a3796SHaren Myneni 	mutex_lock(&rtas_ibm_set_dynamic_indicator_lock);
338f60a3796SHaren Myneni 	work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
339f60a3796SHaren Myneni 	if (IS_ERR(work_area)) {
340f60a3796SHaren Myneni 		ret = PTR_ERR(work_area);
341f60a3796SHaren Myneni 		goto out;
342f60a3796SHaren Myneni 	}
343f60a3796SHaren Myneni 
344f60a3796SHaren Myneni 	do {
345f60a3796SHaren Myneni 		fwrc = rtas_call(token, 3, 1, NULL,
346f60a3796SHaren Myneni 				kbuf.dynamic_param.token,
347f60a3796SHaren Myneni 				kbuf.dynamic_param.state,
348f60a3796SHaren Myneni 				rtas_work_area_phys(work_area));
349f60a3796SHaren Myneni 	} while (rtas_busy_delay(fwrc));
350f60a3796SHaren Myneni 
351f60a3796SHaren Myneni 	rtas_work_area_free(work_area);
352f60a3796SHaren Myneni 
353f60a3796SHaren Myneni 	switch (fwrc) {
354f60a3796SHaren Myneni 	case RTAS_SUCCESS:
355f60a3796SHaren Myneni 		ret = 0;
356f60a3796SHaren Myneni 		break;
357f60a3796SHaren Myneni 	case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR:	/* No such indicator */
358f60a3796SHaren Myneni 		ret = -EOPNOTSUPP;
359f60a3796SHaren Myneni 		break;
360f60a3796SHaren Myneni 	default:
361f60a3796SHaren Myneni 		pr_err("unexpected ibm,set-dynamic-indicator result %d\n",
362f60a3796SHaren Myneni 			fwrc);
363f60a3796SHaren Myneni 		fallthrough;
364f60a3796SHaren Myneni 	case RTAS_HARDWARE_ERROR:	/* Hardware/platform error */
365f60a3796SHaren Myneni 		ret = -EIO;
366f60a3796SHaren Myneni 		break;
367f60a3796SHaren Myneni 	}
368f60a3796SHaren Myneni 
369f60a3796SHaren Myneni out:
370f60a3796SHaren Myneni 	mutex_unlock(&rtas_ibm_set_dynamic_indicator_lock);
371f60a3796SHaren Myneni 	return ret;
372f60a3796SHaren Myneni }
373f60a3796SHaren Myneni 
374*496c7524SHaren Myneni /**
375*496c7524SHaren Myneni  * papr_dynamic_sensor_ioc_get - ibm,get-dynamic-sensor-state RTAS Call
376*496c7524SHaren Myneni  * PAPR 2.13 7.3.19
377*496c7524SHaren Myneni  *
378*496c7524SHaren Myneni  * @ubuf: Input parameters to RTAS call such as sensor token
379*496c7524SHaren Myneni  *        Copies the state in user space buffer.
380*496c7524SHaren Myneni  *
381*496c7524SHaren Myneni  *
382*496c7524SHaren Myneni  * Returns success or -errno.
383*496c7524SHaren Myneni  */
384*496c7524SHaren Myneni 
papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user * ubuf)385*496c7524SHaren Myneni static long papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user *ubuf)
386*496c7524SHaren Myneni {
387*496c7524SHaren Myneni 	struct papr_indices_io_block kbuf;
388*496c7524SHaren Myneni 	struct rtas_work_area *work_area;
389*496c7524SHaren Myneni 	s32 fwrc, token, ret;
390*496c7524SHaren Myneni 	u32 rets;
391*496c7524SHaren Myneni 
392*496c7524SHaren Myneni 	token = rtas_function_token(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE);
393*496c7524SHaren Myneni 	if (token == RTAS_UNKNOWN_SERVICE)
394*496c7524SHaren Myneni 		return -ENOENT;
395*496c7524SHaren Myneni 
396*496c7524SHaren Myneni 	mutex_lock(&rtas_ibm_get_dynamic_sensor_state_lock);
397*496c7524SHaren Myneni 	work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
398*496c7524SHaren Myneni 	if (IS_ERR(work_area)) {
399*496c7524SHaren Myneni 		ret = PTR_ERR(work_area);
400*496c7524SHaren Myneni 		goto out;
401*496c7524SHaren Myneni 	}
402*496c7524SHaren Myneni 
403*496c7524SHaren Myneni 	do {
404*496c7524SHaren Myneni 		fwrc = rtas_call(token, 2, 2, &rets,
405*496c7524SHaren Myneni 				kbuf.dynamic_param.token,
406*496c7524SHaren Myneni 				rtas_work_area_phys(work_area));
407*496c7524SHaren Myneni 	} while (rtas_busy_delay(fwrc));
408*496c7524SHaren Myneni 
409*496c7524SHaren Myneni 	rtas_work_area_free(work_area);
410*496c7524SHaren Myneni 
411*496c7524SHaren Myneni 	switch (fwrc) {
412*496c7524SHaren Myneni 	case RTAS_SUCCESS:
413*496c7524SHaren Myneni 		if (put_user(rets, &ubuf->dynamic_param.state))
414*496c7524SHaren Myneni 			ret = -EFAULT;
415*496c7524SHaren Myneni 		else
416*496c7524SHaren Myneni 			ret = 0;
417*496c7524SHaren Myneni 		break;
418*496c7524SHaren Myneni 	case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR:	/* No such indicator */
419*496c7524SHaren Myneni 		ret = -EOPNOTSUPP;
420*496c7524SHaren Myneni 		break;
421*496c7524SHaren Myneni 	default:
422*496c7524SHaren Myneni 		pr_err("unexpected ibm,get-dynamic-sensor result %d\n",
423*496c7524SHaren Myneni 				fwrc);
424*496c7524SHaren Myneni 		fallthrough;
425*496c7524SHaren Myneni 	case RTAS_HARDWARE_ERROR:	/* Hardware/platform error */
426*496c7524SHaren Myneni 		ret = -EIO;
427*496c7524SHaren Myneni 		break;
428*496c7524SHaren Myneni 	}
429*496c7524SHaren Myneni 
430*496c7524SHaren Myneni out:
431*496c7524SHaren Myneni 	mutex_unlock(&rtas_ibm_get_dynamic_sensor_state_lock);
432*496c7524SHaren Myneni 	return ret;
433*496c7524SHaren Myneni }
434*496c7524SHaren Myneni 
435f60a3796SHaren Myneni /*
4366e9aec54SHaren Myneni  * Top-level ioctl handler for /dev/papr-indices.
4376e9aec54SHaren Myneni  */
papr_indices_dev_ioctl(struct file * filp,unsigned int ioctl,unsigned long arg)4386e9aec54SHaren Myneni static long papr_indices_dev_ioctl(struct file *filp, unsigned int ioctl,
4396e9aec54SHaren Myneni 				unsigned long arg)
4406e9aec54SHaren Myneni {
4416e9aec54SHaren Myneni 	void __user *argp = (__force void __user *)arg;
4426e9aec54SHaren Myneni 	long ret;
4436e9aec54SHaren Myneni 
4446e9aec54SHaren Myneni 	switch (ioctl) {
4456e9aec54SHaren Myneni 	case PAPR_INDICES_IOC_GET:
4466e9aec54SHaren Myneni 		ret = papr_indices_create_handle(argp);
4476e9aec54SHaren Myneni 		break;
448*496c7524SHaren Myneni 	case PAPR_DYNAMIC_SENSOR_IOC_GET:
449*496c7524SHaren Myneni 		ret = papr_dynamic_sensor_ioc_get(argp);
450*496c7524SHaren Myneni 		break;
451f60a3796SHaren Myneni 	case PAPR_DYNAMIC_INDICATOR_IOC_SET:
452f60a3796SHaren Myneni 		if (filp->f_mode & FMODE_WRITE)
453f60a3796SHaren Myneni 			ret = papr_dynamic_indicator_ioc_set(argp);
454f60a3796SHaren Myneni 		else
455f60a3796SHaren Myneni 			ret = -EBADF;
456f60a3796SHaren Myneni 		break;
4576e9aec54SHaren Myneni 	default:
4586e9aec54SHaren Myneni 		ret = -ENOIOCTLCMD;
4596e9aec54SHaren Myneni 		break;
4606e9aec54SHaren Myneni 	}
4616e9aec54SHaren Myneni 
4626e9aec54SHaren Myneni 	return ret;
4636e9aec54SHaren Myneni }
4646e9aec54SHaren Myneni 
4656e9aec54SHaren Myneni static const struct file_operations papr_indices_ops = {
4666e9aec54SHaren Myneni 	.unlocked_ioctl = papr_indices_dev_ioctl,
4676e9aec54SHaren Myneni };
4686e9aec54SHaren Myneni 
4696e9aec54SHaren Myneni static struct miscdevice papr_indices_dev = {
4706e9aec54SHaren Myneni 	.minor = MISC_DYNAMIC_MINOR,
4716e9aec54SHaren Myneni 	.name = "papr-indices",
4726e9aec54SHaren Myneni 	.fops = &papr_indices_ops,
4736e9aec54SHaren Myneni };
4746e9aec54SHaren Myneni 
papr_indices_init(void)4756e9aec54SHaren Myneni static __init int papr_indices_init(void)
4766e9aec54SHaren Myneni {
4776e9aec54SHaren Myneni 	if (!rtas_function_implemented(RTAS_FN_IBM_GET_INDICES))
4786e9aec54SHaren Myneni 		return -ENODEV;
4796e9aec54SHaren Myneni 
480f60a3796SHaren Myneni 	if (!rtas_function_implemented(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR))
481f60a3796SHaren Myneni 		return -ENODEV;
482f60a3796SHaren Myneni 
483*496c7524SHaren Myneni 	if (!rtas_function_implemented(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE))
484*496c7524SHaren Myneni 		return -ENODEV;
485*496c7524SHaren Myneni 
4866e9aec54SHaren Myneni 	return misc_register(&papr_indices_dev);
4876e9aec54SHaren Myneni }
4886e9aec54SHaren Myneni machine_device_initcall(pseries, papr_indices_init);
489