753e6d5d54
- 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
286 lines
8.8 KiB
Diff
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
|
|
|