diff options
21 files changed, 1496 insertions, 2 deletions
@@ -1,5 +1,5 @@ # -# Copyright (c) 2018, Arm Limited. All rights reserved. +# Copyright (c) 2018-2019, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -114,6 +114,7 @@ include tftf/framework/framework.mk include tftf/tests/tests.mk include fwu/ns_bl1u/ns_bl1u.mk include fwu/ns_bl2u/ns_bl2u.mk +include spm/cactus_mm/cactus_mm.mk include spm/cactus/cactus.mk include spm/ivy/ivy.mk @@ -222,6 +223,12 @@ NS_BL2U_CFLAGS += ${COMMON_CFLAGS} NS_BL2U_ASFLAGS += ${COMMON_ASFLAGS} NS_BL2U_LDFLAGS += ${COMMON_LDFLAGS} +CACTUS_MM_SOURCES += ${LIBC_SRCS} +CACTUS_MM_INCLUDES += ${PLAT_INCLUDES} +CACTUS_MM_CFLAGS += ${COMMON_CFLAGS} +CACTUS_MM_ASFLAGS += ${COMMON_ASFLAGS} +CACTUS_MM_LDFLAGS += ${COMMON_LDFLAGS} + CACTUS_SOURCES += ${LIBC_SRCS} CACTUS_INCLUDES += ${PLAT_INCLUDES} CACTUS_CFLAGS += ${COMMON_CFLAGS} @@ -296,6 +303,11 @@ ns_bl1u ns_bl2u: endif ifneq (${ARCH}-${PLAT},aarch64-fvp) +.PHONY: cactus_mm +cactus_mm: + @echo "ERROR: $@ is supported only on AArch64 FVP." + @exit 1 + .PHONY: cactus cactus: @echo "ERROR: $@ is supported only on AArch64 FVP." @@ -430,6 +442,7 @@ ifeq ($(FIRMWARE_UPDATE), 1) endif ifeq (${ARCH}-${PLAT},aarch64-fvp) + $(eval $(call MAKE_IMG,cactus_mm)) $(eval $(call MAKE_IMG,cactus)) $(eval $(call MAKE_IMG,ivy)) endif @@ -465,6 +478,7 @@ help: @echo " ns_bl1u Build the NS_BL1U image" @echo " ns_bl2u Build the NS_BL2U image" @echo " cactus Build the Cactus image (Test S-EL0 payload) and resource description." + @echo " cactus_mm Build the Cactus-MM image (Test S-EL0 payload)." @echo " ivy Build the Ivy image (Test S-EL0 payload) and resource description." @echo " el3_payload Build the EL3 test payload" @echo " checkcodebase Check the coding style of the entire source tree" diff --git a/include/common/debug.h b/include/common/debug.h index 9b2d84216..4b30175d9 100644 --- a/include/common/debug.h +++ b/include/common/debug.h @@ -9,6 +9,10 @@ #include <stdio.h> +#ifdef IMAGE_CACTUS_MM +/* Remove dependency on spinlocks for Cactus-MM */ +#define mp_printf printf +#else /* * Print a formatted string on the UART. * @@ -18,6 +22,7 @@ */ __attribute__((format(printf, 1, 2))) void mp_printf(const char *fmt, ...); +#endif /* IMAGE_CACTUS_MM */ /* * The log output macros print output to the console. These macros produce diff --git a/include/runtime_services/secure_el0_payloads/mm_svc.h b/include/runtime_services/secure_el0_payloads/mm_svc.h new file mode 100644 index 000000000..290fd7d1b --- /dev/null +++ b/include/runtime_services/secure_el0_payloads/mm_svc.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __MM_SVC_H__ +#define __MM_SVC_H__ + +#include <utils_def.h> + +#define MM_VERSION_MAJOR U(1) +#define MM_VERSION_MAJOR_SHIFT 16 +#define MM_VERSION_MAJOR_MASK U(0x7FFF) +#define MM_VERSION_MINOR U(0) +#define MM_VERSION_MINOR_SHIFT 0 +#define MM_VERSION_MINOR_MASK U(0xFFFF) +#define MM_VERSION_FORM(major, minor) ((major << MM_VERSION_MAJOR_SHIFT) | (minor)) +#define MM_VERSION_COMPILED MM_VERSION_FORM(MM_VERSION_MAJOR, MM_VERSION_MINOR) + +/* + * SMC IDs defined in [1] for accessing MM services from the Non-secure world. + * These FIDs occupy the range 0x40 - 0x5f. + * [1] DEN0060A_ARM_MM_Interface_Specification.pdf + */ +#define MM_VERSION_AARCH32 U(0x84000040) + +#define MM_COMMUNICATE_AARCH64 U(0xC4000041) +#define MM_COMMUNICATE_AARCH32 U(0x84000041) + +#endif /* __MM_SVC_H__ */ diff --git a/include/runtime_services/secure_el0_payloads/secure_partition.h b/include/runtime_services/secure_el0_payloads/secure_partition.h index 0a41ddef6..14d209002 100644 --- a/include/runtime_services/secure_el0_payloads/secure_partition.h +++ b/include/runtime_services/secure_el0_payloads/secure_partition.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Arm Limited. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,9 +7,22 @@ #ifndef SECURE_PARTITION_H #define SECURE_PARTITION_H +#ifndef __ASSEMBLY__ +#include <cassert.h> #include <param_header.h> #include <stdint.h> #include <utils_def.h> +#endif + +/* + * Definitions used to access the members of secure_partition_boot_info from + * assembly code. + */ +#define SP_BOOT_INFO_STACK_BASE_OFFSET U(32) +#define SP_BOOT_INFO_IMAGE_SIZE_OFFSET U(64) +#define SP_BOOT_INFO_PCPU_STACK_SIZE_OFFSET U(72) + +#ifndef __ASSEMBLY__ /* * Flags used by the secure_partition_mp_info structure to describe the @@ -30,9 +43,60 @@ typedef struct secure_partition_mp_info { typedef struct secure_partition_boot_info { param_header_t h; + uint64_t sp_mem_base; + uint64_t sp_mem_limit; + uint64_t sp_image_base; + uint64_t sp_stack_base; + uint64_t sp_heap_base; + uint64_t sp_ns_comm_buf_base; + uint64_t sp_shared_buf_base; + uint64_t sp_image_size; + uint64_t sp_pcpu_stack_size; + uint64_t sp_heap_size; + uint64_t sp_ns_comm_buf_size; + uint64_t sp_shared_buf_size; + uint32_t num_sp_mem_regions; uint32_t num_cpus; secure_partition_mp_info_t *mp_info; } secure_partition_boot_info_t; +/* + * This structure is used to pass data associated to secure service requests. + */ +#define SPS_MAX_PAYLOAD_SIZE 32 +typedef struct secure_partition_request_info { + uint32_t id; + uint64_t data_size; + uint8_t data[SPS_MAX_PAYLOAD_SIZE]; +} secure_partition_request_info_t; + +/* Define some fast secure partition requests (SPS) IDs. */ +#define SPS_TIMER_SLEEP 1 +#define SPS_CHECK_ALIVE 2 + +#define CACTUS_FAST_REQUEST_SUCCESS 0xCACF900D + +secure_partition_request_info_t *create_sps_request(uint32_t id, + const void *data, + uint64_t data_size); + +/* + * Compile time assertions related to the 'secure_partition_boot_info' structure + * to ensure that the assembler and the compiler view of the offsets of the + * structure members is the same. + */ +CASSERT(SP_BOOT_INFO_STACK_BASE_OFFSET == + __builtin_offsetof(secure_partition_boot_info_t, sp_stack_base), \ + assert_secure_partition_boot_info_sp_stack_base_offset_mismatch); + +CASSERT(SP_BOOT_INFO_IMAGE_SIZE_OFFSET == + __builtin_offsetof(secure_partition_boot_info_t, sp_image_size), \ + assert_secure_partition_boot_info_sp_image_size_offset_mismatch); + +CASSERT(SP_BOOT_INFO_PCPU_STACK_SIZE_OFFSET == + __builtin_offsetof(secure_partition_boot_info_t, sp_pcpu_stack_size), \ + assert_secure_partition_boot_info_sp_pcpu_stack_size_offset_mismatch); + +#endif /* __ASSEMBLY__ */ #endif /* SECURE_PARTITION_H */ diff --git a/include/runtime_services/secure_el0_payloads/spm_svc.h b/include/runtime_services/secure_el0_payloads/spm_svc.h index e1f9df304..e3b6cc517 100644 --- a/include/runtime_services/secure_el0_payloads/spm_svc.h +++ b/include/runtime_services/secure_el0_payloads/spm_svc.h @@ -33,6 +33,7 @@ * They occupy the range 0x60-0x7f. */ #define SPM_VERSION_AARCH32 U(0x84000060) +#define SP_EVENT_COMPLETE_AARCH64 U(0xC4000061) #define SP_MEMORY_ATTRIBUTES_GET_AARCH64 U(0xC4000064) #define SP_MEMORY_ATTRIBUTES_SET_AARCH64 U(0xC4000065) diff --git a/plat/arm/fvp/include/platform_def.h b/plat/arm/fvp/include/platform_def.h index 55477b446..19be6cb06 100644 --- a/plat/arm/fvp/include/platform_def.h +++ b/plat/arm/fvp/include/platform_def.h @@ -222,4 +222,14 @@ #define PLAT_SUSPEND_ENTRY_TIME 15 #define PLAT_SUSPEND_ENTRY_EXIT_TIME 30 +/******************************************************************************* + * Location of the memory buffer shared between Normal World (i.e. TFTF) and the + * Secure Partition (e.g. Cactus-MM) to pass data associated to secure service + * requests. This is only needed for SPM based on MM. + * Note: This address has to match the one used in TF (see ARM_SP_IMAGE_NS_BUF_* + * macros). + ******************************************************************************/ +#define ARM_SECURE_SERVICE_BUFFER_BASE 0xff600000ull +#define ARM_SECURE_SERVICE_BUFFER_SIZE 0x10000ull + #endif /* __PLATFORM_DEF_H__ */ diff --git a/spm/cactus_mm/aarch64/cactus_mm_entrypoint.S b/spm/cactus_mm/aarch64/cactus_mm_entrypoint.S new file mode 100644 index 000000000..a657aabcc --- /dev/null +++ b/spm/cactus_mm/aarch64/cactus_mm_entrypoint.S @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <asm_macros.S> +#include <secure_partition.h> +#include <spm_svc.h> +#include <xlat_tables_defs.h> + + .globl cactus_mm_entrypoint + +func cactus_mm_entrypoint + + /* + * All the information needed to remap the memory of the Secure + * Partition is in the buffer whose pointer is passed on X0 and size on + * X1. If the size is 0, return with an error. + */ + cmp x1, #0 + beq .return_error + + /* Save the base address and size of the buffer. */ + mov x20, x0 + mov x21, x1 + /* Size of the Secure Partition image. */ + ldr x22, [x20, SP_BOOT_INFO_IMAGE_SIZE_OFFSET] + + /* + * Remap all sections of the image before doing anything else. + * + * Not even the console can be initialized before because it needs to + * initialize variables (that can only be modified after remapping that + * region as RW). + * + * If any of the calls fails, loop, as there is no console to print an + * error message to. + */ + .macro set_sp_mem_attributes + cmp x2, #0 /* If size is 0, skip the call. */ + beq 1f + mov_imm x0, SP_MEMORY_ATTRIBUTES_SET_AARCH64 + svc #0 + cmp x0, #0 + bne .return_error +1: + .endm + + adr x1, __TEXT_START__ + adr x2, __TEXT_END__ + sub x2, x2, x1 /* __TEXT_SIZE__ */ + lsr x2, x2, PAGE_SIZE_SHIFT /* __TEXT_SIZE__ in pages */ + mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_RO | SP_MEMORY_ATTRIBUTES_EXEC + set_sp_mem_attributes + + adr x1, __RODATA_START__ + adr x2, __RODATA_END__ + sub x2, x2, x1 /* __RODATA_SIZE__ */ + lsr x2, x2, PAGE_SIZE_SHIFT /* __RODATA_SIZE__ in pages */ + mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_RO | SP_MEMORY_ATTRIBUTES_NON_EXEC + set_sp_mem_attributes + + adr x1, __RWDATA_START__ + adr x2, __RWDATA_END__ + sub x2, x2, x1 /* __RWDATA_SIZE__ */ + lsr x2, x2, PAGE_SIZE_SHIFT /* __RWDATA_SIZE__ in pages */ + mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_RW | SP_MEMORY_ATTRIBUTES_NON_EXEC + set_sp_mem_attributes + + /* + * To avoid accessing it by mistake, prevent EL0 from accessing the rest + * of the memory reserved for the Secure Partition. + * + * Unused size = Total size - Used size + * = Total size - (__RWDATA_END__ - __TEXT_START__) + */ + adr x1, __RWDATA_END__ + adr x2, __TEXT_START__ + sub x2, x1, x2 /* x2 = Used size, x22 = Total size */ + sub x2, x22, x2 /* x2 = Unused size */ + lsr x2, x2, PAGE_SIZE_SHIFT /* Unused size in pages */ + mov x3, SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS | SP_MEMORY_ATTRIBUTES_NON_EXEC + set_sp_mem_attributes + + adr x0, __BSS_START__ + adr x1, __BSS_END__ + sub x1, x1, x0 + bl zeromem16 + + /* Setup the stack pointer. */ + ldr x0, [x20, SP_BOOT_INFO_STACK_BASE_OFFSET] + ldr x1, [x20, SP_BOOT_INFO_PCPU_STACK_SIZE_OFFSET] + add x0, x0, x1 + mov sp, x0 + + /* And do the rest in C code */ + mov x0, x20 + mov x1, x21 + b cactus_main + +.return_error: + /* Tell SPM that the initialization failed. */ + mov_imm x0, SP_EVENT_COMPLETE_AARCH64 + mov x1, #-1 + svc #0 + + /* Loop forever */ + b . + +endfunc cactus_mm_entrypoint diff --git a/spm/cactus_mm/cactus_mm.h b/spm/cactus_mm/cactus_mm.h new file mode 100644 index 000000000..1e0f24d60 --- /dev/null +++ b/spm/cactus_mm/cactus_mm.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __CACTUS_H__ +#define __CACTUS_H__ + +#include <secure_partition.h> +#include <stdint.h> + +/* Linker symbols used to figure out the memory layout of Cactus. */ +extern uintptr_t __TEXT_START__, __TEXT_END__; +#define CACTUS_TEXT_START ((uintptr_t)&__TEXT_START__) +#define CACTUS_TEXT_END ((uintptr_t)&__TEXT_END__) + +extern uintptr_t __RODATA_START__, __RODATA_END__; +#define CACTUS_RODATA_START ((uintptr_t)&__RODATA_START__) +#define CACTUS_RODATA_END ((uintptr_t)&__RODATA_END__) + +extern uintptr_t __RWDATA_START__, __RWDATA_END__; +#define CACTUS_RWDATA_START ((uintptr_t)&__RWDATA_START__) +#define CACTUS_RWDATA_END ((uintptr_t)&__RWDATA_END__) + +extern uintptr_t __BSS_START__, __BSS_END__; +#define CACTUS_BSS_START ((uintptr_t)&__BSS_START__) +#define CACTUS_BSS_END ((uintptr_t)&__BSS_END__) + +/* + * Once Cactus has finished its initialisation, this is the function it will + * jump to to handle runtime services for the rest of its lifetime. + */ +__dead2 void secure_services_loop(void); + +#endif /* __CACTUS_H__ */ diff --git a/spm/cactus_mm/cactus_mm.ld.S b/spm/cactus_mm/cactus_mm.ld.S new file mode 100644 index 000000000..4ab822192 --- /dev/null +++ b/spm/cactus_mm/cactus_mm.ld.S @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017-2018, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <platform_def.h> +#include <xlat_tables_defs.h> + +OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT) +OUTPUT_ARCH(PLATFORM_LINKER_ARCH) +ENTRY(cactus_mm_entrypoint) + +SECTIONS +{ + ASSERT(. == ALIGN(PAGE_SIZE), + "TEXT_START address is not aligned to PAGE_SIZE.") + + .text : { + __TEXT_START__ = .; + *cactus_mm_entrypoint.o(.text*) + *(.text*) + *(.vectors) + . = NEXT(PAGE_SIZE); + __TEXT_END__ = .; + } + + .rodata : { + . = ALIGN(PAGE_SIZE); + __RODATA_START__ = .; + *(.rodata*) + . = NEXT(PAGE_SIZE); + __RODATA_END__ = .; + } + + + .data : { + . = ALIGN(PAGE_SIZE); + __RWDATA_START__ = .; + *(.data*) + } + + .bss : { + . = ALIGN(16); + __BSS_START__ = .; + *(SORT_BY_ALIGNMENT(.bss*)) + *(COMMON) + . = NEXT(PAGE_SIZE); + __BSS_END__ = .; + __RWDATA_END__ = .; + } +} diff --git a/spm/cactus_mm/cactus_mm.mk b/spm/cactus_mm/cactus_mm.mk new file mode 100644 index 000000000..f00d0399f --- /dev/null +++ b/spm/cactus_mm/cactus_mm.mk @@ -0,0 +1,60 @@ +# +# Copyright (c) 2018-2019, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +CACTUS_MM_INCLUDES := \ + -Iinclude \ + -Iinclude/common \ + -Iinclude/common/${ARCH} \ + -Iinclude/drivers \ + -Iinclude/drivers/arm \ + -Iinclude/lib \ + -Iinclude/lib/${ARCH} \ + -Iinclude/lib/utils \ + -Iinclude/lib/xlat_tables \ + -Iinclude/runtime_services \ + -Iinclude/runtime_services/secure_el0_payloads \ + -Ispm/cactus_mm \ + -Ispm/common \ + +CACTUS_MM_SOURCES := \ + $(addprefix spm/cactus_mm/, \ + aarch64/cactus_mm_entrypoint.S \ + cactus_mm_main.c \ + cactus_mm_service_loop.c \ + cactus_mm_tests_memory_attributes.c \ + cactus_mm_tests_misc.c \ + cactus_mm_tests_system_setup.c \ + ) \ + $(addprefix spm/common/, \ + aarch64/sp_arch_helpers.S \ + sp_helpers.c \ + ) \ + +# TODO: Remove dependency on TFTF files. +CACTUS_MM_SOURCES += \ + tftf/framework/debug.c \ + +CACTUS_MM_SOURCES += \ + drivers/arm/pl011/${ARCH}/pl011_console.S \ + lib/${ARCH}/cache_helpers.S \ + lib/${ARCH}/misc_helpers.S \ + ${STDLIB_SOURCES} + +CACTUS_MM_LINKERFILE := spm/cactus_mm/cactus_mm.ld.S + +CACTUS_MM_DEFINES := + +# TODO: Assertions are disabled because they add several TFTF files as +# dependencies. It is needed to break the dependencies and remove this line when +# that is done. +CACTUS_MM_DEFINES += -DENABLE_ASSERTIONS=0 + +$(eval $(call add_define,CACTUS_MM_DEFINES,DEBUG)) +$(eval $(call add_define,CACTUS_MM_DEFINES,LOG_LEVEL)) +$(eval $(call add_define,CACTUS_MM_DEFINES,PLAT_${PLAT})) +$(eval $(call add_define,CACTUS_MM_DEFINES,AARCH64)) + +cactus_mm: ${AUTOGEN_DIR}/tests_list.h diff --git a/spm/cactus_mm/cactus_mm_main.c b/spm/cactus_mm/cactus_mm_main.c new file mode 100644 index 000000000..8452e656f --- /dev/null +++ b/spm/cactus_mm/cactus_mm_main.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <console.h> +#include <debug.h> +#include <pl011.h> +#include <platform_def.h> +#include <secure_partition.h> +#include <sp_helpers.h> +#include <spm_svc.h> +#include <std_svc.h> + +#include "cactus_mm.h" +#include "cactus_mm_tests.h" + + +/* Host machine information injected by the build system in the ELF file. */ +extern const char build_message[]; +extern const char version_string[]; + +/* + * The ARM Trusted Firmware passes a description of the memory resources + * allocated to the secure partition through the x0 register. This maps to + * a secure_partition_boot_info_t structure type. + * + * This functions prints the information stored in this structure. + */ +static void cactus_print_memory_layout(const secure_partition_boot_info_t *boot_info) +{ + NOTICE("Secure Partition memory layout:\n"); + + NOTICE(" Secure Partition image : %p - %p\n", + (void *) boot_info->sp_image_base, + (void *)(boot_info->sp_image_base + boot_info->sp_image_size)); + NOTICE(" Text region : %p - %p\n", + (void *) CACTUS_TEXT_START, (void *) CACTUS_TEXT_END); + NOTICE(" Read-only data region : %p - %p\n", + (void *) CACTUS_RODATA_START, (void *) CACTUS_RODATA_END); + NOTICE(" Read-write data region : %p - %p\n", + (void *) CACTUS_RWDATA_START, (void *) CACTUS_RWDATA_END); + NOTICE(" BSS region : %p - %p\n", + (void *) CACTUS_BSS_START, (void *) CACTUS_BSS_END); + NOTICE(" Unused SP image space : %p - %p\n", + (void *) CACTUS_BSS_END, + (void *)(boot_info->sp_image_base + boot_info->sp_image_size)); + + NOTICE(" EL3-EL0 shared buffer : %p - %p\n", + (void *) boot_info->sp_shared_buf_base, + (void *)(boot_info->sp_shared_buf_base + boot_info->sp_shared_buf_size)); + + NOTICE(" S-NS shared buffer : %p - %p\n", + (void *) boot_info->sp_ns_comm_buf_base, + (void *)(boot_info->sp_ns_comm_buf_base + boot_info->sp_ns_comm_buf_size)); + + assert(boot_info->sp_ns_comm_buf_base == ARM_SECURE_SERVICE_BUFFER_BASE); + assert(boot_info->sp_ns_comm_buf_size == ARM_SECURE_SERVICE_BUFFER_SIZE); + + NOTICE(" Stacks region (%u CPUS) : %p - %p\n", + boot_info->num_cpus, + (void *) boot_info->sp_stack_base, + (void *)(boot_info->sp_stack_base + + (boot_info->sp_pcpu_stack_size * boot_info->num_cpus))); + + NOTICE(" Heap region : %p - %p\n", + (void *) boot_info->sp_heap_base, + (void *)(boot_info->sp_heap_base + boot_info->sp_heap_size)); + + NOTICE("Total memory : %p - %p\n", + (void *) boot_info->sp_mem_base, (void *) boot_info->sp_mem_limit); +} + + +void __dead2 cactus_main(void *el3_el0_buffer, size_t el3_el0_buffer_size) +{ + console_init(PL011_UART2_BASE, + PL011_UART2_CLK_IN_HZ, + PL011_BAUDRATE); + + NOTICE("Booting test Secure Partition Cactus\n"); + NOTICE("%s\n", build_message); + NOTICE("%s\n", version_string); + NOTICE("Running at S-EL0\n"); + + const secure_partition_boot_info_t *boot_info = + (const secure_partition_boot_info_t *) el3_el0_buffer; + + if (el3_el0_buffer_size < sizeof(secure_partition_boot_info_t)) { + ERROR("The memory buffer shared between EL3/S-EL0 is too small\n"); + ERROR("It is %lu bytes, it should be at least %lu bytes\n", + el3_el0_buffer_size, + sizeof(secure_partition_boot_info_t)); + panic(); + } + + if ((CACTUS_TEXT_START != boot_info->sp_image_base) || + (CACTUS_RWDATA_END > boot_info->sp_image_base + + boot_info->sp_image_size)) { + ERROR("Cactus does not fit in the buffer allocated for the secure partition\n"); + panic(); + } + + cactus_print_memory_layout(boot_info); + + + /* + * Run some initial tests. + * + * These are executed when the system is still booting, just after SPM + * has handed over to Cactus. + */ + misc_tests(); + system_setup_tests(); + /* + * TODO: The following tests fail in the CI: + * mem_attr_changes_tests(boot_info); + */ + + /* + * Handle secure service requests. + */ + secure_services_loop(); +} diff --git a/spm/cactus_mm/cactus_mm_service_loop.c b/spm/cactus_mm/cactus_mm_service_loop.c new file mode 100644 index 000000000..0ad60b62b --- /dev/null +++ b/spm/cactus_mm/cactus_mm_service_loop.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <debug.h> +#include <mm_svc.h> +#include <secure_partition.h> +#include <sp_helpers.h> +#include <spm_svc.h> +#include <string.h> + + +/* + * Handle a fast secure service request, i.e. one made through an MM_COMMUNICATE + * call. + * + * cc + * Calling convention. If MM_COMMUNICATE has been invoked using the SMC32 + * calling convention, this argument must be 32, else 64. + * + * sps + * Communication buffer attached to the secure partition service request. + */ +static int32_t cactus_handle_fast_request(int cc, + secure_partition_request_info_t *sps) +{ + assert(cc == 32 || cc == 64); + + /* No SMC32 is supported at the moment. Just ignore them. */ + if (cc == 32) { + INFO("Ignoring MM_COMMUNICATE_AARCH32 call\n"); + return SPM_SUCCESS; + } + + /* See secure_partition.h for possible ID values. */ + switch (sps->id) { + case SPS_TIMER_SLEEP: { + if (sps->data_size != 1) { + ERROR("Invalid payload size for SPM_SPS_TIMER_SLEEP request (%llu)\n", + sps->data_size); + return SPM_INVALID_PARAMETER; + } + int duration_sec = sps->data[0]; + sp_sleep(duration_sec); + + /* + * Write back to the communication buffer to acknowledge the + * request has been successfully handled. + */ + uint32_t response = CACTUS_FAST_REQUEST_SUCCESS; + memcpy(sps->data, &response, sizeof(response)); + return SPM_SUCCESS; + } + + case SPS_CHECK_ALIVE: + return SPM_SUCCESS; + + default: + INFO("Unsupported MM_COMMUNICATE_AARCH64 call with service ID 0x%x, ignoring it\n", + sps->id); + return SPM_INVALID_PARAMETER; + } +} + +__dead2 void secure_services_loop(void) +{ + int32_t event_status_code; + svc_args svc_values = { 0 }; + + /* + * The first time this loop is executed corresponds to when Cactus has + * finished initialising its run time environment and is ready to handle + * secure service requests. + */ + NOTICE("Cactus: Signal end of init to SPM\n"); + event_status_code = SPM_SUCCESS; + + while (1) { + svc_values.fid = SP_EVENT_COMPLETE_AARCH64; + svc_values.arg1 = event_status_code; + int32_t event_id = sp_svc(&svc_values); + + switch (event_id) { + case MM_COMMUNICATE_AARCH64: + { + uint64_t ctx_addr = svc_values.arg1; + uint32_t ctx_size = svc_values.arg2; + uint64_t cookie = svc_values.arg3; + + NOTICE("Cactus: Received MM_COMMUNICATE_AARCH64 call\n"); + NOTICE("Cactus: Context address: 0x%llx\n", ctx_addr); + NOTICE("Cactus: Context size : %u\n", ctx_size); + NOTICE("Cactus: Cookie : 0x%llx\n", cookie); + + if (ctx_addr == 0) { + ERROR("Context address is invalid\n"); + event_status_code = SPM_INVALID_PARAMETER; + continue; + } + + secure_partition_request_info_t *sps = (void *)(uintptr_t) ctx_addr; + NOTICE("Received fast secure service request with ID #%u\n", + sps->id); + event_status_code = cactus_handle_fast_request(64, sps); + break; + } + + case MM_COMMUNICATE_AARCH32: + { + uint32_t ctx_addr = svc_values.arg1; + uint32_t ctx_size = svc_values.arg2; + uint32_t cookie = svc_values.arg3; + + NOTICE("Cactus: Received MM_COMMUNICATE_AARCH32 call\n"); + NOTICE("Cactus: Context address: 0x%x\n", ctx_addr); + NOTICE("Cactus: Context size : %u\n", ctx_size); + NOTICE("Cactus: Cookie : 0x%x\n", cookie); + + if (ctx_addr == 0) { + ERROR("Context address is invalid\n"); + event_status_code = SPM_INVALID_PARAMETER; + continue; + } + + secure_partition_request_info_t *sps = (void *)(uintptr_t) ctx_addr; + NOTICE("Received fast secure service request with ID #%u\n", + sps->id); + event_status_code = cactus_handle_fast_request(32, sps); + break; + } + + default: + NOTICE("Unhandled Service ID 0x%x\n", event_id); + event_status_code = SPM_NOT_SUPPORTED; + break; + } + } +} diff --git a/spm/cactus_mm/cactus_mm_tests.h b/spm/cactus_mm/cactus_mm_tests.h new file mode 100644 index 000000000..d0e11dcba --- /dev/null +++ b/spm/cactus_mm/cactus_mm_tests.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __CACTUS_TESTS_H__ +#define __CACTUS_TESTS_H__ + +#include <secure_partition.h> + +/* + * Test functions + */ + +/* + * Test other things like the version number returned by SPM. + */ +void misc_tests(void); + +/* + * The Arm TF is responsible for setting up system registers on behalf of the + * Secure Partition. For example, TF is supposed to allow Secure Partitions to + * perform cache maintenance operations (by setting the SCTLR_EL1.UCI bit). + * + * This function attempts to verify that we indeed have access to these system + * features from S-EL0. These tests report their results on the UART. They do + * not recover from a failure : when an error is encountered they will most + * likely trigger an exception into S-EL1. + */ +void system_setup_tests(void); + +/* + * Exercise the SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC interface. A variety of + * valid and invalid requests to change memory attributes are tested. + * + * These tests report their results on the UART. They do not recover from a + * failure : when an error is encountered they endlessly loop. + * + * The argument is a pointer to a secure_partition_boot_info_t struct that has + * been filled by EL3 with the information about the memory map of this Secure + * Partition. + */ +void mem_attr_changes_tests(const secure_partition_boot_info_t *boot_info); + +#endif /* __CACTUS_TESTS_H__ */ diff --git a/spm/cactus_mm/cactus_mm_tests_memory_attributes.c b/spm/cactus_mm/cactus_mm_tests_memory_attributes.c new file mode 100644 index 000000000..1a87f6cfa --- /dev/null +++ b/spm/cactus_mm/cactus_mm_tests_memory_attributes.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <platform_def.h> +#include <secure_partition.h> +#include <sp_helpers.h> +#include <spm_svc.h> +#include <stdint.h> +#include <stdio.h> +#include <xlat_tables_defs.h> + +#include "cactus_mm.h" +#include "cactus_mm_tests.h" + +/* This is filled at runtime. */ +static uintptr_t cactus_tests_start; +static uintptr_t cactus_tests_end; +static uintptr_t cactus_tests_size; + +/* + * Given the required instruction and data access permissions, + * create a memory access controls value that is formatted as expected + * by the SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC. + */ +static inline uint32_t mem_access_perm(int instr_access_perm, + int data_access_perm) +{ + return instr_access_perm | + ((data_access_perm & SP_MEMORY_ATTRIBUTES_ACCESS_MASK) + << SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT); +} + +/* + * Send an SP_MEMORY_ATTRIBUTES_SET_AARCH64 SVC with the given arguments. + * Return the return value of the SVC. + */ +static int32_t request_mem_attr_changes(uintptr_t base_address, + int pages_count, + uint32_t memory_access_controls) +{ + INFO("Requesting memory attributes change\n"); + INFO(" Start address : %p\n", (void *) base_address); + INFO(" Number of pages: %i\n", pages_count); + INFO(" Attributes : 0x%x\n", memory_access_controls); + + svc_args svc_values = { SP_MEMORY_ATTRIBUTES_SET_AARCH64, + base_address, + pages_count, + memory_access_controls }; + return sp_svc(&svc_values); +} + +/* + * Send an SP_MEMORY_ATTRIBUTES_GET_AARCH64 SVC with the given arguments. + * Return the return value of the SVC. + */ +static int32_t request_get_mem_attr(uintptr_t base_address) +{ + INFO("Requesting memory attributes\n"); + INFO(" Base address : %p\n", (void *) base_address); + + svc_args svc_values = { SP_MEMORY_ATTRIBUTES_GET_AARCH64, + base_address }; + return sp_svc(&svc_values); +} + +/* + * This function expects a base address and number of pages identifying the + * extents of some memory region mapped as non-executable, read-only. + * + * 1) It changes its data access permissions to read-write. + * 2) It checks this memory can now be written to. + * 3) It restores the original data access permissions. + * + * If any check fails, it loops forever. It could also trigger a permission + * fault while trying to write to the memory. + */ +static void mem_attr_changes_unittest(uintptr_t addr, int pages_count) +{ + int32_t ret; + uintptr_t end_addr = addr + pages_count * PAGE_SIZE; + uint32_t old_attr, new_attr; + + char test_desc[50]; + + snprintf(test_desc, sizeof(test_desc), + "RO -> RW (%i page(s) from address 0x%lx)", pages_count, addr); + announce_test_start(test_desc); + + /* + * Ensure we don't change the attributes of some random memory + * location + */ + assert(addr >= cactus_tests_start); + assert(end_addr < (cactus_tests_start + cactus_tests_size)); + + old_attr = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RO); + /* Memory was read-only, let's try changing that to RW */ + new_attr = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW); + + ret = request_mem_attr_changes(addr, pages_count, new_attr); + expect(ret, SPM_SUCCESS); + printf("Successfully changed memory attributes\n"); + + /* The attributes should be the ones we have just written. */ + ret = request_get_mem_attr(addr); + expect(ret, new_attr); + + /* If it worked, we should be able to write to this memory now! */ + for (unsigned char *data = (unsigned char *) addr; + (uintptr_t) data != end_addr; + ++data) { + *data = 42; + } + printf("Successfully wrote to the memory\n"); + + /* Let's revert back to the original attributes for the next test */ + ret = request_mem_attr_changes(addr, pages_count, old_attr); + expect(ret, SPM_SUCCESS); + printf("Successfully restored the old attributes\n"); + + /* The attributes should be the original ones again. */ + ret = request_get_mem_attr(addr); + expect(ret, old_attr); + + announce_test_end(test_desc); +} + +/* + * Exercise the ability of the Trusted Firmware to change the data access + * permissions and instruction execution permissions of some memory region. + */ +void mem_attr_changes_tests(const secure_partition_boot_info_t *boot_info) +{ + uint32_t attributes; + int32_t ret; + uintptr_t addr; + + cactus_tests_start = CACTUS_BSS_END; + cactus_tests_end = boot_info->sp_image_base + boot_info->sp_image_size; + cactus_tests_size = cactus_tests_end - cactus_tests_start; + + const char *test_sect_desc = "memory attributes changes"; + + announce_test_section_start(test_sect_desc); + /* + * Start with error cases, i.e. requests that are expected to be denied + */ + const char *test_desc1 = "Read-write, executable"; + + announce_test_start(test_desc1); + attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW); + ret = request_mem_attr_changes(CACTUS_RWDATA_START, 1, attributes); + expect(ret, SPM_INVALID_PARAMETER); + announce_test_end(test_desc1); + + const char *test_desc2 = "Size == 0"; + + announce_test_start(test_desc2); + attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW); + ret = request_mem_attr_changes(CACTUS_RWDATA_START, 0, attributes); + expect(ret, SPM_INVALID_PARAMETER); + announce_test_end(test_desc2); + + const char *test_desc3 = "Unaligned address"; + + announce_test_start(test_desc3); + attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW); + /* Choose an address not aligned to a page boundary. */ + addr = cactus_tests_start + 5; + ret = request_mem_attr_changes(addr, 1, attributes); + expect(ret, SPM_INVALID_PARAMETER); + announce_test_end(test_desc3); + + const char *test_desc4 = "Unmapped memory region"; + + announce_test_start(test_desc4); + addr = boot_info->sp_mem_limit + 2 * PAGE_SIZE; + attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW); + ret = request_mem_attr_changes(addr, 3, attributes); + expect(ret, SPM_INVALID_PARAMETER); + announce_test_end(test_desc4); + + const char *test_desc5 = "Partially unmapped memory region"; + + announce_test_start(test_desc5); + addr = boot_info->sp_mem_base - 2 * PAGE_SIZE; + attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW); + ret = request_mem_attr_changes(addr, 6, attributes); + expect(ret, SPM_INVALID_PARAMETER); + announce_test_end(test_desc5); + + const char *test_desc6 = "Memory region mapped with the wrong granularity"; + + announce_test_start(test_desc6); + /* + * This address is usually mapped at a 2 MiB granularity. By using as + * test address the block after the console we make sure that in case + * the attributes of the block actually changed, the console would work + * and we would get the error message. + */ + addr = ((uintptr_t)PLAT_ARM_UART_BASE + 0x200000ULL) & ~(0x200000ULL - 1ULL); + attributes = mem_access_perm(SP_MEMORY_ATTRIBUTES_NON_EXEC, SP_MEMORY_ATTRIBUTES_ACCESS_RW); + ret = request_mem_attr_changes(addr, 1, attributes); + expect(ret, SPM_INVALID_PARAMETER); + announce_test_end(test_desc6); + + const char *test_desc7 = "Try some valid memory change requests"; + + announce_test_start(test_desc7); + for (unsigned int i = 0; i < 20; ++i) { + /* + * Choose some random address in the pool of memory reserved + * for these tests. + */ + const int pages_max = cactus_tests_size / PAGE_SIZE; + int pages_count = bound_rand(1, pages_max); + + addr = bound_rand( + cactus_tests_start, + cactus_tests_end - (pages_count * PAGE_SIZE)); + /* Align to PAGE_SIZE. */ + addr &= ~(PAGE_SIZE - 1); + + mem_attr_changes_unittest(addr, pages_count); + } + announce_test_end(test_desc7); + + announce_test_section_end(test_sect_desc); +} diff --git a/spm/cactus_mm/cactus_mm_tests_misc.c b/spm/cactus_mm/cactus_mm_tests_misc.c new file mode 100644 index 000000000..1d4d18a52 --- /dev/null +++ b/spm/cactus_mm/cactus_mm_tests_misc.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <sp_helpers.h> +#include <spm_svc.h> +#include <stdint.h> + +#include "cactus_mm.h" +#include "cactus_mm_tests.h" + +/* + * Miscellaneous SPM tests. + */ +void misc_tests(void) +{ + int32_t ret; + + const char *test_sect_desc = "miscellaneous"; + + announce_test_section_start(test_sect_desc); + + const char *test_version = "SPM version check"; + + announce_test_start(test_version); + svc_args svc_values = { SPM_VERSION_AARCH32 }; + ret = sp_svc(&svc_values); + INFO("Version = 0x%x (%u.%u)\n", ret, + (ret >> 16) & 0x7FFF, ret & 0xFFFF); + expect(ret, SPM_VERSION_COMPILED); + announce_test_end(test_version); + + announce_test_section_end(test_sect_desc); +} diff --git a/spm/cactus_mm/cactus_mm_tests_system_setup.c b/spm/cactus_mm/cactus_mm_tests_system_setup.c new file mode 100644 index 000000000..300d5f674 --- /dev/null +++ b/spm/cactus_mm/cactus_mm_tests_system_setup.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <debug.h> +#include <sp_helpers.h> +#include <stdint.h> + +#include "cactus_mm.h" + +extern uintptr_t __TEXT_START__; + +void system_setup_tests(void) +{ + const char *test_sect_desc = "system setup"; + + announce_test_section_start(test_sect_desc); + + /* + * Try accessing CTR_EL0 register. This should work if SCTLR_EL1.UCT bit + * has been correctly setup by TF. + */ + const char *test_desc1 = "Read CTR_EL0 register"; + + announce_test_start(test_desc1); + + uint32_t ctr __unused = read_ctr_el0(); + + INFO("CTR_EL0 = 0x%x\n", ctr); + announce_test_end(test_desc1); + + /* + * Try to execute a cache maintenance instruction. This should work if + * SCTLR_EL1.UCI bit has been correctly setup by TF. + */ + const char *test_desc2 = "Access to cache maintenance operations"; + + announce_test_start(test_desc2); + flush_dcache_range((uintptr_t)&__TEXT_START__, 1); + announce_test_end(test_desc2); + + /* + * Try accessing a floating point register. This should not trap to + * S-EL1. + */ + const char *test_desc3 = "Access to FP regs"; + + announce_test_start(test_desc3); + /* + * Can't use the 'double' type here because Cactus (like the rest of + * the TF code) is compiled with GCC's -mgeneral-regs-only compiler flag + * that disables floating point support in GCC. + */ + uint64_t fp_reg; + + __asm__ volatile("fmov %0, d0" : "=r" (fp_reg) :: "d0"); + INFO("D0 = 0x%llx\n", fp_reg); + __asm__ volatile( + "fmov d0, #1.0 \n\t" + "fmov %0, d0 \n\t" + : "=r" (fp_reg) + : + : "d0"); + INFO("D0 = 0x%llx\n", fp_reg); + announce_test_end(test_desc3); + + announce_test_section_end(test_sect_desc); +} diff --git a/tftf/tests/runtime_services/mm_service/secure_service_helpers.c b/tftf/tests/runtime_services/mm_service/secure_service_helpers.c new file mode 100644 index 000000000..8675daeea --- /dev/null +++ b/tftf/tests/runtime_services/mm_service/secure_service_helpers.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <platform.h> +#include <secure_partition.h> +#include <string.h> + + +secure_partition_request_info_t *create_sps_request(uint32_t id, + const void *data, + uint64_t data_size) +{ + secure_partition_request_info_t *sps_request + = (void *) ARM_SECURE_SERVICE_BUFFER_BASE; + sps_request->id = id; + sps_request->data_size = data_size; + if (data_size != 0) + memcpy(sps_request->data, data, data_size); + return sps_request; +} diff --git a/tftf/tests/runtime_services/mm_service/test_secure_service_handle.c b/tftf/tests/runtime_services/mm_service/test_secure_service_handle.c new file mode 100644 index 000000000..ce4dd5c2a --- /dev/null +++ b/tftf/tests/runtime_services/mm_service/test_secure_service_handle.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2018, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <debug.h> +#include <events.h> +#include <mm_svc.h> +#include <plat_topology.h> +#include <platform.h> +#include <power_management.h> +#include <secure_partition.h> +#include <smccc.h> +#include <spm_svc.h> +#include <test_helpers.h> +#include <tftf_lib.h> +#include <xlat_tables_v2.h> + +static event_t cpu_has_finished_test[PLATFORM_CORE_COUNT]; + +/* Test routine for test_secure_partition_secondary_cores_seq() */ +static test_result_t test_secure_partition_secondary_cores_seq_fn(void) +{ + test_result_t result = TEST_RESULT_SUCCESS; + u_register_t cpu_mpid = read_mpidr_el1() & MPID_MASK; + unsigned int core_pos = platform_get_core_pos(cpu_mpid); + + secure_partition_request_info_t *sps_request = + create_sps_request(SPS_CHECK_ALIVE, NULL, 0); + + INFO("Sending MM_COMMUNICATE_AARCH64 from CPU %u\n", + platform_get_core_pos(read_mpidr_el1() & MPID_MASK)); + + smc_args mm_communicate_smc = { + MM_COMMUNICATE_AARCH64, + 0, + (u_register_t) sps_request, + 0 + }; + + smc_ret_values smc_ret = tftf_smc(&mm_communicate_smc); + + if ((uint32_t)smc_ret.ret0 != 0) { + tftf_testcase_printf("Cactus returned: 0x%x\n", + (uint32_t)smc_ret.ret0); + + result = TEST_RESULT_FAIL; + } + + tftf_send_event(&cpu_has_finished_test[core_pos]); + + return result; +} + +/* + * @Test_Aim@ This tests that secondary CPUs can access SPM services + * sequentially. + */ +test_result_t test_secure_partition_secondary_cores_seq(void) +{ + int psci_ret; + u_register_t lead_mpid, cpu_mpid; + unsigned int cpu_node, core_pos; + test_result_t result = TEST_RESULT_SUCCESS; + + SKIP_TEST_IF_LESS_THAN_N_CPUS(2); + + SKIP_TEST_IF_MM_VERSION_LESS_THAN(1, 0); + + VERBOSE("Mapping NS<->SP shared buffer\n"); + + int rc = mmap_add_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_SIZE, + MT_MEMORY | MT_RW | MT_NS); + if (rc != 0) { + tftf_testcase_printf("%d: mmap_add_dynamic_region() = %d\n", + __LINE__, rc); + result = TEST_RESULT_FAIL; + goto exit; + } + + lead_mpid = read_mpidr_el1() & MPID_MASK; + + INFO("Lead CPU is CPU %u\n", platform_get_core_pos(lead_mpid)); + + if (test_secure_partition_secondary_cores_seq_fn() != TEST_RESULT_SUCCESS) { + result = TEST_RESULT_FAIL; + goto exit_unmap; + } + + for_each_cpu(cpu_node) { + cpu_mpid = tftf_get_mpidr_from_node(cpu_node); + /* Skip lead CPU, we have already tested it */ + if (cpu_mpid == lead_mpid) { + continue; + } + + core_pos = platform_get_core_pos(cpu_mpid); + + tftf_init_event(&cpu_has_finished_test[core_pos]); + + VERBOSE("Powering on CPU %u\n", core_pos); + + psci_ret = tftf_cpu_on(cpu_mpid, + (uintptr_t)test_secure_partition_secondary_cores_seq_fn, 0); + if (psci_ret != PSCI_E_SUCCESS) { + tftf_testcase_printf( + "Failed to power on CPU %d (rc = %d)\n", + core_pos, psci_ret); + result = TEST_RESULT_FAIL; + goto exit_unmap; + } + + tftf_wait_for_event(&cpu_has_finished_test[core_pos]); + } + +exit_unmap: + VERBOSE("Unmapping NS<->SP shared buffer\n"); + + mmap_remove_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_SIZE); + +exit: + return result; +} + +/******************************************************************************/ + +static event_t cpu_can_start_test[PLATFORM_CORE_COUNT]; + +/* Test routine for test_secure_partition_secondary_core() */ +static test_result_t test_secure_partition_secondary_cores_sim_fn(void) +{ + test_result_t result = TEST_RESULT_SUCCESS; + u_register_t cpu_mpid = read_mpidr_el1() & MPID_MASK; + unsigned int core_pos = platform_get_core_pos(cpu_mpid); + + secure_partition_request_info_t *sps_request = + create_sps_request(SPS_CHECK_ALIVE, NULL, 0); + + smc_args mm_communicate_smc = { + MM_COMMUNICATE_AARCH64, + 0, + (u_register_t) sps_request, + 0 + }; + + tftf_wait_for_event(&cpu_can_start_test[core_pos]); + + /* + * Invoke SMCs for some time to make sure that all CPUs are doing it at + * the same time during the test. + */ + for (int i = 0; i < 100; i++) { + smc_ret_values smc_ret = tftf_smc(&mm_communicate_smc); + + if ((uint32_t)smc_ret.ret0 != 0) { + tftf_testcase_printf("Cactus returned 0x%x at CPU %d\n", + (uint32_t)smc_ret.ret0, core_pos); + result = TEST_RESULT_FAIL; + break; + } + } + + tftf_send_event(&cpu_has_finished_test[core_pos]); + + return result; +} + +/* + * @Test_Aim@ This tests that secondary CPUs can access SPM services + * simultaneously. + */ +test_result_t test_secure_partition_secondary_cores_sim(void) +{ + int psci_ret; + u_register_t lead_mpid, cpu_mpid; + unsigned int cpu_node, core_pos; + test_result_t result = TEST_RESULT_SUCCESS; + + SKIP_TEST_IF_LESS_THAN_N_CPUS(2); + + SKIP_TEST_IF_MM_VERSION_LESS_THAN(1, 0); + + VERBOSE("Mapping NS<->SP shared buffer\n"); + + int rc = mmap_add_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_SIZE, + MT_MEMORY | MT_RW | MT_NS); + if (rc != 0) { + tftf_testcase_printf("%d: mmap_add_dynamic_region() = %d\n", + __LINE__, rc); + result = TEST_RESULT_FAIL; + goto exit; + } + + lead_mpid = read_mpidr_el1() & MPID_MASK; + + INFO("Lead CPU is CPU %u\n", platform_get_core_pos(lead_mpid)); + + for_each_cpu(cpu_node) { + cpu_mpid = tftf_get_mpidr_from_node(cpu_node); + core_pos = platform_get_core_pos(cpu_mpid); + tftf_init_event(&cpu_can_start_test[core_pos]); + } + + for_each_cpu(cpu_node) { + cpu_mpid = tftf_get_mpidr_from_node(cpu_node); + /* Skip lead CPU as it is already powered on */ + if (cpu_mpid == lead_mpid) { + continue; + } + + core_pos = platform_get_core_pos(cpu_mpid); + + VERBOSE("Powering on CPU %u\n", core_pos); + + psci_ret = tftf_cpu_on(cpu_mpid, + (uintptr_t)test_secure_partition_secondary_cores_sim_fn, 0); + if (psci_ret != PSCI_E_SUCCESS) { + tftf_testcase_printf( + "Failed to power on CPU %d (rc = %d)\n", + core_pos, psci_ret); + result = TEST_RESULT_FAIL; + goto exit_unmap; + } + } + + for_each_cpu(cpu_node) { + cpu_mpid = tftf_get_mpidr_from_node(cpu_node); + core_pos = platform_get_core_pos(cpu_mpid); + tftf_send_event(&cpu_can_start_test[core_pos]); + } + + result = test_secure_partition_secondary_cores_sim_fn(); + + /* Wait until all CPUs have finished to unmap the NS<->SP buffer */ + for_each_cpu(cpu_node) { + cpu_mpid = tftf_get_mpidr_from_node(cpu_node); + core_pos = platform_get_core_pos(cpu_mpid); + tftf_wait_for_event(&cpu_has_finished_test[core_pos]); + } +exit_unmap: + VERBOSE("Unmapping NS<->SP shared buffer\n"); + + mmap_remove_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_SIZE); +exit: + return result; +} diff --git a/tftf/tests/runtime_services/mm_service/test_secure_service_interrupts.c b/tftf/tests/runtime_services/mm_service/test_secure_service_interrupts.c new file mode 100644 index 000000000..50c3df670 --- /dev/null +++ b/tftf/tests/runtime_services/mm_service/test_secure_service_interrupts.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <debug.h> +#include <mm_svc.h> +#include <secure_partition.h> +#include <smccc.h> +#include <spm_svc.h> +#include <string.h> +#include <test_helpers.h> +#include <tftf_lib.h> +#include <timer.h> +#include <xlat_tables_v2.h> + +static volatile int timer_irq_received; + +/* + * ISR for the timer interrupt. + * Just update a global variable to prove it has been called. + */ +static int timer_handler(void *data) +{ + assert(timer_irq_received == 0); + timer_irq_received = 1; + return 0; +} + + +/* + * @Test_Aim@ Test that non-secure interrupts do not interrupt secure service + * requests. + * + * 1. Register a handler for the non-secure timer interrupt. + * + * 2. Program the non-secure timer to fire in 500 ms. + * + * 3. Make a long-running (> 500 ms) fast secure service request. + * This is achieved by requesting the timer sleep service in Cactus + * with a 1 second sleep delay. + * + * 4. While servicing the timer sleep request, the non-secure timer should + * fire but not interrupt Cactus. + * + * 5. Once back in TFTF, check the response from Cactus, which shows whether the + * secure service indeed ran to completion. + * + * 6. Also check whether the pending non-secure timer interrupt successfully got + * handled in TFTF. + */ +test_result_t test_secure_partition_interrupt_by_ns(void) +{ + secure_partition_request_info_t *sps_request; + test_result_t result = TEST_RESULT_FAIL; + + SKIP_TEST_IF_MM_VERSION_LESS_THAN(1, 0); + + VERBOSE("Mapping NS<->SP shared buffer\n"); + + int rc = mmap_add_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_SIZE, + MT_MEMORY | MT_RW | MT_NS); + if (rc != 0) { + tftf_testcase_printf("%d: mmap_add_dynamic_region() = %d\n", + __LINE__, rc); + return TEST_RESULT_FAIL; + } + + timer_irq_received = 0; + tftf_timer_register_handler(timer_handler); + + NOTICE("Programming the timer...\n"); + rc = tftf_program_timer(500); + if (rc < 0) { + tftf_testcase_printf("Failed to program timer (%d)\n", rc); + goto exit_test; + } + + INFO("Sending MM_COMMUNICATE_AARCH64 to Cactus\n"); + + uint8_t timer_delay = 1; + sps_request = create_sps_request(SPS_TIMER_SLEEP, + &timer_delay, sizeof(timer_delay)); + smc_args mm_communicate_smc = { + MM_COMMUNICATE_AARCH64, + 0, /* cookie, MBZ */ + (uintptr_t) sps_request, + 0 + }; + + smc_ret_values smc_ret = tftf_smc(&mm_communicate_smc); + + INFO("Returned from Cactus, MM_COMMUNICATE_AARCH64 handling complete\n"); + + /* + * If MM_COMMUNICATE gets interrupted, SPM will return SPM_QUEUED, which + * is normally not a valid return value for MM_COMMUNICATE. + */ + if ((uint32_t) smc_ret.ret0 != SPM_SUCCESS) { + tftf_testcase_printf("Cactus returned: 0x%x\n", + (uint32_t) smc_ret.ret0); + goto exit_test; + } + + uint32_t cactus_response; + memcpy(&cactus_response, sps_request->data, sizeof(cactus_response)); + if (cactus_response != CACTUS_FAST_REQUEST_SUCCESS) { + tftf_testcase_printf("Error code from the timer secure service: 0x%x\n", + cactus_response); + goto exit_test; + } + + /* + * If the timer interrupt is still pending, make sure it is taken right + * now. + */ + isb(); + + if (timer_irq_received == 1) + result = TEST_RESULT_SUCCESS; + +exit_test: + tftf_cancel_timer(); + tftf_timer_unregister_handler(); + + VERBOSE("Unmapping NS<->SP shared buffer\n"); + + mmap_remove_dynamic_region(ARM_SECURE_SERVICE_BUFFER_BASE, + ARM_SECURE_SERVICE_BUFFER_SIZE); + + return result; +} diff --git a/tftf/tests/tests-spm-mm.mk b/tftf/tests/tests-spm-mm.mk new file mode 100644 index 000000000..1a6cb3cd0 --- /dev/null +++ b/tftf/tests/tests-spm-mm.mk @@ -0,0 +1,12 @@ +# +# Copyright (c) 2018, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +TESTS_SOURCES += \ + $(addprefix tftf/tests/runtime_services/mm_service/, \ + secure_service_helpers.c \ + test_secure_service_handle.c \ + test_secure_service_interrupts.c \ + ) diff --git a/tftf/tests/tests-spm-mm.xml b/tftf/tests/tests-spm-mm.xml new file mode 100644 index 000000000..fc7d98e15 --- /dev/null +++ b/tftf/tests/tests-spm-mm.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (c) 2018-2019, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-3-Clause +--> + +<testsuites> + + <testsuite name="SPM tests" + description="Test SPM APIs"> +<!-- TODO: This test fails in the CI. + <testcase name="SPM NS interrupts test" + function="test_secure_partition_interrupt_by_ns" /> +--> + <testcase name="SPM secondary CPUs sequential test" + function="test_secure_partition_secondary_cores_seq" /> +<!-- TODO: This test fails in the CI. + <testcase name="SPM secondary CPUs simultaneous test" + function="test_secure_partition_secondary_cores_sim" /> +--> + </testsuite> + +</testsuites> |