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


Reply via email to