[U-Boot] [PATCH 0/2] ARM: support System Reset via PSCI

For ARMv8, the secure firmware (ex. ARM Trusted Firmware BL31) is often implemented, making U-Boot a non-secure world boot loader.
The secure firmware generally includes PSCI. The power management operations, including System Reset, are supposed to be handled by PSCI. In this case, U-Boot should not touch the system reset directly. Instead, U-Boot should call PSCI service to reset the system.
In order to implement this, arm-smccc code is needed. The code has been copied from Linux. (1/2)
2/2 implements a generic sysreset driver based on the PSCI specification.
Masahiro Yamada (2): ARM: import arm-smccc from Linux 4.11-rc6 sysreset: psci: support system reset in a generic way with PSCI
arch/arm/Kconfig | 9 ++ arch/arm/cpu/armv7/Makefile | 1 + arch/arm/cpu/armv7/smccc-call.S | 56 +++++++++ arch/arm/cpu/armv8/Makefile | 2 + arch/arm/cpu/armv8/smccc-call.S | 44 +++++++ arch/arm/include/asm/opcodes-sec.h | 17 +++ arch/arm/include/asm/opcodes-virt.h | 27 +++++ arch/arm/include/asm/opcodes.h | 229 ++++++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 8 ++ drivers/Kconfig | 2 + drivers/Makefile | 5 +- drivers/firmware/Kconfig | 6 + drivers/firmware/Makefile | 2 + drivers/firmware/firmware-uclass.c | 11 ++ drivers/firmware/psci.c | 94 +++++++++++++++ drivers/sysreset/Kconfig | 11 ++ drivers/sysreset/Makefile | 1 + drivers/sysreset/sysreset_psci.c | 41 +++++++ include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 126 ++++++++++++++++++++ include/linux/psci.h | 13 ++ 21 files changed, 704 insertions(+), 2 deletions(-) create mode 100644 arch/arm/cpu/armv7/smccc-call.S create mode 100644 arch/arm/cpu/armv8/smccc-call.S create mode 100644 arch/arm/include/asm/opcodes-sec.h create mode 100644 arch/arm/include/asm/opcodes-virt.h create mode 100644 arch/arm/include/asm/opcodes.h create mode 100644 drivers/firmware/Kconfig create mode 100644 drivers/firmware/Makefile create mode 100644 drivers/firmware/firmware-uclass.c create mode 100644 drivers/firmware/psci.c create mode 100644 drivers/sysreset/sysreset_psci.c create mode 100644 include/linux/arm-smccc.h

