summaryrefslogtreecommitdiff
path: root/arch/powerpc/kernel/rtas.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-25 11:00:06 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-25 11:00:06 -0800
commitd0a32f5520a33e7f2ace396db6913625e0d29544 (patch)
tree988e993eda872c2c30d6befe2c40095736dde493 /arch/powerpc/kernel/rtas.c
parent5596c6adb04d00cad445641a35f1f1745de57119 (diff)
parentf82cdc37c4bd4ba905bf99ade9782a639b5c12e9 (diff)
downloadlinux-d0a32f5520a33e7f2ace396db6913625e0d29544.tar.gz
linux-d0a32f5520a33e7f2ace396db6913625e0d29544.tar.bz2
linux-d0a32f5520a33e7f2ace396db6913625e0d29544.zip
Merge tag 'powerpc-6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc updates from Michael Ellerman: - Support for configuring secure boot with user-defined keys on PowerVM LPARs - Simplify the replay of soft-masked IRQs by making it non-recursive - Add support for KCSAN on 64-bit Book3S - Improvements to the API & code which interacts with RTAS (pseries firmware) - Change 32-bit powermac to assign PCI bus numbers per domain by default - Some improvements to the 32-bit BPF JIT - Various other small features and fixes Thanks to Anders Roxell, Andrew Donnellan, Andrew Jeffery, Benjamin Gray, Christophe Leroy, Frederic Barrat, Ganesh Goudar, Geoff Levand, Greg Kroah-Hartman, Jan-Benedict Glaw, Josh Poimboeuf, Kajol Jain, Laurent Dufour, Mahesh Salgaonkar, Mathieu Desnoyers, Mimi Zohar, Murphy Zhou, Nathan Chancellor, Nathan Lynch, Nayna Jain, Nicholas Piggin, Pali Rohár, Petr Mladek, Rohan McLure, Russell Currey, Sachin Sant, Sathvika Vasireddy, Sourabh Jain, Stefan Berger, Stephen Rothwell, and Sudhakar Kuppusamy. * tag 'powerpc-6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (114 commits) powerpc/pseries: Avoid hcall in plpks_is_available() on non-pseries powerpc: dts: turris1x.dts: Set lower priority for CPLD syscon-reboot powerpc/e500: Add missing prototype for 'relocate_init' powerpc/64: Fix unannotated intra-function call warning powerpc/epapr: Don't use wrteei on non booke powerpc: Pass correct CPU reference to assembler powerpc/mm: Rearrange if-else block to avoid clang warning powerpc/nohash: Fix build with llvm-as powerpc/nohash: Fix build error with binutils >= 2.38 powerpc/pseries: Fix endianness issue when parsing PLPKS secvar flags macintosh: windfarm: Use unsigned type for 1-bit bitfields powerpc/kexec_file: print error string on usable memory property update failure powerpc/machdep: warn when machine_is() used too early powerpc/64: Replace -mcpu=e500mc64 by -mcpu=e5500 powerpc/eeh: Set channel state after notifying the drivers selftests/powerpc: Fix incorrect kernel headers search path powerpc/rtas: arch-wide function token lookup conversions powerpc/rtas: introduce rtas_function_token() API powerpc/pseries/lpar: convert to papr_sysparm API powerpc/pseries/hv-24x7: convert to papr_sysparm API ...
Diffstat (limited to 'arch/powerpc/kernel/rtas.c')
-rw-r--r--arch/powerpc/kernel/rtas.c1056
1 files changed, 832 insertions, 224 deletions
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index deded51a7978..31175b34856a 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -9,10 +9,12 @@
#define pr_fmt(fmt) "rtas: " fmt
+#include <linux/bsearch.h>
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/init.h>
+#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/memblock.h>
#include <linux/of.h>
@@ -26,6 +28,7 @@
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/uaccess.h>
+#include <linux/xarray.h>
#include <asm/delay.h>
#include <asm/firmware.h>
@@ -33,43 +36,604 @@
#include <asm/machdep.h>
#include <asm/mmu.h>
#include <asm/page.h>
+#include <asm/rtas-work-area.h>
#include <asm/rtas.h>
#include <asm/time.h>
+#include <asm/trace.h>
#include <asm/udbg.h>
+struct rtas_filter {
+ /* Indexes into the args buffer, -1 if not used */
+ const int buf_idx1;
+ const int size_idx1;
+ const int buf_idx2;
+ const int size_idx2;
+ /*
+ * Assumed buffer size per the spec if the function does not
+ * have a size parameter, e.g. ibm,errinjct. 0 if unused.
+ */
+ const int fixed_size;
+};
+
+/**
+ * struct rtas_function - Descriptor for RTAS functions.
+ *
+ * @token: Value of @name if it exists under the /rtas node.
+ * @name: Function name.
+ * @filter: If non-NULL, invoking this function via the rtas syscall is
+ * generally allowed, and @filter describes constraints on the
+ * arguments. See also @banned_for_syscall_on_le.
+ * @banned_for_syscall_on_le: Set when call via sys_rtas is generally allowed
+ * but specifically restricted on ppc64le. Such
+ * functions are believed to have no users on
+ * ppc64le, and we want to keep it that way. It does
+ * not make sense for this to be set when @filter
+ * is false.
+ */
+struct rtas_function {
+ s32 token;
+ const bool banned_for_syscall_on_le:1;
+ const char * const name;
+ const struct rtas_filter *filter;
+};
+
+static struct rtas_function rtas_function_table[] __ro_after_init = {
+ [RTAS_FNIDX__CHECK_EXCEPTION] = {
+ .name = "check-exception",
+ },
+ [RTAS_FNIDX__DISPLAY_CHARACTER] = {
+ .name = "display-character",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__EVENT_SCAN] = {
+ .name = "event-scan",
+ },
+ [RTAS_FNIDX__FREEZE_TIME_BASE] = {
+ .name = "freeze-time-base",
+ },
+ [RTAS_FNIDX__GET_POWER_LEVEL] = {
+ .name = "get-power-level",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__GET_SENSOR_STATE] = {
+ .name = "get-sensor-state",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__GET_TERM_CHAR] = {
+ .name = "get-term-char",
+ },
+ [RTAS_FNIDX__GET_TIME_OF_DAY] = {
+ .name = "get-time-of-day",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_ACTIVATE_FIRMWARE] = {
+ .name = "ibm,activate-firmware",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_CBE_START_PTCAL] = {
+ .name = "ibm,cbe-start-ptcal",
+ },
+ [RTAS_FNIDX__IBM_CBE_STOP_PTCAL] = {
+ .name = "ibm,cbe-stop-ptcal",
+ },
+ [RTAS_FNIDX__IBM_CHANGE_MSI] = {
+ .name = "ibm,change-msi",
+ },
+ [RTAS_FNIDX__IBM_CLOSE_ERRINJCT] = {
+ .name = "ibm,close-errinjct",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_CONFIGURE_BRIDGE] = {
+ .name = "ibm,configure-bridge",
+ },
+ [RTAS_FNIDX__IBM_CONFIGURE_CONNECTOR] = {
+ .name = "ibm,configure-connector",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 0, .size_idx1 = -1,
+ .buf_idx2 = 1, .size_idx2 = -1,
+ .fixed_size = 4096,
+ },
+ },
+ [RTAS_FNIDX__IBM_CONFIGURE_KERNEL_DUMP] = {
+ .name = "ibm,configure-kernel-dump",
+ },
+ [RTAS_FNIDX__IBM_CONFIGURE_PE] = {
+ .name = "ibm,configure-pe",
+ },
+ [RTAS_FNIDX__IBM_CREATE_PE_DMA_WINDOW] = {
+ .name = "ibm,create-pe-dma-window",
+ },
+ [RTAS_FNIDX__IBM_DISPLAY_MESSAGE] = {
+ .name = "ibm,display-message",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 0, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_ERRINJCT] = {
+ .name = "ibm,errinjct",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 2, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ .fixed_size = 1024,
+ },
+ },
+ [RTAS_FNIDX__IBM_EXTI2C] = {
+ .name = "ibm,exti2c",
+ },
+ [RTAS_FNIDX__IBM_GET_CONFIG_ADDR_INFO] = {
+ .name = "ibm,get-config-addr-info",
+ },
+ [RTAS_FNIDX__IBM_GET_CONFIG_ADDR_INFO2] = {
+ .name = "ibm,get-config-addr-info2",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_GET_DYNAMIC_SENSOR_STATE] = {
+ .name = "ibm,get-dynamic-sensor-state",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_GET_INDICES] = {
+ .name = "ibm,get-indices",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 2, .size_idx1 = 3,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_GET_RIO_TOPOLOGY] = {
+ .name = "ibm,get-rio-topology",
+ },
+ [RTAS_FNIDX__IBM_GET_SYSTEM_PARAMETER] = {
+ .name = "ibm,get-system-parameter",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 1, .size_idx1 = 2,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_GET_VPD] = {
+ .name = "ibm,get-vpd",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 0, .size_idx1 = -1,
+ .buf_idx2 = 1, .size_idx2 = 2,
+ },
+ },
+ [RTAS_FNIDX__IBM_GET_XIVE] = {
+ .name = "ibm,get-xive",
+ },
+ [RTAS_FNIDX__IBM_INT_OFF] = {
+ .name = "ibm,int-off",
+ },
+ [RTAS_FNIDX__IBM_INT_ON] = {
+ .name = "ibm,int-on",
+ },
+ [RTAS_FNIDX__IBM_IO_QUIESCE_ACK] = {
+ .name = "ibm,io-quiesce-ack",
+ },
+ [RTAS_FNIDX__IBM_LPAR_PERFTOOLS] = {
+ .name = "ibm,lpar-perftools",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 2, .size_idx1 = 3,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_MANAGE_FLASH_IMAGE] = {
+ .name = "ibm,manage-flash-image",
+ },
+ [RTAS_FNIDX__IBM_MANAGE_STORAGE_PRESERVATION] = {
+ .name = "ibm,manage-storage-preservation",
+ },
+ [RTAS_FNIDX__IBM_NMI_INTERLOCK] = {
+ .name = "ibm,nmi-interlock",
+ },
+ [RTAS_FNIDX__IBM_NMI_REGISTER] = {
+ .name = "ibm,nmi-register",
+ },
+ [RTAS_FNIDX__IBM_OPEN_ERRINJCT] = {
+ .name = "ibm,open-errinjct",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_OPEN_SRIOV_ALLOW_UNFREEZE] = {
+ .name = "ibm,open-sriov-allow-unfreeze",
+ },
+ [RTAS_FNIDX__IBM_OPEN_SRIOV_MAP_PE_NUMBER] = {
+ .name = "ibm,open-sriov-map-pe-number",
+ },
+ [RTAS_FNIDX__IBM_OS_TERM] = {
+ .name = "ibm,os-term",
+ },
+ [RTAS_FNIDX__IBM_PARTNER_CONTROL] = {
+ .name = "ibm,partner-control",
+ },
+ [RTAS_FNIDX__IBM_PHYSICAL_ATTESTATION] = {
+ .name = "ibm,physical-attestation",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 0, .size_idx1 = 1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_PLATFORM_DUMP] = {
+ .name = "ibm,platform-dump",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 4, .size_idx1 = 5,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_POWER_OFF_UPS] = {
+ .name = "ibm,power-off-ups",
+ },
+ [RTAS_FNIDX__IBM_QUERY_INTERRUPT_SOURCE_NUMBER] = {
+ .name = "ibm,query-interrupt-source-number",
+ },
+ [RTAS_FNIDX__IBM_QUERY_PE_DMA_WINDOW] = {
+ .name = "ibm,query-pe-dma-window",
+ },
+ [RTAS_FNIDX__IBM_READ_PCI_CONFIG] = {
+ .name = "ibm,read-pci-config",
+ },
+ [RTAS_FNIDX__IBM_READ_SLOT_RESET_STATE] = {
+ .name = "ibm,read-slot-reset-state",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_READ_SLOT_RESET_STATE2] = {
+ .name = "ibm,read-slot-reset-state2",
+ },
+ [RTAS_FNIDX__IBM_REMOVE_PE_DMA_WINDOW] = {
+ .name = "ibm,remove-pe-dma-window",
+ },
+ [RTAS_FNIDX__IBM_RESET_PE_DMA_WINDOWS] = {
+ .name = "ibm,reset-pe-dma-windows",
+ },
+ [RTAS_FNIDX__IBM_SCAN_LOG_DUMP] = {
+ .name = "ibm,scan-log-dump",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 0, .size_idx1 = 1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_SET_DYNAMIC_INDICATOR] = {
+ .name = "ibm,set-dynamic-indicator",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 2, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_SET_EEH_OPTION] = {
+ .name = "ibm,set-eeh-option",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_SET_SLOT_RESET] = {
+ .name = "ibm,set-slot-reset",
+ },
+ [RTAS_FNIDX__IBM_SET_SYSTEM_PARAMETER] = {
+ .name = "ibm,set-system-parameter",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_SET_XIVE] = {
+ .name = "ibm,set-xive",
+ },
+ [RTAS_FNIDX__IBM_SLOT_ERROR_DETAIL] = {
+ .name = "ibm,slot-error-detail",
+ },
+ [RTAS_FNIDX__IBM_SUSPEND_ME] = {
+ .name = "ibm,suspend-me",
+ .banned_for_syscall_on_le = true,
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__IBM_TUNE_DMA_PARMS] = {
+ .name = "ibm,tune-dma-parms",
+ },
+ [RTAS_FNIDX__IBM_UPDATE_FLASH_64_AND_REBOOT] = {
+ .name = "ibm,update-flash-64-and-reboot",
+ },
+ [RTAS_FNIDX__IBM_UPDATE_NODES] = {
+ .name = "ibm,update-nodes",
+ .banned_for_syscall_on_le = true,
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 0, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ .fixed_size = 4096,
+ },
+ },
+ [RTAS_FNIDX__IBM_UPDATE_PROPERTIES] = {
+ .name = "ibm,update-properties",
+ .banned_for_syscall_on_le = true,
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = 0, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ .fixed_size = 4096,
+ },
+ },
+ [RTAS_FNIDX__IBM_VALIDATE_FLASH_IMAGE] = {
+ .name = "ibm,validate-flash-image",
+ },
+ [RTAS_FNIDX__IBM_WRITE_PCI_CONFIG] = {
+ .name = "ibm,write-pci-config",
+ },
+ [RTAS_FNIDX__NVRAM_FETCH] = {
+ .name = "nvram-fetch",
+ },
+ [RTAS_FNIDX__NVRAM_STORE] = {
+ .name = "nvram-store",
+ },
+ [RTAS_FNIDX__POWER_OFF] = {
+ .name = "power-off",
+ },
+ [RTAS_FNIDX__PUT_TERM_CHAR] = {
+ .name = "put-term-char",
+ },
+ [RTAS_FNIDX__QUERY_CPU_STOPPED_STATE] = {
+ .name = "query-cpu-stopped-state",
+ },
+ [RTAS_FNIDX__READ_PCI_CONFIG] = {
+ .name = "read-pci-config",
+ },
+ [RTAS_FNIDX__RTAS_LAST_ERROR] = {
+ .name = "rtas-last-error",
+ },
+ [RTAS_FNIDX__SET_INDICATOR] = {
+ .name = "set-indicator",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__SET_POWER_LEVEL] = {
+ .name = "set-power-level",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__SET_TIME_FOR_POWER_ON] = {
+ .name = "set-time-for-power-on",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__SET_TIME_OF_DAY] = {
+ .name = "set-time-of-day",
+ .filter = &(const struct rtas_filter) {
+ .buf_idx1 = -1, .size_idx1 = -1,
+ .buf_idx2 = -1, .size_idx2 = -1,
+ },
+ },
+ [RTAS_FNIDX__START_CPU] = {
+ .name = "start-cpu",
+ },
+ [RTAS_FNIDX__STOP_SELF] = {
+ .name = "stop-self",
+ },
+ [RTAS_FNIDX__SYSTEM_REBOOT] = {
+ .name = "system-reboot",
+ },
+ [RTAS_FNIDX__THAW_TIME_BASE] = {
+ .name = "thaw-time-base",
+ },
+ [RTAS_FNIDX__WRITE_PCI_CONFIG] = {
+ .name = "write-pci-config",
+ },
+};
+
+/**
+ * rtas_function_token() - RTAS function token lookup.
+ * @handle: Function handle, e.g. RTAS_FN_EVENT_SCAN.
+ *
+ * Context: Any context.
+ * Return: the token value for the function if implemented by this platform,
+ * otherwise RTAS_UNKNOWN_SERVICE.
+ */
+s32 rtas_function_token(const rtas_fn_handle_t handle)
+{
+ const size_t index = handle.index;
+ const bool out_of_bounds = index >= ARRAY_SIZE(rtas_function_table);
+
+ if (WARN_ONCE(out_of_bounds, "invalid function index %zu", index))
+ return RTAS_UNKNOWN_SERVICE;
+ /*
+ * Various drivers attempt token lookups on non-RTAS
+ * platforms.
+ */
+ if (!rtas.dev)
+ return RTAS_UNKNOWN_SERVICE;
+
+ return rtas_function_table[index].token;
+}
+EXPORT_SYMBOL_GPL(rtas_function_token);
+
+static int rtas_function_cmp(const void *a, const void *b)
+{
+ const struct rtas_function *f1 = a;
+ const struct rtas_function *f2 = b;
+
+ return strcmp(f1->name, f2->name);
+}
+
+/*
+ * Boot-time initialization of the function table needs the lookup to
+ * return a non-const-qualified object. Use rtas_name_to_function()
+ * in all other contexts.
+ */
+static struct rtas_function *__rtas_name_to_function(const char *name)
+{
+ const struct rtas_function key = {
+ .name = name,
+ };
+ struct rtas_function *found;
+
+ found = bsearch(&key, rtas_function_table, ARRAY_SIZE(rtas_function_table),
+ sizeof(rtas_function_table[0]), rtas_function_cmp);
+
+ return found;
+}
+
+static const struct rtas_function *rtas_name_to_function(const char *name)
+{
+ return __rtas_name_to_function(name);
+}
+
+static DEFINE_XARRAY(rtas_token_to_function_xarray);
+
+static int __init rtas_token_to_function_xarray_init(void)
+{
+ int err = 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(rtas_function_table); ++i) {
+ const struct rtas_function *func = &rtas_function_table[i];
+ const s32 token = func->token;
+
+ if (token == RTAS_UNKNOWN_SERVICE)
+ continue;
+
+ err = xa_err(xa_store(&rtas_token_to_function_xarray,
+ token, (void *)func, GFP_KERNEL));
+ if (err)
+ break;
+ }
+
+ return err;
+}
+arch_initcall(rtas_token_to_function_xarray_init);
+
+static const struct rtas_function *rtas_token_to_function(s32 token)
+{
+ const struct rtas_function *func;
+
+ if (WARN_ONCE(token < 0, "invalid token %d", token))
+ return NULL;
+
+ func = xa_load(&rtas_token_to_function_xarray, token);
+
+ if (WARN_ONCE(!func, "unexpected failed lookup for token %d", token))
+ return NULL;
+
+ return func;
+}
+
/* This is here deliberately so it's only used in this file */
void enter_rtas(unsigned long);
-static inline void do_enter_rtas(unsigned long args)
+static void __do_enter_rtas(struct rtas_args *args)
+{
+ enter_rtas(__pa(args));
+ srr_regs_clobbered(); /* rtas uses SRRs, invalidate */
+}
+
+static void __do_enter_rtas_trace(struct rtas_args *args)
{
- unsigned long msr;
+ const char *name = NULL;
+ /*
+ * If the tracepoints that consume the function name aren't
+ * active, avoid the lookup.
+ */
+ if ((trace_rtas_input_enabled() || trace_rtas_output_enabled())) {
+ const s32 token = be32_to_cpu(args->token);
+ const struct rtas_function *func = rtas_token_to_function(token);
+
+ name = func->name;
+ }
+
+ trace_rtas_input(args, name);
+ trace_rtas_ll_entry(args);
+
+ __do_enter_rtas(args);
+
+ trace_rtas_ll_exit(args);
+ trace_rtas_output(args, name);
+}
+static void do_enter_rtas(struct rtas_args *args)
+{
+ const unsigned long msr = mfmsr();
+ /*
+ * Situations where we want to skip any active tracepoints for
+ * safety reasons:
+ *
+ * 1. The last code executed on an offline CPU as it stops,
+ * i.e. we're about to call stop-self. The tracepoints'
+ * function name lookup uses xarray, which uses RCU, which
+ * isn't valid to call on an offline CPU. Any events
+ * emitted on an offline CPU will be discarded anyway.
+ *
+ * 2. In real mode, as when invoking ibm,nmi-interlock from
+ * the pseries MCE handler. We cannot count on trace
+ * buffers or the entries in rtas_token_to_function_xarray
+ * to be contained in the RMO.
+ */
+ const unsigned long mask = MSR_IR | MSR_DR;
+ const bool can_trace = likely(cpu_online(raw_smp_processor_id()) &&
+ (msr & mask) == mask);
/*
* Make sure MSR[RI] is currently enabled as it will be forced later
* in enter_rtas.
*/
- msr = mfmsr();
BUG_ON(!(msr & MSR_RI));
BUG_ON(!irqs_disabled());
hard_irq_disable(); /* Ensure MSR[EE] is disabled on PPC64 */
- enter_rtas(args);
-
- srr_regs_clobbered(); /* rtas uses SRRs, invalidate */
+ if (can_trace)
+ __do_enter_rtas_trace(args);
+ else
+ __do_enter_rtas(args);
}
-struct rtas_t rtas = {
- .lock = __ARCH_SPIN_LOCK_UNLOCKED
-};
-EXPORT_SYMBOL(rtas);
+struct rtas_t rtas;
+
+/*
+ * Nearly all RTAS calls need to be serialized. All uses of the
+ * default rtas_args block must hold rtas_lock.
+ *
+ * Exceptions to the RTAS serialization requirement (e.g. stop-self)
+ * must use a separate rtas_args structure.
+ */
+static DEFINE_RAW_SPINLOCK(rtas_lock);
+static struct rtas_args rtas_args;
DEFINE_SPINLOCK(rtas_data_buf_lock);
-EXPORT_SYMBOL(rtas_data_buf_lock);
+EXPORT_SYMBOL_GPL(rtas_data_buf_lock);
-char rtas_data_buf[RTAS_DATA_BUF_SIZE] __cacheline_aligned;
-EXPORT_SYMBOL(rtas_data_buf);
+char rtas_data_buf[RTAS_DATA_BUF_SIZE] __aligned(SZ_4K);
+EXPORT_SYMBOL_GPL(rtas_data_buf);
unsigned long rtas_rmo_buf;
@@ -78,29 +642,7 @@ unsigned long rtas_rmo_buf;
* This is done like this so rtas_flash can be a module.
*/
void (*rtas_flash_term_hook)(int);
-EXPORT_SYMBOL(rtas_flash_term_hook);
-
-/* RTAS use home made raw locking instead of spin_lock_irqsave
- * because those can be called from within really nasty contexts
- * such as having the timebase stopped which would lockup with
- * normal locks and spinlock debugging enabled
- */
-static unsigned long lock_rtas(void)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- preempt_disable();
- arch_spin_lock(&rtas.lock);
- return flags;
-}
-
-static void unlock_rtas(unsigned long flags)
-{
- arch_spin_unlock(&rtas.lock);
- local_irq_restore(flags);
- preempt_enable();
-}
+EXPORT_SYMBOL_GPL(rtas_flash_term_hook);
/*
* call_rtas_display_status and call_rtas_display_status_delay
@@ -109,14 +651,14 @@ static void unlock_rtas(unsigned long flags)
*/
static void call_rtas_display_status(unsigned char c)
{
- unsigned long s;
+ unsigned long flags;
if (!rtas.base)
return;
- s = lock_rtas();
- rtas_call_unlocked(&rtas.args, 10, 1, 1, NULL, c);
- unlock_rtas(s);
+ raw_spin_lock_irqsave(&rtas_lock, flags);
+ rtas_call_unlocked(&rtas_args, 10, 1, 1, NULL, c);
+ raw_spin_unlock_irqrestore(&rtas_lock, flags);
}
static void call_rtas_display_status_delay(char c)
@@ -240,8 +782,8 @@ void rtas_progress(char *s, unsigned short hex)
"ibm,display-truncation-length", NULL);
of_node_put(root);
}
- display_character = rtas_token("display-character");
- set_indicator = rtas_token("set-indicator");
+ display_character = rtas_function_token(RTAS_FN_DISPLAY_CHARACTER);
+ set_indicator = rtas_function_token(RTAS_FN_SET_INDICATOR);
}
if (display_character == RTAS_UNKNOWN_SERVICE) {
@@ -326,23 +868,38 @@ void rtas_progress(char *s, unsigned short hex)
spin_unlock(&progress_lock);
}
-EXPORT_SYMBOL(rtas_progress); /* needed by rtas_flash module */
+EXPORT_SYMBOL_GPL(rtas_progress); /* needed by rtas_flash module */
int rtas_token(const char *service)
{
+ const struct rtas_function *func;
const __be32 *tokp;
+
if (rtas.dev == NULL)
return RTAS_UNKNOWN_SERVICE;
+
+ func = rtas_name_to_function(service);
+ if (func)
+ return func->token;
+ /*
+ * The caller is looking up a name that is not known to be an
+ * RTAS function. Either it's a function that needs to be
+ * added to the table, or they're misusing rtas_token() to
+ * access non-function properties of the /rtas node. Warn and
+ * fall back to the legacy behavior.
+ */
+ WARN_ONCE(1, "unknown function `%s`, should it be added to rtas_function_table?\n",
+ service);
+
tokp = of_get_property(rtas.dev, service, NULL);
return tokp ? be32_to_cpu(*tokp) : RTAS_UNKNOWN_SERVICE;
}
-EXPORT_SYMBOL(rtas_token);
+EXPORT_SYMBOL_GPL(rtas_token);
int rtas_service_present(const char *service)
{
return rtas_token(service) != RTAS_UNKNOWN_SERVICE;
}
-EXPORT_SYMBOL(rtas_service_present);
#ifdef CONFIG_RTAS_ERROR_LOGGING
@@ -357,7 +914,6 @@ int rtas_get_error_log_max(void)
{
return rtas_error_log_max;
}
-EXPORT_SYMBOL(rtas_get_error_log_max);
static void __init init_error_log_max(void)
{
@@ -381,39 +937,39 @@ static void __init init_error_log_max(void)
static char rtas_err_buf[RTAS_ERROR_LOG_MAX];
-static int rtas_last_error_token;
/** Return a copy of the detailed error text associated with the
* most recent failed call to rtas. Because the error text
* might go stale if there are any other intervening rtas calls,
* this routine must be called atomically with whatever produced
- * the error (i.e. with rtas.lock still held from the previous call).
+ * the error (i.e. with rtas_lock still held from the previous call).
*/
static char *__fetch_rtas_last_error(char *altbuf)
{
+ const s32 token = rtas_function_token(RTAS_FN_RTAS_LAST_ERROR);
struct rtas_args err_args, save_args;
u32 bufsz;
char *buf = NULL;
- if (rtas_last_error_token == -1)
+ if (token == -1)
return NULL;
bufsz = rtas_get_error_log_max();
- err_args.token = cpu_to_be32(rtas_last_error_token);
+ err_args.token = cpu_to_be32(token);
err_args.nargs = cpu_to_be32(2);
err_args.nret = cpu_to_be32(1);
err_args.args[0] = cpu_to_be32(__pa(rtas_err_buf));
err_args.args[1] = cpu_to_be32(bufsz);
err_args.args[2] = 0;
- save_args = rtas.args;
- rtas.args = err_args;
+ save_args = rtas_args;
+ rtas_args = err_args;
- do_enter_rtas(__pa(&rtas.args));
+ do_enter_rtas(&rtas_args);
- err_args = rtas.args;
- rtas.args = save_args;
+ err_args = rtas_args;
+ rtas_args = save_args;
/* Log the error in the unlikely case that there was one. */
if (unlikely(err_args.args[2] == 0)) {
@@ -457,7 +1013,7 @@ va_rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret,
for (i = 0; i < nret; ++i)
args->rets[i] = 0;
- do_enter_rtas(__pa(args));
+ do_enter_rtas(args);
}
void rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, ...)
@@ -469,8 +1025,11 @@ void rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret,
va_end(list);
}
-static int ibm_open_errinjct_token;
-static int ibm_errinjct_token;
+static bool token_is_restricted_errinjct(s32 token)
+{
+ return token == rtas_function_token(RTAS_FN_IBM_OPEN_ERRINJCT) ||
+ token == rtas_function_token(RTAS_FN_IBM_ERRINJCT);
+}
/**
* rtas_call() - Invoke an RTAS firmware function.
@@ -481,7 +1040,7 @@ static int ibm_errinjct_token;
* @....: List of @nargs input parameters.
*
* Invokes the RTAS function indicated by @token, which the caller
- * should obtain via rtas_token().
+ * should obtain via rtas_function_token().
*
* The @nargs and @nret arguments must match the number of input and
* output parameters specified for the RTAS function.
@@ -534,15 +1093,15 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
{
va_list list;
int i;
- unsigned long s;
- struct rtas_args *rtas_args;
+ unsigned long flags;
+ struct rtas_args *args;
char *buff_copy = NULL;
int ret;
if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE)
return -1;
- if (token == ibm_open_errinjct_token || token == ibm_errinjct_token) {
+ if (token_is_restricted_errinjct(token)) {
/*
* It would be nicer to not discard the error value
* from security_locked_down(), but callers expect an
@@ -557,26 +1116,25 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
return -1;
}
- s = lock_rtas();
-
+ raw_spin_lock_irqsave(&rtas_lock, flags);
/* We use the global rtas args buffer */
- rtas_args = &rtas.args;
+ args = &rtas_args;
va_start(list, outputs);
- va_rtas_call_unlocked(rtas_args, token, nargs, nret, list);
+ va_rtas_call_unlocked(args, token, nargs, nret, list);
va_end(list);
/* A -1 return code indicates that the last command couldn't
be completed due to a hardware error. */
- if (be32_to_cpu(rtas_args->rets[0]) == -1)
+ if (be32_to_cpu(args->rets[0]) == -1)
buff_copy = __fetch_rtas_last_error(NULL);
if (nret > 1 && outputs != NULL)
for (i = 0; i < nret-1; ++i)
- outputs[i] = be32_to_cpu(rtas_args->rets[i+1]);
- ret = (nret > 0)? be32_to_cpu(rtas_args->rets[0]): 0;
+ outputs[i] = be32_to_cpu(args->rets[i + 1]);
+ ret = (nret > 0) ? be32_to_cpu(args->rets[0]) : 0;
- unlock_rtas(s);
+ raw_spin_unlock_irqrestore(&rtas_lock, flags);
if (buff_copy) {
log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0);
@@ -585,7 +1143,7 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
}
return ret;
}
-EXPORT_SYMBOL(rtas_call);
+EXPORT_SYMBOL_GPL(rtas_call);
/**
* rtas_busy_delay_time() - From an RTAS status value, calculate the
@@ -623,7 +1181,47 @@ unsigned int rtas_busy_delay_time(int status)
return ms;
}
-EXPORT_SYMBOL(rtas_busy_delay_time);
+
+/*
+ * Early boot fallback for rtas_busy_delay().
+ */
+static bool __init rtas_busy_delay_early(int status)
+{
+ static size_t successive_ext_delays __initdata;
+ bool retry;
+
+ switch (status) {
+ case RTAS_EXTENDED_DELAY_MIN...RTAS_EXTENDED_DELAY_MAX:
+ /*
+ * In the unlikely case that we receive an extended
+ * delay status in early boot, the OS is probably not
+ * the cause, and there's nothing we can do to clear
+ * the condition. Best we can do is delay for a bit
+ * and hope it's transient. Lie to the caller if it
+ * seems like we're stuck in a retry loop.
+ */
+ mdelay(1);
+ retry = true;
+ successive_ext_delays += 1;
+ if (successive_ext_delays > 1000) {
+ pr_err("too many extended delays, giving up\n");
+ dump_stack();
+ retry = false;
+ successive_ext_delays = 0;
+ }
+ break;
+ case RTAS_BUSY:
+ retry = true;
+ successive_ext_delays = 0;
+ break;
+ default:
+ retry = false;
+ successive_ext_delays = 0;
+ break;
+ }
+
+ return retry;
+}
/**
* rtas_busy_delay() - helper for RTAS busy and extended delay statuses
@@ -643,11 +1241,17 @@ EXPORT_SYMBOL(rtas_busy_delay_time);
* * false - @status is not @RTAS_BUSY nor an extended delay hint. The
* caller is responsible for handling @status.
*/
-bool rtas_busy_delay(int status)
+bool __ref rtas_busy_delay(int status)
{
unsigned int ms;
bool ret;
+ /*
+ * Can't do timed sleeps before timekeeping is up.
+ */
+ if (system_state < SYSTEM_SCHEDULING)
+ return rtas_busy_delay_early(status);
+
switch (status) {
case RTAS_EXTENDED_DELAY_MIN...RTAS_EXTENDED_DELAY_MAX:
ret = true;
@@ -697,7 +1301,7 @@ bool rtas_busy_delay(int status)
return ret;
}
-EXPORT_SYMBOL(rtas_busy_delay);
+EXPORT_SYMBOL_GPL(rtas_busy_delay);
static int rtas_error_rc(int rtas_rc)
{
@@ -729,7 +1333,7 @@ static int rtas_error_rc(int rtas_rc)
int rtas_get_power_level(int powerdomain, int *level)
{
- int token = rtas_token("get-power-level");
+ int token = rtas_function_token(RTAS_FN_GET_POWER_LEVEL);
int rc;
if (token == RTAS_UNKNOWN_SERVICE)
@@ -742,11 +1346,11 @@ int rtas_get_power_level(int powerdomain, int *level)
return rtas_error_rc(rc);
return rc;
}
-EXPORT_SYMBOL(rtas_get_power_level);
+EXPORT_SYMBOL_GPL(rtas_get_power_level);
int rtas_set_power_level(int powerdomain, int level, int *setlevel)
{
- int token = rtas_token("set-power-level");
+ int token = rtas_function_token(RTAS_FN_SET_POWER_LEVEL);
int rc;
if (token == RTAS_UNKNOWN_SERVICE)
@@ -760,11 +1364,11 @@ int rtas_set_power_level(int powerdomain, int level, int *setlevel)
return rtas_error_rc(rc);
return rc;
}
-EXPORT_SYMBOL(rtas_set_power_level);
+EXPORT_SYMBOL_GPL(rtas_set_power_level);
int rtas_get_sensor(int sensor, int index, int *state)
{
- int token = rtas_token("get-sensor-state");
+ int token = rtas_function_token(RTAS_FN_GET_SENSOR_STATE);
int rc;
if (token == RTAS_UNKNOWN_SERVICE)
@@ -778,11 +1382,11 @@ int rtas_get_sensor(int sensor, int index, int *state)
return rtas_error_rc(rc);
return rc;
}
-EXPORT_SYMBOL(rtas_get_sensor);
+EXPORT_SYMBOL_GPL(rtas_get_sensor);
int rtas_get_sensor_fast(int sensor, int index, int *state)
{
- int token = rtas_token("get-sensor-state");
+ int token = rtas_function_token(RTAS_FN_GET_SENSOR_STATE);
int rc;
if (token == RTAS_UNKNOWN_SERVICE)
@@ -821,11 +1425,10 @@ bool rtas_indicator_present(int token, int *maxindex)
return false;
}
-EXPORT_SYMBOL(rtas_indicator_present);
int rtas_set_indicator(int indicator, int index, int new_value)
{
- int token = rtas_token("set-indicator");
+ int token = rtas_function_token(RTAS_FN_SET_INDICATOR);
int rc;
if (token == RTAS_UNKNOWN_SERVICE)
@@ -839,15 +1442,15 @@ int rtas_set_indicator(int indicator, int index, int new_value)
return rtas_error_rc(rc);
return rc;
}
-EXPORT_SYMBOL(rtas_set_indicator);
+EXPORT_SYMBOL_GPL(rtas_set_indicator);
/*
* Ignoring RTAS extended delay
*/
int rtas_set_indicator_fast(int indicator, int index, int new_value)
{
+ int token = rtas_function_token(RTAS_FN_SET_INDICATOR);
int rc;
- int token = rtas_token("set-indicator");
if (token == RTAS_UNKNOWN_SERVICE)
return -ENOENT;
@@ -889,10 +1492,11 @@ int rtas_set_indicator_fast(int indicator, int index, int new_value)
*/
int rtas_ibm_suspend_me(int *fw_status)
{
+ int token = rtas_function_token(RTAS_FN_IBM_SUSPEND_ME);
int fwrc;
int ret;
- fwrc = rtas_call(rtas_token("ibm,suspend-me"), 0, 1, NULL);
+ fwrc = rtas_call(token, 0, 1, NULL);
switch (fwrc) {
case 0: