librtas/0006-librtas-sysparm-prefer-dev-papr-sysparm-when-availab.patch
Michal Suchanek 753e6d5d54 Accepting request 1143220 from home:michals
- Update character device patches to current version.
  * folded link-lpthread.patch into 0005-librtas-vpd-prefer-dev-papr-vpd-when-available.patch

OBS-URL: https://build.opensuse.org/request/show/1143220
OBS-URL: https://build.opensuse.org/package/show/devel:libraries:c_c++/librtas?expand=0&rev=69
2024-02-01 08:49:01 +00:00

286 lines
8.8 KiB
Diff

From d8d4ee6f5052704ac82bb8aa8d8fe8816dac41cd Mon Sep 17 00:00:00 2001
From: Nathan Lynch <nathanl@linux.ibm.com>
Date: Mon, 25 Sep 2023 11:42:26 -0500
Subject: [PATCH 6/6] librtas/sysparm: prefer /dev/papr-sysparm when available
Change rtas_get_sysparm() and rtas_set_sysparm() to prefer the
/dev/papr-sysparm character device expected in Linux v6.8.
On the first invocation of either function, probe for the new ABI and
initialize internal function pointers according to the result. Use
pthread_once() to ensure that this initialization step is performed
atomically and only once.
When /dev/papr-sysparm is available, use its PAPR_SYSPARM_IOC_GET and
PAPR_SYSPARM_IOC_SET ioctl commands to retrieve and update system
parameters. Most of the complexity of calling RTAS is handled
internally to the kernel and the ioctl follows Linux conventions,
returning 0 on success or -1 w/errno on failure. On failure,
back-convert the errno to an RTAS status value that callers will
recognize.
For now, carry a copy of the kernel uapi header.
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
Makefile.am | 1 +
librtas_src/papr-sysparm.h | 58 +++++++++++++++
librtas_src/sysparm.c | 144 ++++++++++++++++++++++++++++++++++++-
3 files changed, 201 insertions(+), 2 deletions(-)
create mode 100644 librtas_src/papr-sysparm.h
diff --git a/Makefile.am b/Makefile.am
index 89a6eaa..67257e3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,6 +38,7 @@ library_include_HEADERS += librtas_src/librtas.h
noinst_HEADERS += \
librtas_src/internal.h \
librtas_src/papr-miscdev.h \
+ librtas_src/papr-sysparm.h \
librtas_src/papr-vpd.h
# See "Updating library version information" in the libtool manual for
diff --git a/librtas_src/papr-sysparm.h b/librtas_src/papr-sysparm.h
new file mode 100644
index 0000000..e0c0ebb
--- /dev/null
+++ b/librtas_src/papr-sysparm.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+#ifndef _UAPI_PAPR_SYSPARM_H_
+#define _UAPI_PAPR_SYSPARM_H_
+
+#include <linux/types.h>
+#include <asm/ioctl.h>
+#include "papr-miscdev.h"
+
+enum {
+ PAPR_SYSPARM_MAX_INPUT = 1024,
+ PAPR_SYSPARM_MAX_OUTPUT = 4000,
+};
+
+struct papr_sysparm_io_block {
+ __u32 parameter;
+ __u16 length;
+ char data[PAPR_SYSPARM_MAX_OUTPUT];
+};
+
+/**
+ * PAPR_SYSPARM_IOC_GET - Retrieve the value of a PAPR system parameter.
+ *
+ * Uses _IOWR because of one corner case: Retrieving the value of the
+ * "OS Service Entitlement Status" parameter (60) requires the caller
+ * to supply input data (a date string) in the buffer passed to
+ * firmware. So the @length and @data of the incoming
+ * papr_sysparm_io_block are always used to initialize the work area
+ * supplied to ibm,get-system-parameter. No other parameters are known
+ * to parameterize the result this way, and callers are encouraged
+ * (but not required) to zero-initialize @length and @data in the
+ * common case.
+ *
+ * On error the contents of the ioblock are indeterminate.
+ *
+ * Return:
+ * 0: Success; @length is the length of valid data in @data, not to exceed @PAPR_SYSPARM_MAX_OUTPUT.
+ * -EIO: Platform error. (-1)
+ * -EINVAL: Incorrect data length or format. (-9999)
+ * -EPERM: The calling partition is not allowed to access this parameter. (-9002)
+ * -EOPNOTSUPP: Parameter not supported on this platform (-3)
+ */
+#define PAPR_SYSPARM_IOC_GET _IOWR(PAPR_MISCDEV_IOC_ID, 1, struct papr_sysparm_io_block)
+
+/**
+ * PAPR_SYSPARM_IOC_SET - Update the value of a PAPR system parameter.
+ *
+ * The contents of the ioblock are unchanged regardless of success.
+ *
+ * Return:
+ * 0: Success; the parameter has been updated.
+ * -EIO: Platform error. (-1)
+ * -EINVAL: Incorrect data length or format. (-9999)
+ * -EPERM: The calling partition is not allowed to access this parameter. (-9002)
+ * -EOPNOTSUPP: Parameter not supported on this platform (-3)
+ */
+#define PAPR_SYSPARM_IOC_SET _IOW(PAPR_MISCDEV_IOC_ID, 2, struct papr_sysparm_io_block)
+
+#endif /* _UAPI_PAPR_SYSPARM_H_ */
diff --git a/librtas_src/sysparm.c b/librtas_src/sysparm.c
index 40af55e..b4d2054 100644
--- a/librtas_src/sysparm.c
+++ b/librtas_src/sysparm.c
@@ -1,7 +1,16 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdbool.h>
#include <stdint.h>
#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
#include "internal.h"
#include "librtas.h"
+#include "papr-sysparm.h"
+
+static const char sysparm_devpath[] = "/dev/papr-sysparm";
/**
* rtas_get_sysparm
@@ -15,7 +24,8 @@
* @param data reference to buffer to return parameter in
* @return 0 on success, !0 otherwise
*/
-int rtas_get_sysparm(unsigned int parameter, unsigned int length, char *data)
+static int
+get_sysparm_syscall_fallback(unsigned int parameter, unsigned int length, char *data)
{
uint32_t kernbuf_pa;
void *kernbuf;
@@ -49,7 +59,8 @@ int rtas_get_sysparm(unsigned int parameter, unsigned int length, char *data)
* @param data
* @return 0 on success, !0 otherwise
*/
-int rtas_set_sysparm(unsigned int parameter, char *data)
+static int
+set_sysparm_syscall_fallback(unsigned int parameter, char *data)
{
uint32_t kernbuf_pa;
void *kernbuf;
@@ -86,3 +97,132 @@ int rtas_set_sysparm(unsigned int parameter, char *data)
dbg("(%u, %p) = %d\n", parameter, data, rc ? rc : status);
return rc ? rc : status;
}
+
+static bool sysparm_can_use_chardev(void)
+{
+ struct stat statbuf;
+
+ if (stat(sysparm_devpath, &statbuf))
+ return false;
+
+ if (!S_ISCHR(statbuf.st_mode))
+ return false;
+
+ if (close(open(sysparm_devpath, O_RDONLY)))
+ return false;
+
+ return true;
+}
+
+/*
+ * Only to be used when converting an actual error from a syscall.
+ */
+static int chardev_backconvert_errno(int saved_errno)
+{
+ const struct {
+ int linux_errno;
+ int rtas_status;
+ } map[] = {
+#define errno_to_status(e, s) { .linux_errno = (e), .rtas_status = (s), }
+ errno_to_status(EINVAL, -9999),
+ errno_to_status(EPERM, -9002),
+ errno_to_status(EOPNOTSUPP, -3),
+ errno_to_status(EIO, -1),
+ errno_to_status(EFAULT, -1),
+#undef errno_to_status
+ };
+
+ for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); ++i)
+ if (map[i].linux_errno == saved_errno)
+ return map[i].rtas_status;
+ return -1;
+}
+
+static int get_sysparm_chardev(unsigned int parameter, unsigned int length, char *data)
+{
+ const int fd = open(sysparm_devpath, O_RDWR);
+ struct papr_sysparm_io_block buf = {
+ .parameter = parameter,
+ };
+
+ if (fd < 0) {
+ /*
+ * Really shouldn't get here without misconfiguration,
+ * e.g. removal of /dev/papr-sysparm. Synthesize a
+ * hardware/platform error.
+ */
+ return -1;
+ }
+
+ /*
+ * It might make sense to have special handling for parameter
+ * 60 (OS Service Entitlement Status), which takes input data,
+ * but librtas has never handled that one correctly. So ignore
+ * it for now and don't copy incoming data into the block we
+ * pass to PAPR_SYSPARM_IOC_GET.
+ */
+
+ const int res = ioctl(fd, PAPR_SYSPARM_IOC_GET, &buf);
+ const int saved_errno = errno;
+ (void)close(fd);
+
+ if (res != 0)
+ return chardev_backconvert_errno(saved_errno);
+
+ const uint16_t result_size_msb = htobe16(buf.length);
+ memcpy(data, &result_size_msb, sizeof(result_size_msb));
+ length -= sizeof(result_size_msb);
+ data += sizeof(result_size_msb);
+
+ /*
+ * Copy no more than min(@length, sizeof(buf.data)).
+ */
+ const size_t copy_size = sizeof(buf.data) < length ?
+ sizeof(buf.data) : length;
+ memcpy(data, buf.data, copy_size);
+ return 0;
+}
+
+static int set_sysparm_chardev(unsigned int parameter, char *data)
+{
+ const int fd = open(sysparm_devpath, O_RDWR);
+ struct papr_sysparm_io_block buf = {
+ .parameter = parameter,
+ .length = (((unsigned char)data[0] << 8) | (unsigned char)data[1]),
+ };
+
+ memcpy(buf.data, data + 2, buf.length);
+
+ const int res = ioctl(fd, PAPR_SYSPARM_IOC_SET, &buf);
+ const int saved_errno = errno;
+ (void)close(fd);
+
+ return res == 0 ? 0 : chardev_backconvert_errno(saved_errno);
+}
+
+static int (*get_sysparm_fn)(unsigned int parameter, unsigned int length, char *data);
+static int (*set_sysparm_fn)(unsigned int parameter, char *data);
+
+static void sysparm_fn_setup(void)
+{
+ const bool use_chardev = sysparm_can_use_chardev();
+
+ get_sysparm_fn = use_chardev ?
+ get_sysparm_chardev : get_sysparm_syscall_fallback;
+ set_sysparm_fn = use_chardev ?
+ set_sysparm_chardev : set_sysparm_syscall_fallback;
+}
+
+static pthread_once_t sysparm_fn_setup_once = PTHREAD_ONCE_INIT;
+
+int rtas_get_sysparm(unsigned int parameter, unsigned int length, char *data)
+{
+ pthread_once(&sysparm_fn_setup_once, sysparm_fn_setup);
+ return get_sysparm_fn(parameter, length, data);
+}
+
+int rtas_set_sysparm(unsigned int parameter, char *data)
+{
+ pthread_once(&sysparm_fn_setup_once, sysparm_fn_setup);
+ return set_sysparm_fn(parameter, data);
+}
--
2.43.0