This imports ARM SMC Calling Convention code from Linux 4.11-rc6. The files have been copied as follows:
[Linux] [U-Boot] arch/arm/kernel/smccc-call.S -> arch/arm/cpu/armv7/smccc-call.S arch/arm64/kernel/smccc-call.S -> arch/arm/cpu/armv8/smccc-call.S arch/arm/include/asm/opcodes* -> arch/arm/include/asm/opcodes* include/linux/arm-smccc.h -> include/linux/arm-smccc.h
They needed to be adjusted for U-Boot: - Replace the license block with SPDX - Adjust path to asm-offsets.h - define UNWIND() as no-op
Signed-off-by: Masahiro Yamada yamada.masahiro@socionext.com ---
arch/arm/Kconfig | 8 ++ arch/arm/cpu/armv7/Makefile | 1 + arch/arm/cpu/armv7/smccc-call.S | 56 +++++++++ arch/arm/cpu/armv8/Makefile | 2 + arch/arm/cpu/armv8/smccc-call.S | 44 +++++++ arch/arm/include/asm/opcodes-sec.h | 17 +++ arch/arm/include/asm/opcodes-virt.h | 27 +++++ arch/arm/include/asm/opcodes.h | 229 ++++++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 8 ++ include/linux/arm-smccc.h | 126 ++++++++++++++++++++ 10 files changed, 518 insertions(+) create mode 100644 arch/arm/cpu/armv7/smccc-call.S create mode 100644 arch/arm/cpu/armv8/smccc-call.S create mode 100644 arch/arm/include/asm/opcodes-sec.h create mode 100644 arch/arm/include/asm/opcodes-virt.h create mode 100644 arch/arm/include/asm/opcodes.h create mode 100644 include/linux/arm-smccc.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 7b20750..84744ef 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -174,6 +174,14 @@ config SYS_CACHELINE_SIZE default 64 if SYS_CACHE_SHIFT_6 default 32 if SYS_CACHE_SHIFT_5
+config ARM_SMCCC + bool "Support for ARM SMC Calling Convention (SMCCC)" + depends on CPU_V7 || ARM64 + help + Say Y here if you want to enable ARM SMC Calling Convention. + This should be enabled if U-Boot needs to communicate with system + firmware (for example, PSCI) according to SMCCC. + config SEMIHOSTING bool "support boot from semihosting" help diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 02e8778..3a9913a 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -18,6 +18,7 @@ obj-y += lowlevel_init.o endif endif
+obj-$(CONFIG_ARM_SMCCC) += smccc-call.o obj-$(CONFIG_ARMV7_NONSEC) += nonsec_virt.o virt-v7.o virt-dt.o obj-$(CONFIG_ARMV7_PSCI) += psci.o psci-common.o
diff --git a/arch/arm/cpu/armv7/smccc-call.S b/arch/arm/cpu/armv7/smccc-call.S new file mode 100644 index 0000000..c2fdbad --- /dev/null +++ b/arch/arm/cpu/armv7/smccc-call.S @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * SPDX-License-Identifier: GPL-2.0 + */ +#include <linux/linkage.h> + +#include <asm/opcodes-sec.h> +#include <asm/opcodes-virt.h> + +#define UNWIND(x...) + /* + * Wrap c macros in asm macros to delay expansion until after the + * SMCCC asm macro is expanded. + */ + .macro SMCCC_SMC + __SMC(0) + .endm + + .macro SMCCC_HVC + __HVC(0) + .endm + + .macro SMCCC instr +UNWIND( .fnstart) + mov r12, sp + push {r4-r7} +UNWIND( .save {r4-r7}) + ldm r12, {r4-r7} + \instr + pop {r4-r7} + ldr r12, [sp, #(4 * 4)] + stm r12, {r0-r3} + bx lr +UNWIND( .fnend) + .endm + +/* + * void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_smc) + SMCCC SMCCC_SMC +ENDPROC(__arm_smccc_smc) + +/* + * void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_hvc) + SMCCC SMCCC_HVC +ENDPROC(__arm_smccc_hvc) diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile index 65915ee..c447085 100644 --- a/arch/arm/cpu/armv8/Makefile +++ b/arch/arm/cpu/armv8/Makefile @@ -16,6 +16,8 @@ obj-y += tlb.o obj-y += transition.o obj-y += fwcall.o obj-y += cpu-dt.o +obj-$(CONFIG_ARM_SMCCC) += smccc-call.o + ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ARMV8_SPIN_TABLE) += spin_table.o spin_table_v8.o endif diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S new file mode 100644 index 0000000..bbb6cba --- /dev/null +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * SPDX-License-Identifier: GPL-2.0 + */ +#include <linux/linkage.h> +#include <linux/arm-smccc.h> +#include <generated/asm-offsets.h> + + .macro SMCCC instr + .cfi_startproc + \instr #0 + ldr x4, [sp] + stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS] + stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS] + ldr x4, [sp, #8] + cbz x4, 1f /* no quirk structure */ + ldr x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS] + cmp x9, #ARM_SMCCC_QUIRK_QCOM_A6 + b.ne 1f + str x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS] +1: ret + .cfi_endproc + .endm + +/* + * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_smc) + SMCCC smc +ENDPROC(__arm_smccc_smc) + +/* + * void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_hvc) + SMCCC hvc +ENDPROC(__arm_smccc_hvc) diff --git a/arch/arm/include/asm/opcodes-sec.h b/arch/arm/include/asm/opcodes-sec.h new file mode 100644 index 0000000..16dee8f --- /dev/null +++ b/arch/arm/include/asm/opcodes-sec.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2012 ARM Limited + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __ASM_ARM_OPCODES_SEC_H +#define __ASM_ARM_OPCODES_SEC_H + +#include <asm/opcodes.h> + +#define __SMC(imm4) __inst_arm_thumb32( \ + 0xE1600070 | (((imm4) & 0xF) << 0), \ + 0xF7F08000 | (((imm4) & 0xF) << 16) \ +) + +#endif /* __ASM_ARM_OPCODES_SEC_H */ diff --git a/arch/arm/include/asm/opcodes-virt.h b/arch/arm/include/asm/opcodes-virt.h new file mode 100644 index 0000000..923f257 --- /dev/null +++ b/arch/arm/include/asm/opcodes-virt.h @@ -0,0 +1,27 @@ +/* + * opcodes-virt.h: Opcode definitions for the ARM virtualization extensions + * Copyright (C) 2012 Linaro Limited + * + * SPDX-License-Identifier: GPL-2.0 + */ +#ifndef __ASM_ARM_OPCODES_VIRT_H +#define __ASM_ARM_OPCODES_VIRT_H + +#include <asm/opcodes.h> + +#define __HVC(imm16) __inst_arm_thumb32( \ + 0xE1400070 | (((imm16) & 0xFFF0) << 4) | ((imm16) & 0x000F), \ + 0xF7E08000 | (((imm16) & 0xF000) << 4) | ((imm16) & 0x0FFF) \ +) + +#define __ERET __inst_arm_thumb32( \ + 0xE160006E, \ + 0xF3DE8F00 \ +) + +#define __MSR_ELR_HYP(regnum) __inst_arm_thumb32( \ + 0xE12EF300 | regnum, \ + 0xF3808E30 | (regnum << 16) \ +) + +#endif /* ! __ASM_ARM_OPCODES_VIRT_H */ diff --git a/arch/arm/include/asm/opcodes.h b/arch/arm/include/asm/opcodes.h new file mode 100644 index 0000000..199f0ba --- /dev/null +++ b/arch/arm/include/asm/opcodes.h @@ -0,0 +1,229 @@ +/* + * arch/arm/include/asm/opcodes.h + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __ASM_ARM_OPCODES_H +#define __ASM_ARM_OPCODES_H + +#ifndef __ASSEMBLY__ +#include <linux/linkage.h> +extern asmlinkage unsigned int arm_check_condition(u32 opcode, u32 psr); +#endif + +#define ARM_OPCODE_CONDTEST_FAIL 0 +#define ARM_OPCODE_CONDTEST_PASS 1 +#define ARM_OPCODE_CONDTEST_UNCOND 2 + + +/* + * Assembler opcode byteswap helpers. + * These are only intended for use by this header: don't use them directly, + * because they will be suboptimal in most cases. + */ +#define ___asm_opcode_swab32(x) ( \ + (((x) << 24) & 0xFF000000) \ + | (((x) << 8) & 0x00FF0000) \ + | (((x) >> 8) & 0x0000FF00) \ + | (((x) >> 24) & 0x000000FF) \ +) +#define ___asm_opcode_swab16(x) ( \ + (((x) << 8) & 0xFF00) \ + | (((x) >> 8) & 0x00FF) \ +) +#define ___asm_opcode_swahb32(x) ( \ + (((x) << 8) & 0xFF00FF00) \ + | (((x) >> 8) & 0x00FF00FF) \ +) +#define ___asm_opcode_swahw32(x) ( \ + (((x) << 16) & 0xFFFF0000) \ + | (((x) >> 16) & 0x0000FFFF) \ +) +#define ___asm_opcode_identity32(x) ((x) & 0xFFFFFFFF) +#define ___asm_opcode_identity16(x) ((x) & 0xFFFF) + + +/* + * Opcode byteswap helpers + * + * These macros help with converting instructions between a canonical integer + * format and in-memory representation, in an endianness-agnostic manner. + * + * __mem_to_opcode_*() convert from in-memory representation to canonical form. + * __opcode_to_mem_*() convert from canonical form to in-memory representation. + * + * + * Canonical instruction representation: + * + * ARM: 0xKKLLMMNN + * Thumb 16-bit: 0x0000KKLL, where KK < 0xE8 + * Thumb 32-bit: 0xKKLLMMNN, where KK >= 0xE8 + * + * There is no way to distinguish an ARM instruction in canonical representation + * from a Thumb instruction (just as these cannot be distinguished in memory). + * Where this distinction is important, it needs to be tracked separately. + * + * Note that values in the range 0x0000E800..0xE7FFFFFF intentionally do not + * represent any valid Thumb-2 instruction. For this range, + * __opcode_is_thumb32() and __opcode_is_thumb16() will both be false. + * + * The ___asm variants are intended only for use by this header, in situations + * involving inline assembler. For .S files, the normal __opcode_*() macros + * should do the right thing. + */ +#ifdef __ASSEMBLY__ + +#define ___opcode_swab32(x) ___asm_opcode_swab32(x) +#define ___opcode_swab16(x) ___asm_opcode_swab16(x) +#define ___opcode_swahb32(x) ___asm_opcode_swahb32(x) +#define ___opcode_swahw32(x) ___asm_opcode_swahw32(x) +#define ___opcode_identity32(x) ___asm_opcode_identity32(x) +#define ___opcode_identity16(x) ___asm_opcode_identity16(x) + +#else /* ! __ASSEMBLY__ */ + +#include <linux/types.h> +#include <linux/swab.h> + +#define ___opcode_swab32(x) swab32(x) +#define ___opcode_swab16(x) swab16(x) +#define ___opcode_swahb32(x) swahb32(x) +#define ___opcode_swahw32(x) swahw32(x) +#define ___opcode_identity32(x) ((u32)(x)) +#define ___opcode_identity16(x) ((u16)(x)) + +#endif /* ! __ASSEMBLY__ */ + + +#ifdef CONFIG_CPU_ENDIAN_BE8 + +#define __opcode_to_mem_arm(x) ___opcode_swab32(x) +#define __opcode_to_mem_thumb16(x) ___opcode_swab16(x) +#define __opcode_to_mem_thumb32(x) ___opcode_swahb32(x) +#define ___asm_opcode_to_mem_arm(x) ___asm_opcode_swab32(x) +#define ___asm_opcode_to_mem_thumb16(x) ___asm_opcode_swab16(x) +#define ___asm_opcode_to_mem_thumb32(x) ___asm_opcode_swahb32(x) + +#else /* ! CONFIG_CPU_ENDIAN_BE8 */ + +#define __opcode_to_mem_arm(x) ___opcode_identity32(x) +#define __opcode_to_mem_thumb16(x) ___opcode_identity16(x) +#define ___asm_opcode_to_mem_arm(x) ___asm_opcode_identity32(x) +#define ___asm_opcode_to_mem_thumb16(x) ___asm_opcode_identity16(x) +#ifndef CONFIG_CPU_ENDIAN_BE32 +/* + * On BE32 systems, using 32-bit accesses to store Thumb instructions will not + * work in all cases, due to alignment constraints. For now, a correct + * version is not provided for BE32. + */ +#define __opcode_to_mem_thumb32(x) ___opcode_swahw32(x) +#define ___asm_opcode_to_mem_thumb32(x) ___asm_opcode_swahw32(x) +#endif + +#endif /* ! CONFIG_CPU_ENDIAN_BE8 */ + +#define __mem_to_opcode_arm(x) __opcode_to_mem_arm(x) +#define __mem_to_opcode_thumb16(x) __opcode_to_mem_thumb16(x) +#ifndef CONFIG_CPU_ENDIAN_BE32 +#define __mem_to_opcode_thumb32(x) __opcode_to_mem_thumb32(x) +#endif + +/* Operations specific to Thumb opcodes */ + +/* Instruction size checks: */ +#define __opcode_is_thumb32(x) ( \ + ((x) & 0xF8000000) == 0xE8000000 \ + || ((x) & 0xF0000000) == 0xF0000000 \ +) +#define __opcode_is_thumb16(x) ( \ + ((x) & 0xFFFF0000) == 0 \ + && !(((x) & 0xF800) == 0xE800 || ((x) & 0xF000) == 0xF000) \ +) + +/* Operations to construct or split 32-bit Thumb instructions: */ +#define __opcode_thumb32_first(x) (___opcode_identity16((x) >> 16)) +#define __opcode_thumb32_second(x) (___opcode_identity16(x)) +#define __opcode_thumb32_compose(first, second) ( \ + (___opcode_identity32(___opcode_identity16(first)) << 16) \ + | ___opcode_identity32(___opcode_identity16(second)) \ +) +#define ___asm_opcode_thumb32_first(x) (___asm_opcode_identity16((x) >> 16)) +#define ___asm_opcode_thumb32_second(x) (___asm_opcode_identity16(x)) +#define ___asm_opcode_thumb32_compose(first, second) ( \ + (___asm_opcode_identity32(___asm_opcode_identity16(first)) << 16) \ + | ___asm_opcode_identity32(___asm_opcode_identity16(second)) \ +) + +/* + * Opcode injection helpers + * + * In rare cases it is necessary to assemble an opcode which the + * assembler does not support directly, or which would normally be + * rejected because of the CFLAGS or AFLAGS used to build the affected + * file. + * + * Before using these macros, consider carefully whether it is feasible + * instead to change the build flags for your file, or whether it really + * makes sense to support old assembler versions when building that + * particular kernel feature. + * + * The macros defined here should only be used where there is no viable + * alternative. + * + * + * __inst_arm(x): emit the specified ARM opcode + * __inst_thumb16(x): emit the specified 16-bit Thumb opcode + * __inst_thumb32(x): emit the specified 32-bit Thumb opcode + * + * __inst_arm_thumb16(arm, thumb): emit either the specified arm or + * 16-bit Thumb opcode, depending on whether an ARM or Thumb-2 + * kernel is being built + * + * __inst_arm_thumb32(arm, thumb): emit either the specified arm or + * 32-bit Thumb opcode, depending on whether an ARM or Thumb-2 + * kernel is being built + * + * + * Note that using these macros directly is poor practice. Instead, you + * should use them to define human-readable wrapper macros to encode the + * instructions that you care about. In code which might run on ARMv7 or + * above, you can usually use the __inst_arm_thumb{16,32} macros to + * specify the ARM and Thumb alternatives at the same time. This ensures + * that the correct opcode gets emitted depending on the instruction set + * used for the kernel build. + * + * Look at opcodes-virt.h for an example of how to use these macros. + */ +#include <linux/stringify.h> + +#define __inst_arm(x) ___inst_arm(___asm_opcode_to_mem_arm(x)) +#define __inst_thumb32(x) ___inst_thumb32( \ + ___asm_opcode_to_mem_thumb16(___asm_opcode_thumb32_first(x)), \ + ___asm_opcode_to_mem_thumb16(___asm_opcode_thumb32_second(x)) \ +) +#define __inst_thumb16(x) ___inst_thumb16(___asm_opcode_to_mem_thumb16(x)) + +#ifdef CONFIG_THUMB2_KERNEL +#define __inst_arm_thumb16(arm_opcode, thumb_opcode) \ + __inst_thumb16(thumb_opcode) +#define __inst_arm_thumb32(arm_opcode, thumb_opcode) \ + __inst_thumb32(thumb_opcode) +#else +#define __inst_arm_thumb16(arm_opcode, thumb_opcode) __inst_arm(arm_opcode) +#define __inst_arm_thumb32(arm_opcode, thumb_opcode) __inst_arm(arm_opcode) +#endif + +/* Helpers for the helpers. Don't use these directly. */ +#ifdef __ASSEMBLY__ +#define ___inst_arm(x) .long x +#define ___inst_thumb16(x) .short x +#define ___inst_thumb32(first, second) .short first, second +#else +#define ___inst_arm(x) ".long " __stringify(x) "\n\t" +#define ___inst_thumb16(x) ".short " __stringify(x) "\n\t" +#define ___inst_thumb32(first, second) \ + ".short " __stringify(first) ", " __stringify(second) "\n\t" +#endif + +#endif /* __ASM_ARM_OPCODES_H */ diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index e5bcaea..d620dc0 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -14,6 +14,7 @@
#include <common.h> #include <linux/kbuild.h> +#include <linux/arm-smccc.h>
#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) \ || defined(CONFIG_MX51) || defined(CONFIG_MX53) @@ -198,5 +199,12 @@ int main(void) DEFINE(PLL_DP_HFS_MFN, offsetof(struct dpll, dp_hfs_mfn)); #endif
+#ifdef CONFIG_ARM_SMCCC + DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); + DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); + DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); + DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); +#endif + return 0; } diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h new file mode 100644 index 0000000..28e61ce --- /dev/null +++ b/include/linux/arm-smccc.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015, Linaro Limited + * + * SPDX-License-Identifier: GPL-2.0 + */ +#ifndef __LINUX_ARM_SMCCC_H +#define __LINUX_ARM_SMCCC_H + +/* + * This file provides common defines for ARM SMC Calling Convention as + * specified in + * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + */ + +#define ARM_SMCCC_STD_CALL 0 +#define ARM_SMCCC_FAST_CALL 1 +#define ARM_SMCCC_TYPE_SHIFT 31 + +#define ARM_SMCCC_SMC_32 0 +#define ARM_SMCCC_SMC_64 1 +#define ARM_SMCCC_CALL_CONV_SHIFT 30 + +#define ARM_SMCCC_OWNER_MASK 0x3F +#define ARM_SMCCC_OWNER_SHIFT 24 + +#define ARM_SMCCC_FUNC_MASK 0xFFFF + +#define ARM_SMCCC_IS_FAST_CALL(smc_val) \ + ((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT)) +#define ARM_SMCCC_IS_64(smc_val) \ + ((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT)) +#define ARM_SMCCC_FUNC_NUM(smc_val) ((smc_val) & ARM_SMCCC_FUNC_MASK) +#define ARM_SMCCC_OWNER_NUM(smc_val) \ + (((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK) + +#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \ + (((type) << ARM_SMCCC_TYPE_SHIFT) | \ + ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \ + (((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \ + ((func_num) & ARM_SMCCC_FUNC_MASK)) + +#define ARM_SMCCC_OWNER_ARCH 0 +#define ARM_SMCCC_OWNER_CPU 1 +#define ARM_SMCCC_OWNER_SIP 2 +#define ARM_SMCCC_OWNER_OEM 3 +#define ARM_SMCCC_OWNER_STANDARD 4 +#define ARM_SMCCC_OWNER_TRUSTED_APP 48 +#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49 +#define ARM_SMCCC_OWNER_TRUSTED_OS 50 +#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63 + +#define ARM_SMCCC_QUIRK_NONE 0 +#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */ + +#ifndef __ASSEMBLY__ + +#include <linux/linkage.h> +#include <linux/types.h> +/** + * struct arm_smccc_res - Result from SMC/HVC call + * @a0-a3 result values from registers 0 to 3 + */ +struct arm_smccc_res { + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; +}; + +/** + * struct arm_smccc_quirk - Contains quirk information + * @id: quirk identification + * @state: quirk specific information + * @a6: Qualcomm quirk entry for returning post-smc call contents of a6 + */ +struct arm_smccc_quirk { + int id; + union { + unsigned long a6; + } state; +}; + +/** + * __arm_smccc_smc() - make SMC calls + * @a0-a7: arguments passed in registers 0 to 7 + * @res: result values from registers 0 to 3 + * @quirk: points to an arm_smccc_quirk, or NULL when no quirks are required. + * + * This function is used to make SMC calls following SMC Calling Convention. + * The content of the supplied param are copied to registers 0 to 7 prior + * to the SMC instruction. The return values are updated with the content + * from register 0 to 3 on return from the SMC instruction. An optional + * quirk structure provides vendor specific behavior. + */ +asmlinkage void __arm_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res, struct arm_smccc_quirk *quirk); + +/** + * __arm_smccc_hvc() - make HVC calls + * @a0-a7: arguments passed in registers 0 to 7 + * @res: result values from registers 0 to 3 + * @quirk: points to an arm_smccc_quirk, or NULL when no quirks are required. + * + * This function is used to make HVC calls following SMC Calling + * Convention. The content of the supplied param are copied to registers 0 + * to 7 prior to the HVC instruction. The return values are updated with + * the content from register 0 to 3 on return from the HVC instruction. An + * optional quirk structure provides vendor specific behavior. + */ +asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res, struct arm_smccc_quirk *quirk); + +#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL) + +#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) + +#define arm_smccc_hvc(...) __arm_smccc_hvc(__VA_ARGS__, NULL) + +#define arm_smccc_hvc_quirk(...) __arm_smccc_hvc(__VA_ARGS__) + +#endif /*__ASSEMBLY__*/ +#endif /*__LINUX_ARM_SMCCC_H*/

