librtas/0005-librtas-vpd-prefer-dev-papr-vpd-when-available.patch

288 lines
8.1 KiB
Diff

From 57d5e1c07b40e56f6209e7b806208818464f8e8d Mon Sep 17 00:00:00 2001
From: Nathan Lynch <nathanl@linux.ibm.com>
Date: Sat, 12 Aug 2023 13:27:39 -0500
Subject: [PATCH 5/6] librtas/vpd: prefer /dev/papr-vpd when available
Change rtas_get_vpd() to prefer the /dev/papr-vpd character device
expected in Linux v6.8.
On the first invocation of rtas_get_vpd(), probe for the new ABI and
initialize an internal function pointer according to the result. Use
pthread_once() to ensure that this probe step is performed atomically
and only once. It would not make sense to re-probe (and potentially
update the implementation) on every call, since the implementations
differ in the way state for the call sequence is maintained.
Assuming the new ABI is available: upon first call to rtas_get_vpd(),
open /dev/papr-vpd and obtain a file descriptor using the
PAPR_VPD_CREATE_HANDLE ioctl() command. This file descriptor is a
reference to the VPD returned from a complete ibm,get-vpd call
sequence. Subsequent calls sequentially read the VPD from the fd,
which is closed once EOF is reached.
To existing users of rtas_get_vpd(), the new implementation is
indistinguishable from the old one, with one exception:
* When using the rtas() syscall on current systems, RTAS advances the
sequence number by a fixed interval on each call in a sequence. This
is visible in the values returned in the 'seq_next' out parameter.
* When using /dev/papr-vpd, the value returned in 'seq_next' does not
change between calls; librtas treats it as a "handle" for a sequence
in progress.
In PAPR there is no meaning attached to the value returned in the
'seq_next' out parameter, and there is no requirement that it must
change or increment on each call. No user should be relying on the
behavior of this aspect of the interface to judge the status of a
sequence.
I have verified that an unmodified vpdupdate command from a distro
lsvpd package works correctly when linked to a librtas.so with this
change. This is the primary (and perhaps only) user of rtas_get_vpd().
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 | 5 +-
librtas_src/papr-vpd.h | 22 ++++++
librtas_src/vpd.c | 155 ++++++++++++++++++++++++++++++++++++-----
3 files changed, 162 insertions(+), 20 deletions(-)
create mode 100644 librtas_src/papr-vpd.h
diff --git a/Makefile.am b/Makefile.am
index d0cabfb..89a6eaa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,7 +26,7 @@ LIBRTAS_AGE = 0
LIBRTAS_LIBRARY_VERSION = $(LIBRTAS_CURRENT):$(LIBRTAS_REVISION):$(LIBRTAS_AGE)
lib_LTLIBRARIES += librtas.la
-librtas_la_LDFLAGS = -version-info $(LIBRTAS_LIBRARY_VERSION)
+librtas_la_LDFLAGS = -version-info $(LIBRTAS_LIBRARY_VERSION) -lpthread
librtas_la_SOURCES = \
librtas_src/vpd.c \
librtas_src/ofdt.c \
@@ -37,7 +37,8 @@ librtas_la_SOURCES = \
library_include_HEADERS += librtas_src/librtas.h
noinst_HEADERS += \
librtas_src/internal.h \
- librtas_src/papr-miscdev.h
+ librtas_src/papr-miscdev.h \
+ librtas_src/papr-vpd.h
# See "Updating library version information" in the libtool manual for
# how to maintain these values. They are *not* tied to the release
diff --git a/librtas_src/papr-vpd.h b/librtas_src/papr-vpd.h
new file mode 100644
index 0000000..b57cf30
--- /dev/null
+++ b/librtas_src/papr-vpd.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+#ifndef _UAPI_PAPR_VPD_H_
+#define _UAPI_PAPR_VPD_H_
+
+#include <asm/ioctl.h>
+#include "papr-miscdev.h"
+
+struct papr_location_code {
+ /*
+ * PAPR+ 12.3.2.4 Converged Location Code Rules - Length
+ * Restrictions. 79 characters plus nul.
+ */
+ char str[80];
+};
+
+/*
+ * ioctl for /dev/papr-vpd. Returns a VPD handle fd corresponding to
+ * the location code.
+ */
+#define PAPR_VPD_IOC_CREATE_HANDLE _IOW(PAPR_MISCDEV_IOC_ID, 0, struct papr_location_code)
+
+#endif /* _UAPI_PAPR_VPD_H_ */
diff --git a/librtas_src/vpd.c b/librtas_src/vpd.c
index b2689fb..544560f 100644
--- a/librtas_src/vpd.c
+++ b/librtas_src/vpd.c
@@ -1,33 +1,33 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// Support for accessing IBM Power systems Vital Product Data (VPD)
-// via the rtas() syscall.
+// via /dev/papr-vpd or the legacy rtas() syscall.
-#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
#include <linux/types.h>
#include <linux/unistd.h>
-#include <stdint.h>
+#include <pthread.h>
+#include <search.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
#include <sys/syscall.h>
+#include <unistd.h>
#include "internal.h"
#include "librtas.h"
+#include "papr-vpd.h"
-/**
- * rtas_get_vpd
- * @brief Interface to the ibm,get-vpd rtas call
- *
- * @param loc_code location code
- * @param workarea additional args to rtas call
- * @param size
- * @param sequence
- * @param seq_next
- * @param bytes_ret
- * @return 0 on success, !0 otherwise
- */
-int rtas_get_vpd(char *loc_code, char *workarea, size_t size,
- unsigned int sequence, unsigned int *seq_next,
- unsigned int *bytes_ret)
+static int
+get_vpd_syscall_fallback(const char *loc_code, char *workarea, size_t size,
+ unsigned int sequence, unsigned int *seq_next,
+ unsigned int *bytes_ret)
{
uint32_t kernbuf_pa;
uint32_t loc_pa = 0;
@@ -79,3 +79,122 @@ int rtas_get_vpd(char *loc_code, char *workarea, size_t size,
workarea, size, sequence, status, *seq_next, *bytes_ret);
return rc ? rc : status;
}
+
+static bool get_vpd_can_use_chardev(void)
+{
+ struct stat statbuf;
+
+ if (stat("/dev/papr-vpd", &statbuf))
+ return false;
+
+ if (!S_ISCHR(statbuf.st_mode))
+ return false;
+
+ if (close(open("/dev/papr-vpd", O_RDONLY)))
+ return false;
+
+ return true;
+}
+
+#define DEVPATH "/dev/papr-vpd"
+
+static int vpd_fd_new(const char *loc_code)
+{
+ const int devfd = open(DEVPATH, O_WRONLY);
+ struct papr_location_code lc = {};
+ int fd = -1;
+
+ if (devfd < 0)
+ return -1;
+
+ if (!loc_code)
+ loc_code = "";
+
+ if (sizeof(lc.str) < strlen(loc_code))
+ goto close_devfd;
+
+ strncpy(lc.str, loc_code, sizeof(lc.str));
+ fd = ioctl(devfd, PAPR_VPD_IOC_CREATE_HANDLE, &lc);
+close_devfd:
+ close(devfd);
+ return fd;
+}
+
+static int
+get_vpd_chardev(const char *loc_code, char *workarea, size_t size,
+ unsigned int sequence, unsigned int *seq_next,
+ unsigned int *bytes_ret)
+{
+ int fd = (sequence == 1) ? vpd_fd_new(loc_code) : (int)sequence;
+
+ /*
+ * Ensure we return a fd > 0 in seq_next.
+ */
+ if (fd == 1) {
+ int newfd = dup(fd);
+ close(fd);
+ fd = newfd;
+ }
+
+ if (fd < 0)
+ return -3; /* Synthesize ibm,get-vpd "parameter error" */
+
+ int rtas_status = 0;
+ ssize_t res = read(fd, workarea, size);
+ if (res < 0) {
+ rtas_status = -1; /* Synthesize ibm,get-vpd "hardware error" */
+ close(fd);
+ } else if (res == 0 || res < (ssize_t)size) {
+ rtas_status = 0; /* Done with sequence, no more data */
+ close(fd);
+ if (seq_next)
+ *seq_next = 1;
+ if (bytes_ret)
+ *bytes_ret = res;
+ } else {
+ rtas_status = 1; /* More data available, call again */
+ if (seq_next)
+ *seq_next = fd;
+ if (bytes_ret)
+ *bytes_ret = res;
+ }
+
+ return rtas_status;
+}
+
+static int (*get_vpd_fn)(const char *loc_code,
+ char *workarea,
+ size_t size,
+ unsigned int sequence,
+ unsigned int *seq_next,
+ unsigned int *bytes_ret);
+
+static void get_vpd_fn_setup(void)
+{
+ get_vpd_fn = get_vpd_can_use_chardev() ?
+ get_vpd_chardev : get_vpd_syscall_fallback;
+}
+
+/**
+ * rtas_get_vpd
+ * @brief Interface to the ibm,get-vpd rtas call
+ *
+ * @param loc_code location code
+ * @param workarea additional args to rtas call
+ * @param size
+ * @param sequence
+ * @param seq_next
+ * @param bytes_ret
+ * @return 0 on success, !0 otherwise
+ */
+int rtas_get_vpd(char *loc_code, char *workarea, size_t size,
+ unsigned int sequence, unsigned int *seq_next,
+ unsigned int *bytes_ret)
+{
+ static pthread_once_t get_vpd_fn_once = PTHREAD_ONCE_INIT;
+
+ pthread_once(&get_vpd_fn_once, get_vpd_fn_setup);
+
+ return get_vpd_fn(loc_code, workarea, size, sequence,
+ seq_next, bytes_ret);
+}
--
2.43.0