From b61d94a1a2a13ea4395013fe9d8df3f0cb105faf Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Sun, 19 Dec 2021 21:37:50 +0000 Subject: [PATCH 01/10] refactor(spm_mm): reorganize secure partition manager code In preparation for adding the EL3 SPMC configuration as defined in the FF-A specification, restructure the existing SPM_MM code. With this restructuring of the code, the 'spm_mm' directory is renamed as 'spm' and the code inside has been split into two sub-directories named 'common' and 'spm_mm'. The code in 'spm_mm' directory contains the code that implements the MM interface. In subsequent patches, the 'spmc' directory will be introduced under the 'spm' directory providing the code that implements the 'FF-A' interface. Currently the common functionality for S-EL1 partitions is limited to assembler functions to enter and exit an SP synchronously. Signed-off-by: Marc Bonnici Change-Id: I37739b9b53bc68e151ab5c1c0c6a15b3ee362241 --- bl31/bl31.mk | 4 +- .../common/aarch64/spm_helpers.S} | 4 +- .../std_svc/spm/common/include/spm_common.h | 42 +++++++++++++++++++ services/std_svc/spm/common/spm.mk | 17 ++++++++ .../spm_mm/aarch64/spm_mm_shim_exceptions.S | 2 +- services/std_svc/{ => spm}/spm_mm/spm_mm.mk | 9 ++-- .../std_svc/{ => spm}/spm_mm/spm_mm_main.c | 3 +- .../std_svc/{ => spm}/spm_mm/spm_mm_private.h | 6 +-- .../std_svc/{ => spm}/spm_mm/spm_mm_setup.c | 3 +- .../{ => spm}/spm_mm/spm_mm_shim_private.h | 2 +- .../std_svc/{ => spm}/spm_mm/spm_mm_xlat.c | 2 +- 11 files changed, 77 insertions(+), 17 deletions(-) rename services/std_svc/{spm_mm/aarch64/spm_mm_helpers.S => spm/common/aarch64/spm_helpers.S} (96%) create mode 100644 services/std_svc/spm/common/include/spm_common.h create mode 100644 services/std_svc/spm/common/spm.mk rename services/std_svc/{ => spm}/spm_mm/aarch64/spm_mm_shim_exceptions.S (97%) rename services/std_svc/{ => spm}/spm_mm/spm_mm.mk (78%) rename services/std_svc/{ => spm}/spm_mm/spm_mm_main.c (99%) rename services/std_svc/{ => spm}/spm_mm/spm_mm_private.h (88%) rename services/std_svc/{ => spm}/spm_mm/spm_mm_setup.c (98%) rename services/std_svc/{ => spm}/spm_mm/spm_mm_shim_private.h (90%) rename services/std_svc/{ => spm}/spm_mm/spm_mm_xlat.c (98%) diff --git a/bl31/bl31.mk b/bl31/bl31.mk index 25c7964a1..7d83e3cb3 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -18,7 +18,8 @@ ifeq (${SPM_MM},1) $(error EL3_EXCEPTION_HANDLING must be 1 for SPM-MM support) else $(info Including SPM Management Mode (MM) makefile) - include services/std_svc/spm_mm/spm_mm.mk + include services/std_svc/spm/common/spm.mk + include services/std_svc/spm/spm_mm/spm_mm.mk endif endif @@ -40,6 +41,7 @@ BL31_SOURCES += bl31/bl31_main.c \ services/std_svc/std_svc_setup.c \ ${PSCI_LIB_SOURCES} \ ${SPMD_SOURCES} \ + ${SPM_MM_SOURCES} \ ${SPM_SOURCES} ifeq (${DISABLE_MTPMU},1) diff --git a/services/std_svc/spm_mm/aarch64/spm_mm_helpers.S b/services/std_svc/spm/common/aarch64/spm_helpers.S similarity index 96% rename from services/std_svc/spm_mm/aarch64/spm_mm_helpers.S rename to services/std_svc/spm/common/aarch64/spm_helpers.S index 2c3aaf7ae..95e69fb68 100644 --- a/services/std_svc/spm_mm/aarch64/spm_mm_helpers.S +++ b/services/std_svc/spm/common/aarch64/spm_helpers.S @@ -1,11 +1,11 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include -#include "../spm_mm_private.h" +#include "spm_common.h" .global spm_secure_partition_enter .global spm_secure_partition_exit diff --git a/services/std_svc/spm/common/include/spm_common.h b/services/std_svc/spm/common/include/spm_common.h new file mode 100644 index 000000000..68805fc4d --- /dev/null +++ b/services/std_svc/spm/common/include/spm_common.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SPM_COMMON_H +#define SPM_COMMON_H + +#include + +/******************************************************************************* + * Constants that allow assembler code to preserve callee-saved registers of the + * C runtime context while performing a security state switch. + ******************************************************************************/ +#define SP_C_RT_CTX_X19 0x0 +#define SP_C_RT_CTX_X20 0x8 +#define SP_C_RT_CTX_X21 0x10 +#define SP_C_RT_CTX_X22 0x18 +#define SP_C_RT_CTX_X23 0x20 +#define SP_C_RT_CTX_X24 0x28 +#define SP_C_RT_CTX_X25 0x30 +#define SP_C_RT_CTX_X26 0x38 +#define SP_C_RT_CTX_X27 0x40 +#define SP_C_RT_CTX_X28 0x48 +#define SP_C_RT_CTX_X29 0x50 +#define SP_C_RT_CTX_X30 0x58 + +#define SP_C_RT_CTX_SIZE 0x60 +#define SP_C_RT_CTX_ENTRIES (SP_C_RT_CTX_SIZE >> DWORD_SHIFT) + +#ifndef __ASSEMBLER__ + +#include + +/* Assembly helpers */ +uint64_t spm_secure_partition_enter(uint64_t *c_rt_ctx); +void __dead2 spm_secure_partition_exit(uint64_t c_rt_ctx, uint64_t ret); + +#endif /* __ASSEMBLER__ */ + +#endif /* SPM_COMMON_H */ diff --git a/services/std_svc/spm/common/spm.mk b/services/std_svc/spm/common/spm.mk new file mode 100644 index 000000000..9aa96be3f --- /dev/null +++ b/services/std_svc/spm/common/spm.mk @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifneq (${ARCH},aarch64) + $(error "Error: SPM is only supported on aarch64.") +endif + +INCLUDES += -Iservices/std_svc/spm/common/include + +SPM_SOURCES := $(addprefix services/std_svc/spm/common/,\ + ${ARCH}/spm_helpers.S) + +# Let the top-level Makefile know that we intend to include a BL32 image +NEED_BL32 := yes diff --git a/services/std_svc/spm_mm/aarch64/spm_mm_shim_exceptions.S b/services/std_svc/spm/spm_mm/aarch64/spm_mm_shim_exceptions.S similarity index 97% rename from services/std_svc/spm_mm/aarch64/spm_mm_shim_exceptions.S rename to services/std_svc/spm/spm_mm/aarch64/spm_mm_shim_exceptions.S index be4084cfb..836f75c8d 100644 --- a/services/std_svc/spm_mm/aarch64/spm_mm_shim_exceptions.S +++ b/services/std_svc/spm/spm_mm/aarch64/spm_mm_shim_exceptions.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/services/std_svc/spm_mm/spm_mm.mk b/services/std_svc/spm/spm_mm/spm_mm.mk similarity index 78% rename from services/std_svc/spm_mm/spm_mm.mk rename to services/std_svc/spm/spm_mm/spm_mm.mk index a87bdd878..78ef0c9fb 100644 --- a/services/std_svc/spm_mm/spm_mm.mk +++ b/services/std_svc/spm/spm_mm/spm_mm.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -17,11 +17,10 @@ ifeq (${ENABLE_SME_FOR_NS},1) $(error "Error: SPM_MM is not compatible with ENABLE_SME_FOR_NS") endif -SPM_SOURCES := $(addprefix services/std_svc/spm_mm/, \ - ${ARCH}/spm_mm_helpers.S \ +SPM_MM_SOURCES := $(addprefix services/std_svc/spm/spm_mm/, \ ${ARCH}/spm_mm_shim_exceptions.S \ - spm_mm_main.c \ - spm_mm_setup.c \ + spm_mm_main.c \ + spm_mm_setup.c \ spm_mm_xlat.c) diff --git a/services/std_svc/spm_mm/spm_mm_main.c b/services/std_svc/spm/spm_mm/spm_mm_main.c similarity index 99% rename from services/std_svc/spm_mm/spm_mm_main.c rename to services/std_svc/spm/spm_mm/spm_mm_main.c index 14c0038ba..e71e65b18 100644 --- a/services/std_svc/spm_mm/spm_mm_main.c +++ b/services/std_svc/spm/spm_mm/spm_mm_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,6 +22,7 @@ #include #include +#include "spm_common.h" #include "spm_mm_private.h" /******************************************************************************* diff --git a/services/std_svc/spm_mm/spm_mm_private.h b/services/std_svc/spm/spm_mm/spm_mm_private.h similarity index 88% rename from services/std_svc/spm_mm/spm_mm_private.h rename to services/std_svc/spm/spm_mm/spm_mm_private.h index 45b4789ad..0eff1c071 100644 --- a/services/std_svc/spm_mm/spm_mm_private.h +++ b/services/std_svc/spm/spm_mm/spm_mm_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,6 +8,7 @@ #define SPM_MM_PRIVATE_H #include +#include "spm_common.h" /******************************************************************************* * Constants that allow assembler code to preserve callee-saved registers of the @@ -51,9 +52,6 @@ typedef struct sp_context { spinlock_t state_lock; } sp_context_t; -/* Assembly helpers */ -uint64_t spm_secure_partition_enter(uint64_t *c_rt_ctx); -void __dead2 spm_secure_partition_exit(uint64_t c_rt_ctx, uint64_t ret); void spm_sp_setup(sp_context_t *sp_ctx); diff --git a/services/std_svc/spm_mm/spm_mm_setup.c b/services/std_svc/spm/spm_mm/spm_mm_setup.c similarity index 98% rename from services/std_svc/spm_mm/spm_mm_setup.c rename to services/std_svc/spm/spm_mm/spm_mm_setup.c index 9d681c2da..04dc21291 100644 --- a/services/std_svc/spm_mm/spm_mm_setup.c +++ b/services/std_svc/spm/spm_mm/spm_mm_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2021, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -19,6 +19,7 @@ #include #include +#include "spm_common.h" #include "spm_mm_private.h" #include "spm_mm_shim_private.h" diff --git a/services/std_svc/spm_mm/spm_mm_shim_private.h b/services/std_svc/spm/spm_mm/spm_mm_shim_private.h similarity index 90% rename from services/std_svc/spm_mm/spm_mm_shim_private.h rename to services/std_svc/spm/spm_mm/spm_mm_shim_private.h index 0c8d894f1..f69c748ad 100644 --- a/services/std_svc/spm_mm/spm_mm_shim_private.h +++ b/services/std_svc/spm/spm_mm/spm_mm_shim_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/services/std_svc/spm_mm/spm_mm_xlat.c b/services/std_svc/spm/spm_mm/spm_mm_xlat.c similarity index 98% rename from services/std_svc/spm_mm/spm_mm_xlat.c rename to services/std_svc/spm/spm_mm/spm_mm_xlat.c index eae597cab..6261016f7 100644 --- a/services/std_svc/spm_mm/spm_mm_xlat.c +++ b/services/std_svc/spm/spm_mm/spm_mm_xlat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ From 1d63ae4d0d8374a732113565be90d58861506e39 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Wed, 1 Dec 2021 18:00:40 +0000 Subject: [PATCH 02/10] feat(spmc): enable building of the SPMC at EL3 Introduce build flag for enabling the secure partition manager core, SPMC_AT_EL3. When enabled, the SPMC module will be included into the BL31 image. By default the flag is disabled. Signed-off-by: Marc Bonnici Change-Id: I5ea1b953e5880a07ffc91c4dea876a375850cf2a --- Makefile | 8 ++++ bl31/bl31.mk | 9 +++++ docs/components/secure-partition-manager.rst | 40 +++++++++++++++----- docs/getting_started/build-options.rst | 13 +++++-- make_helpers/defaults.mk | 3 ++ 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index f91699c44..313688782 100644 --- a/Makefile +++ b/Makefile @@ -527,6 +527,9 @@ ifneq (${SPD},none) ifeq ($(CTX_INCLUDE_EL2_REGS),0) $(error SPMD with SPM at S-EL2 requires CTX_INCLUDE_EL2_REGS option) endif + ifeq ($(SPMC_AT_EL3),1) + $(error SPM cannot be enabled in both S-EL2 and EL3.) + endif endif ifeq ($(findstring optee_sp,$(ARM_SPMC_MANIFEST_DTS)),optee_sp) @@ -577,6 +580,9 @@ ifneq (${ENABLE_RME},0) ifneq (${ARCH},aarch64) $(error ENABLE_RME requires AArch64) endif +ifeq ($(SPMC_AT_EL3),1) + $(error SPMC_AT_EL3 and ENABLE_RME cannot both be enabled.) +endif include services/std_svc/rmmd/rmmd.mk $(warning "RME is an experimental feature") endif @@ -1006,6 +1012,7 @@ $(eval $(call assert_booleans,\ SEPARATE_NOBITS_REGION \ SPIN_ON_BL1_EXIT \ SPM_MM \ + SPMC_AT_EL3 \ SPMD_SPM_AT_SEL2 \ TRUSTED_BOARD_BOOT \ CRYPTO_SUPPORT \ @@ -1138,6 +1145,7 @@ $(eval $(call add_defines,\ SPD_${SPD} \ SPIN_ON_BL1_EXIT \ SPM_MM \ + SPMC_AT_EL3 \ SPMD_SPM_AT_SEL2 \ TRUSTED_BOARD_BOOT \ CRYPTO_SUPPORT \ diff --git a/bl31/bl31.mk b/bl31/bl31.mk index 7d83e3cb3..214cf2f66 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -25,6 +25,14 @@ endif include lib/extensions/amu/amu.mk include lib/mpmm/mpmm.mk + +ifeq (${SPMC_AT_EL3},1) + $(warning "EL3 SPMC is an experimental feature") + $(info Including EL3 SPMC makefile) + include services/std_svc/spm/common/spm.mk + include services/std_svc/spm/el3_spmc/spmc.mk +endif + include lib/psci/psci_lib.mk BL31_SOURCES += bl31/bl31_main.c \ @@ -42,6 +50,7 @@ BL31_SOURCES += bl31/bl31_main.c \ ${PSCI_LIB_SOURCES} \ ${SPMD_SOURCES} \ ${SPM_MM_SOURCES} \ + ${SPMC_SOURCES} \ ${SPM_SOURCES} ifeq (${DISABLE_MTPMU},1) diff --git a/docs/components/secure-partition-manager.rst b/docs/components/secure-partition-manager.rst index af298e3e6..f2b8659dc 100644 --- a/docs/components/secure-partition-manager.rst +++ b/docs/components/secure-partition-manager.rst @@ -127,14 +127,18 @@ TF-A build options This section explains the TF-A build options involved in building with support for an FF-A based SPM where the SPMD is located at EL3 and the -SPMC located at S-EL1 or S-EL2: +SPMC located at S-EL1, S-EL2 or EL3: - **SPD=spmd**: this option selects the SPMD component to relay the FF-A protocol from NWd to SWd back and forth. It is not possible to enable another Secure Payload Dispatcher when this option is chosen. - **SPMD_SPM_AT_SEL2**: this option adjusts the SPMC exception - level to being S-EL1 or S-EL2. It defaults to enabled (value 1) when + level to being at S-EL2. It defaults to enabled (value 1) when SPD=spmd is chosen. +- **SPMC_AT_EL3**: this option adjusts the SPMC exception level to being + at EL3. +- If neither **SPMD_SPM_AT_SEL2** or **SPMC_AT_EL3** are enabled the SPMC + exception level is set to S-EL1. - **CTX_INCLUDE_EL2_REGS**: this option permits saving (resp. restoring) the EL2 system register context before entering (resp. after leaving) the SPMC. It is mandatorily enabled when @@ -146,14 +150,16 @@ SPMC located at S-EL1 or S-EL2: is required when ``SPMD_SPM_AT_SEL2`` is enabled hence when multiple secure partitions are to be loaded on behalf of the SPMC. -+---------------+----------------------+------------------+ -| | CTX_INCLUDE_EL2_REGS | SPMD_SPM_AT_SEL2 | -+---------------+----------------------+------------------+ -| SPMC at S-EL1 | 0 | 0 | -+---------------+----------------------+------------------+ -| SPMC at S-EL2 | 1 | 1 (default when | -| | | SPD=spmd) | -+---------------+----------------------+------------------+ ++---------------+----------------------+------------------+-------------+ +| | CTX_INCLUDE_EL2_REGS | SPMD_SPM_AT_SEL2 | SPMC_AT_EL3 | ++---------------+----------------------+------------------+-------------+ +| SPMC at S-EL1 | 0 | 0 | 0 | ++---------------+----------------------+------------------+-------------+ +| SPMC at S-EL2 | 1 | 1 (default when | 0 | +| | | SPD=spmd) | | ++---------------+----------------------+------------------+-------------+ +| SPMC at EL3 | 0 | 0 | 1 | ++---------------+----------------------+------------------+-------------+ Other combinations of such build options either break the build or are not supported. @@ -229,6 +235,20 @@ Same as above with enabling secure boot in addition: GENERATE_COT=1 \ all fip +Sample TF-A build command line when SPMC is located at EL3: + +.. code:: shell + + make \ + CROSS_COMPILE=aarch64-none-elf- \ + SPD=spmd \ + SPMD_SPM_AT_SEL2=0 \ + SPMC_AT_EL3=1 \ + BL32= \ + BL33= \ + PLAT=fvp \ + all fip + FVP model invocation ==================== diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst index 4dbf5cb9f..d30e22f0a 100644 --- a/docs/getting_started/build-options.rst +++ b/docs/getting_started/build-options.rst @@ -780,13 +780,20 @@ Common build options firmware images have been loaded in memory, and the MMU and caches are turned off. Refer to the "Debugging options" section for more details. +- ``SPMC_AT_EL3`` : This boolean option is used jointly with the SPM + Dispatcher option (``SPD=spmd``). When enabled (1) it indicates the SPMC + component runs at the EL3 exception level. The default value is ``0`` ( + disabled). This configuration supports pre-Armv8.4 platforms (aka not + implementing the ``FEAT_SEL2`` extension). This is an experimental feature. + - ``SPMD_SPM_AT_SEL2`` : This boolean option is used jointly with the SPM Dispatcher option (``SPD=spmd``). When enabled (1) it indicates the SPMC - component runs at the S-EL2 execution state provided by the Armv8.4-SecEL2 + component runs at the S-EL2 exception level provided by the ``FEAT_SEL2`` extension. This is the default when enabling the SPM Dispatcher. When disabled (0) it indicates the SPMC component runs at the S-EL1 execution - state. This latter configuration supports pre-Armv8.4 platforms (aka not - implementing the Armv8.4-SecEL2 extension). + state or at EL3 if ``SPMC_AT_EL3`` is enabled. The latter configurations + support pre-Armv8.4 platforms (aka not implementing the ``FEAT_SEL2`` + extension). - ``SPM_MM`` : Boolean option to enable the Management Mode (MM)-based Secure Partition Manager (SPM) implementation. The default value is ``0`` diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index 99f44a4ec..7b66569de 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -288,6 +288,9 @@ SPD := none # Enable the Management Mode (MM)-based Secure Partition Manager implementation SPM_MM := 0 +# Use the FF-A SPMC implementation in EL3. +SPMC_AT_EL3 := 0 + # Use SPM at S-EL2 as a default config for SPMD SPMD_SPM_AT_SEL2 := 1 From 70d986ddbbf56a20c7550c079dd4dc9462332594 Mon Sep 17 00:00:00 2001 From: Sayanta Pattanayak Date: Sat, 6 Mar 2021 11:43:06 +0530 Subject: [PATCH 03/10] feat(spmc): prevent read only xlat tables with the EL3 SPMC If using the EL3 SPMC ensure that we don't mark the translation tables as read only. The SPMC requires the ability to map and unmap a partitions RX/TX buffers at runtime. Signed-off-by: Sayanta Pattanayak Signed-off-by: Marc Bonnici Change-Id: Ibb78a6a2e3847ce4ec74ce81a9bb61ce34fec24c --- lib/xlat_tables_v2/ro_xlat_tables.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/xlat_tables_v2/ro_xlat_tables.mk b/lib/xlat_tables_v2/ro_xlat_tables.mk index 7991e1afd..fb8a426bf 100644 --- a/lib/xlat_tables_v2/ro_xlat_tables.mk +++ b/lib/xlat_tables_v2/ro_xlat_tables.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, ARM Limited. All rights reserved. +# Copyright (c) 2020-2022, ARM Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -34,4 +34,8 @@ else # if AArch64 attributes, which is not possible once the translation tables \ have been made read-only.") endif + ifeq (${SPMC_AT_EL3},1) + $(error "EL3 SPMC requires functionality from the dynamic translation \ + library and is incompatible with ALLOW_RO_XLAT_TABLES.") + endif endif From 5096aeb2ba646548a7a6ab59e975b996e6c9026a Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Wed, 1 Dec 2021 17:57:04 +0000 Subject: [PATCH 04/10] feat(spmc): add FF-A secure partition manager core This patch introduces the core support for enabling an SPMC in EL3 as per the FF-A spec. The current implemented functionality is targeted to enable initialization of the SPMC itself and initial support for bringing up a single S-EL1 SP. This includes initialization of the SPMC's internal state, parsing of an SP's manifest, preparing the cpu contexts and appropriate system registers for the Secure Partition. The spmc_smc_handler is the main handler for all incoming SMCs to the SPMC, FF-A ABI handlers and functionality will be implemented in subsequent patches. Signed-off-by: Marc Bonnici Change-Id: Ib33c240b91e54cbd018a69fec880d02adfbe12b9 --- include/services/ffa_svc.h | 33 +- include/services/spmc_svc.h | 38 ++ services/std_svc/spm/el3_spmc/spmc.h | 185 +++++++++ services/std_svc/spm/el3_spmc/spmc.mk | 17 + services/std_svc/spm/el3_spmc/spmc_main.c | 438 +++++++++++++++++++++ services/std_svc/spm/el3_spmc/spmc_setup.c | 112 ++++++ services/std_svc/spmd/spmd_private.h | 6 - 7 files changed, 822 insertions(+), 7 deletions(-) create mode 100644 include/services/spmc_svc.h create mode 100644 services/std_svc/spm/el3_spmc/spmc.h create mode 100644 services/std_svc/spm/el3_spmc/spmc.mk create mode 100644 services/std_svc/spm/el3_spmc/spmc_main.c create mode 100644 services/std_svc/spm/el3_spmc/spmc_setup.c diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h index 9a7c48989..ff1e04a91 100644 --- a/include/services/ffa_svc.h +++ b/include/services/ffa_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Arm Limited. All rights reserved. + * Copyright (c) 2020-2022, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,6 +7,8 @@ #ifndef FFA_SVC_H #define FFA_SVC_H +#include + #include #include #include @@ -175,6 +177,15 @@ */ #define FFA_ENDPOINT_ID_MAX U(1 << 16) +/* + * Reserve endpoint id for the SPMD. + */ +#define SPMD_DIRECT_MSG_ENDPOINT_ID U(FFA_ENDPOINT_ID_MAX - 1) + +/* Mask and shift to check valid secure FF-A Endpoint ID. */ +#define SPMC_SECURE_ID_MASK U(1) +#define SPMC_SECURE_ID_SHIFT U(15) + /* * Mask for source and destination endpoint id in * a direct message request/response. @@ -209,4 +220,24 @@ static inline uint16_t ffa_endpoint_source(unsigned int ep) FFA_DIRECT_MSG_ENDPOINT_ID_MASK; } +/****************************************************************************** + * FF-A helper functions to determine partition ID world. + *****************************************************************************/ + +/* + * Determine if provided ID is in the secure world. + */ +static inline bool ffa_is_secure_world_id(uint16_t id) +{ + return ((id >> SPMC_SECURE_ID_SHIFT) & SPMC_SECURE_ID_MASK) == 1; +} + +/* + * Determine if provided ID is in the normal world. + */ +static inline bool ffa_is_normal_world_id(uint16_t id) +{ + return !ffa_is_secure_world_id(id); +} + #endif /* FFA_SVC_H */ diff --git a/include/services/spmc_svc.h b/include/services/spmc_svc.h new file mode 100644 index 000000000..9dbe04589 --- /dev/null +++ b/include/services/spmc_svc.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SPMC_SVC_H +#define SPMC_SVC_H + +#ifndef __ASSEMBLER__ +#include + +#include +#include + +int spmc_setup(void); +void *spmc_get_config_addr(void); + +void spmc_set_config_addr(uintptr_t soc_fw_config); + +uint64_t spmc_smc_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags); + +static inline bool is_spmc_at_el3(void) +{ + return SPMC_AT_EL3 == 1; +} + +#endif /* __ASSEMBLER__ */ + +#endif /* SPMC_SVC_H */ diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h new file mode 100644 index 000000000..e89151673 --- /dev/null +++ b/services/std_svc/spm/el3_spmc/spmc.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SPMC_H +#define SPMC_H + +#include + +#include +#include +#include "spm_common.h" + +/* + * Ranges of FF-A IDs for Normal world and Secure world components. The + * convention matches that used by other SPMCs i.e. Hafnium and OP-TEE. + */ +#define FFA_NWD_ID_BASE 0x0 +#define FFA_NWD_ID_LIMIT 0x7FFF +#define FFA_SWD_ID_BASE 0x8000 +#define FFA_SWD_ID_LIMIT SPMD_DIRECT_MSG_ENDPOINT_ID - 1 +#define FFA_SWD_ID_MASK 0x8000 + +/* First ID is reserved for the SPMC */ +#define FFA_SPMC_ID U(FFA_SWD_ID_BASE) +/* SP IDs are allocated after the SPMC ID */ +#define FFA_SP_ID_BASE (FFA_SPMC_ID + 1) +/* Align with Hafnium implementation */ +#define INV_SP_ID 0x7FFF + +/* FF-A warm boot types. */ +#define FFA_WB_TYPE_S2RAM 0 +#define FFA_WB_TYPE_NOTS2RAM 1 + +/* + * Runtime states of an execution context as per the FF-A v1.1 specification. + */ +enum sp_runtime_states { + RT_STATE_WAITING, + RT_STATE_RUNNING, + RT_STATE_PREEMPTED, + RT_STATE_BLOCKED +}; + +/* + * Runtime model of an execution context as per the FF-A v1.1 specification. Its + * value is valid only if the execution context is not in the waiting state. + */ +enum sp_runtime_model { + RT_MODEL_DIR_REQ, + RT_MODEL_RUN, + RT_MODEL_INIT, + RT_MODEL_INTR +}; + +enum sp_runtime_el { + EL1 = 0, + S_EL0, + S_EL1 +}; + +enum sp_execution_state { + SP_STATE_AARCH64 = 0, + SP_STATE_AARCH32 +}; + +/* + * Execution context members for an SP. This is a bit like struct + * vcpu in a hypervisor. + */ +struct sp_exec_ctx { + /* + * Store the stack address to restore C runtime context from after + * returning from a synchronous entry into the SP. + */ + uint64_t c_rt_ctx; + + /* Space to maintain the architectural state of an SP. */ + cpu_context_t cpu_ctx; + + /* Track the current runtime state of the SP. */ + enum sp_runtime_states rt_state; + + /* Track the current runtime model of the SP. */ + enum sp_runtime_model rt_model; +}; + +/* + * Structure to describe the cumulative properties of an SP. + */ +struct secure_partition_desc { + /* + * Execution contexts allocated to this endpoint. Ideally, + * we need as many contexts as there are physical cpus only + * for a S-EL1 SP which is MP-pinned. + */ + struct sp_exec_ctx ec[PLATFORM_CORE_COUNT]; + + /* ID of the Secure Partition. */ + uint16_t sp_id; + + /* Runtime EL. */ + enum sp_runtime_el runtime_el; + + /* Partition UUID. */ + uint32_t uuid[4]; + + /* Partition Properties. */ + uint32_t properties; + + /* Supported FF-A Version. */ + uint32_t ffa_version; + + /* Execution State. */ + enum sp_execution_state execution_state; + + /* Secondary entrypoint. Only valid for a S-EL1 SP. */ + uintptr_t secondary_ep; +}; + +/* + * This define identifies the only SP that will be initialised and participate + * in FF-A communication. The implementation leaves the door open for more SPs + * to be managed in future but for now it is reasonable to assume that either a + * single S-EL0 or a single S-EL1 SP will be supported. This define will be used + * to identify which SP descriptor to initialise and manage during SP runtime. + */ +#define ACTIVE_SP_DESC_INDEX 0 + +/* + * Structure to describe the cumulative properties of the Hypervisor and + * NS-Endpoints. + */ +struct ns_endpoint_desc { + /* + * ID of the NS-Endpoint or Hypervisor. + */ + uint16_t ns_ep_id; + + /* + * Supported FF-A Version. + */ + uint32_t ffa_version; +}; + +/* Setup Function for different SP types. */ +void spmc_sp_common_setup(struct secure_partition_desc *sp, + entry_point_info_t *ep_info); +void spmc_el1_sp_setup(struct secure_partition_desc *sp, + entry_point_info_t *ep_info); +void spmc_sp_common_ep_commit(struct secure_partition_desc *sp, + entry_point_info_t *ep_info); + +/* + * Helper function to perform a synchronous entry into a SP. + */ +uint64_t spmc_sp_synchronous_entry(struct sp_exec_ctx *ec); + +/* + * Helper function to obtain the descriptor of the current SP on a physical cpu. + */ +struct secure_partition_desc *spmc_get_current_sp_ctx(void); + +/* + * Helper function to obtain the execution context of an SP on a + * physical cpu. + */ +struct sp_exec_ctx *spmc_get_sp_ec(struct secure_partition_desc *sp); + +/* + * Helper function to obtain the index of the execution context of an SP on a + * physical cpu. + */ +unsigned int get_ec_index(struct secure_partition_desc *sp); + +uint64_t spmc_ffa_error_return(void *handle, int error_code); + +/* + * Ensure a partition ID does not clash and follows the secure world convention. + */ +bool is_ffa_secure_id_valid(uint16_t partition_id); + +#endif /* SPMC_H */ diff --git a/services/std_svc/spm/el3_spmc/spmc.mk b/services/std_svc/spm/el3_spmc/spmc.mk new file mode 100644 index 000000000..2b154dd7d --- /dev/null +++ b/services/std_svc/spm/el3_spmc/spmc.mk @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifneq (${ARCH},aarch64) + $(error "Error: SPMC is only supported on aarch64.") +endif + +SPMC_SOURCES := $(addprefix services/std_svc/spm/el3_spmc/, \ + spmc_main.c \ + spmc_setup.c) + + +# Let the top-level Makefile know that we intend to include a BL32 image +NEED_BL32 := yes diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c new file mode 100644 index 000000000..80d7a4c72 --- /dev/null +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spmc.h" + +#include + +/* + * Allocate a secure partition descriptor to describe each SP in the system that + * does not reside at EL3. + */ +static struct secure_partition_desc sp_desc[SECURE_PARTITION_COUNT]; + +/* + * Allocate an NS endpoint descriptor to describe each VM and the Hypervisor in + * the system that interacts with a SP. It is used to track the Hypervisor + * buffer pair, version and ID for now. It could be extended to track VM + * properties when the SPMC supports indirect messaging. + */ +static struct ns_endpoint_desc ns_ep_desc[NS_PARTITION_COUNT]; + +/* + * Helper function to obtain the descriptor of the last SP to whom control was + * handed to on this physical cpu. Currently, we assume there is only one SP. + * TODO: Expand to track multiple partitions when required. + */ +struct secure_partition_desc *spmc_get_current_sp_ctx(void) +{ + return &(sp_desc[ACTIVE_SP_DESC_INDEX]); +} + +/* + * Helper function to obtain the execution context of an SP on the + * current physical cpu. + */ +struct sp_exec_ctx *spmc_get_sp_ec(struct secure_partition_desc *sp) +{ + return &(sp->ec[get_ec_index(sp)]); +} + +/* Helper function to get pointer to SP context from its ID. */ +struct secure_partition_desc *spmc_get_sp_ctx(uint16_t id) +{ + /* Check for SWd Partitions. */ + for (unsigned int i = 0U; i < SECURE_PARTITION_COUNT; i++) { + if (sp_desc[i].sp_id == id) { + return &(sp_desc[i]); + } + } + return NULL; +} + +/****************************************************************************** + * This function returns to the place where spmc_sp_synchronous_entry() was + * called originally. + ******************************************************************************/ +__dead2 void spmc_sp_synchronous_exit(struct sp_exec_ctx *ec, uint64_t rc) +{ + /* + * The SPM must have initiated the original request through a + * synchronous entry into the secure partition. Jump back to the + * original C runtime context with the value of rc in x0; + */ + spm_secure_partition_exit(ec->c_rt_ctx, rc); + + panic(); +} + +/******************************************************************************* + * Return FFA_ERROR with specified error code. + ******************************************************************************/ +uint64_t spmc_ffa_error_return(void *handle, int error_code) +{ + SMC_RET8(handle, FFA_ERROR, + FFA_TARGET_INFO_MBZ, error_code, + FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ, + FFA_PARAM_MBZ, FFA_PARAM_MBZ); +} + +/****************************************************************************** + * Helper function to validate a secure partition ID to ensure it does not + * conflict with any other FF-A component and follows the convention to + * indicate it resides within the secure world. + ******************************************************************************/ +bool is_ffa_secure_id_valid(uint16_t partition_id) +{ + /* Ensure the ID is not the invalid partition ID. */ + if (partition_id == INV_SP_ID) { + return false; + } + + /* Ensure the ID is not the SPMD ID. */ + if (partition_id == SPMD_DIRECT_MSG_ENDPOINT_ID) { + return false; + } + + /* + * Ensure the ID follows the convention to indicate it resides + * in the secure world. + */ + if (!ffa_is_secure_world_id(partition_id)) { + return false; + } + + /* Ensure we don't conflict with the SPMC partition ID. */ + if (partition_id == FFA_SPMC_ID) { + return false; + } + + /* Ensure we do not already have an SP context with this ID. */ + if (spmc_get_sp_ctx(partition_id)) { + return false; + } + + return true; +} + +/******************************************************************************* + * This function will parse the Secure Partition Manifest. From manifest, it + * will fetch details for preparing Secure partition image context and secure + * partition image boot arguments if any. + ******************************************************************************/ +static int sp_manifest_parse(void *sp_manifest, int offset, + struct secure_partition_desc *sp, + entry_point_info_t *ep_info) +{ + int32_t ret, node; + uint32_t config_32; + + /* + * Look for the mandatory fields that are expected to be present in + * the SP manifests. + */ + node = fdt_path_offset(sp_manifest, "/"); + if (node < 0) { + ERROR("Did not find root node.\n"); + return node; + } + + ret = fdt_read_uint32(sp_manifest, node, "exception-level", &config_32); + if (ret != 0) { + ERROR("Missing SP Exception Level information.\n"); + return ret; + } + + sp->runtime_el = config_32; + + ret = fdt_read_uint32(sp_manifest, node, "ffa-version", &config_32); + if (ret != 0) { + ERROR("Missing Secure Partition FF-A Version.\n"); + return ret; + } + + sp->ffa_version = config_32; + + ret = fdt_read_uint32(sp_manifest, node, "execution-state", &config_32); + if (ret != 0) { + ERROR("Missing Secure Partition Execution State.\n"); + return ret; + } + + sp->execution_state = config_32; + + /* + * Look for the optional fields that are expected to be present in + * an SP manifest. + */ + ret = fdt_read_uint32(sp_manifest, node, "id", &config_32); + if (ret != 0) { + WARN("Missing Secure Partition ID.\n"); + } else { + if (!is_ffa_secure_id_valid(config_32)) { + ERROR("Invalid Secure Partition ID (0x%x).\n", + config_32); + return -EINVAL; + } + sp->sp_id = config_32; + } + + return 0; +} + +/******************************************************************************* + * This function gets the Secure Partition Manifest base and maps the manifest + * region. + * Currently only one Secure Partition manifest is considered which is used to + * prepare the context for the single Secure Partition. + ******************************************************************************/ +static int find_and_prepare_sp_context(void) +{ + void *sp_manifest; + uintptr_t manifest_base; + uintptr_t manifest_base_align; + entry_point_info_t *next_image_ep_info; + int32_t ret; + struct secure_partition_desc *sp; + + next_image_ep_info = bl31_plat_get_next_image_ep_info(SECURE); + if (next_image_ep_info == NULL) { + WARN("No Secure Partition image provided by BL2.\n"); + return -ENOENT; + } + + sp_manifest = (void *)next_image_ep_info->args.arg0; + if (sp_manifest == NULL) { + WARN("Secure Partition manifest absent.\n"); + return -ENOENT; + } + + manifest_base = (uintptr_t)sp_manifest; + manifest_base_align = page_align(manifest_base, DOWN); + + /* + * Map the secure partition manifest region in the EL3 translation + * regime. + * Map an area equal to (2 * PAGE_SIZE) for now. During manifest base + * alignment the region of 1 PAGE_SIZE from manifest align base may + * not completely accommodate the secure partition manifest region. + */ + ret = mmap_add_dynamic_region((unsigned long long)manifest_base_align, + manifest_base_align, + PAGE_SIZE * 2, + MT_RO_DATA); + if (ret != 0) { + ERROR("Error while mapping SP manifest (%d).\n", ret); + return ret; + } + + ret = fdt_node_offset_by_compatible(sp_manifest, -1, + "arm,ffa-manifest-1.0"); + if (ret < 0) { + ERROR("Error happened in SP manifest reading.\n"); + return -EINVAL; + } + + /* + * Store the size of the manifest so that it can be used later to pass + * the manifest as boot information later. + */ + next_image_ep_info->args.arg1 = fdt_totalsize(sp_manifest); + INFO("Manifest size = %lu bytes.\n", next_image_ep_info->args.arg1); + + /* + * Select an SP descriptor for initialising the partition's execution + * context on the primary CPU. + */ + sp = spmc_get_current_sp_ctx(); + + /* Initialize entry point information for the SP */ + SET_PARAM_HEAD(next_image_ep_info, PARAM_EP, VERSION_1, + SECURE | EP_ST_ENABLE); + + /* Parse the SP manifest. */ + ret = sp_manifest_parse(sp_manifest, ret, sp, next_image_ep_info); + if (ret != 0) { + ERROR("Error in Secure Partition manifest parsing.\n"); + return ret; + } + + /* Check that the runtime EL in the manifest was correct. */ + if (sp->runtime_el != S_EL1) { + ERROR("Unexpected runtime EL: %d\n", sp->runtime_el); + return -EINVAL; + } + + /* Perform any common initialisation. */ + spmc_sp_common_setup(sp, next_image_ep_info); + + /* Perform any initialisation specific to S-EL1 SPs. */ + spmc_el1_sp_setup(sp, next_image_ep_info); + + /* Initialize the SP context with the required ep info. */ + spmc_sp_common_ep_commit(sp, next_image_ep_info); + + return 0; +} + +/******************************************************************************* + * This function takes an SP context pointer and performs a synchronous entry + * into it. + ******************************************************************************/ +uint64_t spmc_sp_synchronous_entry(struct sp_exec_ctx *ec) +{ + uint64_t rc; + + assert(ec != NULL); + + /* Assign the context of the SP to this CPU */ + cm_set_context(&(ec->cpu_ctx), SECURE); + + /* Restore the context assigned above */ + cm_el1_sysregs_context_restore(SECURE); + cm_set_next_eret_context(SECURE); + + /* Invalidate TLBs at EL1. */ + tlbivmalle1(); + dsbish(); + + /* Enter Secure Partition */ + rc = spm_secure_partition_enter(&ec->c_rt_ctx); + + /* Save secure state */ + cm_el1_sysregs_context_save(SECURE); + + return rc; +} + +/******************************************************************************* + * SPMC Helper Functions. + ******************************************************************************/ +static int32_t sp_init(void) +{ + uint64_t rc; + struct secure_partition_desc *sp; + struct sp_exec_ctx *ec; + + sp = spmc_get_current_sp_ctx(); + ec = spmc_get_sp_ec(sp); + ec->rt_model = RT_MODEL_INIT; + ec->rt_state = RT_STATE_RUNNING; + + INFO("Secure Partition (0x%x) init start.\n", sp->sp_id); + + rc = spmc_sp_synchronous_entry(ec); + if (rc != 0) { + /* Indicate SP init was not successful. */ + ERROR("SP (0x%x) failed to initialize (%lu).\n", + sp->sp_id, rc); + return 0; + } + + ec->rt_state = RT_STATE_WAITING; + INFO("Secure Partition initialized.\n"); + + return 1; +} + +static void initalize_sp_descs(void) +{ + struct secure_partition_desc *sp; + + for (unsigned int i = 0U; i < SECURE_PARTITION_COUNT; i++) { + sp = &sp_desc[i]; + sp->sp_id = INV_SP_ID; + sp->secondary_ep = 0; + } +} + +static void initalize_ns_ep_descs(void) +{ + struct ns_endpoint_desc *ns_ep; + + for (unsigned int i = 0U; i < NS_PARTITION_COUNT; i++) { + ns_ep = &ns_ep_desc[i]; + /* + * Clashes with the Hypervisor ID but will not be a + * problem in practice. + */ + ns_ep->ns_ep_id = 0; + ns_ep->ffa_version = 0; + } +} + +/******************************************************************************* + * Initialize contexts of all Secure Partitions. + ******************************************************************************/ +int32_t spmc_setup(void) +{ + int32_t ret; + + /* Initialize endpoint descriptors */ + initalize_sp_descs(); + initalize_ns_ep_descs(); + + /* Perform physical SP setup. */ + + /* Disable MMU at EL1 (initialized by BL2) */ + disable_mmu_icache_el1(); + + /* Initialize context of the SP */ + INFO("Secure Partition context setup start.\n"); + + ret = find_and_prepare_sp_context(); + if (ret != 0) { + ERROR("Error in SP finding and context preparation.\n"); + return ret; + } + + /* Register init function for deferred init. */ + bl31_register_bl32_init(&sp_init); + + INFO("Secure Partition setup done.\n"); + + return 0; +} + +/******************************************************************************* + * Secure Partition Manager SMC handler. + ******************************************************************************/ +uint64_t spmc_smc_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + switch (smc_fid) { + + default: + WARN("Unsupported FF-A call 0x%08x.\n", smc_fid); + break; + } + return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); +} diff --git a/services/std_svc/spm/el3_spmc/spmc_setup.c b/services/std_svc/spm/el3_spmc/spmc_setup.c new file mode 100644 index 000000000..7b23c9e3b --- /dev/null +++ b/services/std_svc/spm/el3_spmc/spmc_setup.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spm_common.h" +#include "spmc.h" + +#include + +/* + * We are assuming that the index of the execution + * context used is the linear index of the current physical cpu. + */ +unsigned int get_ec_index(struct secure_partition_desc *sp) +{ + return plat_my_core_pos(); +} + +/* S-EL1 partition specific initialisation. */ +void spmc_el1_sp_setup(struct secure_partition_desc *sp, + entry_point_info_t *ep_info) +{ + /* Sanity check input arguments. */ + assert(sp != NULL); + assert(ep_info != NULL); + + /* Initialise the SPSR for S-EL1 SPs. */ + ep_info->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); + + /* + * Check whether setup is being performed for the primary or a secondary + * execution context. In the latter case, indicate to the SP that this + * is a warm boot. + * TODO: This check would need to be reworked if the same entry point is + * used for both primary and secondary initialisation. + */ + if (sp->secondary_ep != 0U) { + /* + * Sanity check that the secondary entry point is still what was + * originally set. + */ + assert(sp->secondary_ep == ep_info->pc); + ep_info->args.arg0 = FFA_WB_TYPE_S2RAM; + } +} + +/* Common initialisation for all SPs. */ +void spmc_sp_common_setup(struct secure_partition_desc *sp, + entry_point_info_t *ep_info) +{ + uint16_t sp_id; + + /* Assign FF-A Partition ID if not already assigned. */ + if (sp->sp_id == INV_SP_ID) { + sp_id = FFA_SP_ID_BASE + ACTIVE_SP_DESC_INDEX; + /* + * Ensure we don't clash with previously assigned partition + * IDs. + */ + while (!is_ffa_secure_id_valid(sp_id)) { + sp_id++; + + if (sp_id == FFA_SWD_ID_LIMIT) { + ERROR("Unable to determine valid SP ID.\n"); + panic(); + } + } + sp->sp_id = sp_id; + } + + /* + * We currently only support S-EL1 partitions so ensure this is the + * case. + */ + assert(sp->runtime_el == S_EL1); + + /* + * Clear the general purpose registers. These should be populated as + * required. + */ + zeromem(&ep_info->args, sizeof(ep_info->args)); +} + +/* + * Initialise the SP context now we have populated the common and EL specific + * entrypoint information. + */ +void spmc_sp_common_ep_commit(struct secure_partition_desc *sp, + entry_point_info_t *ep_info) +{ + cpu_context_t *cpu_ctx; + + cpu_ctx = &(spmc_get_sp_ec(sp)->cpu_ctx); + print_entry_point_info(ep_info); + cm_setup_context(cpu_ctx, ep_info); +} diff --git a/services/std_svc/spmd/spmd_private.h b/services/std_svc/spmd/spmd_private.h index 4cd6a744b..4c298c9e8 100644 --- a/services/std_svc/spmd/spmd_private.h +++ b/services/std_svc/spmd/spmd_private.h @@ -58,12 +58,6 @@ typedef struct spmd_spm_core_context { */ #define FFA_NS_ENDPOINT_ID U(0) -/* Mask and shift to check valid secure FF-A Endpoint ID. */ -#define SPMC_SECURE_ID_MASK U(1) -#define SPMC_SECURE_ID_SHIFT U(15) - -#define SPMD_DIRECT_MSG_ENDPOINT_ID U(FFA_ENDPOINT_ID_MAX - 1) - /* Define SPMD target function IDs for framework messages to the SPMC */ #define SPMD_FWK_MSG_BIT BIT(31) #define SPMD_FWK_MSG_PSCI U(0) From 6da76075bf4b953d621aa15c379e62a5f785de3f Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Mon, 29 Nov 2021 17:57:03 +0000 Subject: [PATCH 05/10] feat(spmd): update SPMC init flow to use EL3 implementation Allow the SPMD to initialise an SPMC implementation at EL3 directly rather than at a lower EL. This includes removing the requirement to parse an SPMC manifest to obtain information about the SPMC implementation, in this case since the SPMD and SPMC reside in the same EL we can hardcode the required information directly. Signed-off-by: Marc Bonnici Change-Id: I66d1e1b3ec2d0abbfc28b011a32445ee890a331d --- include/services/spmc_svc.h | 2 ++ services/std_svc/spm/el3_spmc/spmc_main.c | 11 +++++++++++ services/std_svc/spmd/spmd_main.c | 21 +++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/include/services/spmc_svc.h b/include/services/spmc_svc.h index 9dbe04589..8ee61e90f 100644 --- a/include/services/spmc_svc.h +++ b/include/services/spmc_svc.h @@ -12,8 +12,10 @@ #include #include +#include int spmc_setup(void); +void spmc_populate_attrs(spmc_manifest_attribute_t *spmc_attrs); void *spmc_get_config_addr(void); void spmc_set_config_addr(uintptr_t soc_fw_config); diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index 80d7a4c72..ddfae95e1 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -382,6 +382,17 @@ static void initalize_ns_ep_descs(void) } } +/******************************************************************************* + * Initialize SPMC attributes for the SPMD. + ******************************************************************************/ +void spmc_populate_attrs(spmc_manifest_attribute_t *spmc_attrs) +{ + spmc_attrs->major_version = FFA_VERSION_MAJOR; + spmc_attrs->minor_version = FFA_VERSION_MINOR; + spmc_attrs->exec_state = MODE_RW_64; + spmc_attrs->spmc_id = FFA_SPMC_ID; +} + /******************************************************************************* * Initialize contexts of all Secure Partitions. ******************************************************************************/ diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index 27a8382fe..448e12d40 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "spmd_private.h" @@ -34,7 +35,8 @@ static spmd_spm_core_context_t spm_core_context[PLATFORM_CORE_COUNT]; /******************************************************************************* - * SPM Core attribute information read from its manifest. + * SPM Core attribute information is read from its manifest if the SPMC is not + * at EL3. Else, it is populated from the SPMC directly. ******************************************************************************/ static spmc_manifest_attribute_t spmc_attrs; @@ -385,8 +387,23 @@ static int spmd_spmc_init(void *pm_addr) ******************************************************************************/ int spmd_setup(void) { - void *spmc_manifest; int rc; + void *spmc_manifest; + + /* + * If the SPMC is at EL3, then just initialise it directly. The + * shenanigans of when it is at a lower EL are not needed. + */ + if (is_spmc_at_el3()) { + /* Allow the SPMC to populate its attributes directly. */ + spmc_populate_attrs(&spmc_attrs); + + rc = spmc_setup(); + if (rc != 0) { + ERROR("SPMC initialisation failed 0x%x.\n", rc); + } + return rc; + } spmc_ep_info = bl31_plat_get_next_image_ep_info(SECURE); if (spmc_ep_info == NULL) { From bb01a67306f47271adde051e541c760028c1a0f1 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Mon, 29 Nov 2021 18:02:45 +0000 Subject: [PATCH 06/10] feat(spmd): enable handling of FF-A SMCs with the SPMC at EL3 Any FF-A SMC that arrives from the normal world is handled by the SPMD before being forwarded to the SPMC. Similarly any SMC arriving from the secure world will hit the SPMC first and be forwarded to the SPMD if required, otherwise the SPMC will respond directly. This allows for the existing flow of handling FF-A ABI's when the SPMC resides at a lower EL to be preserved. In order to facilitate this flow the spmd_smc_forward function has been split and control is either passed to the SPMC or it is forwarded as before. To allow this the flags and cookie parameters must now also be passed into this method as the SPMC must be able to provide these when calling back into the SPMD handler as appropriate. Signed-off-by: Marc Bonnici Change-Id: I84fee8390023295b9689067e14cd25cba23ca39b --- include/services/spmd_svc.h | 17 ++++- services/std_svc/spmd/spmd_main.c | 100 +++++++++++++++++++++++++----- services/std_svc/std_svc_setup.c | 5 +- 3 files changed, 102 insertions(+), 20 deletions(-) diff --git a/include/services/spmd_svc.h b/include/services/spmd_svc.h index 1e7e6aa87..29dfdad34 100644 --- a/include/services/spmd_svc.h +++ b/include/services/spmd_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,6 +12,14 @@ #include int spmd_setup(void); +uint64_t spmd_ffa_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags); uint64_t spmd_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, @@ -20,6 +28,13 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, void *cookie, void *handle, uint64_t flags); +uint64_t spmd_smc_switch_state(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *handle); #endif /* __ASSEMBLER__ */ #endif /* SPMD_SVC_H */ diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index 448e12d40..ca441afe7 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -90,7 +90,9 @@ static uint64_t spmd_smc_forward(uint32_t smc_fid, uint64_t x2, uint64_t x3, uint64_t x4, - void *handle); + void *cookie, + void *handle, + uint64_t flags); /****************************************************************************** * Builds an SPMD to SPMC direct message request. @@ -434,15 +436,15 @@ int spmd_setup(void) } /******************************************************************************* - * Forward SMC to the other security state + * Forward FF-A SMCs to the other security state. ******************************************************************************/ -static uint64_t spmd_smc_forward(uint32_t smc_fid, - bool secure_origin, - uint64_t x1, - uint64_t x2, - uint64_t x3, - uint64_t x4, - void *handle) +uint64_t spmd_smc_switch_state(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *handle) { unsigned int secure_state_in = (secure_origin) ? SECURE : NON_SECURE; unsigned int secure_state_out = (!secure_origin) ? SECURE : NON_SECURE; @@ -474,6 +476,28 @@ static uint64_t spmd_smc_forward(uint32_t smc_fid, SMC_GET_GP(handle, CTX_GPREG_X7)); } +/******************************************************************************* + * Forward SMCs to the other security state. + ******************************************************************************/ +static uint64_t spmd_smc_forward(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + if (is_spmc_at_el3() && !secure_origin) { + return spmc_smc_handler(smc_fid, secure_origin, x1, x2, x3, x4, + cookie, handle, flags); + } + return spmd_smc_switch_state(smc_fid, secure_origin, x1, x2, x3, x4, + handle); + +} + /******************************************************************************* * Return FFA_ERROR with specified error code ******************************************************************************/ @@ -501,6 +525,10 @@ bool spmd_check_address_in_binary_image(uint64_t address) *****************************************************************************/ static bool spmd_is_spmc_message(unsigned int ep) { + if (is_spmc_at_el3()) { + return false; + } + return ((ffa_endpoint_destination(ep) == SPMD_DIRECT_MSG_ENDPOINT_ID) && (ffa_endpoint_source(ep) == spmc_attrs.spmc_id)); } @@ -518,6 +546,35 @@ static int spmd_handle_spmc_message(unsigned long long msg, return -EINVAL; } +/******************************************************************************* + * This function forwards FF-A SMCs to either the main SPMD handler or the + * SPMC at EL3, depending on the origin security state, if enabled. + ******************************************************************************/ +uint64_t spmd_ffa_smc_handler(uint32_t smc_fid, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + if (is_spmc_at_el3()) { + /* + * If we have an SPMC at EL3 allow handling of the SMC first. + * The SPMC will call back through to SPMD handler if required. + */ + if (is_caller_secure(flags)) { + return spmc_smc_handler(smc_fid, + is_caller_secure(flags), + x1, x2, x3, x4, cookie, + handle, flags); + } + } + return spmd_smc_handler(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); +} + /******************************************************************************* * This function handles all SMCs in the range reserved for FFA. Each call is * either forwarded to the other security state or handled by the SPM dispatcher @@ -559,7 +616,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, } return spmd_smc_forward(smc_fid, secure_origin, - x1, x2, x3, x4, handle); + x1, x2, x3, x4, cookie, + handle, flags); break; /* not reached */ case FFA_VERSION: @@ -570,9 +628,11 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, * If caller is non secure and SPMC was initialized, * return SPMC's version. * Sanity check to "input_version". + * If the EL3 SPMC is enabled, ignore the SPMC state as + * this is not used. */ if ((input_version & FFA_VERSION_BIT31_MASK) || - (ctx->state == SPMC_STATE_RESET)) { + (!is_spmc_at_el3() && (ctx->state == SPMC_STATE_RESET))) { ret = FFA_ERROR_NOT_SUPPORTED; } else if (!secure_origin) { gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); @@ -627,7 +687,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, */ return spmd_smc_forward(ret, true, FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ, - FFA_PARAM_MBZ, gpregs); + FFA_PARAM_MBZ, cookie, gpregs, + flags); } else { ret = MAKE_FFA_VERSION(FFA_VERSION_MAJOR, FFA_VERSION_MINOR); @@ -647,7 +708,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, /* Forward SMC from Normal world to the SPM Core */ if (!secure_origin) { return spmd_smc_forward(smc_fid, secure_origin, - x1, x2, x3, x4, handle); + x1, x2, x3, x4, cookie, + handle, flags); } /* @@ -743,7 +805,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, } else { /* Forward direct message to the other world */ return spmd_smc_forward(smc_fid, secure_origin, - x1, x2, x3, x4, handle); + x1, x2, x3, x4, cookie, + handle, flags); } break; /* Not reached */ @@ -753,7 +816,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, } else { /* Forward direct message to the other world */ return spmd_smc_forward(smc_fid, secure_origin, - x1, x2, x3, x4, handle); + x1, x2, x3, x4, cookie, + handle, flags); } break; /* Not reached */ @@ -808,7 +872,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, */ return spmd_smc_forward(smc_fid, secure_origin, - x1, x2, x3, x4, handle); + x1, x2, x3, x4, cookie, + handle, flags); break; /* not reached */ case FFA_MSG_WAIT: @@ -831,7 +896,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, } return spmd_smc_forward(smc_fid, secure_origin, - x1, x2, x3, x4, handle); + x1, x2, x3, x4, cookie, + handle, flags); break; /* not reached */ case FFA_NORMAL_WORLD_RESUME: diff --git a/services/std_svc/std_svc_setup.c b/services/std_svc/std_svc_setup.c index bfe26cab1..b1e3db977 100644 --- a/services/std_svc/std_svc_setup.c +++ b/services/std_svc/std_svc_setup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -147,8 +148,8 @@ static uintptr_t std_svc_smc_handler(uint32_t smc_fid, * dispatcher and return its return value */ if (is_ffa_fid(smc_fid)) { - return spmd_smc_handler(smc_fid, x1, x2, x3, x4, cookie, - handle, flags); + return spmd_ffa_smc_handler(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); } #endif From 20fae0a7ce7fd407cd3efb7745017ee6ab605159 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Mon, 29 Nov 2021 17:17:29 +0000 Subject: [PATCH 07/10] feat(spmc): add function to determine the return path from the SPMC Use knowledge of the target partition ID and source security state to determine which route should be used to exit the SPMC. There are 3 exit paths: 1) Return to the normal world via the SPMD, this will take care of switching contexts if required. 2) Return to the secure world when the call originated in the normal world and therefore switch contexts. 3) Return to the secure world when the call originated in the secure world, therefore we can return directly. Signed-off-by: Marc Bonnici Change-Id: I4037f3a8a8519e2c9f1876be92806d2c41d0d154 --- services/std_svc/spm/el3_spmc/spmc_main.c | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index ddfae95e1..8470cf342 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -136,6 +136,46 @@ bool is_ffa_secure_id_valid(uint16_t partition_id) return true; } +/******************************************************************************* + * This function either forwards the request to the other world or returns + * with an ERET depending on the source of the call. + ******************************************************************************/ +static uint64_t spmc_smc_return(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *handle, + void *cookie, + uint64_t flags, + uint16_t dst_id) +{ + /* If the destination is in the normal world always go via the SPMD. */ + if (ffa_is_normal_world_id(dst_id)) { + return spmd_smc_handler(smc_fid, x1, x2, x3, x4, + cookie, handle, flags); + } + /* + * If the caller is secure and we want to return to the secure world, + * ERET directly. + */ + else if (secure_origin && ffa_is_secure_world_id(dst_id)) { + SMC_RET5(handle, smc_fid, x1, x2, x3, x4); + } + /* If we originated in the normal world then switch contexts. */ + else if (!secure_origin && ffa_is_secure_world_id(dst_id)) { + return spmd_smc_switch_state(smc_fid, secure_origin, x1, x2, + x3, x4, handle); + } else { + /* Unknown State. */ + panic(); + } + + /* Shouldn't be Reached. */ + return 0; +} + /******************************************************************************* * This function will parse the Secure Partition Manifest. From manifest, it * will fetch details for preparing Secure partition image context and secure From c4db76f066f236fe490ebc7a50833a04e08f5151 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Mon, 29 Nov 2021 17:05:33 +0000 Subject: [PATCH 08/10] feat(spmc): add support for FFA_MSG_WAIT Handle an incoming call of FFA_MSG_WAIT from the secure world and update the runtime state of the calling partition accordingly. This ABI can be called in the following scenarios: - Used by an SP to signal it has finished initializing. - To resume the normal world after handling a secure interrupt that interrupted the normal world. - To relinquish control back to the normal world. Signed-off-by: Marc Bonnici Change-Id: I929713a2280e8ec291b5b4e8f6d4b49df337228c --- services/std_svc/spm/el3_spmc/spmc.h | 2 + services/std_svc/spm/el3_spmc/spmc_main.c | 83 +++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h index e89151673..df0aa61ce 100644 --- a/services/std_svc/spm/el3_spmc/spmc.h +++ b/services/std_svc/spm/el3_spmc/spmc.h @@ -23,6 +23,8 @@ #define FFA_SWD_ID_LIMIT SPMD_DIRECT_MSG_ENDPOINT_ID - 1 #define FFA_SWD_ID_MASK 0x8000 +/* ID 0 is reserved for the normal world entity, (Hypervisor or OS Kernel). */ +#define FFA_NWD_ID U(0) /* First ID is reserved for the SPMC */ #define FFA_SPMC_ID U(FFA_SWD_ID_BASE) /* SP IDs are allocated after the SPMC ID */ diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index 8470cf342..ccebcff2e 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -176,6 +176,85 @@ static uint64_t spmc_smc_return(uint32_t smc_fid, return 0; } +/******************************************************************************* + * FF-A ABI Handlers. + ******************************************************************************/ +/******************************************************************************* + * This function handles the FFA_MSG_WAIT SMC to allow an SP to relinquish its + * cycles. + ******************************************************************************/ +static uint64_t msg_wait_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + struct secure_partition_desc *sp; + unsigned int idx; + + /* + * Check that the response did not originate from the Normal world as + * only the secure world can call this ABI. + */ + if (!secure_origin) { + VERBOSE("Normal world cannot call FFA_MSG_WAIT.\n"); + return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + } + + /* Get the descriptor of the SP that invoked FFA_MSG_WAIT. */ + sp = spmc_get_current_sp_ctx(); + if (sp == NULL) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* + * Get the execution context of the SP that invoked FFA_MSG_WAIT. + */ + idx = get_ec_index(sp); + + /* Ensure SP execution context was in the right runtime model. */ + if (sp->ec[idx].rt_model == RT_MODEL_DIR_REQ) { + return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); + } + + /* Sanity check the state is being tracked correctly in the SPMC. */ + assert(sp->ec[idx].rt_state == RT_STATE_RUNNING); + + /* + * Perform a synchronous exit if the partition was initialising. The + * state is updated after the exit. + */ + if (sp->ec[idx].rt_model == RT_MODEL_INIT) { + spmc_sp_synchronous_exit(&sp->ec[idx], x4); + /* Should not get here */ + panic(); + } + + /* Update the state of the SP execution context. */ + sp->ec[idx].rt_state = RT_STATE_WAITING; + + /* Resume normal world if a secure interrupt was handled. */ + if (sp->ec[idx].rt_model == RT_MODEL_INTR) { + /* FFA_MSG_WAIT can only be called from the secure world. */ + unsigned int secure_state_in = SECURE; + unsigned int secure_state_out = NON_SECURE; + + cm_el1_sysregs_context_save(secure_state_in); + cm_el1_sysregs_context_restore(secure_state_out); + cm_set_next_eret_context(secure_state_out); + SMC_RET0(cm_get_context(secure_state_out)); + } + + /* Forward the response to the Normal world. */ + return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4, + handle, cookie, flags, FFA_NWD_ID); +} + /******************************************************************************* * This function will parse the Secure Partition Manifest. From manifest, it * will fetch details for preparing Secure partition image context and secure @@ -481,6 +560,10 @@ uint64_t spmc_smc_handler(uint32_t smc_fid, { switch (smc_fid) { + case FFA_MSG_WAIT: + return msg_wait_handler(smc_fid, secure_origin, x1, x2, x3, x4, + cookie, handle, flags); + default: WARN("Unsupported FF-A call 0x%08x.\n", smc_fid); break; From d663fe7a3002ff028c190eb732278b878e78b7b7 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Fri, 10 Dec 2021 09:21:56 +0000 Subject: [PATCH 09/10] feat(spmc): add support for handling FFA_ERROR ABI This ABI is only valid during SP initialisation to indicate failure. If this occurs during SP initialisation signal a failure, otherwise respond with a not supported error code. Signed-off-by: Marc Bonnici Change-Id: I0182a1641c0f6850e82173af333be79b594f2318 --- services/std_svc/spm/el3_spmc/spmc_main.c | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index ccebcff2e..cfd5a80ba 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -255,6 +255,48 @@ static uint64_t msg_wait_handler(uint32_t smc_fid, handle, cookie, flags, FFA_NWD_ID); } +static uint64_t ffa_error_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + struct secure_partition_desc *sp; + unsigned int idx; + + /* Check that the response did not originate from the Normal world. */ + if (!secure_origin) { + return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + } + + /* Get the descriptor of the SP that invoked FFA_ERROR. */ + sp = spmc_get_current_sp_ctx(); + if (sp == NULL) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Get the execution context of the SP that invoked FFA_ERROR. */ + idx = get_ec_index(sp); + + /* + * We only expect FFA_ERROR to be received during SP initialisation + * otherwise this is an invalid call. + */ + if (sp->ec[idx].rt_model == RT_MODEL_INIT) { + ERROR("SP 0x%x failed to initialize.\n", sp->sp_id); + spmc_sp_synchronous_exit(&sp->ec[idx], x2); + /* Should not get here. */ + panic(); + } + + return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); +} + /******************************************************************************* * This function will parse the Secure Partition Manifest. From manifest, it * will fetch details for preparing Secure partition image context and secure @@ -564,6 +606,10 @@ uint64_t spmc_smc_handler(uint32_t smc_fid, return msg_wait_handler(smc_fid, secure_origin, x1, x2, x3, x4, cookie, handle, flags); + case FFA_ERROR: + return ffa_error_handler(smc_fid, secure_origin, x1, x2, x3, x4, + cookie, handle, flags); + default: WARN("Unsupported FF-A call 0x%08x.\n", smc_fid); break; From 9741327df577c3f43db42b26bda607429e62af0b Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Mon, 29 Nov 2021 17:05:57 +0000 Subject: [PATCH 10/10] feat(spmc): add support for direct req/resp Enable the SPMC to handle FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP ABIs. Signed-off-by: Marc Bonnici Change-Id: Ia196c7405993f600e4fdbf467397ea3fb035a62a --- services/std_svc/spm/el3_spmc/spmc_main.c | 170 ++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index cfd5a80ba..3fd8c7836 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -179,6 +179,166 @@ static uint64_t spmc_smc_return(uint32_t smc_fid, /******************************************************************************* * FF-A ABI Handlers. ******************************************************************************/ + +/******************************************************************************* + * Helper function to validate arg2 as part of a direct message. + ******************************************************************************/ +static inline bool direct_msg_validate_arg2(uint64_t x2) +{ + /* + * We currently only support partition messages, therefore ensure x2 is + * not set. + */ + if (x2 != (uint64_t) 0) { + VERBOSE("Arg2 MBZ for partition messages (0x%lx).\n", x2); + return false; + } + return true; +} + +/******************************************************************************* + * Handle direct request messages and route to the appropriate destination. + ******************************************************************************/ +static uint64_t direct_req_smc_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + uint16_t dst_id = ffa_endpoint_destination(x1); + struct secure_partition_desc *sp; + unsigned int idx; + + /* Check if arg2 has been populated correctly based on message type. */ + if (!direct_msg_validate_arg2(x2)) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* + * If called by the secure world it is an invalid call since a + * SP cannot call into the Normal world and there is no other SP to call + * into. If there are other SPs in future then the partition runtime + * model would need to be validated as well. + */ + if (secure_origin) { + VERBOSE("Direct request not supported to the Normal World.\n"); + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Check if the SP ID is valid. */ + sp = spmc_get_sp_ctx(dst_id); + if (sp == NULL) { + VERBOSE("Direct request to unknown partition ID (0x%x).\n", + dst_id); + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* + * Check that the target execution context is in a waiting state before + * forwarding the direct request to it. + */ + idx = get_ec_index(sp); + if (sp->ec[idx].rt_state != RT_STATE_WAITING) { + VERBOSE("SP context on core%u is not waiting (%u).\n", + idx, sp->ec[idx].rt_model); + return spmc_ffa_error_return(handle, FFA_ERROR_BUSY); + } + + /* + * Everything checks out so forward the request to the SP after updating + * its state and runtime model. + */ + sp->ec[idx].rt_state = RT_STATE_RUNNING; + sp->ec[idx].rt_model = RT_MODEL_DIR_REQ; + return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4, + handle, cookie, flags, dst_id); +} + +/******************************************************************************* + * Handle direct response messages and route to the appropriate destination. + ******************************************************************************/ +static uint64_t direct_resp_smc_handler(uint32_t smc_fid, + bool secure_origin, + uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *cookie, + void *handle, + uint64_t flags) +{ + uint16_t dst_id = ffa_endpoint_destination(x1); + struct secure_partition_desc *sp; + unsigned int idx; + + /* Check if arg2 has been populated correctly based on message type. */ + if (!direct_msg_validate_arg2(x2)) { + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Check that the response did not originate from the Normal world. */ + if (!secure_origin) { + VERBOSE("Direct Response not supported from Normal World.\n"); + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* + * Check that the response is either targeted to the Normal world or the + * SPMC e.g. a PM response. + */ + if ((dst_id != FFA_SPMC_ID) && ffa_is_secure_world_id(dst_id)) { + VERBOSE("Direct response to invalid partition ID (0x%x).\n", + dst_id); + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Obtain the SP descriptor and update its runtime state. */ + sp = spmc_get_sp_ctx(ffa_endpoint_source(x1)); + if (sp == NULL) { + VERBOSE("Direct response to unknown partition ID (0x%x).\n", + dst_id); + return spmc_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + /* Sanity check state is being tracked correctly in the SPMC. */ + idx = get_ec_index(sp); + assert(sp->ec[idx].rt_state == RT_STATE_RUNNING); + + /* Ensure SP execution context was in the right runtime model. */ + if (sp->ec[idx].rt_model != RT_MODEL_DIR_REQ) { + VERBOSE("SP context on core%u not handling direct req (%u).\n", + idx, sp->ec[idx].rt_model); + return spmc_ffa_error_return(handle, FFA_ERROR_DENIED); + } + + /* Update the state of the SP execution context. */ + sp->ec[idx].rt_state = RT_STATE_WAITING; + + /* + * If the receiver is not the SPMC then forward the response to the + * Normal world. + */ + if (dst_id == FFA_SPMC_ID) { + spmc_sp_synchronous_exit(&sp->ec[idx], x4); + /* Should not get here. */ + panic(); + } + + return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4, + handle, cookie, flags, dst_id); +} + /******************************************************************************* * This function handles the FFA_MSG_WAIT SMC to allow an SP to relinquish its * cycles. @@ -602,6 +762,16 @@ uint64_t spmc_smc_handler(uint32_t smc_fid, { switch (smc_fid) { + case FFA_MSG_SEND_DIRECT_REQ_SMC32: + case FFA_MSG_SEND_DIRECT_REQ_SMC64: + return direct_req_smc_handler(smc_fid, secure_origin, x1, x2, + x3, x4, cookie, handle, flags); + + case FFA_MSG_SEND_DIRECT_RESP_SMC32: + case FFA_MSG_SEND_DIRECT_RESP_SMC64: + return direct_resp_smc_handler(smc_fid, secure_origin, x1, x2, + x3, x4, cookie, handle, flags); + case FFA_MSG_WAIT: return msg_wait_handler(smc_fid, secure_origin, x1, x2, x3, x4, cookie, handle, flags);