If the system is running PSCI firmware, the System Reset function (func ID: 0x80000009) is supposed to be handled by PSCI, that is, the SoC/board specific reset implementation should be moved to PSCI. U-Boot should call the PSCI service according to the arm-smccc manner. The arm-smccc is supported on ARMv7 or later. Especially, ARMv8 generation SoCs are likely to run ARM Trusted Firmware. In this case, U-Boot is a non-secure world boot loader, so it should not be able to reset the system directly.
Signed-off-by: Masahiro Yamada yamada.masahiro@socionext.com ---
arch/arm/Kconfig | 1 + drivers/Kconfig | 2 + drivers/Makefile | 5 +- drivers/firmware/Kconfig | 6 +++ drivers/firmware/Makefile | 2 + drivers/firmware/firmware-uclass.c | 11 +++++ drivers/firmware/psci.c | 94 ++++++++++++++++++++++++++++++++++++++ drivers/sysreset/Kconfig | 11 +++++ drivers/sysreset/Makefile | 1 + drivers/sysreset/sysreset_psci.c | 41 +++++++++++++++++ include/dm/uclass-id.h | 1 + include/linux/psci.h | 13 ++++++ 12 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 drivers/firmware/Kconfig create mode 100644 drivers/firmware/Makefile create mode 100644 drivers/firmware/firmware-uclass.c create mode 100644 drivers/firmware/psci.c create mode 100644 drivers/sysreset/sysreset_psci.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 84744ef..64984dc 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -177,6 +177,7 @@ config SYS_CACHELINE_SIZE config ARM_SMCCC bool "Support for ARM SMC Calling Convention (SMCCC)" depends on CPU_V7 || ARM64 + select ARM_PSCI_FW help Say Y here if you want to enable ARM SMC Calling Convention. This should be enabled if U-Boot needs to communicate with system diff --git a/drivers/Kconfig b/drivers/Kconfig index 0e5d97d..d0a36ab 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -22,6 +22,8 @@ source "drivers/dfu/Kconfig"
source "drivers/dma/Kconfig"
+source "drivers/firmware/Kconfig" + source "drivers/fpga/Kconfig"
source "drivers/gpio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 5d8baa5..4a4b237 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,7 +23,7 @@ obj-$(CONFIG_SPL_SERIAL_SUPPORT) += serial/ obj-$(CONFIG_SPL_SPI_SUPPORT) += spi/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/ power/pmic/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/regulator/ -obj-$(CONFIG_SPL_DRIVERS_MISC_SUPPORT) += misc/ sysreset/ +obj-$(CONFIG_SPL_DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/ obj-$(CONFIG_SPL_MTD_SUPPORT) += mtd/ obj-$(CONFIG_SPL_NAND_SUPPORT) += mtd/nand/ obj-$(CONFIG_SPL_ONENAND_SUPPORT) += mtd/onenand/ @@ -52,7 +52,7 @@ endif ifdef CONFIG_TPL_BUILD
obj-$(CONFIG_TPL_I2C_SUPPORT) += i2c/ -obj-$(CONFIG_TPL_DRIVERS_MISC_SUPPORT) += misc/ sysreset/ +obj-$(CONFIG_TPL_DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/ obj-$(CONFIG_TPL_MMC_SUPPORT) += mmc/ obj-$(CONFIG_TPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/ obj-$(CONFIG_TPL_NAND_SUPPORT) += mtd/nand/ @@ -71,6 +71,7 @@ obj-y += block/ obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ +obj-y += firmware/ obj-$(CONFIG_FPGA) += fpga/ obj-y += hwmon/ obj-y += misc/ diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig new file mode 100644 index 0000000..4c32426 --- /dev/null +++ b/drivers/firmware/Kconfig @@ -0,0 +1,6 @@ +config FIRMWARE + bool + +config ARM_PSCI_FW + bool + select FIRMWARE diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile new file mode 100644 index 0000000..b208255 --- /dev/null +++ b/drivers/firmware/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_FIRMWARE) += firmware-uclass.o +obj-$(CONFIG_ARM_PSCI_FW) += psci.o diff --git a/drivers/firmware/firmware-uclass.c b/drivers/firmware/firmware-uclass.c new file mode 100644 index 0000000..01b6a44 --- /dev/null +++ b/drivers/firmware/firmware-uclass.c @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <dm/uclass.h> + +/* Firmware access is platform-dependent. No generic code in uclass */ +UCLASS_DRIVER(firmware) = { + .id = UCLASS_FIRMWARE, + .name = "firmware", +}; diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c new file mode 100644 index 0000000..40fba64 --- /dev/null +++ b/drivers/firmware/psci.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 Masahiro Yamada yamada.masahiro@socionext.com + * + * Based on drivers/firmware/psci.c from Linux: + * Copyright (C) 2015 ARM Limited + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm/device.h> +#include <dm/lists.h> +#include <libfdt.h> +#include <linux/arm-smccc.h> +#include <linux/errno.h> +#include <linux/psci.h> + +psci_fn *invoke_psci_fn; + +static unsigned long __invoke_psci_fn_hvc(unsigned long function_id, + unsigned long arg0, unsigned long arg1, + unsigned long arg2) +{ + struct arm_smccc_res res; + + arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + return res.a0; +} + +static unsigned long __invoke_psci_fn_smc(unsigned long function_id, + unsigned long arg0, unsigned long arg1, + unsigned long arg2) +{ + struct arm_smccc_res res; + + arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + return res.a0; +} + +static int psci_bind(struct udevice *dev) +{ + /* No SYSTEM_RESET support for PSCI 0.1 */ + if (of_device_is_compatible(dev, "arm,psci-0.2") || + of_device_is_compatible(dev, "arm,psci-1.0")) { + int ret; + + /* bind psci-sysreset optionally */ + ret = device_bind_driver(dev, "psci-sysreset", "psci-sysreset", + NULL); + if (ret) + debug("PSCI System Reset was not bound.\n"); + } + + return 0; +} + +static int psci_probe(struct udevice *dev) +{ + DECLARE_GLOBAL_DATA_PTR; + const char *method; + + method = fdt_stringlist_get(gd->fdt_blob, dev->of_offset, "method", 0, + NULL); + if (!method) { + printf("missing "method" property\n"); + return -ENXIO; + } + + if (!strcmp("hvc", method)) { + invoke_psci_fn = __invoke_psci_fn_hvc; + } else if (!strcmp("smc", method)) { + invoke_psci_fn = __invoke_psci_fn_smc; + } else { + printf("invalid "method" property: %s\n", method); + return -EINVAL; + } + + return 0; +} + +static const struct udevice_id psci_of_match[] = { + { .compatible = "arm,psci" }, + { .compatible = "arm,psci-0.2" }, + { .compatible = "arm,psci-1.0" }, + {}, +}; + +U_BOOT_DRIVER(psci) = { + .name = "psci", + .id = UCLASS_FIRMWARE, + .of_match = psci_of_match, + .bind = psci_bind, + .probe = psci_probe, +}; diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 05a37b9..1498d37 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -13,4 +13,15 @@ config SYSRESET to effect a reset. The uclass will try all available drivers when reset_walk() is called.
+if SYSRESET + +config SYSRESET_PSCI + bool "Enable support for PSCI System Reset" + depends on ARM_PSCI_FW + help + Enable PSCI SYSTEM_RESET function call. To use this, PSCI service + (for example, ARM Trusted Firmware BL31) must be running on your + system. + +endif endmenu diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index 49b8bb6..7bb8406 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -5,6 +5,7 @@ #
obj-$(CONFIG_SYSRESET) += sysreset-uclass.o +obj-$(CONFIG_SYSRESET_PSCI) += sysreset_psci.o
ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ROCKCHIP_RK3036) += sysreset_rk3036.o diff --git a/drivers/sysreset/sysreset_psci.c b/drivers/sysreset/sysreset_psci.c new file mode 100644 index 0000000..a4911b7 --- /dev/null +++ b/drivers/sysreset/sysreset_psci.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Masahiro Yamada yamada.masahiro@socionext.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <dm/device.h> +#include <sysreset.h> +#include <linux/errno.h> +#include <linux/psci.h> + +static int psci_sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + unsigned long function_id; + + switch (type) { + case SYSRESET_WARM: + case SYSRESET_COLD: + function_id = PSCI_0_2_FN_SYSTEM_RESET; + break; + case SYSRESET_POWER: + function_id = PSCI_0_2_FN_SYSTEM_OFF; + break; + default: + return -ENOSYS; + } + + invoke_psci_fn(function_id, 0, 0, 0); + + return -EINPROGRESS; +} + +static struct sysreset_ops psci_sysreset_ops = { + .request = psci_sysreset_request, +}; + +U_BOOT_DRIVER(psci_sysreset) = { + .name = "psci-sysreset", + .id = UCLASS_SYSRESET, + .ops = &psci_sysreset_ops, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 8c92d0b..1b635e4 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -35,6 +35,7 @@ enum uclass_id { UCLASS_DMA, /* Direct Memory Access */ UCLASS_ETH, /* Ethernet device */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ + UCLASS_FIRMWARE, /* Firmware */ UCLASS_I2C, /* I2C bus */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ UCLASS_I2C_GENERIC, /* Generic I2C device */ diff --git a/include/linux/psci.h b/include/linux/psci.h index 310d83e..8d13bd2 100644 --- a/include/linux/psci.h +++ b/include/linux/psci.h @@ -87,4 +87,17 @@ #define PSCI_RET_NOT_PRESENT -7 #define PSCI_RET_DISABLED -8
+#ifdef CONFIG_ARM_PSCI_FW +typedef unsigned long (psci_fn)(unsigned long, unsigned long, + unsigned long, unsigned long); + +extern psci_fn *invoke_psci_fn; +#else +unsigned long invoke_psci_fn(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3) +{ + return PSCI_RET_DISABLED; +} +#endif + #endif /* _UAPI_LINUX_PSCI_H */

