The RTAS call ibm,set-dynamic-indicator is used to set the new indicator state identified by a location code. The current implementation uses rtas_set_dynamic_indicator() API provided by librtas library which allocates RMO buffer and issue this RTAS call in the user space. But /dev/mem access by the user space is prohibited under system lockdown.
This patch provides an interface with new ioctl PAPR_DYNAMIC_INDICATOR_IOC_SET to the papr-indices character driver and expose this interface to the user space that is compatible with lockdown. Refer PAPR 7.3.18 ibm,set-dynamic-indicator for more information on this RTAS call. - User input parameters to the RTAS call: location code string, indicator token and new state Expose these interfaces to user space with a /dev/papr-indices character device using the following programming model: int fd = open("/dev/papr-indices", O_RDWR); int ret = ioctl(fd, PAPR_DYNAMIC_INDICATOR_IOC_SET, struct papr_indices_io_block) - The user space passes input parameters in papr_indices_io_block struct Signed-off-by: Haren Myneni <ha...@linux.ibm.com> --- arch/powerpc/include/asm/rtas.h | 1 + arch/powerpc/kernel/rtas.c | 2 +- arch/powerpc/platforms/pseries/papr-indices.c | 122 ++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 7dc527a5aaac..2da52f59e4c6 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -516,6 +516,7 @@ extern unsigned long rtas_rmo_buf; extern struct mutex rtas_ibm_get_vpd_lock; extern struct mutex rtas_ibm_get_indices_lock; +extern struct mutex rtas_ibm_set_dynamic_indicator_lock; #define GLOBAL_INTERRUPT_QUEUE 9005 diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 76c634b92cb2..88fa416730af 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -95,9 +95,9 @@ static DEFINE_MUTEX(rtas_ibm_activate_firmware_lock); static DEFINE_MUTEX(rtas_ibm_get_dynamic_sensor_state_lock); static DEFINE_MUTEX(rtas_ibm_lpar_perftools_lock); static DEFINE_MUTEX(rtas_ibm_physical_attestation_lock); -static DEFINE_MUTEX(rtas_ibm_set_dynamic_indicator_lock); DEFINE_MUTEX(rtas_ibm_get_vpd_lock); DEFINE_MUTEX(rtas_ibm_get_indices_lock); +DEFINE_MUTEX(rtas_ibm_set_dynamic_indicator_lock); static struct rtas_function rtas_function_table[] __ro_after_init = { [RTAS_FNIDX__CHECK_EXCEPTION] = { diff --git a/arch/powerpc/platforms/pseries/papr-indices.c b/arch/powerpc/platforms/pseries/papr-indices.c index c5cd63dae609..e4b4eb8ece84 100644 --- a/arch/powerpc/platforms/pseries/papr-indices.c +++ b/arch/powerpc/platforms/pseries/papr-indices.c @@ -27,6 +27,15 @@ #define RTAS_IBM_GET_INDICES_MORE_DATA 1 /* More data is available. */ #define RTAS_IBM_GET_INDICES_START_OVER -4 /* Indices list changed, restart call sequence. */ +/* + * Function-specific return values for ibm,set-dynamic-indicator and + * ibm,get-dynamic-sensor-state RTAS calls, drived from PAPR+ + * v2.13 7.3.18 and 7.3.19. + */ +#define RTAS_IBM_DYNAMIC_INDICE_SUCCESS 0 +#define RTAS_IBM_DYNAMIC_INDICE_HW_ERROR -1 +#define RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR -3 + /** * struct rtas_get_indices_params - Parameters (in and out) for * ibm,get-indices. @@ -328,6 +337,110 @@ static long papr_indices_create_handle(struct papr_indices_io_block __user *ubuf return fd; } +/* + * Create work area with the input parameters. This function is used + * for both ibm,set-dynamic-indicator and ibm,get-dynamic-sensor-state + * RTAS Calls. + */ +static struct rtas_work_area * +papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user *ubuf, + struct papr_indices_io_block *kbuf) +{ + struct rtas_work_area *work_area; + u32 length; + __be32 len_be; + + if (copy_from_user(kbuf, ubuf, sizeof(*kbuf))) + return ERR_PTR(-EFAULT); + + + if (!string_is_terminated(kbuf->dynamic_param.location_code_str, + ARRAY_SIZE(kbuf->dynamic_param.location_code_str))) + return ERR_PTR(-EINVAL); + + /* + * The input data in the work area should be as follows: + * - 32-bit integer length of the location code string, + * including NULL. + * - Location code string, NULL terminated, identifying the + * token (sensor or indicator). + * PAPR 2.13 - R1–7.3.18–5 ibm,set-dynamic-indicator + * - R1–7.3.19–5 ibm,get-dynamic-sensor-state + */ + /* + * Length that user space passed should also include NULL + * terminator. + */ + length = strlen(kbuf->dynamic_param.location_code_str) + 1; + if (length > LOC_CODE_SIZE) + return ERR_PTR(-EINVAL); + + len_be = cpu_to_be32(length); + + work_area = rtas_work_area_alloc(LOC_CODE_SIZE + sizeof(u32)); + memcpy(rtas_work_area_raw_buf(work_area), &len_be, sizeof(u32)); + memcpy((rtas_work_area_raw_buf(work_area) + sizeof(u32)), + &kbuf->dynamic_param.location_code_str, length); + + return work_area; +} + +/** + * papr_dynamic_indicator_ioc_set - ibm,set-dynamic-indicator RTAS Call + * PAPR 2.13 7.3.18 + * + * @ubuf: Input parameters to RTAS call such as indicator token and + * new state. + * + * Returns success or -errno. + */ +static long papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user *ubuf) +{ + struct papr_indices_io_block kbuf; + struct rtas_work_area *work_area; + s32 fwrc, token, ret; + + token = rtas_function_token(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR); + if (token == RTAS_UNKNOWN_SERVICE) + return -ENOENT; + + mutex_lock(&rtas_ibm_set_dynamic_indicator_lock); + work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf); + if (IS_ERR(work_area)) { + ret = PTR_ERR(work_area); + goto out; + } + + do { + fwrc = rtas_call(token, 3, 1, NULL, + kbuf.dynamic_param.token, + kbuf.dynamic_param.state, + rtas_work_area_phys(work_area)); + } while (rtas_busy_delay(fwrc)); + + rtas_work_area_free(work_area); + + switch (fwrc) { + case RTAS_IBM_DYNAMIC_INDICE_SUCCESS: + ret = 0; + break; + case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */ + ret = -EOPNOTSUPP; + break; + default: + pr_err("unexpected ibm,set-dynamic-indicator result %d\n", + fwrc); + fallthrough; + case RTAS_IBM_DYNAMIC_INDICE_HW_ERROR: /* Hardware/platform error */ + ret = -EIO; + break; + } + +out: + mutex_unlock(&rtas_ibm_set_dynamic_indicator_lock); + return ret; +} + /* * Top-level ioctl handler for /dev/papr-indices. */ @@ -341,6 +454,12 @@ static long papr_indices_dev_ioctl(struct file *filp, unsigned int ioctl, case PAPR_INDICES_IOC_GET: ret = papr_indices_create_handle(argp); break; + case PAPR_DYNAMIC_INDICATOR_IOC_SET: + if (filp->f_mode & FMODE_WRITE) + ret = papr_dynamic_indicator_ioc_set(argp); + else + ret = -EBADF; + break; default: ret = -ENOIOCTLCMD; break; @@ -364,6 +483,9 @@ static __init int papr_indices_init(void) if (!rtas_function_implemented(RTAS_FN_IBM_GET_INDICES)) return -ENODEV; + if (!rtas_function_implemented(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR)) + return -ENODEV; + return misc_register(&papr_indices_dev); } machine_device_initcall(pseries, papr_indices_init); -- 2.43.5