diff options
-rw-r--r-- | arch/arm/cpu/armv8/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/sec_firmware.c | 270 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/sec_firmware_asm.S | 53 | ||||
-rw-r--r-- | arch/arm/include/asm/armv8/sec_firmware.h | 18 |
4 files changed, 342 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile index bf8644ccd2..ee9e0091a4 100644 --- a/arch/arm/cpu/armv8/Makefile +++ b/arch/arm/cpu/armv8/Makefile @@ -15,6 +15,7 @@ obj-y += cache.o obj-y += tlb.o obj-y += transition.o obj-y += fwcall.o +obj-$(CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT) += sec_firmware.o sec_firmware_asm.o obj-$(CONFIG_FSL_LAYERSCAPE) += fsl-layerscape/ obj-$(CONFIG_S32V234) += s32v234/ diff --git a/arch/arm/cpu/armv8/sec_firmware.c b/arch/arm/cpu/armv8/sec_firmware.c new file mode 100644 index 0000000000..e21e199381 --- /dev/null +++ b/arch/arm/cpu/armv8/sec_firmware.c @@ -0,0 +1,270 @@ +/* + * Copyright 2016 NXP Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/types.h> +#include <asm/macro.h> +#include <asm/armv8/sec_firmware.h> + +DECLARE_GLOBAL_DATA_PTR; +extern void c_runtime_cpu_setup(void); + +#define SEC_FIRMWARE_LOADED 0x1 +#define SEC_FIRMWARE_RUNNING 0x2 +#define SEC_FIRMWARE_ADDR_MASK (~0x3) + /* + * Secure firmware load addr + * Flags used: 0x1 secure firmware has been loaded to secure memory + * 0x2 secure firmware is running + */ + phys_addr_t sec_firmware_addr; + +static int sec_firmware_get_data(const void *sec_firmware_img, + const void **data, size_t *size) +{ + int conf_node_off, fw_node_off; + char *conf_node_name = NULL; + char *desc; + int ret; + + conf_node_name = SEC_FIRMEWARE_FIT_CNF_NAME; + + conf_node_off = fit_conf_get_node(sec_firmware_img, conf_node_name); + if (conf_node_off < 0) { + printf("SEC Firmware: %s: no such config\n", conf_node_name); + return -ENOENT; + } + + fw_node_off = fit_conf_get_prop_node(sec_firmware_img, conf_node_off, + SEC_FIRMWARE_FIT_IMAGE); + if (fw_node_off < 0) { + printf("SEC Firmware: No '%s' in config\n", + SEC_FIRMWARE_FIT_IMAGE); + return -ENOLINK; + } + + /* Verify secure firmware image */ + if (!(fit_image_verify(sec_firmware_img, fw_node_off))) { + printf("SEC Firmware: Bad firmware image (bad CRC)\n"); + return -EINVAL; + } + + if (fit_image_get_data(sec_firmware_img, fw_node_off, data, size)) { + printf("SEC Firmware: Can't get %s subimage data/size", + SEC_FIRMWARE_FIT_IMAGE); + return -ENOENT; + } + + ret = fit_get_desc(sec_firmware_img, fw_node_off, &desc); + if (ret) + printf("SEC Firmware: Can't get description\n"); + else + printf("%s\n", desc); + + return ret; +} + +/* + * SEC Firmware FIT image parser checks if the image is in FIT + * format, verifies integrity of the image and calculates raw + * image address and size values. + * + * Returns 0 on success and a negative errno on error task fail. + */ +static int sec_firmware_parse_image(const void *sec_firmware_img, + const void **raw_image_addr, + size_t *raw_image_size) +{ + int ret; + + ret = sec_firmware_get_data(sec_firmware_img, raw_image_addr, + raw_image_size); + if (ret) + return ret; + + debug("SEC Firmware: raw_image_addr = 0x%p, raw_image_size = 0x%lx\n", + *raw_image_addr, *raw_image_size); + + return 0; +} + +static int sec_firmware_copy_image(const char *title, + u64 image_addr, u32 image_size, u64 sec_firmware) +{ + debug("%s copied to address 0x%p\n", title, (void *)sec_firmware); + memcpy((void *)sec_firmware, (void *)image_addr, image_size); + flush_dcache_range(sec_firmware, sec_firmware + image_size); + + return 0; +} + +/* + * This function will parse the SEC Firmware image, and then load it + * to secure memory. + */ +static int sec_firmware_load_image(const void *sec_firmware_img) +{ + const void *raw_image_addr; + size_t raw_image_size = 0; + int ret; + + /* + * The Excetpion Level must be EL3 to load and initialize + * the SEC Firmware. + */ + if (current_el() != 3) { + ret = -EACCES; + goto out; + } + +#ifdef CONFIG_SYS_MEM_RESERVE_SECURE + /* + * The SEC Firmware must be stored in secure memory. + * Append SEC Firmware to secure mmu table. + */ + if (!(gd->arch.secure_ram & MEM_RESERVE_SECURE_MAINTAINED)) { + ret = -ENXIO; + goto out; + } + + sec_firmware_addr = (gd->arch.secure_ram & MEM_RESERVE_SECURE_ADDR_MASK) + + gd->arch.tlb_size; +#else +#error "The CONFIG_SYS_MEM_RESERVE_SECURE must be defined when enabled SEC Firmware support" +#endif + + /* Align SEC Firmware base address to 4K */ + sec_firmware_addr = (sec_firmware_addr + 0xfff) & ~0xfff; + debug("SEC Firmware: Load address: 0x%llx\n", + sec_firmware_addr & SEC_FIRMWARE_ADDR_MASK); + + ret = sec_firmware_parse_image(sec_firmware_img, &raw_image_addr, + &raw_image_size); + if (ret) + goto out; + + /* TODO: + * Check if the end addr of SEC Firmware has been extend the secure + * memory. + */ + + /* Copy the secure firmware to secure memory */ + ret = sec_firmware_copy_image("SEC Firmware", (u64)raw_image_addr, + raw_image_size, sec_firmware_addr & + SEC_FIRMWARE_ADDR_MASK); + if (ret) + goto out; + + sec_firmware_addr |= SEC_FIRMWARE_LOADED; + debug("SEC Firmware: Entry point: 0x%llx\n", + sec_firmware_addr & SEC_FIRMWARE_ADDR_MASK); + + return 0; + +out: + printf("SEC Firmware: error (%d)\n", ret); + sec_firmware_addr = 0; + + return ret; +} + +static int sec_firmware_entry(u32 *eret_hold_l, u32 *eret_hold_h) +{ + const void *entry = (void *)(sec_firmware_addr & + SEC_FIRMWARE_ADDR_MASK); + + return _sec_firmware_entry(entry, eret_hold_l, eret_hold_h); +} + +/* Check the secure firmware FIT image */ +__weak bool sec_firmware_is_valid(const void *sec_firmware_img) +{ + if (fdt_check_header(sec_firmware_img)) { + printf("SEC Firmware: Bad firmware image (not a FIT image)\n"); + return false; + } + + if (!fit_check_format(sec_firmware_img)) { + printf("SEC Firmware: Bad firmware image (bad FIT header)\n"); + return false; + } + + return true; +} + +#ifdef CONFIG_ARMV8_PSCI +/* + * The PSCI_VERSION function is added from PSCI v0.2. When the PSCI + * v0.1 received this function, the NOT_SUPPORTED (0xffff_ffff) error + * number will be returned according to SMC Calling Conventions. But + * when getting the NOT_SUPPORTED error number, we cannot ensure if + * the PSCI version is v0.1 or other error occurred. So, PSCI v0.1 + * won't be supported by this framework. + * And if the secure firmware isn't running, return NOT_SUPPORTED. + * + * The return value on success is PSCI version in format + * major[31:16]:minor[15:0]. + */ +unsigned int sec_firmware_support_psci_version(void) +{ + if (sec_firmware_addr & SEC_FIRMWARE_RUNNING) + return _sec_firmware_support_psci_version(); + + return 0xffffffff; +} +#endif + +/* + * sec_firmware_init - Initialize the SEC Firmware + * @sec_firmware_img: the SEC Firmware image address + * @eret_hold_l: the address to hold exception return address low + * @eret_hold_h: the address to hold exception return address high + */ +int sec_firmware_init(const void *sec_firmware_img, + u32 *eret_hold_l, + u32 *eret_hold_h) +{ + int ret; + + if (!sec_firmware_is_valid(sec_firmware_img)) + return -EINVAL; + + ret = sec_firmware_load_image(sec_firmware_img); + if (ret) { + printf("SEC Firmware: Failed to load image\n"); + return ret; + } else if (sec_firmware_addr & SEC_FIRMWARE_LOADED) { + ret = sec_firmware_entry(eret_hold_l, eret_hold_h); + if (ret) { + printf("SEC Firmware: Failed to initialize\n"); + return ret; + } + } + + debug("SEC Firmware: Return from SEC Firmware: current_el = %d\n", + current_el()); + + /* + * The PE will be turned into target EL when returned from + * SEC Firmware. + */ + if (current_el() != SEC_FIRMWARE_TARGET_EL) + return -EACCES; + + sec_firmware_addr |= SEC_FIRMWARE_RUNNING; + + /* Set exception table and enable caches if it isn't EL3 */ + if (current_el() != 3) { + c_runtime_cpu_setup(); + enable_caches(); + } + + return 0; +} diff --git a/arch/arm/cpu/armv8/sec_firmware_asm.S b/arch/arm/cpu/armv8/sec_firmware_asm.S new file mode 100644 index 0000000000..0c6a46249a --- /dev/null +++ b/arch/arm/cpu/armv8/sec_firmware_asm.S @@ -0,0 +1,53 @@ +/* + * Copyright 2016 NXP Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <linux/linkage.h> +#include <asm/system.h> +#include <asm/macro.h> + +WEAK(_sec_firmware_entry) + /* + * x0: Secure Firmware entry point + * x1: Exception return address Low + * x2: Exception return address High + */ + + /* Save stack pointer for EL2 */ + mov x3, sp + msr sp_el2, x3 + + /* Set exception return address hold pointer */ + adr x4, 1f + mov x3, x4 +#ifdef SEC_FIRMWARE_ERET_ADDR_REVERT + rev w3, w3 +#endif + str w3, [x1] + lsr x3, x4, #32 +#ifdef SEC_FIRMWARE_ERET_ADDR_REVERT + rev w3, w3 +#endif + str w3, [x2] + + /* Call SEC monitor */ + br x0 + +1: + mov x0, #0 + ret +ENDPROC(_sec_firmware_entry) + +#ifdef CONFIG_ARMV8_PSCI +ENTRY(_sec_firmware_support_psci_version) + mov x0, 0x84000000 + mov x1, 0x0 + mov x2, 0x0 + mov x3, 0x0 + smc #0 + ret +ENDPROC(_sec_firmware_support_psci_version) +#endif diff --git a/arch/arm/include/asm/armv8/sec_firmware.h b/arch/arm/include/asm/armv8/sec_firmware.h new file mode 100644 index 0000000000..39a1333c93 --- /dev/null +++ b/arch/arm/include/asm/armv8/sec_firmware.h @@ -0,0 +1,18 @@ +/* + * Copyright 2016 NXP Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SEC_FIRMWARE_H_ +#define __SEC_FIRMWARE_H_ + +int sec_firmware_init(const void *, u32 *, u32 *); +int _sec_firmware_entry(const void *, u32 *, u32 *); +bool sec_firmware_is_valid(const void *); +#ifdef CONFIG_ARMV8_PSCI +unsigned int sec_firmware_support_psci_version(void); +unsigned int _sec_firmware_support_psci_version(void); +#endif + +#endif /* __SEC_FIRMWARE_H_ */ |