Hi Masahiro,
For ARMv8, the secure firmware (ex. ARM Trusted Firmware BL31) is often implemented, making U-Boot a non-secure world boot loader.
The secure firmware generally includes PSCI. The power management operations, including System Reset, are supposed to be handled by PSCI. In this case, U-Boot should not touch the system reset directly. Instead, U-Boot should call PSCI service to reset the system.
In order to implement this, arm-smccc code is needed. The code has been copied from Linux. (1/2)
2/2 implements a generic sysreset driver based on the PSCI specification.
I'm just wondering if it would be possible to:
1. Provide the exact Linux files (with SHA1) from main line
2. On top of this patch add u-boot adjustments
3. Add u-boot specific support for PSCI
In this way we would have exact info about the source of porting (or was it ported exactly from v4.11-rc6 tag?)
Best regards, Ćukasz Majewski
Masahiro Yamada (2): ARM: import arm-smccc from Linux 4.11-rc6 sysreset: psci: support system reset in a generic way with PSCI
arch/arm/Kconfig | 9 ++ arch/arm/cpu/armv7/Makefile | 1 + arch/arm/cpu/armv7/smccc-call.S | 56 +++++++++ arch/arm/cpu/armv8/Makefile | 2 + arch/arm/cpu/armv8/smccc-call.S | 44 +++++++ arch/arm/include/asm/opcodes-sec.h | 17 +++ arch/arm/include/asm/opcodes-virt.h | 27 +++++ arch/arm/include/asm/opcodes.h | 229 ++++++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 8 ++ drivers/Kconfig | 2 + drivers/Makefile | 5 +- drivers/firmware/Kconfig | 6 + drivers/firmware/Makefile | 2 + drivers/firmware/firmware-uclass.c | 11 ++ drivers/firmware/psci.c | 94 +++++++++++++++ drivers/sysreset/Kconfig | 11 ++ drivers/sysreset/Makefile | 1 + drivers/sysreset/sysreset_psci.c | 41 +++++++ include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 126 ++++++++++++++++++++ include/linux/psci.h | 13 ++ 21 files changed, 704 insertions(+), 2 deletions(-) create mode 100644 arch/arm/cpu/armv7/smccc-call.S create mode 100644 arch/arm/cpu/armv8/smccc-call.S create mode 100644 arch/arm/include/asm/opcodes-sec.h create mode 100644 arch/arm/include/asm/opcodes-virt.h create mode 100644 arch/arm/include/asm/opcodes.h create mode 100644 drivers/firmware/Kconfig create mode 100644 drivers/firmware/Makefile create mode 100644 drivers/firmware/firmware-uclass.c create mode 100644 drivers/firmware/psci.c create mode 100644 drivers/sysreset/sysreset_psci.c create mode 100644 include/linux/arm-smccc.h
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Hi Lukasz,
2017-04-10 18:16 GMT+09:00 Lukasz Majewski lukma@denx.de:
Hi Masahiro,
For ARMv8, the secure firmware (ex. ARM Trusted Firmware BL31) is often implemented, making U-Boot a non-secure world boot loader.
The secure firmware generally includes PSCI. The power management operations, including System Reset, are supposed to be handled by PSCI. In this case, U-Boot should not touch the system reset directly. Instead, U-Boot should call PSCI service to reset the system.
In order to implement this, arm-smccc code is needed. The code has been copied from Linux. (1/2)
2/2 implements a generic sysreset driver based on the PSCI specification.
I'm just wondering if it would be possible to:
Provide the exact Linux files (with SHA1) from main line
On top of this patch add u-boot adjustments
Add u-boot specific support for PSCI
In this way we would have exact info about the source of porting (or was it ported exactly from v4.11-rc6 tag?)
I am OK with this.
I will wait for a little more comments before sending v2.
Thanks!
participants (2)
-
Lukasz Majewski
-
Masahiro Yamada