diff --git a/Documentation/devicetree/bindings/arm/firmware/tlm,trusted-foundations.txt b/Documentation/devicetree/bindings/arm/firmware/tlm,trusted-foundations.txt new file mode 100644 index 000000000000..780d0392a66b --- /dev/null +++ b/Documentation/devicetree/bindings/arm/firmware/tlm,trusted-foundations.txt @@ -0,0 +1,20 @@ +Trusted Foundations +------------------- + +Boards that use the Trusted Foundations secure monitor can signal its +presence by declaring a node compatible with "tlm,trusted-foundations" +under the /firmware/ node + +Required properties: +- compatible: "tlm,trusted-foundations" +- tlm,version-major: major version number of Trusted Foundations firmware +- tlm,version-minor: minor version number of Trusted Foundations firmware + +Example: + firmware { + trusted-foundations { + compatible = "tlm,trusted-foundations"; + tlm,version-major = <2>; + tlm,version-minor = <8>; + }; + }; diff --git a/Documentation/devicetree/bindings/arm/tegra.txt b/Documentation/devicetree/bindings/arm/tegra.txt index ed9c85334436..558ed4b4ef39 100644 --- a/Documentation/devicetree/bindings/arm/tegra.txt +++ b/Documentation/devicetree/bindings/arm/tegra.txt @@ -32,3 +32,8 @@ board-specific compatible values: nvidia,whistler toradex,colibri_t20-512 toradex,iris + +Trusted Foundations +------------------------------------------- +Tegra supports the Trusted Foundation secure monitor. See the +"tlm,trusted-foundations" binding's documentation for more details. diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ce95ed1c6d3e..30261c831097 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -71,6 +71,7 @@ st STMicroelectronics ste ST-Ericsson stericsson ST-Ericsson ti Texas Instruments +tlm Trusted Logic Mobility toshiba Toshiba Corporation toumaz Toumaz v3 V3 Semiconductor diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ba0e23234ecf..ab8f2eb731bb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1054,6 +1054,8 @@ config ARM_TIMER_SP804 select CLKSRC_MMIO select CLKSRC_OF if OF +source "arch/arm/firmware/Kconfig" + source arch/arm/mm/Kconfig config ARM_NR_BANKS diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c99b1086d83d..2cb05d4e4230 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -268,6 +268,7 @@ core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ core-y += arch/arm/net/ core-y += arch/arm/crypto/ +core-y += arch/arm/firmware/ core-y += $(machdirs) $(platdirs) drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig index 4934295bb4f0..da753e31c850 100644 --- a/arch/arm/configs/tegra_defconfig +++ b/arch/arm/configs/tegra_defconfig @@ -33,6 +33,7 @@ CONFIG_PCI=y CONFIG_PCI_MSI=y CONFIG_PCI_TEGRA=y CONFIG_PCIEPORTBUS=y +CONFIG_TRUSTED_FOUNDATIONS=y CONFIG_SMP=y CONFIG_PREEMPT=y CONFIG_AEABI=y diff --git a/arch/arm/firmware/Kconfig b/arch/arm/firmware/Kconfig new file mode 100644 index 000000000000..bb00ccf00d66 --- /dev/null +++ b/arch/arm/firmware/Kconfig @@ -0,0 +1,28 @@ +config ARCH_SUPPORTS_FIRMWARE + bool + +config ARCH_SUPPORTS_TRUSTED_FOUNDATIONS + bool + select ARCH_SUPPORTS_FIRMWARE + +menu "Firmware options" + depends on ARCH_SUPPORTS_FIRMWARE + +config TRUSTED_FOUNDATIONS + bool "Trusted Foundations secure monitor support" + depends on ARCH_SUPPORTS_TRUSTED_FOUNDATIONS + help + Some devices (including most Tegra-based consumer devices on the + market) are booted with the Trusted Foundations secure monitor + active, requiring some core operations to be performed by the secure + monitor instead of the kernel. + + This option allows the kernel to invoke the secure monitor whenever + required on devices using Trusted Foundations. See + arch/arm/include/asm/trusted_foundations.h or the + tl,trusted-foundations device tree binding documentation for details + on how to use it. + + Say n if you don't know what this is about. + +endmenu diff --git a/arch/arm/firmware/Makefile b/arch/arm/firmware/Makefile new file mode 100644 index 000000000000..a71f16536b6c --- /dev/null +++ b/arch/arm/firmware/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o diff --git a/arch/arm/firmware/trusted_foundations.c b/arch/arm/firmware/trusted_foundations.c new file mode 100644 index 000000000000..ef1e3d8f4af0 --- /dev/null +++ b/arch/arm/firmware/trusted_foundations.c @@ -0,0 +1,81 @@ +/* + * Trusted Foundations support for ARM CPUs + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#define TF_SET_CPU_BOOT_ADDR_SMC 0xfffff200 + +static void __naked tf_generic_smc(u32 type, u32 arg1, u32 arg2) +{ + asm volatile( + ".arch_extension sec\n\t" + "stmfd sp!, {r4 - r11, lr}\n\t" + __asmeq("%0", "r0") + __asmeq("%1", "r1") + __asmeq("%2", "r2") + "mov r3, #0\n\t" + "mov r4, #0\n\t" + "smc #0\n\t" + "ldmfd sp!, {r4 - r11, pc}" + : + : "r" (type), "r" (arg1), "r" (arg2) + : "memory"); +} + +static int tf_set_cpu_boot_addr(int cpu, unsigned long boot_addr) +{ + tf_generic_smc(TF_SET_CPU_BOOT_ADDR_SMC, boot_addr, 0); + + return 0; +} + +static const struct firmware_ops trusted_foundations_ops = { + .set_cpu_boot_addr = tf_set_cpu_boot_addr, +}; + +void register_trusted_foundations(struct trusted_foundations_platform_data *pd) +{ + /* + * we are not using version information for now since currently + * supported SMCs are compatible with all TF releases + */ + register_firmware_ops(&trusted_foundations_ops); +} + +void of_register_trusted_foundations(void) +{ + struct device_node *node; + struct trusted_foundations_platform_data pdata; + int err; + + node = of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations"); + if (!node) + return; + + err = of_property_read_u32(node, "tlm,version-major", + &pdata.version_major); + if (err != 0) + panic("Trusted Foundation: missing version-major property\n"); + err = of_property_read_u32(node, "tlm,version-minor", + &pdata.version_minor); + if (err != 0) + panic("Trusted Foundation: missing version-minor property\n"); + register_trusted_foundations(&pdata); +} diff --git a/arch/arm/include/asm/trusted_foundations.h b/arch/arm/include/asm/trusted_foundations.h new file mode 100644 index 000000000000..3bd36e2c5f2e --- /dev/null +++ b/arch/arm/include/asm/trusted_foundations.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +/* + * Support for the Trusted Foundations secure monitor. + * + * Trusted Foundation comes active on some ARM consumer devices (most + * Tegra-based devices sold on the market are concerned). Such devices can only + * perform some basic operations, like setting the CPU reset vector, through + * SMC calls to the secure monitor. The calls are completely specific to + * Trusted Foundations, and do *not* follow the SMC calling convention or the + * PSCI standard. + */ + +#ifndef __ASM_ARM_TRUSTED_FOUNDATIONS_H +#define __ASM_ARM_TRUSTED_FOUNDATIONS_H + +#include +#include +#include +#include + +struct trusted_foundations_platform_data { + unsigned int version_major; + unsigned int version_minor; +}; + +#if IS_ENABLED(CONFIG_TRUSTED_FOUNDATIONS) + +void register_trusted_foundations(struct trusted_foundations_platform_data *pd); +void of_register_trusted_foundations(void); + +#else /* CONFIG_TRUSTED_FOUNDATIONS */ + +static inline void register_trusted_foundations( + struct trusted_foundations_platform_data *pd) +{ + /* + * If we try to register TF, this means the system needs it to continue. + * Its absence if thus a fatal error. + */ + panic("No support for Trusted Foundations, stopping...\n"); +} + +static inline void of_register_trusted_foundations(void) +{ + /* + * If we find the target should enable TF but does not support it, + * fail as the system won't be able to do much anyway + */ + if (of_find_compatible_node(NULL, NULL, "tl,trusted-foundations")) + register_trusted_foundations(NULL); +} +#endif /* CONFIG_TRUSTED_FOUNDATIONS */ + +#endif diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 09e740f58b27..00b85fd9285d 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -2,6 +2,7 @@ config ARCH_TEGRA bool "NVIDIA Tegra" if ARCH_MULTI_V7 select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB + select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS select ARM_GIC select CLKSRC_MMIO select CLKSRC_OF diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c index 568f5bbf979d..146fe8e0ae7c 100644 --- a/arch/arm/mach-tegra/reset.c +++ b/arch/arm/mach-tegra/reset.c @@ -21,6 +21,7 @@ #include #include +#include #include "iomap.h" #include "irammap.h" @@ -33,26 +34,18 @@ static bool is_enabled; -static void __init tegra_cpu_reset_handler_enable(void) +static void __init tegra_cpu_reset_handler_set(const u32 reset_address) { - void __iomem *iram_base = IO_ADDRESS(TEGRA_IRAM_RESET_BASE); void __iomem *evp_cpu_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE + 0x100); void __iomem *sb_ctrl = IO_ADDRESS(TEGRA_SB_BASE); u32 reg; - BUG_ON(is_enabled); - BUG_ON(tegra_cpu_reset_handler_size > TEGRA_IRAM_RESET_HANDLER_SIZE); - - memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start, - tegra_cpu_reset_handler_size); - /* * NOTE: This must be the one and only write to the EVP CPU reset * vector in the entire system. */ - writel(TEGRA_IRAM_RESET_BASE + tegra_cpu_reset_handler_offset, - evp_cpu_reset); + writel(reset_address, evp_cpu_reset); wmb(); reg = readl(evp_cpu_reset); @@ -66,8 +59,33 @@ static void __init tegra_cpu_reset_handler_enable(void) writel(reg, sb_ctrl); wmb(); } +} - is_enabled = true; +static void __init tegra_cpu_reset_handler_enable(void) +{ + void __iomem *iram_base = IO_ADDRESS(TEGRA_IRAM_RESET_BASE); + const u32 reset_address = TEGRA_IRAM_RESET_BASE + + tegra_cpu_reset_handler_offset; + int err; + + BUG_ON(is_enabled); + BUG_ON(tegra_cpu_reset_handler_size > TEGRA_IRAM_RESET_HANDLER_SIZE); + + memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start, + tegra_cpu_reset_handler_size); + + err = call_firmware_op(set_cpu_boot_addr, 0, reset_address); + switch (err) { + case -ENOSYS: + tegra_cpu_reset_handler_set(reset_address); + /* pass-through */ + case 0: + is_enabled = true; + break; + default: + pr_crit("Cannot set CPU reset handler: %d\n", err); + BUG(); + } } void __init tegra_cpu_reset_handler_init(void) diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 73368176c6e8..09a1f8d98ca2 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "apbio.h" #include "board.h" @@ -90,6 +91,7 @@ static void __init tegra_init_cache(void) static void __init tegra_init_early(void) { + of_register_trusted_foundations(); tegra_apb_io_init(); tegra_init_fuse(); tegra_cpu_reset_handler_init();