xref: /openbmc/linux/arch/powerpc/kernel/rtas.c (revision 6b6282d5)
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