Path: blob/master/arch/powerpc/platforms/pseries/papr-indices.c
26481 views
// SPDX-License-Identifier: GPL-2.0-only12#define pr_fmt(fmt) "papr-indices: " fmt34#include <linux/build_bug.h>5#include <linux/file.h>6#include <linux/fs.h>7#include <linux/init.h>8#include <linux/lockdep.h>9#include <linux/kernel.h>10#include <linux/miscdevice.h>11#include <linux/signal.h>12#include <linux/slab.h>13#include <linux/string.h>14#include <linux/string_helpers.h>15#include <linux/uaccess.h>16#include <asm/machdep.h>17#include <asm/rtas-work-area.h>18#include <asm/rtas.h>19#include <uapi/asm/papr-indices.h>20#include "papr-rtas-common.h"2122/*23* Function-specific return values for ibm,set-dynamic-indicator and24* ibm,get-dynamic-sensor-state RTAS calls.25* PAPR+ v2.13 7.3.18 and 7.3.19.26*/27#define RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR -32829/**30* struct rtas_get_indices_params - Parameters (in and out) for31* ibm,get-indices.32* @is_sensor: In: Caller-provided whether sensor or indicator.33* @indice_type:In: Caller-provided indice (sensor or indicator) token34* @work_area: In: Caller-provided work area buffer for results.35* @next: In: Sequence number. Out: Next sequence number.36* @status: Out: RTAS call status.37*/38struct rtas_get_indices_params {39u8 is_sensor;40u32 indice_type;41struct rtas_work_area *work_area;42u32 next;43s32 status;44};4546/*47* rtas_ibm_get_indices() - Call ibm,get-indices to fill a work area buffer.48* @params: See &struct rtas_ibm_get_indices_params.49*50* Calls ibm,get-indices until it errors or successfully deposits data51* into the supplied work area. Handles RTAS retry statuses. Maps RTAS52* error statuses to reasonable errno values.53*54* The caller is expected to invoke rtas_ibm_get_indices() multiple times55* to retrieve all indices data for the provided indice type. Only one56* sequence should be in progress at any time; starting a new sequence57* will disrupt any sequence already in progress. Serialization of58* indices retrieval sequences is the responsibility of the caller.59*60* The caller should inspect @params.status to determine whether more61* calls are needed to complete the sequence.62*63* Context: May sleep.64* Return: -ve on error, 0 otherwise.65*/66static int rtas_ibm_get_indices(struct rtas_get_indices_params *params)67{68struct rtas_work_area *work_area = params->work_area;69const s32 token = rtas_function_token(RTAS_FN_IBM_GET_INDICES);70u32 rets;71s32 fwrc;72int ret;7374if (token == RTAS_UNKNOWN_SERVICE)75return -ENOENT;7677lockdep_assert_held(&rtas_ibm_get_indices_lock);7879do {80fwrc = rtas_call(token, 5, 2, &rets, params->is_sensor,81params->indice_type,82rtas_work_area_phys(work_area),83rtas_work_area_size(work_area),84params->next);85} while (rtas_busy_delay(fwrc));8687switch (fwrc) {88case RTAS_HARDWARE_ERROR:89ret = -EIO;90break;91case RTAS_INVALID_PARAMETER: /* Indicator type is not supported */92ret = -EINVAL;93break;94case RTAS_SEQ_START_OVER:95ret = -EAGAIN;96pr_info_ratelimited("Indices changed during retrieval, retrying\n");97params->next = 1;98break;99case RTAS_SEQ_MORE_DATA:100params->next = rets;101ret = 0;102break;103case RTAS_SEQ_COMPLETE:104params->next = 0;105ret = 0;106break;107default:108ret = -EIO;109pr_err_ratelimited("unexpected ibm,get-indices status %d\n", fwrc);110break;111}112113params->status = fwrc;114return ret;115}116117/*118* Internal indices sequence APIs. A sequence is a series of calls to119* ibm,get-indices for a given location code. The sequence ends when120* an error is encountered or all indices for the input has been121* returned.122*/123124/*125* indices_sequence_begin() - Begin a indices retrieval sequence.126*127* Context: May sleep.128*/129static void indices_sequence_begin(struct papr_rtas_sequence *seq)130{131struct rtas_get_indices_params *param;132133param = (struct rtas_get_indices_params *)seq->params;134/*135* We could allocate the work area before acquiring the136* function lock, but that would allow concurrent requests to137* exhaust the limited work area pool for no benefit. So138* allocate the work area under the lock.139*/140mutex_lock(&rtas_ibm_get_indices_lock);141param->work_area = rtas_work_area_alloc(RTAS_GET_INDICES_BUF_SIZE);142param->next = 1;143param->status = 0;144}145146/*147* indices_sequence_end() - Finalize a indices retrieval sequence.148*149* Releases resources obtained by indices_sequence_begin().150*/151static void indices_sequence_end(struct papr_rtas_sequence *seq)152{153struct rtas_get_indices_params *param;154155param = (struct rtas_get_indices_params *)seq->params;156rtas_work_area_free(param->work_area);157mutex_unlock(&rtas_ibm_get_indices_lock);158}159160/*161* Work function to be passed to papr_rtas_blob_generate().162*163* ibm,get-indices RTAS call fills the work area with the certain164* format but does not return the bytes written in the buffer. So165* instead of kernel parsing this work area to determine the buffer166* length, copy the complete work area (RTAS_GET_INDICES_BUF_SIZE)167* to the blob and let the user space to obtain the data.168* Means RTAS_GET_INDICES_BUF_SIZE data will be returned for each169* read().170*/171172static const char *indices_sequence_fill_work_area(struct papr_rtas_sequence *seq,173size_t *len)174{175struct rtas_get_indices_params *p;176bool init_state;177178p = (struct rtas_get_indices_params *)seq->params;179init_state = (p->next == 1) ? true : false;180181if (papr_rtas_sequence_should_stop(seq, p->status, init_state))182return NULL;183if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_indices(p)))184return NULL;185186*len = RTAS_GET_INDICES_BUF_SIZE;187return rtas_work_area_raw_buf(p->work_area);188}189190/*191* papr_indices_handle_read - returns indices blob data to the user space192*193* ibm,get-indices RTAS call fills the work area with the certian194* format but does not return the bytes written in the buffer and195* copied RTAS_GET_INDICES_BUF_SIZE data to the blob for each RTAS196* call. So send RTAS_GET_INDICES_BUF_SIZE buffer to the user space197* for each read().198*/199static ssize_t papr_indices_handle_read(struct file *file,200char __user *buf, size_t size, loff_t *off)201{202const struct papr_rtas_blob *blob = file->private_data;203204/* we should not instantiate a handle without any data attached. */205if (!papr_rtas_blob_has_data(blob)) {206pr_err_once("handle without data\n");207return -EIO;208}209210if (size < RTAS_GET_INDICES_BUF_SIZE) {211pr_err_once("Invalid buffer length %ld, expect %d\n",212size, RTAS_GET_INDICES_BUF_SIZE);213return -EINVAL;214} else if (size > RTAS_GET_INDICES_BUF_SIZE)215size = RTAS_GET_INDICES_BUF_SIZE;216217return simple_read_from_buffer(buf, size, off, blob->data, blob->len);218}219220static const struct file_operations papr_indices_handle_ops = {221.read = papr_indices_handle_read,222.llseek = papr_rtas_common_handle_seek,223.release = papr_rtas_common_handle_release,224};225226/*227* papr_indices_create_handle() - Create a fd-based handle for reading228* indices data229* @ubuf: Input parameters to RTAS call such as whether sensor or indicator230* and indice type in user memory231*232* Handler for PAPR_INDICES_IOC_GET ioctl command. Validates @ubuf233* and instantiates an immutable indices "blob" for it. The blob is234* attached to a file descriptor for reading by user space. The memory235* backing the blob is freed when the file is released.236*237* The entire requested indices is retrieved by this call and all238* necessary RTAS interactions are performed before returning the fd239* to user space. This keeps the read handler simple and ensures that240* the kernel can prevent interleaving of ibm,get-indices call sequences.241*242* Return: The installed fd number if successful, -ve errno otherwise.243*/244static long papr_indices_create_handle(struct papr_indices_io_block __user *ubuf)245{246struct papr_rtas_sequence seq = {};247struct rtas_get_indices_params params = {};248int fd;249250if (get_user(params.is_sensor, &ubuf->indices.is_sensor))251return -EFAULT;252253if (get_user(params.indice_type, &ubuf->indices.indice_type))254return -EFAULT;255256seq = (struct papr_rtas_sequence) {257.begin = indices_sequence_begin,258.end = indices_sequence_end,259.work = indices_sequence_fill_work_area,260};261262seq.params = ¶ms;263fd = papr_rtas_setup_file_interface(&seq,264&papr_indices_handle_ops, "[papr-indices]");265266return fd;267}268269/*270* Create work area with the input parameters. This function is used271* for both ibm,set-dynamic-indicator and ibm,get-dynamic-sensor-state272* RTAS Calls.273*/274static struct rtas_work_area *275papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user *ubuf,276struct papr_indices_io_block *kbuf)277{278struct rtas_work_area *work_area;279u32 length;280__be32 len_be;281282if (copy_from_user(kbuf, ubuf, sizeof(*kbuf)))283return ERR_PTR(-EFAULT);284285286if (!string_is_terminated(kbuf->dynamic_param.location_code_str,287ARRAY_SIZE(kbuf->dynamic_param.location_code_str)))288return ERR_PTR(-EINVAL);289290/*291* The input data in the work area should be as follows:292* - 32-bit integer length of the location code string,293* including NULL.294* - Location code string, NULL terminated, identifying the295* token (sensor or indicator).296* PAPR 2.13 - R1–7.3.18–5 ibm,set-dynamic-indicator297* - R1–7.3.19–5 ibm,get-dynamic-sensor-state298*/299/*300* Length that user space passed should also include NULL301* terminator.302*/303length = strlen(kbuf->dynamic_param.location_code_str) + 1;304if (length > LOC_CODE_SIZE)305return ERR_PTR(-EINVAL);306307len_be = cpu_to_be32(length);308309work_area = rtas_work_area_alloc(LOC_CODE_SIZE + sizeof(u32));310memcpy(rtas_work_area_raw_buf(work_area), &len_be, sizeof(u32));311memcpy((rtas_work_area_raw_buf(work_area) + sizeof(u32)),312&kbuf->dynamic_param.location_code_str, length);313314return work_area;315}316317/**318* papr_dynamic_indicator_ioc_set - ibm,set-dynamic-indicator RTAS Call319* PAPR 2.13 7.3.18320*321* @ubuf: Input parameters to RTAS call such as indicator token and322* new state.323*324* Returns success or -errno.325*/326static long papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user *ubuf)327{328struct papr_indices_io_block kbuf;329struct rtas_work_area *work_area;330s32 fwrc, token, ret;331332token = rtas_function_token(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR);333if (token == RTAS_UNKNOWN_SERVICE)334return -ENOENT;335336mutex_lock(&rtas_ibm_set_dynamic_indicator_lock);337work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);338if (IS_ERR(work_area)) {339ret = PTR_ERR(work_area);340goto out;341}342343do {344fwrc = rtas_call(token, 3, 1, NULL,345kbuf.dynamic_param.token,346kbuf.dynamic_param.state,347rtas_work_area_phys(work_area));348} while (rtas_busy_delay(fwrc));349350rtas_work_area_free(work_area);351352switch (fwrc) {353case RTAS_SUCCESS:354ret = 0;355break;356case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */357ret = -EOPNOTSUPP;358break;359default:360pr_err("unexpected ibm,set-dynamic-indicator result %d\n",361fwrc);362fallthrough;363case RTAS_HARDWARE_ERROR: /* Hardware/platform error */364ret = -EIO;365break;366}367368out:369mutex_unlock(&rtas_ibm_set_dynamic_indicator_lock);370return ret;371}372373/**374* papr_dynamic_sensor_ioc_get - ibm,get-dynamic-sensor-state RTAS Call375* PAPR 2.13 7.3.19376*377* @ubuf: Input parameters to RTAS call such as sensor token378* Copies the state in user space buffer.379*380*381* Returns success or -errno.382*/383384static long papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user *ubuf)385{386struct papr_indices_io_block kbuf;387struct rtas_work_area *work_area;388s32 fwrc, token, ret;389u32 rets;390391token = rtas_function_token(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE);392if (token == RTAS_UNKNOWN_SERVICE)393return -ENOENT;394395mutex_lock(&rtas_ibm_get_dynamic_sensor_state_lock);396work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);397if (IS_ERR(work_area)) {398ret = PTR_ERR(work_area);399goto out;400}401402do {403fwrc = rtas_call(token, 2, 2, &rets,404kbuf.dynamic_param.token,405rtas_work_area_phys(work_area));406} while (rtas_busy_delay(fwrc));407408rtas_work_area_free(work_area);409410switch (fwrc) {411case RTAS_SUCCESS:412if (put_user(rets, &ubuf->dynamic_param.state))413ret = -EFAULT;414else415ret = 0;416break;417case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */418ret = -EOPNOTSUPP;419break;420default:421pr_err("unexpected ibm,get-dynamic-sensor result %d\n",422fwrc);423fallthrough;424case RTAS_HARDWARE_ERROR: /* Hardware/platform error */425ret = -EIO;426break;427}428429out:430mutex_unlock(&rtas_ibm_get_dynamic_sensor_state_lock);431return ret;432}433434/*435* Top-level ioctl handler for /dev/papr-indices.436*/437static long papr_indices_dev_ioctl(struct file *filp, unsigned int ioctl,438unsigned long arg)439{440void __user *argp = (__force void __user *)arg;441long ret;442443switch (ioctl) {444case PAPR_INDICES_IOC_GET:445ret = papr_indices_create_handle(argp);446break;447case PAPR_DYNAMIC_SENSOR_IOC_GET:448ret = papr_dynamic_sensor_ioc_get(argp);449break;450case PAPR_DYNAMIC_INDICATOR_IOC_SET:451if (filp->f_mode & FMODE_WRITE)452ret = papr_dynamic_indicator_ioc_set(argp);453else454ret = -EBADF;455break;456default:457ret = -ENOIOCTLCMD;458break;459}460461return ret;462}463464static const struct file_operations papr_indices_ops = {465.unlocked_ioctl = papr_indices_dev_ioctl,466};467468static struct miscdevice papr_indices_dev = {469.minor = MISC_DYNAMIC_MINOR,470.name = "papr-indices",471.fops = &papr_indices_ops,472};473474static __init int papr_indices_init(void)475{476if (!rtas_function_implemented(RTAS_FN_IBM_GET_INDICES))477return -ENODEV;478479if (!rtas_function_implemented(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR))480return -ENODEV;481482if (!rtas_function_implemented(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE))483return -ENODEV;484485return misc_register(&papr_indices_dev);486}487machine_device_initcall(pseries, papr_indices_init);488489490