12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2033ef338SPaul Mackerras /*
3033ef338SPaul Mackerras *
4033ef338SPaul Mackerras * Procedures for interfacing to the RTAS on CHRP machines.
5033ef338SPaul Mackerras *
6033ef338SPaul Mackerras * Peter Bergner, IBM March 2001.
7033ef338SPaul Mackerras * Copyright (C) 2001 IBM.
8033ef338SPaul Mackerras */
9033ef338SPaul Mackerras
10f975b655SNathan Lynch #define pr_fmt(fmt) "rtas: " fmt
11f975b655SNathan Lynch
128252b882SNathan Lynch #include <linux/bsearch.h>
13a9415644SRandy Dunlap #include <linux/capability.h>
1421fe3301SBenjamin Herrenschmidt #include <linux/delay.h>
159581f8a0SNathan Lynch #include <linux/export.h>
169581f8a0SNathan Lynch #include <linux/init.h>
178252b882SNathan Lynch #include <linux/kconfig.h>
189581f8a0SNathan Lynch #include <linux/kernel.h>
19af8bc682SNathan Lynch #include <linux/lockdep.h>
2095f72d1eSYinghai Lu #include <linux/memblock.h>
21e6f6390aSChristophe Leroy #include <linux/of.h>
22e6f6390aSChristophe Leroy #include <linux/of_fdt.h>
239581f8a0SNathan Lynch #include <linux/reboot.h>
249581f8a0SNathan Lynch #include <linux/sched.h>
259581f8a0SNathan Lynch #include <linux/security.h>
269581f8a0SNathan Lynch #include <linux/slab.h>
279581f8a0SNathan Lynch #include <linux/spinlock.h>
289581f8a0SNathan Lynch #include <linux/stdarg.h>
299581f8a0SNathan Lynch #include <linux/syscalls.h>
309581f8a0SNathan Lynch #include <linux/types.h>
317c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
328252b882SNathan Lynch #include <linux/xarray.h>
339581f8a0SNathan Lynch
349581f8a0SNathan Lynch #include <asm/delay.h>
359581f8a0SNathan Lynch #include <asm/firmware.h>
369581f8a0SNathan Lynch #include <asm/interrupt.h>
379581f8a0SNathan Lynch #include <asm/machdep.h>
3846db2f86SBrian King #include <asm/mmu.h>
399581f8a0SNathan Lynch #include <asm/page.h>
4043033bc6SNathan Lynch #include <asm/rtas-work-area.h>
419581f8a0SNathan Lynch #include <asm/rtas.h>
429581f8a0SNathan Lynch #include <asm/time.h>
4324098f58SNathan Lynch #include <asm/trace.h>
449581f8a0SNathan Lynch #include <asm/udbg.h>
45033ef338SPaul Mackerras
468252b882SNathan Lynch struct rtas_filter {
478252b882SNathan Lynch /* Indexes into the args buffer, -1 if not used */
488252b882SNathan Lynch const int buf_idx1;
498252b882SNathan Lynch const int size_idx1;
508252b882SNathan Lynch const int buf_idx2;
518252b882SNathan Lynch const int size_idx2;
528252b882SNathan Lynch /*
538252b882SNathan Lynch * Assumed buffer size per the spec if the function does not
548252b882SNathan Lynch * have a size parameter, e.g. ibm,errinjct. 0 if unused.
558252b882SNathan Lynch */
568252b882SNathan Lynch const int fixed_size;
578252b882SNathan Lynch };
588252b882SNathan Lynch
598252b882SNathan Lynch /**
608252b882SNathan Lynch * struct rtas_function - Descriptor for RTAS functions.
618252b882SNathan Lynch *
628252b882SNathan Lynch * @token: Value of @name if it exists under the /rtas node.
638252b882SNathan Lynch * @name: Function name.
648252b882SNathan Lynch * @filter: If non-NULL, invoking this function via the rtas syscall is
658252b882SNathan Lynch * generally allowed, and @filter describes constraints on the
668252b882SNathan Lynch * arguments. See also @banned_for_syscall_on_le.
678252b882SNathan Lynch * @banned_for_syscall_on_le: Set when call via sys_rtas is generally allowed
688252b882SNathan Lynch * but specifically restricted on ppc64le. Such
698252b882SNathan Lynch * functions are believed to have no users on
708252b882SNathan Lynch * ppc64le, and we want to keep it that way. It does
718252b882SNathan Lynch * not make sense for this to be set when @filter
7232740fceSNathan Lynch * is NULL.
738252b882SNathan Lynch */
748252b882SNathan Lynch struct rtas_function {
758252b882SNathan Lynch s32 token;
768252b882SNathan Lynch const bool banned_for_syscall_on_le:1;
778252b882SNathan Lynch const char * const name;
788252b882SNathan Lynch const struct rtas_filter *filter;
798252b882SNathan Lynch };
808252b882SNathan Lynch
818252b882SNathan Lynch static struct rtas_function rtas_function_table[] __ro_after_init = {
828252b882SNathan Lynch [RTAS_FNIDX__CHECK_EXCEPTION] = {
838252b882SNathan Lynch .name = "check-exception",
848252b882SNathan Lynch },
858252b882SNathan Lynch [RTAS_FNIDX__DISPLAY_CHARACTER] = {
868252b882SNathan Lynch .name = "display-character",
878252b882SNathan Lynch .filter = &(const struct rtas_filter) {
888252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
898252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
908252b882SNathan Lynch },
918252b882SNathan Lynch },
928252b882SNathan Lynch [RTAS_FNIDX__EVENT_SCAN] = {
938252b882SNathan Lynch .name = "event-scan",
948252b882SNathan Lynch },
958252b882SNathan Lynch [RTAS_FNIDX__FREEZE_TIME_BASE] = {
968252b882SNathan Lynch .name = "freeze-time-base",
978252b882SNathan Lynch },
988252b882SNathan Lynch [RTAS_FNIDX__GET_POWER_LEVEL] = {
998252b882SNathan Lynch .name = "get-power-level",
1008252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1018252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
1028252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1038252b882SNathan Lynch },
1048252b882SNathan Lynch },
1058252b882SNathan Lynch [RTAS_FNIDX__GET_SENSOR_STATE] = {
1068252b882SNathan Lynch .name = "get-sensor-state",
1078252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1088252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
1098252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1108252b882SNathan Lynch },
1118252b882SNathan Lynch },
1128252b882SNathan Lynch [RTAS_FNIDX__GET_TERM_CHAR] = {
1138252b882SNathan Lynch .name = "get-term-char",
1148252b882SNathan Lynch },
1158252b882SNathan Lynch [RTAS_FNIDX__GET_TIME_OF_DAY] = {
1168252b882SNathan Lynch .name = "get-time-of-day",
1178252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1188252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
1198252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1208252b882SNathan Lynch },
1218252b882SNathan Lynch },
1228252b882SNathan Lynch [RTAS_FNIDX__IBM_ACTIVATE_FIRMWARE] = {
1238252b882SNathan Lynch .name = "ibm,activate-firmware",
1248252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1258252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
1268252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1278252b882SNathan Lynch },
1288252b882SNathan Lynch },
1298252b882SNathan Lynch [RTAS_FNIDX__IBM_CBE_START_PTCAL] = {
1308252b882SNathan Lynch .name = "ibm,cbe-start-ptcal",
1318252b882SNathan Lynch },
1328252b882SNathan Lynch [RTAS_FNIDX__IBM_CBE_STOP_PTCAL] = {
1338252b882SNathan Lynch .name = "ibm,cbe-stop-ptcal",
1348252b882SNathan Lynch },
1358252b882SNathan Lynch [RTAS_FNIDX__IBM_CHANGE_MSI] = {
1368252b882SNathan Lynch .name = "ibm,change-msi",
1378252b882SNathan Lynch },
1388252b882SNathan Lynch [RTAS_FNIDX__IBM_CLOSE_ERRINJCT] = {
1398252b882SNathan Lynch .name = "ibm,close-errinjct",
1408252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1418252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
1428252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1438252b882SNathan Lynch },
1448252b882SNathan Lynch },
1458252b882SNathan Lynch [RTAS_FNIDX__IBM_CONFIGURE_BRIDGE] = {
1468252b882SNathan Lynch .name = "ibm,configure-bridge",
1478252b882SNathan Lynch },
1488252b882SNathan Lynch [RTAS_FNIDX__IBM_CONFIGURE_CONNECTOR] = {
1498252b882SNathan Lynch .name = "ibm,configure-connector",
1508252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1518252b882SNathan Lynch .buf_idx1 = 0, .size_idx1 = -1,
1528252b882SNathan Lynch .buf_idx2 = 1, .size_idx2 = -1,
1538252b882SNathan Lynch .fixed_size = 4096,
1548252b882SNathan Lynch },
1558252b882SNathan Lynch },
1568252b882SNathan Lynch [RTAS_FNIDX__IBM_CONFIGURE_KERNEL_DUMP] = {
1578252b882SNathan Lynch .name = "ibm,configure-kernel-dump",
1588252b882SNathan Lynch },
1598252b882SNathan Lynch [RTAS_FNIDX__IBM_CONFIGURE_PE] = {
1608252b882SNathan Lynch .name = "ibm,configure-pe",
1618252b882SNathan Lynch },
1628252b882SNathan Lynch [RTAS_FNIDX__IBM_CREATE_PE_DMA_WINDOW] = {
1638252b882SNathan Lynch .name = "ibm,create-pe-dma-window",
1648252b882SNathan Lynch },
1658252b882SNathan Lynch [RTAS_FNIDX__IBM_DISPLAY_MESSAGE] = {
1668252b882SNathan Lynch .name = "ibm,display-message",
1678252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1688252b882SNathan Lynch .buf_idx1 = 0, .size_idx1 = -1,
1698252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1708252b882SNathan Lynch },
1718252b882SNathan Lynch },
1728252b882SNathan Lynch [RTAS_FNIDX__IBM_ERRINJCT] = {
1738252b882SNathan Lynch .name = "ibm,errinjct",
1748252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1758252b882SNathan Lynch .buf_idx1 = 2, .size_idx1 = -1,
1768252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1778252b882SNathan Lynch .fixed_size = 1024,
1788252b882SNathan Lynch },
1798252b882SNathan Lynch },
1808252b882SNathan Lynch [RTAS_FNIDX__IBM_EXTI2C] = {
1818252b882SNathan Lynch .name = "ibm,exti2c",
1828252b882SNathan Lynch },
1838252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_CONFIG_ADDR_INFO] = {
1848252b882SNathan Lynch .name = "ibm,get-config-addr-info",
1858252b882SNathan Lynch },
1868252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_CONFIG_ADDR_INFO2] = {
1878252b882SNathan Lynch .name = "ibm,get-config-addr-info2",
1888252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1898252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
1908252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1918252b882SNathan Lynch },
1928252b882SNathan Lynch },
1938252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_DYNAMIC_SENSOR_STATE] = {
1948252b882SNathan Lynch .name = "ibm,get-dynamic-sensor-state",
1958252b882SNathan Lynch .filter = &(const struct rtas_filter) {
1968252b882SNathan Lynch .buf_idx1 = 1, .size_idx1 = -1,
1978252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
1988252b882SNathan Lynch },
1998252b882SNathan Lynch },
2008252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_INDICES] = {
2018252b882SNathan Lynch .name = "ibm,get-indices",
2028252b882SNathan Lynch .filter = &(const struct rtas_filter) {
2038252b882SNathan Lynch .buf_idx1 = 2, .size_idx1 = 3,
2048252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
2058252b882SNathan Lynch },
2068252b882SNathan Lynch },
2078252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_RIO_TOPOLOGY] = {
2088252b882SNathan Lynch .name = "ibm,get-rio-topology",
2098252b882SNathan Lynch },
2108252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_SYSTEM_PARAMETER] = {
2118252b882SNathan Lynch .name = "ibm,get-system-parameter",
2128252b882SNathan Lynch .filter = &(const struct rtas_filter) {
2138252b882SNathan Lynch .buf_idx1 = 1, .size_idx1 = 2,
2148252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
2158252b882SNathan Lynch },
2168252b882SNathan Lynch },
2178252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_VPD] = {
2188252b882SNathan Lynch .name = "ibm,get-vpd",
2198252b882SNathan Lynch .filter = &(const struct rtas_filter) {
2208252b882SNathan Lynch .buf_idx1 = 0, .size_idx1 = -1,
2218252b882SNathan Lynch .buf_idx2 = 1, .size_idx2 = 2,
2228252b882SNathan Lynch },
2238252b882SNathan Lynch },
2248252b882SNathan Lynch [RTAS_FNIDX__IBM_GET_XIVE] = {
2258252b882SNathan Lynch .name = "ibm,get-xive",
2268252b882SNathan Lynch },
2278252b882SNathan Lynch [RTAS_FNIDX__IBM_INT_OFF] = {
2288252b882SNathan Lynch .name = "ibm,int-off",
2298252b882SNathan Lynch },
2308252b882SNathan Lynch [RTAS_FNIDX__IBM_INT_ON] = {
2318252b882SNathan Lynch .name = "ibm,int-on",
2328252b882SNathan Lynch },
2338252b882SNathan Lynch [RTAS_FNIDX__IBM_IO_QUIESCE_ACK] = {
2348252b882SNathan Lynch .name = "ibm,io-quiesce-ack",
2358252b882SNathan Lynch },
2368252b882SNathan Lynch [RTAS_FNIDX__IBM_LPAR_PERFTOOLS] = {
2378252b882SNathan Lynch .name = "ibm,lpar-perftools",
2388252b882SNathan Lynch .filter = &(const struct rtas_filter) {
2398252b882SNathan Lynch .buf_idx1 = 2, .size_idx1 = 3,
2408252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
2418252b882SNathan Lynch },
2428252b882SNathan Lynch },
2438252b882SNathan Lynch [RTAS_FNIDX__IBM_MANAGE_FLASH_IMAGE] = {
2448252b882SNathan Lynch .name = "ibm,manage-flash-image",
2458252b882SNathan Lynch },
2468252b882SNathan Lynch [RTAS_FNIDX__IBM_MANAGE_STORAGE_PRESERVATION] = {
2478252b882SNathan Lynch .name = "ibm,manage-storage-preservation",
2488252b882SNathan Lynch },
2498252b882SNathan Lynch [RTAS_FNIDX__IBM_NMI_INTERLOCK] = {
2508252b882SNathan Lynch .name = "ibm,nmi-interlock",
2518252b882SNathan Lynch },
2528252b882SNathan Lynch [RTAS_FNIDX__IBM_NMI_REGISTER] = {
2538252b882SNathan Lynch .name = "ibm,nmi-register",
2548252b882SNathan Lynch },
2558252b882SNathan Lynch [RTAS_FNIDX__IBM_OPEN_ERRINJCT] = {
2568252b882SNathan Lynch .name = "ibm,open-errinjct",
2578252b882SNathan Lynch .filter = &(const struct rtas_filter) {
2588252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
2598252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
2608252b882SNathan Lynch },
2618252b882SNathan Lynch },
2628252b882SNathan Lynch [RTAS_FNIDX__IBM_OPEN_SRIOV_ALLOW_UNFREEZE] = {
2638252b882SNathan Lynch .name = "ibm,open-sriov-allow-unfreeze",
2648252b882SNathan Lynch },
2658252b882SNathan Lynch [RTAS_FNIDX__IBM_OPEN_SRIOV_MAP_PE_NUMBER] = {
2668252b882SNathan Lynch .name = "ibm,open-sriov-map-pe-number",
2678252b882SNathan Lynch },
2688252b882SNathan Lynch [RTAS_FNIDX__IBM_OS_TERM] = {
2698252b882SNathan Lynch .name = "ibm,os-term",
2708252b882SNathan Lynch },
2718252b882SNathan Lynch [RTAS_FNIDX__IBM_PARTNER_CONTROL] = {
2728252b882SNathan Lynch .name = "ibm,partner-control",
2738252b882SNathan Lynch },
2748252b882SNathan Lynch [RTAS_FNIDX__IBM_PHYSICAL_ATTESTATION] = {
2758252b882SNathan Lynch .name = "ibm,physical-attestation",
2768252b882SNathan Lynch .filter = &(const struct rtas_filter) {
2778252b882SNathan Lynch .buf_idx1 = 0, .size_idx1 = 1,
2788252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
2798252b882SNathan Lynch },
2808252b882SNathan Lynch },
2818252b882SNathan Lynch [RTAS_FNIDX__IBM_PLATFORM_DUMP] = {
2828252b882SNathan Lynch .name = "ibm,platform-dump",
2838252b882SNathan Lynch .filter = &(const struct rtas_filter) {
2848252b882SNathan Lynch .buf_idx1 = 4, .size_idx1 = 5,
2858252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
2868252b882SNathan Lynch },
2878252b882SNathan Lynch },
2888252b882SNathan Lynch [RTAS_FNIDX__IBM_POWER_OFF_UPS] = {
2898252b882SNathan Lynch .name = "ibm,power-off-ups",
2908252b882SNathan Lynch },
2918252b882SNathan Lynch [RTAS_FNIDX__IBM_QUERY_INTERRUPT_SOURCE_NUMBER] = {
2928252b882SNathan Lynch .name = "ibm,query-interrupt-source-number",
2938252b882SNathan Lynch },
2948252b882SNathan Lynch [RTAS_FNIDX__IBM_QUERY_PE_DMA_WINDOW] = {
2958252b882SNathan Lynch .name = "ibm,query-pe-dma-window",
2968252b882SNathan Lynch },
2978252b882SNathan Lynch [RTAS_FNIDX__IBM_READ_PCI_CONFIG] = {
2988252b882SNathan Lynch .name = "ibm,read-pci-config",
2998252b882SNathan Lynch },
3008252b882SNathan Lynch [RTAS_FNIDX__IBM_READ_SLOT_RESET_STATE] = {
3018252b882SNathan Lynch .name = "ibm,read-slot-reset-state",
3028252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3038252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
3048252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3058252b882SNathan Lynch },
3068252b882SNathan Lynch },
3078252b882SNathan Lynch [RTAS_FNIDX__IBM_READ_SLOT_RESET_STATE2] = {
3088252b882SNathan Lynch .name = "ibm,read-slot-reset-state2",
3098252b882SNathan Lynch },
3108252b882SNathan Lynch [RTAS_FNIDX__IBM_REMOVE_PE_DMA_WINDOW] = {
3118252b882SNathan Lynch .name = "ibm,remove-pe-dma-window",
3128252b882SNathan Lynch },
313*6b6282d5SNathan Lynch [RTAS_FNIDX__IBM_RESET_PE_DMA_WINDOW] = {
314*6b6282d5SNathan Lynch /*
315*6b6282d5SNathan Lynch * Note: PAPR+ v2.13 7.3.31.4.1 spells this as
316*6b6282d5SNathan Lynch * "ibm,reset-pe-dma-windows" (plural), but RTAS
317*6b6282d5SNathan Lynch * implementations use the singular form in practice.
318*6b6282d5SNathan Lynch */
319*6b6282d5SNathan Lynch .name = "ibm,reset-pe-dma-window",
3208252b882SNathan Lynch },
3218252b882SNathan Lynch [RTAS_FNIDX__IBM_SCAN_LOG_DUMP] = {
3228252b882SNathan Lynch .name = "ibm,scan-log-dump",
3238252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3248252b882SNathan Lynch .buf_idx1 = 0, .size_idx1 = 1,
3258252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3268252b882SNathan Lynch },
3278252b882SNathan Lynch },
3288252b882SNathan Lynch [RTAS_FNIDX__IBM_SET_DYNAMIC_INDICATOR] = {
3298252b882SNathan Lynch .name = "ibm,set-dynamic-indicator",
3308252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3318252b882SNathan Lynch .buf_idx1 = 2, .size_idx1 = -1,
3328252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3338252b882SNathan Lynch },
3348252b882SNathan Lynch },
3358252b882SNathan Lynch [RTAS_FNIDX__IBM_SET_EEH_OPTION] = {
3368252b882SNathan Lynch .name = "ibm,set-eeh-option",
3378252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3388252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
3398252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3408252b882SNathan Lynch },
3418252b882SNathan Lynch },
3428252b882SNathan Lynch [RTAS_FNIDX__IBM_SET_SLOT_RESET] = {
3438252b882SNathan Lynch .name = "ibm,set-slot-reset",
3448252b882SNathan Lynch },
3458252b882SNathan Lynch [RTAS_FNIDX__IBM_SET_SYSTEM_PARAMETER] = {
3468252b882SNathan Lynch .name = "ibm,set-system-parameter",
3478252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3488252b882SNathan Lynch .buf_idx1 = 1, .size_idx1 = -1,
3498252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3508252b882SNathan Lynch },
3518252b882SNathan Lynch },
3528252b882SNathan Lynch [RTAS_FNIDX__IBM_SET_XIVE] = {
3538252b882SNathan Lynch .name = "ibm,set-xive",
3548252b882SNathan Lynch },
3558252b882SNathan Lynch [RTAS_FNIDX__IBM_SLOT_ERROR_DETAIL] = {
3568252b882SNathan Lynch .name = "ibm,slot-error-detail",
3578252b882SNathan Lynch },
3588252b882SNathan Lynch [RTAS_FNIDX__IBM_SUSPEND_ME] = {
3598252b882SNathan Lynch .name = "ibm,suspend-me",
3608252b882SNathan Lynch .banned_for_syscall_on_le = true,
3618252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3628252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
3638252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3648252b882SNathan Lynch },
3658252b882SNathan Lynch },
3668252b882SNathan Lynch [RTAS_FNIDX__IBM_TUNE_DMA_PARMS] = {
3678252b882SNathan Lynch .name = "ibm,tune-dma-parms",
3688252b882SNathan Lynch },
3698252b882SNathan Lynch [RTAS_FNIDX__IBM_UPDATE_FLASH_64_AND_REBOOT] = {
3708252b882SNathan Lynch .name = "ibm,update-flash-64-and-reboot",
3718252b882SNathan Lynch },
3728252b882SNathan Lynch [RTAS_FNIDX__IBM_UPDATE_NODES] = {
3738252b882SNathan Lynch .name = "ibm,update-nodes",
3748252b882SNathan Lynch .banned_for_syscall_on_le = true,
3758252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3768252b882SNathan Lynch .buf_idx1 = 0, .size_idx1 = -1,
3778252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3788252b882SNathan Lynch .fixed_size = 4096,
3798252b882SNathan Lynch },
3808252b882SNathan Lynch },
3818252b882SNathan Lynch [RTAS_FNIDX__IBM_UPDATE_PROPERTIES] = {
3828252b882SNathan Lynch .name = "ibm,update-properties",
3838252b882SNathan Lynch .banned_for_syscall_on_le = true,
3848252b882SNathan Lynch .filter = &(const struct rtas_filter) {
3858252b882SNathan Lynch .buf_idx1 = 0, .size_idx1 = -1,
3868252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
3878252b882SNathan Lynch .fixed_size = 4096,
3888252b882SNathan Lynch },
3898252b882SNathan Lynch },
3908252b882SNathan Lynch [RTAS_FNIDX__IBM_VALIDATE_FLASH_IMAGE] = {
3918252b882SNathan Lynch .name = "ibm,validate-flash-image",
3928252b882SNathan Lynch },
3938252b882SNathan Lynch [RTAS_FNIDX__IBM_WRITE_PCI_CONFIG] = {
3948252b882SNathan Lynch .name = "ibm,write-pci-config",
3958252b882SNathan Lynch },
3968252b882SNathan Lynch [RTAS_FNIDX__NVRAM_FETCH] = {
3978252b882SNathan Lynch .name = "nvram-fetch",
3988252b882SNathan Lynch },
3998252b882SNathan Lynch [RTAS_FNIDX__NVRAM_STORE] = {
4008252b882SNathan Lynch .name = "nvram-store",
4018252b882SNathan Lynch },
4028252b882SNathan Lynch [RTAS_FNIDX__POWER_OFF] = {
4038252b882SNathan Lynch .name = "power-off",
4048252b882SNathan Lynch },
4058252b882SNathan Lynch [RTAS_FNIDX__PUT_TERM_CHAR] = {
4068252b882SNathan Lynch .name = "put-term-char",
4078252b882SNathan Lynch },
4088252b882SNathan Lynch [RTAS_FNIDX__QUERY_CPU_STOPPED_STATE] = {
4098252b882SNathan Lynch .name = "query-cpu-stopped-state",
4108252b882SNathan Lynch },
4118252b882SNathan Lynch [RTAS_FNIDX__READ_PCI_CONFIG] = {
4128252b882SNathan Lynch .name = "read-pci-config",
4138252b882SNathan Lynch },
4148252b882SNathan Lynch [RTAS_FNIDX__RTAS_LAST_ERROR] = {
4158252b882SNathan Lynch .name = "rtas-last-error",
4168252b882SNathan Lynch },
4178252b882SNathan Lynch [RTAS_FNIDX__SET_INDICATOR] = {
4188252b882SNathan Lynch .name = "set-indicator",
4198252b882SNathan Lynch .filter = &(const struct rtas_filter) {
4208252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
4218252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
4228252b882SNathan Lynch },
4238252b882SNathan Lynch },
4248252b882SNathan Lynch [RTAS_FNIDX__SET_POWER_LEVEL] = {
4258252b882SNathan Lynch .name = "set-power-level",
4268252b882SNathan Lynch .filter = &(const struct rtas_filter) {
4278252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
4288252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
4298252b882SNathan Lynch },
4308252b882SNathan Lynch },
4318252b882SNathan Lynch [RTAS_FNIDX__SET_TIME_FOR_POWER_ON] = {
4328252b882SNathan Lynch .name = "set-time-for-power-on",
4338252b882SNathan Lynch .filter = &(const struct rtas_filter) {
4348252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
4358252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
4368252b882SNathan Lynch },
4378252b882SNathan Lynch },
4388252b882SNathan Lynch [RTAS_FNIDX__SET_TIME_OF_DAY] = {
4398252b882SNathan Lynch .name = "set-time-of-day",
4408252b882SNathan Lynch .filter = &(const struct rtas_filter) {
4418252b882SNathan Lynch .buf_idx1 = -1, .size_idx1 = -1,
4428252b882SNathan Lynch .buf_idx2 = -1, .size_idx2 = -1,
4438252b882SNathan Lynch },
4448252b882SNathan Lynch },
4458252b882SNathan Lynch [RTAS_FNIDX__START_CPU] = {
4468252b882SNathan Lynch .name = "start-cpu",
4478252b882SNathan Lynch },
4488252b882SNathan Lynch [RTAS_FNIDX__STOP_SELF] = {
4498252b882SNathan Lynch .name = "stop-self",
4508252b882SNathan Lynch },
4518252b882SNathan Lynch [RTAS_FNIDX__SYSTEM_REBOOT] = {
4528252b882SNathan Lynch .name = "system-reboot",
4538252b882SNathan Lynch },
4548252b882SNathan Lynch [RTAS_FNIDX__THAW_TIME_BASE] = {
4558252b882SNathan Lynch .name = "thaw-time-base",
4568252b882SNathan Lynch },
4578252b882SNathan Lynch [RTAS_FNIDX__WRITE_PCI_CONFIG] = {
4588252b882SNathan Lynch .name = "write-pci-config",
4598252b882SNathan Lynch },
4608252b882SNathan Lynch };
4618252b882SNathan Lynch
462af8bc682SNathan Lynch /*
463af8bc682SNathan Lynch * Nearly all RTAS calls need to be serialized. All uses of the
464af8bc682SNathan Lynch * default rtas_args block must hold rtas_lock.
465af8bc682SNathan Lynch *
466af8bc682SNathan Lynch * Exceptions to the RTAS serialization requirement (e.g. stop-self)
467af8bc682SNathan Lynch * must use a separate rtas_args structure.
468af8bc682SNathan Lynch */
469af8bc682SNathan Lynch static DEFINE_RAW_SPINLOCK(rtas_lock);
470af8bc682SNathan Lynch static struct rtas_args rtas_args;
471af8bc682SNathan Lynch
472716bfc97SNathan Lynch /**
473716bfc97SNathan Lynch * rtas_function_token() - RTAS function token lookup.
474716bfc97SNathan Lynch * @handle: Function handle, e.g. RTAS_FN_EVENT_SCAN.
475716bfc97SNathan Lynch *
476716bfc97SNathan Lynch * Context: Any context.
477716bfc97SNathan Lynch * Return: the token value for the function if implemented by this platform,
478716bfc97SNathan Lynch * otherwise RTAS_UNKNOWN_SERVICE.
479716bfc97SNathan Lynch */
rtas_function_token(const rtas_fn_handle_t handle)480716bfc97SNathan Lynch s32 rtas_function_token(const rtas_fn_handle_t handle)
481716bfc97SNathan Lynch {
482716bfc97SNathan Lynch const size_t index = handle.index;
483716bfc97SNathan Lynch const bool out_of_bounds = index >= ARRAY_SIZE(rtas_function_table);
484716bfc97SNathan Lynch
485716bfc97SNathan Lynch if (WARN_ONCE(out_of_bounds, "invalid function index %zu", index))
486716bfc97SNathan Lynch return RTAS_UNKNOWN_SERVICE;
487716bfc97SNathan Lynch /*
488716bfc97SNathan Lynch * Various drivers attempt token lookups on non-RTAS
489716bfc97SNathan Lynch * platforms.
490716bfc97SNathan Lynch */
491716bfc97SNathan Lynch if (!rtas.dev)
492716bfc97SNathan Lynch return RTAS_UNKNOWN_SERVICE;
493716bfc97SNathan Lynch
494716bfc97SNathan Lynch return rtas_function_table[index].token;
495716bfc97SNathan Lynch }
496716bfc97SNathan Lynch EXPORT_SYMBOL_GPL(rtas_function_token);
497716bfc97SNathan Lynch
rtas_function_cmp(const void * a,const void * b)4988252b882SNathan Lynch static int rtas_function_cmp(const void *a, const void *b)
4998252b882SNathan Lynch {
5008252b882SNathan Lynch const struct rtas_function *f1 = a;
5018252b882SNathan Lynch const struct rtas_function *f2 = b;
5028252b882SNathan Lynch
5038252b882SNathan Lynch return strcmp(f1->name, f2->name);
5048252b882SNathan Lynch }
5058252b882SNathan Lynch
5068252b882SNathan Lynch /*
5078252b882SNathan Lynch * Boot-time initialization of the function table needs the lookup to
5088252b882SNathan Lynch * return a non-const-qualified object. Use rtas_name_to_function()
5098252b882SNathan Lynch * in all other contexts.
5108252b882SNathan Lynch */
__rtas_name_to_function(const char * name)5118252b882SNathan Lynch static struct rtas_function *__rtas_name_to_function(const char *name)
5128252b882SNathan Lynch {
5138252b882SNathan Lynch const struct rtas_function key = {
5148252b882SNathan Lynch .name = name,
5158252b882SNathan Lynch };
5168252b882SNathan Lynch struct rtas_function *found;
5178252b882SNathan Lynch
5188252b882SNathan Lynch found = bsearch(&key, rtas_function_table, ARRAY_SIZE(rtas_function_table),
5198252b882SNathan Lynch sizeof(rtas_function_table[0]), rtas_function_cmp);
5208252b882SNathan Lynch
5218252b882SNathan Lynch return found;
5228252b882SNathan Lynch }
5238252b882SNathan Lynch
rtas_name_to_function(const char * name)5248252b882SNathan Lynch static const struct rtas_function *rtas_name_to_function(const char *name)
5258252b882SNathan Lynch {
5268252b882SNathan Lynch return __rtas_name_to_function(name);
5278252b882SNathan Lynch }
5288252b882SNathan Lynch
5298252b882SNathan Lynch static DEFINE_XARRAY(rtas_token_to_function_xarray);
5308252b882SNathan Lynch
rtas_token_to_function_xarray_init(void)5318252b882SNathan Lynch static int __init rtas_token_to_function_xarray_init(void)
5328252b882SNathan Lynch {
5338252b882SNathan Lynch int err = 0;
5348252b882SNathan Lynch
5358252b882SNathan Lynch for (size_t i = 0; i < ARRAY_SIZE(rtas_function_table); ++i) {
5368252b882SNathan Lynch const struct rtas_function *func = &rtas_function_table[i];
5378252b882SNathan Lynch const s32 token = func->token;
5388252b882SNathan Lynch
5398252b882SNathan Lynch if (token == RTAS_UNKNOWN_SERVICE)
5408252b882SNathan Lynch continue;
5418252b882SNathan Lynch
5428252b882SNathan Lynch err = xa_err(xa_store(&rtas_token_to_function_xarray,
5438252b882SNathan Lynch token, (void *)func, GFP_KERNEL));
5448252b882SNathan Lynch if (err)
5458252b882SNathan Lynch break;
5468252b882SNathan Lynch }
5478252b882SNathan Lynch
5488252b882SNathan Lynch return err;
5498252b882SNathan Lynch }
5508252b882SNathan Lynch arch_initcall(rtas_token_to_function_xarray_init);
5518252b882SNathan Lynch
552bc063bf0SNathan Lynch /*
553bc063bf0SNathan Lynch * For use by sys_rtas(), where the token value is provided by user
554bc063bf0SNathan Lynch * space and we don't want to warn on failed lookups.
555bc063bf0SNathan Lynch */
rtas_token_to_function_untrusted(s32 token)556bc063bf0SNathan Lynch static const struct rtas_function *rtas_token_to_function_untrusted(s32 token)
557bc063bf0SNathan Lynch {
558bc063bf0SNathan Lynch return xa_load(&rtas_token_to_function_xarray, token);
559bc063bf0SNathan Lynch }
560bc063bf0SNathan Lynch
561bc063bf0SNathan Lynch /*
562bc063bf0SNathan Lynch * Reverse lookup for deriving the function descriptor from a
563bc063bf0SNathan Lynch * known-good token value in contexts where the former is not already
564bc063bf0SNathan Lynch * available. @token must be valid, e.g. derived from the result of a
565bc063bf0SNathan Lynch * prior lookup against the function table.
566bc063bf0SNathan Lynch */
rtas_token_to_function(s32 token)5678252b882SNathan Lynch static const struct rtas_function *rtas_token_to_function(s32 token)
5688252b882SNathan Lynch {
5698252b882SNathan Lynch const struct rtas_function *func;
5708252b882SNathan Lynch
5718252b882SNathan Lynch if (WARN_ONCE(token < 0, "invalid token %d", token))
5728252b882SNathan Lynch return NULL;
5738252b882SNathan Lynch
574bc063bf0SNathan Lynch func = rtas_token_to_function_untrusted(token);
5758252b882SNathan Lynch
5768252b882SNathan Lynch if (WARN_ONCE(!func, "unexpected failed lookup for token %d", token))
5778252b882SNathan Lynch return NULL;
5788252b882SNathan Lynch
5798252b882SNathan Lynch return func;
5808252b882SNathan Lynch }
5818252b882SNathan Lynch
582cd5cdeb6SMichael Ellerman /* This is here deliberately so it's only used in this file */
583cd5cdeb6SMichael Ellerman void enter_rtas(unsigned long);
584cd5cdeb6SMichael Ellerman
__do_enter_rtas(struct rtas_args * args)58524098f58SNathan Lynch static void __do_enter_rtas(struct rtas_args *args)
58624098f58SNathan Lynch {
58724098f58SNathan Lynch enter_rtas(__pa(args));
58824098f58SNathan Lynch srr_regs_clobbered(); /* rtas uses SRRs, invalidate */
58924098f58SNathan Lynch }
59024098f58SNathan Lynch
__do_enter_rtas_trace(struct rtas_args * args)59124098f58SNathan Lynch static void __do_enter_rtas_trace(struct rtas_args *args)
59224098f58SNathan Lynch {
59324098f58SNathan Lynch const char *name = NULL;
594af8bc682SNathan Lynch
595af8bc682SNathan Lynch if (args == &rtas_args)
596af8bc682SNathan Lynch lockdep_assert_held(&rtas_lock);
59724098f58SNathan Lynch /*
59824098f58SNathan Lynch * If the tracepoints that consume the function name aren't
59924098f58SNathan Lynch * active, avoid the lookup.
60024098f58SNathan Lynch */
60124098f58SNathan Lynch if ((trace_rtas_input_enabled() || trace_rtas_output_enabled())) {
60224098f58SNathan Lynch const s32 token = be32_to_cpu(args->token);
60324098f58SNathan Lynch const struct rtas_function *func = rtas_token_to_function(token);
60424098f58SNathan Lynch
60524098f58SNathan Lynch name = func->name;
60624098f58SNathan Lynch }
60724098f58SNathan Lynch
60824098f58SNathan Lynch trace_rtas_input(args, name);
60924098f58SNathan Lynch trace_rtas_ll_entry(args);
61024098f58SNathan Lynch
61124098f58SNathan Lynch __do_enter_rtas(args);
61224098f58SNathan Lynch
61324098f58SNathan Lynch trace_rtas_ll_exit(args);
61424098f58SNathan Lynch trace_rtas_output(args, name);
61524098f58SNathan Lynch }
61624098f58SNathan Lynch
do_enter_rtas(struct rtas_args * args)61777f85f69SNathan Lynch static void do_enter_rtas(struct rtas_args *args)
61859dc5bfcSNicholas Piggin {
61924098f58SNathan Lynch const unsigned long msr = mfmsr();
62024098f58SNathan Lynch /*
62124098f58SNathan Lynch * Situations where we want to skip any active tracepoints for
62224098f58SNathan Lynch * safety reasons:
62324098f58SNathan Lynch *
62424098f58SNathan Lynch * 1. The last code executed on an offline CPU as it stops,
62524098f58SNathan Lynch * i.e. we're about to call stop-self. The tracepoints'
62624098f58SNathan Lynch * function name lookup uses xarray, which uses RCU, which
62724098f58SNathan Lynch * isn't valid to call on an offline CPU. Any events
62824098f58SNathan Lynch * emitted on an offline CPU will be discarded anyway.
62924098f58SNathan Lynch *
63024098f58SNathan Lynch * 2. In real mode, as when invoking ibm,nmi-interlock from
63124098f58SNathan Lynch * the pseries MCE handler. We cannot count on trace
63224098f58SNathan Lynch * buffers or the entries in rtas_token_to_function_xarray
63324098f58SNathan Lynch * to be contained in the RMO.
63424098f58SNathan Lynch */
63524098f58SNathan Lynch const unsigned long mask = MSR_IR | MSR_DR;
63624098f58SNathan Lynch const bool can_trace = likely(cpu_online(raw_smp_processor_id()) &&
63724098f58SNathan Lynch (msr & mask) == mask);
638b6b1c3ceSLaurent Dufour /*
639b6b1c3ceSLaurent Dufour * Make sure MSR[RI] is currently enabled as it will be forced later
640b6b1c3ceSLaurent Dufour * in enter_rtas.
641b6b1c3ceSLaurent Dufour */
642b6b1c3ceSLaurent Dufour BUG_ON(!(msr & MSR_RI));
643b6b1c3ceSLaurent Dufour
644c5a65e0aSNicholas Piggin BUG_ON(!irqs_disabled());
645c5a65e0aSNicholas Piggin
646c5a65e0aSNicholas Piggin hard_irq_disable(); /* Ensure MSR[EE] is disabled on PPC64 */
647c5a65e0aSNicholas Piggin
64824098f58SNathan Lynch if (can_trace)
64924098f58SNathan Lynch __do_enter_rtas_trace(args);
65024098f58SNathan Lynch else
65124098f58SNathan Lynch __do_enter_rtas(args);
65259dc5bfcSNicholas Piggin }
65359dc5bfcSNicholas Piggin
654599af491SNathan Lynch struct rtas_t rtas;
655599af491SNathan Lynch
656033ef338SPaul Mackerras DEFINE_SPINLOCK(rtas_data_buf_lock);
6579bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_data_buf_lock);
658ab3ab74dSMichael Ellerman
659836b5b9fSNathan Lynch char rtas_data_buf[RTAS_DATA_BUF_SIZE] __aligned(SZ_4K);
6609bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_data_buf);
661ab3ab74dSMichael Ellerman
662033ef338SPaul Mackerras unsigned long rtas_rmo_buf;
663033ef338SPaul Mackerras
664033ef338SPaul Mackerras /*
665f4fcbbe9SPaul Mackerras * If non-NULL, this gets called when the kernel terminates.
666f4fcbbe9SPaul Mackerras * This is done like this so rtas_flash can be a module.
667f4fcbbe9SPaul Mackerras */
668f4fcbbe9SPaul Mackerras void (*rtas_flash_term_hook)(int);
6699bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_flash_term_hook);
670f4fcbbe9SPaul Mackerras
671f4fcbbe9SPaul Mackerras /*
672033ef338SPaul Mackerras * call_rtas_display_status and call_rtas_display_status_delay
673033ef338SPaul Mackerras * are designed only for very early low-level debugging, which
674033ef338SPaul Mackerras * is why the token is hard-coded to 10.
675033ef338SPaul Mackerras */
call_rtas_display_status(unsigned char c)67627128264SAnton Blanchard static void call_rtas_display_status(unsigned char c)
677033ef338SPaul Mackerras {
67812fd6665SNathan Lynch unsigned long flags;
679033ef338SPaul Mackerras
680033ef338SPaul Mackerras if (!rtas.base)
681033ef338SPaul Mackerras return;
6824456f452SMichael Ellerman
68312fd6665SNathan Lynch raw_spin_lock_irqsave(&rtas_lock, flags);
684599af491SNathan Lynch rtas_call_unlocked(&rtas_args, 10, 1, 1, NULL, c);
68512fd6665SNathan Lynch raw_spin_unlock_irqrestore(&rtas_lock, flags);
686033ef338SPaul Mackerras }
687033ef338SPaul Mackerras
call_rtas_display_status_delay(char c)688296167aeSMichael Ellerman static void call_rtas_display_status_delay(char c)
689033ef338SPaul Mackerras {
690033ef338SPaul Mackerras static int pending_newline = 0; /* did last write end with unprinted newline? */
691033ef338SPaul Mackerras static int width = 16;
692033ef338SPaul Mackerras
693033ef338SPaul Mackerras if (c == '\n') {
694033ef338SPaul Mackerras while (width-- > 0)
695033ef338SPaul Mackerras call_rtas_display_status(' ');
696033ef338SPaul Mackerras width = 16;
69721fe3301SBenjamin Herrenschmidt mdelay(500);
698033ef338SPaul Mackerras pending_newline = 1;
699033ef338SPaul Mackerras } else {
700033ef338SPaul Mackerras if (pending_newline) {
701033ef338SPaul Mackerras call_rtas_display_status('\r');
702033ef338SPaul Mackerras call_rtas_display_status('\n');
703033ef338SPaul Mackerras }
704033ef338SPaul Mackerras pending_newline = 0;
705033ef338SPaul Mackerras if (width--) {
706033ef338SPaul Mackerras call_rtas_display_status(c);
707033ef338SPaul Mackerras udelay(10000);
708033ef338SPaul Mackerras }
709033ef338SPaul Mackerras }
710033ef338SPaul Mackerras }
711033ef338SPaul Mackerras
udbg_init_rtas_panel(void)712cc46bb98SMichael Ellerman void __init udbg_init_rtas_panel(void)
713296167aeSMichael Ellerman {
714296167aeSMichael Ellerman udbg_putc = call_rtas_display_status_delay;
715296167aeSMichael Ellerman }
716296167aeSMichael Ellerman
717cc46bb98SMichael Ellerman #ifdef CONFIG_UDBG_RTAS_CONSOLE
718cc46bb98SMichael Ellerman
719cc46bb98SMichael Ellerman /* If you think you're dying before early_init_dt_scan_rtas() does its
720cc46bb98SMichael Ellerman * work, you can hard code the token values for your firmware here and
721cc46bb98SMichael Ellerman * hardcode rtas.base/entry etc.
722cc46bb98SMichael Ellerman */
723cc46bb98SMichael Ellerman static unsigned int rtas_putchar_token = RTAS_UNKNOWN_SERVICE;
724cc46bb98SMichael Ellerman static unsigned int rtas_getchar_token = RTAS_UNKNOWN_SERVICE;
725cc46bb98SMichael Ellerman
udbg_rtascon_putc(char c)726cc46bb98SMichael Ellerman static void udbg_rtascon_putc(char c)
727cc46bb98SMichael Ellerman {
728cc46bb98SMichael Ellerman int tries;
729cc46bb98SMichael Ellerman
730cc46bb98SMichael Ellerman if (!rtas.base)
731cc46bb98SMichael Ellerman return;
732cc46bb98SMichael Ellerman
733cc46bb98SMichael Ellerman /* Add CRs before LFs */
734cc46bb98SMichael Ellerman if (c == '\n')
735cc46bb98SMichael Ellerman udbg_rtascon_putc('\r');
736cc46bb98SMichael Ellerman
737cc46bb98SMichael Ellerman /* if there is more than one character to be displayed, wait a bit */
738cc46bb98SMichael Ellerman for (tries = 0; tries < 16; tries++) {
739cc46bb98SMichael Ellerman if (rtas_call(rtas_putchar_token, 1, 1, NULL, c) == 0)
740cc46bb98SMichael Ellerman break;
741cc46bb98SMichael Ellerman udelay(1000);
742cc46bb98SMichael Ellerman }
743cc46bb98SMichael Ellerman }
744cc46bb98SMichael Ellerman
udbg_rtascon_getc_poll(void)745cc46bb98SMichael Ellerman static int udbg_rtascon_getc_poll(void)
746cc46bb98SMichael Ellerman {
747cc46bb98SMichael Ellerman int c;
748cc46bb98SMichael Ellerman
749cc46bb98SMichael Ellerman if (!rtas.base)
750cc46bb98SMichael Ellerman return -1;
751cc46bb98SMichael Ellerman
752cc46bb98SMichael Ellerman if (rtas_call(rtas_getchar_token, 0, 2, &c))
753cc46bb98SMichael Ellerman return -1;
754cc46bb98SMichael Ellerman
755cc46bb98SMichael Ellerman return c;
756cc46bb98SMichael Ellerman }
757cc46bb98SMichael Ellerman
udbg_rtascon_getc(void)758cc46bb98SMichael Ellerman static int udbg_rtascon_getc(void)
759cc46bb98SMichael Ellerman {
760cc46bb98SMichael Ellerman int c;
761cc46bb98SMichael Ellerman
762cc46bb98SMichael Ellerman while ((c = udbg_rtascon_getc_poll()) == -1)
763cc46bb98SMichael Ellerman ;
764cc46bb98SMichael Ellerman
765cc46bb98SMichael Ellerman return c;
766cc46bb98SMichael Ellerman }
767cc46bb98SMichael Ellerman
768cc46bb98SMichael Ellerman
udbg_init_rtas_console(void)769cc46bb98SMichael Ellerman void __init udbg_init_rtas_console(void)
770cc46bb98SMichael Ellerman {
771cc46bb98SMichael Ellerman udbg_putc = udbg_rtascon_putc;
772cc46bb98SMichael Ellerman udbg_getc = udbg_rtascon_getc;
773cc46bb98SMichael Ellerman udbg_getc_poll = udbg_rtascon_getc_poll;
774cc46bb98SMichael Ellerman }
775cc46bb98SMichael Ellerman #endif /* CONFIG_UDBG_RTAS_CONSOLE */
776cc46bb98SMichael Ellerman
rtas_progress(char * s,unsigned short hex)777033ef338SPaul Mackerras void rtas_progress(char *s, unsigned short hex)
778033ef338SPaul Mackerras {
779033ef338SPaul Mackerras struct device_node *root;
780a7f67bdfSJeremy Kerr int width;
78108bc1dc5SAnton Blanchard const __be32 *p;
782033ef338SPaul Mackerras char *os;
783033ef338SPaul Mackerras static int display_character, set_indicator;
784a7f67bdfSJeremy Kerr static int display_width, display_lines, form_feed;
785c5a69d57STobias Klauser static const int *row_width;
786033ef338SPaul Mackerras static DEFINE_SPINLOCK(progress_lock);
787033ef338SPaul Mackerras static int current_line;
788033ef338SPaul Mackerras static int pending_newline = 0; /* did last write end with unprinted newline? */
789033ef338SPaul Mackerras
790033ef338SPaul Mackerras if (!rtas.base)
791033ef338SPaul Mackerras return;
792033ef338SPaul Mackerras
793033ef338SPaul Mackerras if (display_width == 0) {
794033ef338SPaul Mackerras display_width = 0x10;
7958c8dc322SStephen Rothwell if ((root = of_find_node_by_path("/rtas"))) {
796e2eb6392SStephen Rothwell if ((p = of_get_property(root,
797033ef338SPaul Mackerras "ibm,display-line-length", NULL)))
79808bc1dc5SAnton Blanchard display_width = be32_to_cpu(*p);
799e2eb6392SStephen Rothwell if ((p = of_get_property(root,
800033ef338SPaul Mackerras "ibm,form-feed", NULL)))
80108bc1dc5SAnton Blanchard form_feed = be32_to_cpu(*p);
802e2eb6392SStephen Rothwell if ((p = of_get_property(root,
803033ef338SPaul Mackerras "ibm,display-number-of-lines", NULL)))
80408bc1dc5SAnton Blanchard display_lines = be32_to_cpu(*p);
805e2eb6392SStephen Rothwell row_width = of_get_property(root,
806033ef338SPaul Mackerras "ibm,display-truncation-length", NULL);
8078c8dc322SStephen Rothwell of_node_put(root);
808033ef338SPaul Mackerras }
80908273c9fSNathan Lynch display_character = rtas_function_token(RTAS_FN_DISPLAY_CHARACTER);
81008273c9fSNathan Lynch set_indicator = rtas_function_token(RTAS_FN_SET_INDICATOR);
811033ef338SPaul Mackerras }
812033ef338SPaul Mackerras
813033ef338SPaul Mackerras if (display_character == RTAS_UNKNOWN_SERVICE) {
814033ef338SPaul Mackerras /* use hex display if available */
815033ef338SPaul Mackerras if (set_indicator != RTAS_UNKNOWN_SERVICE)
816033ef338SPaul Mackerras rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex);
817033ef338SPaul Mackerras return;
818033ef338SPaul Mackerras }
819033ef338SPaul Mackerras
820033ef338SPaul Mackerras spin_lock(&progress_lock);
821033ef338SPaul Mackerras
822033ef338SPaul Mackerras /*
823033ef338SPaul Mackerras * Last write ended with newline, but we didn't print it since
824033ef338SPaul Mackerras * it would just clear the bottom line of output. Print it now
825033ef338SPaul Mackerras * instead.
826033ef338SPaul Mackerras *
827033ef338SPaul Mackerras * If no newline is pending and form feed is supported, clear the
828033ef338SPaul Mackerras * display with a form feed; otherwise, print a CR to start output
829033ef338SPaul Mackerras * at the beginning of the line.
830033ef338SPaul Mackerras */
831033ef338SPaul Mackerras if (pending_newline) {
832033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL, '\r');
833033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL, '\n');
834033ef338SPaul Mackerras pending_newline = 0;
835033ef338SPaul Mackerras } else {
836033ef338SPaul Mackerras current_line = 0;
837033ef338SPaul Mackerras if (form_feed)
838033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL,
839033ef338SPaul Mackerras (char)form_feed);
840033ef338SPaul Mackerras else
841033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL, '\r');
842033ef338SPaul Mackerras }
843033ef338SPaul Mackerras
844033ef338SPaul Mackerras if (row_width)
845033ef338SPaul Mackerras width = row_width[current_line];
846033ef338SPaul Mackerras else
847033ef338SPaul Mackerras width = display_width;
848033ef338SPaul Mackerras os = s;
849033ef338SPaul Mackerras while (*os) {
850033ef338SPaul Mackerras if (*os == '\n' || *os == '\r') {
851033ef338SPaul Mackerras /* If newline is the last character, save it
852033ef338SPaul Mackerras * until next call to avoid bumping up the
853033ef338SPaul Mackerras * display output.
854033ef338SPaul Mackerras */
855033ef338SPaul Mackerras if (*os == '\n' && !os[1]) {
856033ef338SPaul Mackerras pending_newline = 1;
857033ef338SPaul Mackerras current_line++;
858033ef338SPaul Mackerras if (current_line > display_lines-1)
859033ef338SPaul Mackerras current_line = display_lines-1;
860033ef338SPaul Mackerras spin_unlock(&progress_lock);
861033ef338SPaul Mackerras return;
862033ef338SPaul Mackerras }
863033ef338SPaul Mackerras
864033ef338SPaul Mackerras /* RTAS wants CR-LF, not just LF */
865033ef338SPaul Mackerras
866033ef338SPaul Mackerras if (*os == '\n') {
867033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL, '\r');
868033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL, '\n');
869033ef338SPaul Mackerras } else {
870033ef338SPaul Mackerras /* CR might be used to re-draw a line, so we'll
871033ef338SPaul Mackerras * leave it alone and not add LF.
872033ef338SPaul Mackerras */
873033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL, *os);
874033ef338SPaul Mackerras }
875033ef338SPaul Mackerras
876033ef338SPaul Mackerras if (row_width)
877033ef338SPaul Mackerras width = row_width[current_line];
878033ef338SPaul Mackerras else
879033ef338SPaul Mackerras width = display_width;
880033ef338SPaul Mackerras } else {
881033ef338SPaul Mackerras width--;
882033ef338SPaul Mackerras rtas_call(display_character, 1, 1, NULL, *os);
883033ef338SPaul Mackerras }
884033ef338SPaul Mackerras
885033ef338SPaul Mackerras os++;
886033ef338SPaul Mackerras
887033ef338SPaul Mackerras /* if we overwrite the screen length */
888033ef338SPaul Mackerras if (width <= 0)
889033ef338SPaul Mackerras while ((*os != 0) && (*os != '\n') && (*os != '\r'))
890033ef338SPaul Mackerras os++;
891033ef338SPaul Mackerras }
892033ef338SPaul Mackerras
893033ef338SPaul Mackerras spin_unlock(&progress_lock);
894033ef338SPaul Mackerras }
8959bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_progress); /* needed by rtas_flash module */
896033ef338SPaul Mackerras
rtas_token(const char * service)897033ef338SPaul Mackerras int rtas_token(const char *service)
898033ef338SPaul Mackerras {
8998252b882SNathan Lynch const struct rtas_function *func;
90008bc1dc5SAnton Blanchard const __be32 *tokp;
9018252b882SNathan Lynch
902033ef338SPaul Mackerras if (rtas.dev == NULL)
903033ef338SPaul Mackerras return RTAS_UNKNOWN_SERVICE;
9048252b882SNathan Lynch
9058252b882SNathan Lynch func = rtas_name_to_function(service);
9068252b882SNathan Lynch if (func)
9078252b882SNathan Lynch return func->token;
9088252b882SNathan Lynch /*
9098252b882SNathan Lynch * The caller is looking up a name that is not known to be an
9108252b882SNathan Lynch * RTAS function. Either it's a function that needs to be
9118252b882SNathan Lynch * added to the table, or they're misusing rtas_token() to
9128252b882SNathan Lynch * access non-function properties of the /rtas node. Warn and
9138252b882SNathan Lynch * fall back to the legacy behavior.
9148252b882SNathan Lynch */
9158252b882SNathan Lynch WARN_ONCE(1, "unknown function `%s`, should it be added to rtas_function_table?\n",
9168252b882SNathan Lynch service);
9178252b882SNathan Lynch
918e2eb6392SStephen Rothwell tokp = of_get_property(rtas.dev, service, NULL);
91908bc1dc5SAnton Blanchard return tokp ? be32_to_cpu(*tokp) : RTAS_UNKNOWN_SERVICE;
920033ef338SPaul Mackerras }
9219bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_token);
922033ef338SPaul Mackerras
rtas_service_present(const char * service)923f2d6d2d8SNathan Lynch int rtas_service_present(const char *service)
924f2d6d2d8SNathan Lynch {
925f2d6d2d8SNathan Lynch return rtas_token(service) != RTAS_UNKNOWN_SERVICE;
926f2d6d2d8SNathan Lynch }
927f2d6d2d8SNathan Lynch
928033ef338SPaul Mackerras #ifdef CONFIG_RTAS_ERROR_LOGGING
929c67a0e41SNathan Lynch
930c67a0e41SNathan Lynch static u32 rtas_error_log_max __ro_after_init = RTAS_ERROR_LOG_MAX;
931c67a0e41SNathan Lynch
932033ef338SPaul Mackerras /*
933033ef338SPaul Mackerras * Return the firmware-specified size of the error log buffer
934033ef338SPaul Mackerras * for all rtas calls that require an error buffer argument.
935033ef338SPaul Mackerras * This includes 'check-exception' and 'rtas-last-error'.
936033ef338SPaul Mackerras */
rtas_get_error_log_max(void)937033ef338SPaul Mackerras int rtas_get_error_log_max(void)
938033ef338SPaul Mackerras {
939033ef338SPaul Mackerras return rtas_error_log_max;
940033ef338SPaul Mackerras }
941033ef338SPaul Mackerras
init_error_log_max(void)942c67a0e41SNathan Lynch static void __init init_error_log_max(void)
943c67a0e41SNathan Lynch {
944c67a0e41SNathan Lynch static const char propname[] __initconst = "rtas-error-log-max";
945c67a0e41SNathan Lynch u32 max;
946c67a0e41SNathan Lynch
947c67a0e41SNathan Lynch if (of_property_read_u32(rtas.dev, propname, &max)) {
948c67a0e41SNathan Lynch pr_warn("%s not found, using default of %u\n",
949c67a0e41SNathan Lynch propname, RTAS_ERROR_LOG_MAX);
950c67a0e41SNathan Lynch max = RTAS_ERROR_LOG_MAX;
951c67a0e41SNathan Lynch }
952c67a0e41SNathan Lynch
953c67a0e41SNathan Lynch if (max > RTAS_ERROR_LOG_MAX) {
954c67a0e41SNathan Lynch pr_warn("%s = %u, clamping max error log size to %u\n",
955c67a0e41SNathan Lynch propname, max, RTAS_ERROR_LOG_MAX);
956c67a0e41SNathan Lynch max = RTAS_ERROR_LOG_MAX;
957c67a0e41SNathan Lynch }
958c67a0e41SNathan Lynch
959c67a0e41SNathan Lynch rtas_error_log_max = max;
960c67a0e41SNathan Lynch }
961c67a0e41SNathan Lynch
962033ef338SPaul Mackerras
9631c21a293SMichael Ellerman static char rtas_err_buf[RTAS_ERROR_LOG_MAX];
964033ef338SPaul Mackerras
965033ef338SPaul Mackerras /** Return a copy of the detailed error text associated with the
966033ef338SPaul Mackerras * most recent failed call to rtas. Because the error text
967033ef338SPaul Mackerras * might go stale if there are any other intervening rtas calls,
968033ef338SPaul Mackerras * this routine must be called atomically with whatever produced
969599af491SNathan Lynch * the error (i.e. with rtas_lock still held from the previous call).
970033ef338SPaul Mackerras */
__fetch_rtas_last_error(char * altbuf)971033ef338SPaul Mackerras static char *__fetch_rtas_last_error(char *altbuf)
972033ef338SPaul Mackerras {
97308273c9fSNathan Lynch const s32 token = rtas_function_token(RTAS_FN_RTAS_LAST_ERROR);
974033ef338SPaul Mackerras struct rtas_args err_args, save_args;
975033ef338SPaul Mackerras u32 bufsz;
976033ef338SPaul Mackerras char *buf = NULL;
977033ef338SPaul Mackerras
978af8bc682SNathan Lynch lockdep_assert_held(&rtas_lock);
979af8bc682SNathan Lynch
98008273c9fSNathan Lynch if (token == -1)
981033ef338SPaul Mackerras return NULL;
982033ef338SPaul Mackerras
983033ef338SPaul Mackerras bufsz = rtas_get_error_log_max();
984033ef338SPaul Mackerras
98508273c9fSNathan Lynch err_args.token = cpu_to_be32(token);
98627128264SAnton Blanchard err_args.nargs = cpu_to_be32(2);
98727128264SAnton Blanchard err_args.nret = cpu_to_be32(1);
98827128264SAnton Blanchard err_args.args[0] = cpu_to_be32(__pa(rtas_err_buf));
98927128264SAnton Blanchard err_args.args[1] = cpu_to_be32(bufsz);
990033ef338SPaul Mackerras err_args.args[2] = 0;
991033ef338SPaul Mackerras
992599af491SNathan Lynch save_args = rtas_args;
993599af491SNathan Lynch rtas_args = err_args;
994033ef338SPaul Mackerras
99577f85f69SNathan Lynch do_enter_rtas(&rtas_args);
996033ef338SPaul Mackerras
997599af491SNathan Lynch err_args = rtas_args;
998599af491SNathan Lynch rtas_args = save_args;
999033ef338SPaul Mackerras
1000033ef338SPaul Mackerras /* Log the error in the unlikely case that there was one. */
1001033ef338SPaul Mackerras if (unlikely(err_args.args[2] == 0)) {
1002033ef338SPaul Mackerras if (altbuf) {
1003033ef338SPaul Mackerras buf = altbuf;
1004033ef338SPaul Mackerras } else {
1005033ef338SPaul Mackerras buf = rtas_err_buf;
1006f691fa10SMichael Ellerman if (slab_is_available())
1007033ef338SPaul Mackerras buf = kmalloc(RTAS_ERROR_LOG_MAX, GFP_ATOMIC);
1008033ef338SPaul Mackerras }
1009033ef338SPaul Mackerras if (buf)
1010271208eeSNathan Lynch memmove(buf, rtas_err_buf, RTAS_ERROR_LOG_MAX);
1011033ef338SPaul Mackerras }
1012033ef338SPaul Mackerras
1013033ef338SPaul Mackerras return buf;
1014033ef338SPaul Mackerras }
1015033ef338SPaul Mackerras
1016033ef338SPaul Mackerras #define get_errorlog_buffer() kmalloc(RTAS_ERROR_LOG_MAX, GFP_KERNEL)
1017033ef338SPaul Mackerras
1018033ef338SPaul Mackerras #else /* CONFIG_RTAS_ERROR_LOGGING */
1019033ef338SPaul Mackerras #define __fetch_rtas_last_error(x) NULL
1020033ef338SPaul Mackerras #define get_errorlog_buffer() NULL
init_error_log_max(void)1021c67a0e41SNathan Lynch static void __init init_error_log_max(void) {}
1022033ef338SPaul Mackerras #endif
1023033ef338SPaul Mackerras
1024209eb4e5SMichael Ellerman
1025209eb4e5SMichael Ellerman static void
va_rtas_call_unlocked(struct rtas_args * args,int token,int nargs,int nret,va_list list)1026209eb4e5SMichael Ellerman va_rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret,
1027209eb4e5SMichael Ellerman va_list list)
1028209eb4e5SMichael Ellerman {
1029209eb4e5SMichael Ellerman int i;
1030209eb4e5SMichael Ellerman
1031209eb4e5SMichael Ellerman args->token = cpu_to_be32(token);
1032209eb4e5SMichael Ellerman args->nargs = cpu_to_be32(nargs);
1033209eb4e5SMichael Ellerman args->nret = cpu_to_be32(nret);
1034209eb4e5SMichael Ellerman args->rets = &(args->args[nargs]);
1035209eb4e5SMichael Ellerman
1036209eb4e5SMichael Ellerman for (i = 0; i < nargs; ++i)
1037209eb4e5SMichael Ellerman args->args[i] = cpu_to_be32(va_arg(list, __u32));
1038209eb4e5SMichael Ellerman
1039209eb4e5SMichael Ellerman for (i = 0; i < nret; ++i)
1040209eb4e5SMichael Ellerman args->rets[i] = 0;
1041209eb4e5SMichael Ellerman
104277f85f69SNathan Lynch do_enter_rtas(args);
1043209eb4e5SMichael Ellerman }
1044209eb4e5SMichael Ellerman
10451792e46eSNathan Lynch /**
10461792e46eSNathan Lynch * rtas_call_unlocked() - Invoke an RTAS firmware function without synchronization.
10471792e46eSNathan Lynch * @args: RTAS parameter block to be used for the call, must obey RTAS addressing
10481792e46eSNathan Lynch * constraints.
10491792e46eSNathan Lynch * @token: Identifies the function being invoked.
10501792e46eSNathan Lynch * @nargs: Number of input parameters. Does not include token.
10511792e46eSNathan Lynch * @nret: Number of output parameters, including the call status.
10521792e46eSNathan Lynch * @....: List of @nargs input parameters.
10531792e46eSNathan Lynch *
10541792e46eSNathan Lynch * Invokes the RTAS function indicated by @token, which the caller
10551792e46eSNathan Lynch * should obtain via rtas_function_token().
10561792e46eSNathan Lynch *
10571792e46eSNathan Lynch * This function is similar to rtas_call(), but must be used with a
10581792e46eSNathan Lynch * limited set of RTAS calls specifically exempted from the general
10591792e46eSNathan Lynch * requirement that only one RTAS call may be in progress at any
10601792e46eSNathan Lynch * time. Examples include stop-self and ibm,nmi-interlock.
10611792e46eSNathan Lynch */
rtas_call_unlocked(struct rtas_args * args,int token,int nargs,int nret,...)1062209eb4e5SMichael Ellerman void rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, ...)
1063209eb4e5SMichael Ellerman {
1064209eb4e5SMichael Ellerman va_list list;
1065209eb4e5SMichael Ellerman
1066209eb4e5SMichael Ellerman va_start(list, nret);
1067209eb4e5SMichael Ellerman va_rtas_call_unlocked(args, token, nargs, nret, list);
1068209eb4e5SMichael Ellerman va_end(list);
1069209eb4e5SMichael Ellerman }
1070209eb4e5SMichael Ellerman
token_is_restricted_errinjct(s32 token)107108273c9fSNathan Lynch static bool token_is_restricted_errinjct(s32 token)
107208273c9fSNathan Lynch {
107308273c9fSNathan Lynch return token == rtas_function_token(RTAS_FN_IBM_OPEN_ERRINJCT) ||
107408273c9fSNathan Lynch token == rtas_function_token(RTAS_FN_IBM_ERRINJCT);
107508273c9fSNathan Lynch }
1076b8f3e488SNathan Lynch
1077336e2554SNathan Lynch /**
1078336e2554SNathan Lynch * rtas_call() - Invoke an RTAS firmware function.
1079336e2554SNathan Lynch * @token: Identifies the function being invoked.
1080336e2554SNathan Lynch * @nargs: Number of input parameters. Does not include token.
1081336e2554SNathan Lynch * @nret: Number of output parameters, including the call status.
1082336e2554SNathan Lynch * @outputs: Array of @nret output words.
1083336e2554SNathan Lynch * @....: List of @nargs input parameters.
1084336e2554SNathan Lynch *
1085336e2554SNathan Lynch * Invokes the RTAS function indicated by @token, which the caller
1086716bfc97SNathan Lynch * should obtain via rtas_function_token().
1087336e2554SNathan Lynch *
1088336e2554SNathan Lynch * The @nargs and @nret arguments must match the number of input and
1089336e2554SNathan Lynch * output parameters specified for the RTAS function.
1090336e2554SNathan Lynch *
1091336e2554SNathan Lynch * rtas_call() returns RTAS status codes, not conventional Linux errno
1092336e2554SNathan Lynch * values. Callers must translate any failure to an appropriate errno
1093336e2554SNathan Lynch * in syscall context. Most callers of RTAS functions that can return
1094336e2554SNathan Lynch * -2 or 990x should use rtas_busy_delay() to correctly handle those
1095336e2554SNathan Lynch * statuses before calling again.
1096336e2554SNathan Lynch *
1097336e2554SNathan Lynch * The return value descriptions are adapted from 7.2.8 [RTAS] Return
1098336e2554SNathan Lynch * Codes of the PAPR and CHRP specifications.
1099336e2554SNathan Lynch *
1100336e2554SNathan Lynch * Context: Process context preferably, interrupt context if
1101336e2554SNathan Lynch * necessary. Acquires an internal spinlock and may perform
1102336e2554SNathan Lynch * GFP_ATOMIC slab allocation in error path. Unsafe for NMI
1103336e2554SNathan Lynch * context.
1104336e2554SNathan Lynch * Return:
1105336e2554SNathan Lynch * * 0 - RTAS function call succeeded.
1106336e2554SNathan Lynch * * -1 - RTAS function encountered a hardware or
1107336e2554SNathan Lynch * platform error, or the token is invalid,
1108336e2554SNathan Lynch * or the function is restricted by kernel policy.
1109336e2554SNathan Lynch * * -2 - Specs say "A necessary hardware device was busy,
1110336e2554SNathan Lynch * and the requested function could not be
1111336e2554SNathan Lynch * performed. The operation should be retried at
1112336e2554SNathan Lynch * a later time." This is misleading, at least with
1113336e2554SNathan Lynch * respect to current RTAS implementations. What it
1114336e2554SNathan Lynch * usually means in practice is that the function
1115336e2554SNathan Lynch * could not be completed while meeting RTAS's
1116336e2554SNathan Lynch * deadline for returning control to the OS (250us
1117336e2554SNathan Lynch * for PAPR/PowerVM, typically), but the call may be
1118336e2554SNathan Lynch * immediately reattempted to resume work on it.
1119336e2554SNathan Lynch * * -3 - Parameter error.
1120336e2554SNathan Lynch * * -7 - Unexpected state change.
1121336e2554SNathan Lynch * * 9000...9899 - Vendor-specific success codes.
1122336e2554SNathan Lynch * * 9900...9905 - Advisory extended delay. Caller should try
1123336e2554SNathan Lynch * again after ~10^x ms has elapsed, where x is
1124336e2554SNathan Lynch * the last digit of the status [0-5]. Again going
1125336e2554SNathan Lynch * beyond the PAPR text, 990x on PowerVM indicates
1126336e2554SNathan Lynch * contention for RTAS-internal resources. Other
1127336e2554SNathan Lynch * RTAS call sequences in progress should be
1128336e2554SNathan Lynch * allowed to complete before reattempting the
1129336e2554SNathan Lynch * call.
1130336e2554SNathan Lynch * * -9000 - Multi-level isolation error.
1131336e2554SNathan Lynch * * -9999...-9004 - Vendor-specific error codes.
1132336e2554SNathan Lynch * * Additional negative values - Function-specific error.
1133336e2554SNathan Lynch * * Additional positive values - Function-specific success.
1134336e2554SNathan Lynch */
rtas_call(int token,int nargs,int nret,int * outputs,...)1135033ef338SPaul Mackerras int rtas_call(int token, int nargs, int nret, int *outputs, ...)
1136033ef338SPaul Mackerras {
1137af8bc682SNathan Lynch struct pin_cookie cookie;
1138033ef338SPaul Mackerras va_list list;
1139033ef338SPaul Mackerras int i;
114012fd6665SNathan Lynch unsigned long flags;
1141599af491SNathan Lynch struct rtas_args *args;
1142033ef338SPaul Mackerras char *buff_copy = NULL;
1143033ef338SPaul Mackerras int ret;
1144033ef338SPaul Mackerras
114524da3dd5SMichael Ellerman if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE)
1146033ef338SPaul Mackerras return -1;
1147033ef338SPaul Mackerras
114808273c9fSNathan Lynch if (token_is_restricted_errinjct(token)) {
1149b8f3e488SNathan Lynch /*
1150b8f3e488SNathan Lynch * It would be nicer to not discard the error value
1151b8f3e488SNathan Lynch * from security_locked_down(), but callers expect an
1152b8f3e488SNathan Lynch * RTAS status, not an errno.
1153b8f3e488SNathan Lynch */
1154b8f3e488SNathan Lynch if (security_locked_down(LOCKDOWN_RTAS_ERROR_INJECTION))
1155b8f3e488SNathan Lynch return -1;
1156b8f3e488SNathan Lynch }
1157b8f3e488SNathan Lynch
1158804c0a16SNicholas Piggin if ((mfmsr() & (MSR_IR|MSR_DR)) != (MSR_IR|MSR_DR)) {
1159804c0a16SNicholas Piggin WARN_ON_ONCE(1);
1160804c0a16SNicholas Piggin return -1;
1161804c0a16SNicholas Piggin }
1162804c0a16SNicholas Piggin
116312fd6665SNathan Lynch raw_spin_lock_irqsave(&rtas_lock, flags);
1164af8bc682SNathan Lynch cookie = lockdep_pin_lock(&rtas_lock);
1165af8bc682SNathan Lynch
1166209eb4e5SMichael Ellerman /* We use the global rtas args buffer */
1167599af491SNathan Lynch args = &rtas_args;
1168033ef338SPaul Mackerras
1169033ef338SPaul Mackerras va_start(list, outputs);
1170599af491SNathan Lynch va_rtas_call_unlocked(args, token, nargs, nret, list);
1171033ef338SPaul Mackerras va_end(list);
1172033ef338SPaul Mackerras
1173033ef338SPaul Mackerras /* A -1 return code indicates that the last command couldn't
1174033ef338SPaul Mackerras be completed due to a hardware error. */
1175599af491SNathan Lynch if (be32_to_cpu(args->rets[0]) == -1)
1176033ef338SPaul Mackerras buff_copy = __fetch_rtas_last_error(NULL);
1177033ef338SPaul Mackerras
1178033ef338SPaul Mackerras if (nret > 1 && outputs != NULL)
1179033ef338SPaul Mackerras for (i = 0; i < nret-1; ++i)
1180599af491SNathan Lynch outputs[i] = be32_to_cpu(args->rets[i + 1]);
1181599af491SNathan Lynch ret = (nret > 0) ? be32_to_cpu(args->rets[0]) : 0;
1182033ef338SPaul Mackerras
1183af8bc682SNathan Lynch lockdep_unpin_lock(&rtas_lock, cookie);
118412fd6665SNathan Lynch raw_spin_unlock_irqrestore(&rtas_lock, flags);
1185033ef338SPaul Mackerras
1186033ef338SPaul Mackerras if (buff_copy) {
1187033ef338SPaul Mackerras log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0);
1188f691fa10SMichael Ellerman if (slab_is_available())
1189033ef338SPaul Mackerras kfree(buff_copy);
1190033ef338SPaul Mackerras }
1191033ef338SPaul Mackerras return ret;
1192033ef338SPaul Mackerras }
11939bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_call);
1194033ef338SPaul Mackerras
1195dd5cde45SNathan Lynch /**
1196dd5cde45SNathan Lynch * rtas_busy_delay_time() - From an RTAS status value, calculate the
1197dd5cde45SNathan Lynch * suggested delay time in milliseconds.
1198dd5cde45SNathan Lynch *
1199dd5cde45SNathan Lynch * @status: a value returned from rtas_call() or similar APIs which return
1200dd5cde45SNathan Lynch * the status of a RTAS function call.
1201dd5cde45SNathan Lynch *
1202dd5cde45SNathan Lynch * Context: Any context.
1203dd5cde45SNathan Lynch *
1204dd5cde45SNathan Lynch * Return:
1205dd5cde45SNathan Lynch * * 100000 - If @status is 9905.
1206dd5cde45SNathan Lynch * * 10000 - If @status is 9904.
1207dd5cde45SNathan Lynch * * 1000 - If @status is 9903.
1208dd5cde45SNathan Lynch * * 100 - If @status is 9902.
1209dd5cde45SNathan Lynch * * 10 - If @status is 9901.
1210dd5cde45SNathan Lynch * * 1 - If @status is either 9900 or -2. This is "wrong" for -2, but
1211dd5cde45SNathan Lynch * some callers depend on this behavior, and the worst outcome
1212dd5cde45SNathan Lynch * is that they will delay for longer than necessary.
1213dd5cde45SNathan Lynch * * 0 - If @status is not a busy or extended delay value.
1214033ef338SPaul Mackerras */
rtas_busy_delay_time(int status)1215507279dbSJohn Rose unsigned int rtas_busy_delay_time(int status)
1216033ef338SPaul Mackerras {
1217507279dbSJohn Rose int order;
1218507279dbSJohn Rose unsigned int ms = 0;
1219033ef338SPaul Mackerras
1220507279dbSJohn Rose if (status == RTAS_BUSY) {
1221507279dbSJohn Rose ms = 1;
12229ef03193SThomas Huth } else if (status >= RTAS_EXTENDED_DELAY_MIN &&
12239ef03193SThomas Huth status <= RTAS_EXTENDED_DELAY_MAX) {
12249ef03193SThomas Huth order = status - RTAS_EXTENDED_DELAY_MIN;
1225033ef338SPaul Mackerras for (ms = 1; order > 0; order--)
1226033ef338SPaul Mackerras ms *= 10;
1227507279dbSJohn Rose }
1228507279dbSJohn Rose
1229507279dbSJohn Rose return ms;
1230507279dbSJohn Rose }
1231507279dbSJohn Rose
123209d1ea72SNathan Lynch /*
123309d1ea72SNathan Lynch * Early boot fallback for rtas_busy_delay().
123409d1ea72SNathan Lynch */
rtas_busy_delay_early(int status)123509d1ea72SNathan Lynch static bool __init rtas_busy_delay_early(int status)
123609d1ea72SNathan Lynch {
123709d1ea72SNathan Lynch static size_t successive_ext_delays __initdata;
123809d1ea72SNathan Lynch bool retry;
123909d1ea72SNathan Lynch
124009d1ea72SNathan Lynch switch (status) {
124109d1ea72SNathan Lynch case RTAS_EXTENDED_DELAY_MIN...RTAS_EXTENDED_DELAY_MAX:
124209d1ea72SNathan Lynch /*
124309d1ea72SNathan Lynch * In the unlikely case that we receive an extended
124409d1ea72SNathan Lynch * delay status in early boot, the OS is probably not
124509d1ea72SNathan Lynch * the cause, and there's nothing we can do to clear
124609d1ea72SNathan Lynch * the condition. Best we can do is delay for a bit
124709d1ea72SNathan Lynch * and hope it's transient. Lie to the caller if it
124809d1ea72SNathan Lynch * seems like we're stuck in a retry loop.
124909d1ea72SNathan Lynch */
125009d1ea72SNathan Lynch mdelay(1);
125109d1ea72SNathan Lynch retry = true;
125209d1ea72SNathan Lynch successive_ext_delays += 1;
125309d1ea72SNathan Lynch if (successive_ext_delays > 1000) {
125409d1ea72SNathan Lynch pr_err("too many extended delays, giving up\n");
125509d1ea72SNathan Lynch dump_stack();
125609d1ea72SNathan Lynch retry = false;
125709d1ea72SNathan Lynch successive_ext_delays = 0;
125809d1ea72SNathan Lynch }
125909d1ea72SNathan Lynch break;
126009d1ea72SNathan Lynch case RTAS_BUSY:
126109d1ea72SNathan Lynch retry = true;
126209d1ea72SNathan Lynch successive_ext_delays = 0;
126309d1ea72SNathan Lynch break;
126409d1ea72SNathan Lynch default:
126509d1ea72SNathan Lynch retry = false;
126609d1ea72SNathan Lynch successive_ext_delays = 0;
126709d1ea72SNathan Lynch break;
126809d1ea72SNathan Lynch }
126909d1ea72SNathan Lynch
127009d1ea72SNathan Lynch return retry;
127109d1ea72SNathan Lynch }
127209d1ea72SNathan Lynch
127338f7b706SNathan Lynch /**
127438f7b706SNathan Lynch * rtas_busy_delay() - helper for RTAS busy and extended delay statuses
127538f7b706SNathan Lynch *
127638f7b706SNathan Lynch * @status: a value returned from rtas_call() or similar APIs which return
127738f7b706SNathan Lynch * the status of a RTAS function call.
127838f7b706SNathan Lynch *
127938f7b706SNathan Lynch * Context: Process context. May sleep or schedule.
128038f7b706SNathan Lynch *
128138f7b706SNathan Lynch * Return:
128238f7b706SNathan Lynch * * true - @status is RTAS_BUSY or an extended delay hint. The
128338f7b706SNathan Lynch * caller may assume that the CPU has been yielded if necessary,
128438f7b706SNathan Lynch * and that an appropriate delay for @status has elapsed.
128538f7b706SNathan Lynch * Generally the caller should reattempt the RTAS call which
128638f7b706SNathan Lynch * yielded @status.
128738f7b706SNathan Lynch *
128838f7b706SNathan Lynch * * false - @status is not @RTAS_BUSY nor an extended delay hint. The
128938f7b706SNathan Lynch * caller is responsible for handling @status.
129038f7b706SNathan Lynch */
rtas_busy_delay(int status)129109d1ea72SNathan Lynch bool __ref rtas_busy_delay(int status)
1292507279dbSJohn Rose {
1293507279dbSJohn Rose unsigned int ms;
129438f7b706SNathan Lynch bool ret;
1295507279dbSJohn Rose
129609d1ea72SNathan Lynch /*
129709d1ea72SNathan Lynch * Can't do timed sleeps before timekeeping is up.
129809d1ea72SNathan Lynch */
129909d1ea72SNathan Lynch if (system_state < SYSTEM_SCHEDULING)
130009d1ea72SNathan Lynch return rtas_busy_delay_early(status);
130109d1ea72SNathan Lynch
130238f7b706SNathan Lynch switch (status) {
130338f7b706SNathan Lynch case RTAS_EXTENDED_DELAY_MIN...RTAS_EXTENDED_DELAY_MAX:
130438f7b706SNathan Lynch ret = true;
1305507279dbSJohn Rose ms = rtas_busy_delay_time(status);
130638f7b706SNathan Lynch /*
130738f7b706SNathan Lynch * The extended delay hint can be as high as 100 seconds.
130838f7b706SNathan Lynch * Surely any function returning such a status is either
130938f7b706SNathan Lynch * buggy or isn't going to be significantly slowed by us
131038f7b706SNathan Lynch * polling at 1HZ. Clamp the sleep time to one second.
131138f7b706SNathan Lynch */
131238f7b706SNathan Lynch ms = clamp(ms, 1U, 1000U);
131338f7b706SNathan Lynch /*
131438f7b706SNathan Lynch * The delay hint is an order-of-magnitude suggestion, not
131538f7b706SNathan Lynch * a minimum. It is fine, possibly even advantageous, for
131638f7b706SNathan Lynch * us to pause for less time than hinted. For small values,
131738f7b706SNathan Lynch * use usleep_range() to ensure we don't sleep much longer
131838f7b706SNathan Lynch * than actually needed.
131938f7b706SNathan Lynch *
132038f7b706SNathan Lynch * See Documentation/timers/timers-howto.rst for
132138f7b706SNathan Lynch * explanation of the threshold used here. In effect we use
132238f7b706SNathan Lynch * usleep_range() for 9900 and 9901, msleep() for
132338f7b706SNathan Lynch * 9902-9905.
132438f7b706SNathan Lynch */
132538f7b706SNathan Lynch if (ms <= 20)
132638f7b706SNathan Lynch usleep_range(ms * 100, ms * 1000);
132738f7b706SNathan Lynch else
1328507279dbSJohn Rose msleep(ms);
132938f7b706SNathan Lynch break;
133038f7b706SNathan Lynch case RTAS_BUSY:
133138f7b706SNathan Lynch ret = true;
133238f7b706SNathan Lynch /*
133338f7b706SNathan Lynch * We should call again immediately if there's no other
133438f7b706SNathan Lynch * work to do.
133538f7b706SNathan Lynch */
133638f7b706SNathan Lynch cond_resched();
133738f7b706SNathan Lynch break;
133838f7b706SNathan Lynch default:
133938f7b706SNathan Lynch ret = false;
134038f7b706SNathan Lynch /*
134138f7b706SNathan Lynch * Not a busy or extended delay status; the caller should
134238f7b706SNathan Lynch * handle @status itself. Ensure we warn on misuses in
134338f7b706SNathan Lynch * atomic context regardless.
134438f7b706SNathan Lynch */
134538f7b706SNathan Lynch might_sleep();
134638f7b706SNathan Lynch break;
134738f7b706SNathan Lynch }
1348033ef338SPaul Mackerras
134938f7b706SNathan Lynch return ret;
1350033ef338SPaul Mackerras }
13519bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_busy_delay);
1352033ef338SPaul Mackerras
rtas_error_rc(int rtas_rc)1353e160bf64SMahesh Salgaonkar int rtas_error_rc(int rtas_rc)
1354033ef338SPaul Mackerras {
1355033ef338SPaul Mackerras int rc;
1356033ef338SPaul Mackerras
1357033ef338SPaul Mackerras switch (rtas_rc) {
1358e160bf64SMahesh Salgaonkar case RTAS_HARDWARE_ERROR: /* Hardware Error */
1359033ef338SPaul Mackerras rc = -EIO;
1360033ef338SPaul Mackerras break;
1361e160bf64SMahesh Salgaonkar case RTAS_INVALID_PARAMETER: /* Bad indicator/domain/etc */
1362033ef338SPaul Mackerras rc = -EINVAL;
1363033ef338SPaul Mackerras break;
1364033ef338SPaul Mackerras case -9000: /* Isolation error */
1365033ef338SPaul Mackerras rc = -EFAULT;
1366033ef338SPaul Mackerras break;
1367033ef338SPaul Mackerras case -9001: /* Outstanding TCE/PTE */
1368033ef338SPaul Mackerras rc = -EEXIST;
1369033ef338SPaul Mackerras break;
1370033ef338SPaul Mackerras case -9002: /* No usable slot */
1371033ef338SPaul Mackerras rc = -ENODEV;
1372033ef338SPaul Mackerras break;
1373033ef338SPaul Mackerras default:
1374f975b655SNathan Lynch pr_err("%s: unexpected error %d\n", __func__, rtas_rc);
1375033ef338SPaul Mackerras rc = -ERANGE;
1376033ef338SPaul Mackerras break;
1377033ef338SPaul Mackerras }
1378033ef338SPaul Mackerras return rc;
1379033ef338SPaul Mackerras }
1380e160bf64SMahesh Salgaonkar EXPORT_SYMBOL_GPL(rtas_error_rc);
1381033ef338SPaul Mackerras
rtas_get_power_level(int powerdomain,int * level)1382033ef338SPaul Mackerras int rtas_get_power_level(int powerdomain, int *level)
1383033ef338SPaul Mackerras {
138408273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_GET_POWER_LEVEL);
1385033ef338SPaul Mackerras int rc;
1386033ef338SPaul Mackerras
1387033ef338SPaul Mackerras if (token == RTAS_UNKNOWN_SERVICE)
1388033ef338SPaul Mackerras return -ENOENT;
1389033ef338SPaul Mackerras
1390033ef338SPaul Mackerras while ((rc = rtas_call(token, 1, 2, level, powerdomain)) == RTAS_BUSY)
1391033ef338SPaul Mackerras udelay(1);
1392033ef338SPaul Mackerras
1393033ef338SPaul Mackerras if (rc < 0)
1394033ef338SPaul Mackerras return rtas_error_rc(rc);
1395033ef338SPaul Mackerras return rc;
1396033ef338SPaul Mackerras }
13979bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_get_power_level);
1398033ef338SPaul Mackerras
rtas_set_power_level(int powerdomain,int level,int * setlevel)1399033ef338SPaul Mackerras int rtas_set_power_level(int powerdomain, int level, int *setlevel)
1400033ef338SPaul Mackerras {
140108273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_SET_POWER_LEVEL);
1402033ef338SPaul Mackerras int rc;
1403033ef338SPaul Mackerras
1404033ef338SPaul Mackerras if (token == RTAS_UNKNOWN_SERVICE)
1405033ef338SPaul Mackerras return -ENOENT;
1406033ef338SPaul Mackerras
1407507279dbSJohn Rose do {
1408033ef338SPaul Mackerras rc = rtas_call(token, 2, 2, setlevel, powerdomain, level);
1409507279dbSJohn Rose } while (rtas_busy_delay(rc));
1410033ef338SPaul Mackerras
1411033ef338SPaul Mackerras if (rc < 0)
1412033ef338SPaul Mackerras return rtas_error_rc(rc);
1413033ef338SPaul Mackerras return rc;
1414033ef338SPaul Mackerras }
14159bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_set_power_level);
1416033ef338SPaul Mackerras
rtas_get_sensor(int sensor,int index,int * state)1417033ef338SPaul Mackerras int rtas_get_sensor(int sensor, int index, int *state)
1418033ef338SPaul Mackerras {
141908273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_GET_SENSOR_STATE);
1420033ef338SPaul Mackerras int rc;
1421033ef338SPaul Mackerras
1422033ef338SPaul Mackerras if (token == RTAS_UNKNOWN_SERVICE)
1423033ef338SPaul Mackerras return -ENOENT;
1424033ef338SPaul Mackerras
1425507279dbSJohn Rose do {
1426033ef338SPaul Mackerras rc = rtas_call(token, 2, 2, state, sensor, index);
1427507279dbSJohn Rose } while (rtas_busy_delay(rc));
1428033ef338SPaul Mackerras
1429033ef338SPaul Mackerras if (rc < 0)
1430033ef338SPaul Mackerras return rtas_error_rc(rc);
1431033ef338SPaul Mackerras return rc;
1432033ef338SPaul Mackerras }
14339bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_get_sensor);
1434033ef338SPaul Mackerras
rtas_get_sensor_fast(int sensor,int index,int * state)14351c2cb594SThomas Huth int rtas_get_sensor_fast(int sensor, int index, int *state)
14361c2cb594SThomas Huth {
143708273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_GET_SENSOR_STATE);
14381c2cb594SThomas Huth int rc;
14391c2cb594SThomas Huth
14401c2cb594SThomas Huth if (token == RTAS_UNKNOWN_SERVICE)
14411c2cb594SThomas Huth return -ENOENT;
14421c2cb594SThomas Huth
14431c2cb594SThomas Huth rc = rtas_call(token, 2, 2, state, sensor, index);
14441c2cb594SThomas Huth WARN_ON(rc == RTAS_BUSY || (rc >= RTAS_EXTENDED_DELAY_MIN &&
14451c2cb594SThomas Huth rc <= RTAS_EXTENDED_DELAY_MAX));
14461c2cb594SThomas Huth
14471c2cb594SThomas Huth if (rc < 0)
14481c2cb594SThomas Huth return rtas_error_rc(rc);
14491c2cb594SThomas Huth return rc;
14501c2cb594SThomas Huth }
14511c2cb594SThomas Huth
rtas_indicator_present(int token,int * maxindex)1452edc72ac4SNathan Lynch bool rtas_indicator_present(int token, int *maxindex)
1453edc72ac4SNathan Lynch {
1454edc72ac4SNathan Lynch int proplen, count, i;
1455edc72ac4SNathan Lynch const struct indicator_elem {
145608bc1dc5SAnton Blanchard __be32 token;
145708bc1dc5SAnton Blanchard __be32 maxindex;
1458edc72ac4SNathan Lynch } *indicators;
1459edc72ac4SNathan Lynch
1460edc72ac4SNathan Lynch indicators = of_get_property(rtas.dev, "rtas-indicators", &proplen);
1461edc72ac4SNathan Lynch if (!indicators)
1462edc72ac4SNathan Lynch return false;
1463edc72ac4SNathan Lynch
1464edc72ac4SNathan Lynch count = proplen / sizeof(struct indicator_elem);
1465edc72ac4SNathan Lynch
1466edc72ac4SNathan Lynch for (i = 0; i < count; i++) {
146708bc1dc5SAnton Blanchard if (__be32_to_cpu(indicators[i].token) != token)
1468edc72ac4SNathan Lynch continue;
1469edc72ac4SNathan Lynch if (maxindex)
147008bc1dc5SAnton Blanchard *maxindex = __be32_to_cpu(indicators[i].maxindex);
1471edc72ac4SNathan Lynch return true;
1472edc72ac4SNathan Lynch }
1473edc72ac4SNathan Lynch
1474edc72ac4SNathan Lynch return false;
1475edc72ac4SNathan Lynch }
1476edc72ac4SNathan Lynch
rtas_set_indicator(int indicator,int index,int new_value)1477033ef338SPaul Mackerras int rtas_set_indicator(int indicator, int index, int new_value)
1478033ef338SPaul Mackerras {
147908273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_SET_INDICATOR);
1480033ef338SPaul Mackerras int rc;
1481033ef338SPaul Mackerras
1482033ef338SPaul Mackerras if (token == RTAS_UNKNOWN_SERVICE)
1483033ef338SPaul Mackerras return -ENOENT;
1484033ef338SPaul Mackerras
1485507279dbSJohn Rose do {
1486033ef338SPaul Mackerras rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value);
1487507279dbSJohn Rose } while (rtas_busy_delay(rc));
1488033ef338SPaul Mackerras
1489033ef338SPaul Mackerras if (rc < 0)
1490033ef338SPaul Mackerras return rtas_error_rc(rc);
1491033ef338SPaul Mackerras return rc;
1492033ef338SPaul Mackerras }
14939bce6243SNathan Lynch EXPORT_SYMBOL_GPL(rtas_set_indicator);
1494033ef338SPaul Mackerras
149581b73dd9SHaren Myneni /*
149681b73dd9SHaren Myneni * Ignoring RTAS extended delay
149781b73dd9SHaren Myneni */
rtas_set_indicator_fast(int indicator,int index,int new_value)149881b73dd9SHaren Myneni int rtas_set_indicator_fast(int indicator, int index, int new_value)
149981b73dd9SHaren Myneni {
150008273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_SET_INDICATOR);
150181b73dd9SHaren Myneni int rc;
150281b73dd9SHaren Myneni
150381b73dd9SHaren Myneni if (token == RTAS_UNKNOWN_SERVICE)
150481b73dd9SHaren Myneni return -ENOENT;
150581b73dd9SHaren Myneni
150681b73dd9SHaren Myneni rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value);
150781b73dd9SHaren Myneni
15089ef03193SThomas Huth WARN_ON(rc == RTAS_BUSY || (rc >= RTAS_EXTENDED_DELAY_MIN &&
15099ef03193SThomas Huth rc <= RTAS_EXTENDED_DELAY_MAX));
151081b73dd9SHaren Myneni
151181b73dd9SHaren Myneni if (rc < 0)
151281b73dd9SHaren Myneni return rtas_error_rc(rc);
151381b73dd9SHaren Myneni
151481b73dd9SHaren Myneni return rc;
151581b73dd9SHaren Myneni }
151681b73dd9SHaren Myneni
1517701ba683SNathan Lynch /**
1518701ba683SNathan Lynch * rtas_ibm_suspend_me() - Call ibm,suspend-me to suspend the LPAR.
1519701ba683SNathan Lynch *
1520701ba683SNathan Lynch * @fw_status: RTAS call status will be placed here if not NULL.
1521701ba683SNathan Lynch *
1522701ba683SNathan Lynch * rtas_ibm_suspend_me() should be called only on a CPU which has
1523701ba683SNathan Lynch * received H_CONTINUE from the H_JOIN hcall. All other active CPUs
1524701ba683SNathan Lynch * should be waiting to return from H_JOIN.
1525701ba683SNathan Lynch *
1526701ba683SNathan Lynch * rtas_ibm_suspend_me() may suspend execution of the OS
1527701ba683SNathan Lynch * indefinitely. Callers should take appropriate measures upon return, such as
1528701ba683SNathan Lynch * resetting watchdog facilities.
1529701ba683SNathan Lynch *
1530701ba683SNathan Lynch * Callers may choose to retry this call if @fw_status is
1531701ba683SNathan Lynch * %RTAS_THREADS_ACTIVE.
1532701ba683SNathan Lynch *
1533701ba683SNathan Lynch * Return:
1534701ba683SNathan Lynch * 0 - The partition has resumed from suspend, possibly after
1535701ba683SNathan Lynch * migration to a different host.
1536701ba683SNathan Lynch * -ECANCELED - The operation was aborted.
1537701ba683SNathan Lynch * -EAGAIN - There were other CPUs not in H_JOIN at the time of the call.
1538701ba683SNathan Lynch * -EBUSY - Some other condition prevented the suspend from succeeding.
1539701ba683SNathan Lynch * -EIO - Hardware/platform error.
1540701ba683SNathan Lynch */
rtas_ibm_suspend_me(int * fw_status)1541701ba683SNathan Lynch int rtas_ibm_suspend_me(int *fw_status)
1542701ba683SNathan Lynch {
154308273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_IBM_SUSPEND_ME);
1544701ba683SNathan Lynch int fwrc;
1545701ba683SNathan Lynch int ret;
1546701ba683SNathan Lynch
154708273c9fSNathan Lynch fwrc = rtas_call(token, 0, 1, NULL);
1548701ba683SNathan Lynch
1549701ba683SNathan Lynch switch (fwrc) {
1550701ba683SNathan Lynch case 0:
1551701ba683SNathan Lynch ret = 0;
1552701ba683SNathan Lynch break;
1553701ba683SNathan Lynch case RTAS_SUSPEND_ABORTED:
1554701ba683SNathan Lynch ret = -ECANCELED;
1555701ba683SNathan Lynch break;
1556701ba683SNathan Lynch case RTAS_THREADS_ACTIVE:
1557701ba683SNathan Lynch ret = -EAGAIN;
1558701ba683SNathan Lynch break;
1559701ba683SNathan Lynch case RTAS_NOT_SUSPENDABLE:
1560701ba683SNathan Lynch case RTAS_OUTSTANDING_COPROC:
1561701ba683SNathan Lynch ret = -EBUSY;
1562701ba683SNathan Lynch break;
1563701ba683SNathan Lynch case -1:
1564701ba683SNathan Lynch default:
1565701ba683SNathan Lynch ret = -EIO;
1566701ba683SNathan Lynch break;
1567701ba683SNathan Lynch }
1568701ba683SNathan Lynch
1569701ba683SNathan Lynch if (fw_status)
1570701ba683SNathan Lynch *fw_status = fwrc;
1571701ba683SNathan Lynch
1572701ba683SNathan Lynch return ret;
1573701ba683SNathan Lynch }
1574701ba683SNathan Lynch
rtas_restart(char * cmd)157595ec77c0SDaniel Axtens void __noreturn rtas_restart(char *cmd)
1576033ef338SPaul Mackerras {
1577f4fcbbe9SPaul Mackerras if (rtas_flash_term_hook)
1578f4fcbbe9SPaul Mackerras rtas_flash_term_hook(SYS_RESTART);
1579f975b655SNathan Lynch pr_emerg("system-reboot returned %d\n",
158008273c9fSNathan Lynch rtas_call(rtas_function_token(RTAS_FN_SYSTEM_REBOOT), 0, 1, NULL));
1581033ef338SPaul Mackerras for (;;);
1582033ef338SPaul Mackerras }
1583033ef338SPaul Mackerras
rtas_power_off(void)1584033ef338SPaul Mackerras void rtas_power_off(void)
1585033ef338SPaul Mackerras {
1586f4fcbbe9SPaul Mackerras if (rtas_flash_term_hook)
1587f4fcbbe9SPaul Mackerras rtas_flash_term_hook(SYS_POWER_OFF);
1588033ef338SPaul Mackerras /* allow power on only with power button press */
1589f975b655SNathan Lynch pr_emerg("power-off returned %d\n",
159008273c9fSNathan Lynch rtas_call(rtas_function_token(RTAS_FN_POWER_OFF), 2, 1, NULL, -1, -1));
1591033ef338SPaul Mackerras for (;;);
1592033ef338SPaul Mackerras }
1593033ef338SPaul Mackerras
rtas_halt(void)159495ec77c0SDaniel Axtens void __noreturn rtas_halt(void)
1595033ef338SPaul Mackerras {
1596f4fcbbe9SPaul Mackerras if (rtas_flash_term_hook)
1597f4fcbbe9SPaul Mackerras rtas_flash_term_hook(SYS_HALT);
1598f4fcbbe9SPaul Mackerras /* allow power on only with power button press */
1599f975b655SNathan Lynch pr_emerg("power-off returned %d\n",
160008273c9fSNathan Lynch rtas_call(rtas_function_token(RTAS_FN_POWER_OFF), 2, 1, NULL, -1, -1));
1601f4fcbbe9SPaul Mackerras for (;;);
1602033ef338SPaul Mackerras }
1603033ef338SPaul Mackerras
1604033ef338SPaul Mackerras /* Must be in the RMO region, so we place it here */
1605033ef338SPaul Mackerras static char rtas_os_term_buf[2048];
160608273c9fSNathan Lynch static bool ibm_extended_os_term;
1607033ef338SPaul Mackerras
rtas_os_term(char * str)16088f515061SPaul Mackerras void rtas_os_term(char *str)
1609033ef338SPaul Mackerras {
161008273c9fSNathan Lynch s32 token = rtas_function_token(RTAS_FN_IBM_OS_TERM);
1611b949ee68SHari Bathini static struct rtas_args args;
1612033ef338SPaul Mackerras int status;
1613033ef338SPaul Mackerras
1614e9bbc8cdSAnton Blanchard /*
1615e9bbc8cdSAnton Blanchard * Firmware with the ibm,extended-os-term property is guaranteed
1616e9bbc8cdSAnton Blanchard * to always return from an ibm,os-term call. Earlier versions without
1617e9bbc8cdSAnton Blanchard * this property may terminate the partition which we want to avoid
1618e9bbc8cdSAnton Blanchard * since it interferes with panic_timeout.
1619e9bbc8cdSAnton Blanchard */
162008273c9fSNathan Lynch
162108273c9fSNathan Lynch if (token == RTAS_UNKNOWN_SERVICE || !ibm_extended_os_term)
1622033ef338SPaul Mackerras return;
1623033ef338SPaul Mackerras
16248f515061SPaul Mackerras snprintf(rtas_os_term_buf, 2048, "OS panic: %s", str);
16258f515061SPaul Mackerras
16266c606e57SNathan Lynch /*
16276c606e57SNathan Lynch * Keep calling as long as RTAS returns a "try again" status,
16286c606e57SNathan Lynch * but don't use rtas_busy_delay(), which potentially
16296c606e57SNathan Lynch * schedules.
16306c606e57SNathan Lynch */
1631033ef338SPaul Mackerras do {
1632b949ee68SHari Bathini rtas_call_unlocked(&args, token, 1, 1, NULL, __pa(rtas_os_term_buf));
1633b949ee68SHari Bathini status = be32_to_cpu(args.rets[0]);
16346c606e57SNathan Lynch } while (rtas_busy_delay_time(status));
1635033ef338SPaul Mackerras
1636507279dbSJohn Rose if (status != 0)
1637f975b655SNathan Lynch pr_emerg("ibm,os-term call failed %d\n", status);
1638033ef338SPaul Mackerras }
1639033ef338SPaul Mackerras
16405f485a66SNathan Lynch /**
16415f485a66SNathan Lynch * rtas_activate_firmware() - Activate a new version of firmware.
16425f485a66SNathan Lynch *
164353cadf7dSNathan Lynch * Context: This function may sleep.
164453cadf7dSNathan Lynch *
16455f485a66SNathan Lynch * Activate a new version of partition firmware. The OS must call this
16465f485a66SNathan Lynch * after resuming from a partition hibernation or migration in order
16475f485a66SNathan Lynch * to maintain the ability to perform live firmware updates. It's not
16485f485a66SNathan Lynch * catastrophic for this method to be absent or to fail; just log the
16495f485a66SNathan Lynch * condition in that case.
16505f485a66SNathan Lynch */
rtas_activate_firmware(void)16515f485a66SNathan Lynch void rtas_activate_firmware(void)
16525f485a66SNathan Lynch {
165308273c9fSNathan Lynch int token = rtas_function_token(RTAS_FN_IBM_ACTIVATE_FIRMWARE);
16545f485a66SNathan Lynch int fwrc;
16555f485a66SNathan Lynch
16565f485a66SNathan Lynch if (token == RTAS_UNKNOWN_SERVICE) {
16575f485a66SNathan Lynch pr_notice("ibm,activate-firmware method unavailable\n");
16585f485a66SNathan Lynch return;
16595f485a66SNathan Lynch }
16605f485a66SNathan Lynch
16615f485a66SNathan Lynch do {
16625f485a66SNathan Lynch fwrc = rtas_call(token, 0, 1, NULL);
16635f485a66SNathan Lynch } while (rtas_busy_delay(fwrc));
16645f485a66SNathan Lynch
16655f485a66SNathan Lynch if (fwrc)
16665f485a66SNathan Lynch pr_err("ibm,activate-firmware failed (%i)\n", fwrc);
16675f485a66SNathan Lynch }
16685f485a66SNathan Lynch
16696431f208SAnton Blanchard /**
167053cadf7dSNathan Lynch * get_pseries_errorlog() - Find a specific pseries error log in an RTAS
167153cadf7dSNathan Lynch * extended event log.
16726431f208SAnton Blanchard * @log: RTAS error/event log
16736431f208SAnton Blanchard * @section_id: two character section identifier
16746431f208SAnton Blanchard *
167553cadf7dSNathan Lynch * Return: A pointer to the specified errorlog or NULL if not found.
16766431f208SAnton Blanchard */
get_pseries_errorlog(struct rtas_error_log * log,uint16_t section_id)1677743cdb7bSPaul Mackerras noinstr struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log,
16786431f208SAnton Blanchard uint16_t section_id)
16796431f208SAnton Blanchard {
16806431f208SAnton Blanchard struct rtas_ext_event_log_v6 *ext_log =
16816431f208SAnton Blanchard (struct rtas_ext_event_log_v6 *)log->buffer;
16826431f208SAnton Blanchard struct pseries_errorlog *sect;
16836431f208SAnton Blanchard unsigned char *p, *log_end;
1684a08a53eaSGreg Kurz uint32_t ext_log_length = rtas_error_extended_log_length(log);
1685a08a53eaSGreg Kurz uint8_t log_format = rtas_ext_event_log_format(ext_log);
1686a08a53eaSGreg Kurz uint32_t company_id = rtas_ext_event_company_id(ext_log);
16876431f208SAnton Blanchard
16886431f208SAnton Blanchard /* Check that we understand the format */
1689a08a53eaSGreg Kurz if (ext_log_length < sizeof(struct rtas_ext_event_log_v6) ||
1690a08a53eaSGreg Kurz log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG ||
1691a08a53eaSGreg Kurz company_id != RTAS_V6EXT_COMPANY_ID_IBM)
16926431f208SAnton Blanchard return NULL;
16936431f208SAnton Blanchard
1694a08a53eaSGreg Kurz log_end = log->buffer + ext_log_length;
16956431f208SAnton Blanchard p = ext_log->vendor_log;
16966431f208SAnton Blanchard
16976431f208SAnton Blanchard while (p < log_end) {
16986431f208SAnton Blanchard sect = (struct pseries_errorlog *)p;
1699a08a53eaSGreg Kurz if (pseries_errorlog_id(sect) == section_id)
17006431f208SAnton Blanchard return sect;
1701a08a53eaSGreg Kurz p += pseries_errorlog_length(sect);
17026431f208SAnton Blanchard }
17036431f208SAnton Blanchard
17046431f208SAnton Blanchard return NULL;
17056431f208SAnton Blanchard }
17066431f208SAnton Blanchard
1707bd59380cSAndrew Donnellan /*
1708bd59380cSAndrew Donnellan * The sys_rtas syscall, as originally designed, allows root to pass
1709bd59380cSAndrew Donnellan * arbitrary physical addresses to RTAS calls. A number of RTAS calls
1710bd59380cSAndrew Donnellan * can be abused to write to arbitrary memory and do other things that
1711bd59380cSAndrew Donnellan * are potentially harmful to system integrity, and thus should only
1712bd59380cSAndrew Donnellan * be used inside the kernel and not exposed to userspace.
1713bd59380cSAndrew Donnellan *
1714bd59380cSAndrew Donnellan * All known legitimate users of the sys_rtas syscall will only ever
1715bd59380cSAndrew Donnellan * pass addresses that fall within the RMO buffer, and use a known
1716bd59380cSAndrew Donnellan * subset of RTAS calls.
1717bd59380cSAndrew Donnellan *
1718bd59380cSAndrew Donnellan * Accordingly, we filter RTAS requests to check that the call is
1719bd59380cSAndrew Donnellan * permitted, and that provided pointers fall within the RMO buffer.
17208252b882SNathan Lynch * If a function is allowed to be invoked via the syscall, then its
17218252b882SNathan Lynch * entry in the rtas_functions table points to a rtas_filter that
17228252b882SNathan Lynch * describes its constraints, with the indexes of the parameters which
17238252b882SNathan Lynch * are expected to contain addresses and sizes of buffers allocated
17248252b882SNathan Lynch * inside the RMO buffer.
1725bd59380cSAndrew Donnellan */
1726bd59380cSAndrew Donnellan
in_rmo_buf(u32 base,u32 end)1727bd59380cSAndrew Donnellan static bool in_rmo_buf(u32 base, u32 end)
1728bd59380cSAndrew Donnellan {
1729bd59380cSAndrew Donnellan return base >= rtas_rmo_buf &&
1730e5d56763SNathan Lynch base < (rtas_rmo_buf + RTAS_USER_REGION_SIZE) &&
1731bd59380cSAndrew Donnellan base <= end &&
1732bd59380cSAndrew Donnellan end >= rtas_rmo_buf &&
1733e5d56763SNathan Lynch end < (rtas_rmo_buf + RTAS_USER_REGION_SIZE);
1734bd59380cSAndrew Donnellan }
1735bd59380cSAndrew Donnellan
block_rtas_call(int token,int nargs,struct rtas_args * args)1736bd59380cSAndrew Donnellan static bool block_rtas_call(int token, int nargs,
1737bd59380cSAndrew Donnellan struct rtas_args *args)
1738bd59380cSAndrew Donnellan {
17398252b882SNathan Lynch const struct rtas_function *func;
17408252b882SNathan Lynch const struct rtas_filter *f;
174108273c9fSNathan Lynch const bool is_platform_dump = token == rtas_function_token(RTAS_FN_IBM_PLATFORM_DUMP);
174208273c9fSNathan Lynch const bool is_config_conn = token == rtas_function_token(RTAS_FN_IBM_CONFIGURE_CONNECTOR);
1743bd59380cSAndrew Donnellan u32 base, size, end;
1744bd59380cSAndrew Donnellan
17458252b882SNathan Lynch /*
17468252b882SNathan Lynch * If this token doesn't correspond to a function the kernel
17478252b882SNathan Lynch * understands, you're not allowed to call it.
17488252b882SNathan Lynch */
1749bc063bf0SNathan Lynch func = rtas_token_to_function_untrusted(token);
17508252b882SNathan Lynch if (!func)
17518252b882SNathan Lynch goto err;
17528252b882SNathan Lynch /*
17538252b882SNathan Lynch * And only functions with filters attached are allowed.
17548252b882SNathan Lynch */
17558252b882SNathan Lynch f = func->filter;
17568252b882SNathan Lynch if (!f)
17578252b882SNathan Lynch goto err;
17588252b882SNathan Lynch /*
17598252b882SNathan Lynch * And some functions aren't allowed on LE.
17608252b882SNathan Lynch */
17618252b882SNathan Lynch if (IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN) && func->banned_for_syscall_on_le)
17628252b882SNathan Lynch goto err;
1763bd59380cSAndrew Donnellan
1764bd59380cSAndrew Donnellan if (f->buf_idx1 != -1) {
1765bd59380cSAndrew Donnellan base = be32_to_cpu(args->args[f->buf_idx1]);
1766bd59380cSAndrew Donnellan if (f->size_idx1 != -1)
1767bd59380cSAndrew Donnellan size = be32_to_cpu(args->args[f->size_idx1]);
1768bd59380cSAndrew Donnellan else if (f->fixed_size)
1769bd59380cSAndrew Donnellan size = f->fixed_size;
1770bd59380cSAndrew Donnellan else
1771bd59380cSAndrew Donnellan size = 1;
1772bd59380cSAndrew Donnellan
1773bd59380cSAndrew Donnellan end = base + size - 1;
17747bc08056SAndrew Donnellan
17757bc08056SAndrew Donnellan /*
17767bc08056SAndrew Donnellan * Special case for ibm,platform-dump - NULL buffer
17777bc08056SAndrew Donnellan * address is used to indicate end of dump processing
17787bc08056SAndrew Donnellan */
177908273c9fSNathan Lynch if (is_platform_dump && base == 0)
17807bc08056SAndrew Donnellan return false;
17817bc08056SAndrew Donnellan
1782bd59380cSAndrew Donnellan if (!in_rmo_buf(base, end))
1783bd59380cSAndrew Donnellan goto err;
1784bd59380cSAndrew Donnellan }
1785bd59380cSAndrew Donnellan
1786bd59380cSAndrew Donnellan if (f->buf_idx2 != -1) {
1787bd59380cSAndrew Donnellan base = be32_to_cpu(args->args[f->buf_idx2]);
1788bd59380cSAndrew Donnellan if (f->size_idx2 != -1)
1789bd59380cSAndrew Donnellan size = be32_to_cpu(args->args[f->size_idx2]);
1790bd59380cSAndrew Donnellan else if (f->fixed_size)
1791bd59380cSAndrew Donnellan size = f->fixed_size;
1792bd59380cSAndrew Donnellan else
1793bd59380cSAndrew Donnellan size = 1;
1794bd59380cSAndrew Donnellan end = base + size - 1;
1795bd59380cSAndrew Donnellan
1796bd59380cSAndrew Donnellan /*
1797bd59380cSAndrew Donnellan * Special case for ibm,configure-connector where the
1798bd59380cSAndrew Donnellan * address can be 0
1799bd59380cSAndrew Donnellan */
180008273c9fSNathan Lynch if (is_config_conn && base == 0)
1801bd59380cSAndrew Donnellan return false;
1802bd59380cSAndrew Donnellan
1803bd59380cSAndrew Donnellan if (!in_rmo_buf(base, end))
1804bd59380cSAndrew Donnellan goto err;
1805bd59380cSAndrew Donnellan }
1806bd59380cSAndrew Donnellan
1807bd59380cSAndrew Donnellan return false;
1808bd59380cSAndrew Donnellan err:
1809bd59380cSAndrew Donnellan pr_err_ratelimited("sys_rtas: RTAS call blocked - exploit attempt?\n");
1810bd59380cSAndrew Donnellan pr_err_ratelimited("sys_rtas: token=0x%x, nargs=%d (called by %s)\n",
1811bd59380cSAndrew Donnellan token, nargs, current->comm);
1812bd59380cSAndrew Donnellan return true;
1813bd59380cSAndrew Donnellan }
1814bd59380cSAndrew Donnellan
1815599d2870SGreg Kurz /* We assume to be passed big endian arguments */
SYSCALL_DEFINE1(rtas,struct rtas_args __user *,uargs)18164c392e65SAl Viro SYSCALL_DEFINE1(rtas, struct rtas_args __user *, uargs)
1817033ef338SPaul Mackerras {
1818af8bc682SNathan Lynch struct pin_cookie cookie;
1819033ef338SPaul Mackerras struct rtas_args args;
1820033ef338SPaul Mackerras unsigned long flags;
1821033ef338SPaul Mackerras char *buff_copy, *errbuf = NULL;
1822599d2870SGreg Kurz int nargs, nret, token;
1823033ef338SPaul Mackerras
1824033ef338SPaul Mackerras if (!capable(CAP_SYS_ADMIN))
1825033ef338SPaul Mackerras return -EPERM;
1826033ef338SPaul Mackerras
18278832317fSVasant Hegde if (!rtas.entry)
18288832317fSVasant Hegde return -EINVAL;
18298832317fSVasant Hegde
1830033ef338SPaul Mackerras if (copy_from_user(&args, uargs, 3 * sizeof(u32)) != 0)
1831033ef338SPaul Mackerras return -EFAULT;
1832033ef338SPaul Mackerras
1833599d2870SGreg Kurz nargs = be32_to_cpu(args.nargs);
1834599d2870SGreg Kurz nret = be32_to_cpu(args.nret);
1835599d2870SGreg Kurz token = be32_to_cpu(args.token);
1836599d2870SGreg Kurz
1837a9862c74SAndrew Donnellan if (nargs >= ARRAY_SIZE(args.args)
1838599d2870SGreg Kurz || nret > ARRAY_SIZE(args.args)
1839599d2870SGreg Kurz || nargs + nret > ARRAY_SIZE(args.args))
1840033ef338SPaul Mackerras return -EINVAL;
1841033ef338SPaul Mackerras
1842033ef338SPaul Mackerras /* Copy in args. */
1843033ef338SPaul Mackerras if (copy_from_user(args.args, uargs->args,
1844033ef338SPaul Mackerras nargs * sizeof(rtas_arg_t)) != 0)
1845033ef338SPaul Mackerras return -EFAULT;
1846033ef338SPaul Mackerras
1847599d2870SGreg Kurz if (token == RTAS_UNKNOWN_SERVICE)
184891dc182cSDave C Boutcher return -EINVAL;
184991dc182cSDave C Boutcher
1850b79998fcSNathan Fontenot args.rets = &args.args[nargs];
1851599d2870SGreg Kurz memset(args.rets, 0, nret * sizeof(rtas_arg_t));
1852b79998fcSNathan Fontenot
1853bd59380cSAndrew Donnellan if (block_rtas_call(token, nargs, &args))
1854bd59380cSAndrew Donnellan return -EINVAL;
1855bd59380cSAndrew Donnellan
185608273c9fSNathan Lynch if (token_is_restricted_errinjct(token)) {
1857b8f3e488SNathan Lynch int err;
1858b8f3e488SNathan Lynch
1859b8f3e488SNathan Lynch err = security_locked_down(LOCKDOWN_RTAS_ERROR_INJECTION);
1860b8f3e488SNathan Lynch if (err)
1861b8f3e488SNathan Lynch return err;
1862b8f3e488SNathan Lynch }
1863b8f3e488SNathan Lynch
186491dc182cSDave C Boutcher /* Need to handle ibm,suspend_me call specially */
186508273c9fSNathan Lynch if (token == rtas_function_token(RTAS_FN_IBM_SUSPEND_ME)) {
18663df76a9dSCyril Bur
18673df76a9dSCyril Bur /*
1868c03e7374STyrel Datwyler * rtas_ibm_suspend_me assumes the streamid handle is in cpu
1869c03e7374STyrel Datwyler * endian, or at least the hcall within it requires it.
18703df76a9dSCyril Bur */
1871c03e7374STyrel Datwyler int rc = 0;
18723df76a9dSCyril Bur u64 handle = ((u64)be32_to_cpu(args.args[0]) << 32)
18733df76a9dSCyril Bur | be32_to_cpu(args.args[1]);
18744d756894SNathan Lynch rc = rtas_syscall_dispatch_ibm_suspend_me(handle);
1875c03e7374STyrel Datwyler if (rc == -EAGAIN)
1876c03e7374STyrel Datwyler args.rets[0] = cpu_to_be32(RTAS_NOT_SUSPENDABLE);
1877c03e7374STyrel Datwyler else if (rc == -EIO)
1878c03e7374STyrel Datwyler args.rets[0] = cpu_to_be32(-1);
1879c03e7374STyrel Datwyler else if (rc)
188091dc182cSDave C Boutcher return rc;
188191dc182cSDave C Boutcher goto copy_return;
188291dc182cSDave C Boutcher }
188391dc182cSDave C Boutcher
1884033ef338SPaul Mackerras buff_copy = get_errorlog_buffer();
1885033ef338SPaul Mackerras
188612fd6665SNathan Lynch raw_spin_lock_irqsave(&rtas_lock, flags);
1887af8bc682SNathan Lynch cookie = lockdep_pin_lock(&rtas_lock);
1888033ef338SPaul Mackerras
1889599af491SNathan Lynch rtas_args = args;
189077f85f69SNathan Lynch do_enter_rtas(&rtas_args);
1891599af491SNathan Lynch args = rtas_args;
1892033ef338SPaul Mackerras
1893033ef338SPaul Mackerras /* A -1 return code indicates that the last command couldn't
1894033ef338SPaul Mackerras be completed due to a hardware error. */
1895599d2870SGreg Kurz if (be32_to_cpu(args.rets[0]) == -1)
1896033ef338SPaul Mackerras errbuf = __fetch_rtas_last_error(buff_copy);
1897033ef338SPaul Mackerras
1898af8bc682SNathan Lynch lockdep_unpin_lock(&rtas_lock, cookie);
189912fd6665SNathan Lynch raw_spin_unlock_irqrestore(&rtas_lock, flags);
1900033ef338SPaul Mackerras
1901033ef338SPaul Mackerras if (buff_copy) {
1902033ef338SPaul Mackerras if (errbuf)
1903033ef338SPaul Mackerras log_error(errbuf, ERR_TYPE_RTAS_LOG, 0);
1904033ef338SPaul Mackerras kfree(buff_copy);
1905033ef338SPaul Mackerras }
1906033ef338SPaul Mackerras
190791dc182cSDave C Boutcher copy_return:
1908033ef338SPaul Mackerras /* Copy out args. */
1909033ef338SPaul Mackerras if (copy_to_user(uargs->args + nargs,
1910033ef338SPaul Mackerras args.args + nargs,
1911599d2870SGreg Kurz nret * sizeof(rtas_arg_t)) != 0)
1912033ef338SPaul Mackerras return -EFAULT;
1913033ef338SPaul Mackerras
1914033ef338SPaul Mackerras return 0;
1915033ef338SPaul Mackerras }
1916033ef338SPaul Mackerras
rtas_function_table_init(void)19178252b882SNathan Lynch static void __init rtas_function_table_init(void)
19188252b882SNathan Lynch {
19198252b882SNathan Lynch struct property *prop;
19208252b882SNathan Lynch
19218252b882SNathan Lynch for (size_t i = 0; i < ARRAY_SIZE(rtas_function_table); ++i) {
19228252b882SNathan Lynch struct rtas_function *curr = &rtas_function_table[i];
19238252b882SNathan Lynch struct rtas_function *prior;
19248252b882SNathan Lynch int cmp;
19258252b882SNathan Lynch
19268252b882SNathan Lynch curr->token = RTAS_UNKNOWN_SERVICE;
19278252b882SNathan Lynch
19288252b882SNathan Lynch if (i == 0)
19298252b882SNathan Lynch continue;
19308252b882SNathan Lynch /*
19318252b882SNathan Lynch * Ensure table is sorted correctly for binary search
19328252b882SNathan Lynch * on function names.
19338252b882SNathan Lynch */
19348252b882SNathan Lynch prior = &rtas_function_table[i - 1];
19358252b882SNathan Lynch
19368252b882SNathan Lynch cmp = strcmp(prior->name, curr->name);
19378252b882SNathan Lynch if (cmp < 0)
19388252b882SNathan Lynch continue;
19398252b882SNathan Lynch
19408252b882SNathan Lynch if (cmp == 0) {
19418252b882SNathan Lynch pr_err("'%s' has duplicate function table entries\n",
19428252b882SNathan Lynch curr->name);
19438252b882SNathan Lynch } else {
19448252b882SNathan Lynch pr_err("function table unsorted: '%s' wrongly precedes '%s'\n",
19458252b882SNathan Lynch prior->name, curr->name);
19468252b882SNathan Lynch }
19478252b882SNathan Lynch }
19488252b882SNathan Lynch
19498252b882SNathan Lynch for_each_property_of_node(rtas.dev, prop) {
19508252b882SNathan Lynch struct rtas_function *func;
19518252b882SNathan Lynch
19528252b882SNathan Lynch if (prop->length != sizeof(u32))
19538252b882SNathan Lynch continue;
19548252b882SNathan Lynch
19558252b882SNathan Lynch func = __rtas_name_to_function(prop->name);
19568252b882SNathan Lynch if (!func)
19578252b882SNathan Lynch continue;
19588252b882SNathan Lynch
19598252b882SNathan Lynch func->token = be32_to_cpup((__be32 *)prop->value);
19608252b882SNathan Lynch
19618252b882SNathan Lynch pr_debug("function %s has token %u\n", func->name, func->token);
19628252b882SNathan Lynch }
19638252b882SNathan Lynch }
19648252b882SNathan Lynch
1965033ef338SPaul Mackerras /*
196614ed7409SAnton Blanchard * Call early during boot, before mem init, to retrieve the RTAS
196714ed7409SAnton Blanchard * information from the device-tree and allocate the RMO buffer for userland
1968033ef338SPaul Mackerras * accesses.
1969033ef338SPaul Mackerras */
rtas_initialize(void)1970033ef338SPaul Mackerras void __init rtas_initialize(void)
1971033ef338SPaul Mackerras {
1972033ef338SPaul Mackerras unsigned long rtas_region = RTAS_INSTANTIATE_MAX;
1973de6d2d1bSGavin Shan u32 base, size, entry;
1974de6d2d1bSGavin Shan int no_base, no_size, no_entry;
1975033ef338SPaul Mackerras
1976033ef338SPaul Mackerras /* Get RTAS dev node and fill up our "rtas" structure with infos
1977033ef338SPaul Mackerras * about it.
1978033ef338SPaul Mackerras */
1979033ef338SPaul Mackerras rtas.dev = of_find_node_by_name(NULL, "rtas");
1980dbecd509SGavin Shan if (!rtas.dev)
1981dbecd509SGavin Shan return;
1982033ef338SPaul Mackerras
1983de6d2d1bSGavin Shan no_base = of_property_read_u32(rtas.dev, "linux,rtas-base", &base);
1984de6d2d1bSGavin Shan no_size = of_property_read_u32(rtas.dev, "rtas-size", &size);
1985de6d2d1bSGavin Shan if (no_base || no_size) {
19868b257783SGavin Shan of_node_put(rtas.dev);
1987dbecd509SGavin Shan rtas.dev = NULL;
1988dbecd509SGavin Shan return;
1989dbecd509SGavin Shan }
1990dbecd509SGavin Shan
1991de6d2d1bSGavin Shan rtas.base = base;
1992de6d2d1bSGavin Shan rtas.size = size;
1993de6d2d1bSGavin Shan no_entry = of_property_read_u32(rtas.dev, "linux,rtas-entry", &entry);
1994de6d2d1bSGavin Shan rtas.entry = no_entry ? rtas.base : entry;
1995033ef338SPaul Mackerras
1996c67a0e41SNathan Lynch init_error_log_max();
1997c67a0e41SNathan Lynch
19988252b882SNathan Lynch /* Must be called before any function token lookups */
19998252b882SNathan Lynch rtas_function_table_init();
20008252b882SNathan Lynch
2001ed2213bfSNathan Lynch /*
200208273c9fSNathan Lynch * Discover this now to avoid a device tree lookup in the
2003ed2213bfSNathan Lynch * panic path.
2004ed2213bfSNathan Lynch */
200508273c9fSNathan Lynch ibm_extended_os_term = of_property_read_bool(rtas.dev, "ibm,extended-os-term");
2006ed2213bfSNathan Lynch
2007033ef338SPaul Mackerras /* If RTAS was found, allocate the RMO buffer for it and look for
2008033ef338SPaul Mackerras * the stop-self token if any
2009033ef338SPaul Mackerras */
2010033ef338SPaul Mackerras #ifdef CONFIG_PPC64
20110ab1c929SNathan Lynch if (firmware_has_feature(FW_FEATURE_LPAR))
2012cd3db0c4SBenjamin Herrenschmidt rtas_region = min(ppc64_rma_size, RTAS_INSTANTIATE_MAX);
2013033ef338SPaul Mackerras #endif
2014e5d56763SNathan Lynch rtas_rmo_buf = memblock_phys_alloc_range(RTAS_USER_REGION_SIZE, PAGE_SIZE,
20150ba9e6edSMike Rapoport 0, rtas_region);
20160ba9e6edSMike Rapoport if (!rtas_rmo_buf)
20170ba9e6edSMike Rapoport panic("ERROR: RTAS: Failed to allocate %lx bytes below %pa\n",
20180ba9e6edSMike Rapoport PAGE_SIZE, &rtas_region);
2019033ef338SPaul Mackerras
202043033bc6SNathan Lynch rtas_work_area_reserve_arena(rtas_region);
2021033ef338SPaul Mackerras }
2022458148c0SMichael Ellerman
early_init_dt_scan_rtas(unsigned long node,const char * uname,int depth,void * data)2023458148c0SMichael Ellerman int __init early_init_dt_scan_rtas(unsigned long node,
2024458148c0SMichael Ellerman const char *uname, int depth, void *data)
2025458148c0SMichael Ellerman {
20269d0c4dfeSRob Herring const u32 *basep, *entryp, *sizep;
2027458148c0SMichael Ellerman
2028458148c0SMichael Ellerman if (depth != 1 || strcmp(uname, "rtas") != 0)
2029458148c0SMichael Ellerman return 0;
2030458148c0SMichael Ellerman
2031458148c0SMichael Ellerman basep = of_get_flat_dt_prop(node, "linux,rtas-base", NULL);
2032458148c0SMichael Ellerman entryp = of_get_flat_dt_prop(node, "linux,rtas-entry", NULL);
2033458148c0SMichael Ellerman sizep = of_get_flat_dt_prop(node, "rtas-size", NULL);
2034458148c0SMichael Ellerman
20357c5ed82bSSourabh Jain #ifdef CONFIG_PPC64
20367c5ed82bSSourabh Jain /* need this feature to decide the crashkernel offset */
20377c5ed82bSSourabh Jain if (of_get_flat_dt_prop(node, "ibm,hypertas-functions", NULL))
20387c5ed82bSSourabh Jain powerpc_firmware_features |= FW_FEATURE_LPAR;
20397c5ed82bSSourabh Jain #endif
20407c5ed82bSSourabh Jain
2041458148c0SMichael Ellerman if (basep && entryp && sizep) {
2042458148c0SMichael Ellerman rtas.base = *basep;
2043458148c0SMichael Ellerman rtas.entry = *entryp;
2044458148c0SMichael Ellerman rtas.size = *sizep;
2045458148c0SMichael Ellerman }
2046458148c0SMichael Ellerman
2047cc46bb98SMichael Ellerman #ifdef CONFIG_UDBG_RTAS_CONSOLE
2048cc46bb98SMichael Ellerman basep = of_get_flat_dt_prop(node, "put-term-char", NULL);
2049cc46bb98SMichael Ellerman if (basep)
2050cc46bb98SMichael Ellerman rtas_putchar_token = *basep;
2051cc46bb98SMichael Ellerman
2052cc46bb98SMichael Ellerman basep = of_get_flat_dt_prop(node, "get-term-char", NULL);
2053cc46bb98SMichael Ellerman if (basep)
2054cc46bb98SMichael Ellerman rtas_getchar_token = *basep;
20559a2ded55SMichael Neuling
20569a2ded55SMichael Neuling if (rtas_putchar_token != RTAS_UNKNOWN_SERVICE &&
20579a2ded55SMichael Neuling rtas_getchar_token != RTAS_UNKNOWN_SERVICE)
20589a2ded55SMichael Neuling udbg_init_rtas_console();
20599a2ded55SMichael Neuling
2060cc46bb98SMichael Ellerman #endif
2061cc46bb98SMichael Ellerman
2062458148c0SMichael Ellerman /* break now */
2063458148c0SMichael Ellerman return 1;
2064458148c0SMichael Ellerman }
2065c4007a2fSBenjamin Herrenschmidt
206612fd6665SNathan Lynch static DEFINE_RAW_SPINLOCK(timebase_lock);
2067c4007a2fSBenjamin Herrenschmidt static u64 timebase = 0;
2068c4007a2fSBenjamin Herrenschmidt
rtas_give_timebase(void)2069061d19f2SPaul Gortmaker void rtas_give_timebase(void)
2070c4007a2fSBenjamin Herrenschmidt {
2071c4007a2fSBenjamin Herrenschmidt unsigned long flags;
2072c4007a2fSBenjamin Herrenschmidt
207312fd6665SNathan Lynch raw_spin_lock_irqsave(&timebase_lock, flags);
2074c4007a2fSBenjamin Herrenschmidt hard_irq_disable();
207508273c9fSNathan Lynch rtas_call(rtas_function_token(RTAS_FN_FREEZE_TIME_BASE), 0, 1, NULL);
2076c4007a2fSBenjamin Herrenschmidt timebase = get_tb();
207712fd6665SNathan Lynch raw_spin_unlock(&timebase_lock);
2078c4007a2fSBenjamin Herrenschmidt
2079c4007a2fSBenjamin Herrenschmidt while (timebase)
2080c4007a2fSBenjamin Herrenschmidt barrier();
208108273c9fSNathan Lynch rtas_call(rtas_function_token(RTAS_FN_THAW_TIME_BASE), 0, 1, NULL);
2082c4007a2fSBenjamin Herrenschmidt local_irq_restore(flags);
2083c4007a2fSBenjamin Herrenschmidt }
2084c4007a2fSBenjamin Herrenschmidt
rtas_take_timebase(void)2085061d19f2SPaul Gortmaker void rtas_take_timebase(void)
2086c4007a2fSBenjamin Herrenschmidt {
2087c4007a2fSBenjamin Herrenschmidt while (!timebase)
2088c4007a2fSBenjamin Herrenschmidt barrier();
208912fd6665SNathan Lynch raw_spin_lock(&timebase_lock);
2090c4007a2fSBenjamin Herrenschmidt set_tb(timebase >> 32, timebase & 0xffffffff);
2091c4007a2fSBenjamin Herrenschmidt timebase = 0;
209212fd6665SNathan Lynch raw_spin_unlock(&timebase_lock);
2093c4007a2fSBenjamin Herrenschmidt }
2094