From 890b5088203e990d683a9c837e976be62c6501aa Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Sat, 25 Feb 2023 13:26:10 -0800 Subject: [PATCH 1/8] feat(spmd): add spmd logical partitions Add header file to help with creation of SPMD logical partitions. Also update linker files to create sections to record SPMD logical partitions declared. This follows the same pattern as the EL3 SPMC's logical partitions. This patch also adds initialization of SPMD logical partitions when the SPMD comes up. ENABLE_SPMD_LP is a build flag that is used to enable support for SPMD logical partitions. Note that the approach chosen is to keep SPMD and SPMC logical partition support separate, as opposed to extend the existing SPMC logical partition support since the code would need to have a number of ifdefs and the interactions with various build options such as SPMC_AT_EL3 needs to be accounted for, which would make code more complicated. Signed-off-by: Raghu Krishnamurthy Change-Id: I9642ddbf6ea26dd3f4a283baec598d61c07e3661 --- Makefile | 11 ++ include/common/bl_common.h | 4 + include/common/bl_common.ld.h | 12 +- include/services/el3_spmd_logical_sp.h | 63 +++++++++ make_helpers/defaults.mk | 3 + services/std_svc/spmd/spmd.mk | 3 +- services/std_svc/spmd/spmd_logical_sp.c | 165 ++++++++++++++++++++++++ services/std_svc/spmd/spmd_main.c | 7 + 8 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 include/services/el3_spmd_logical_sp.h create mode 100644 services/std_svc/spmd/spmd_logical_sp.c diff --git a/Makefile b/Makefile index 4b96be16b..f3ab71aa2 100644 --- a/Makefile +++ b/Makefile @@ -623,6 +623,15 @@ ifneq (${SPD},none) # over the sources. endif #(SPD=none) +ifeq (${ENABLE_SPMD_LP}, 1) +ifneq (${SPD},spmd) + $(error Error: ENABLE_SPMD_LP requires SPD=spmd.) +endif +ifeq ($(SPMC_AT_EL3),1) + $(error SPMC at EL3 not supported when enabling SPMD Logical partitions.) +endif +endif + ifeq (${CTX_INCLUDE_EL2_REGS}, 1) ifeq (${SPD},none) ifeq (${ENABLE_RME},0) @@ -1181,6 +1190,7 @@ $(eval $(call assert_booleans,\ SPM_MM \ SPMC_AT_EL3 \ SPMD_SPM_AT_SEL2 \ + ENABLE_SPMD_LP \ TRUSTED_BOARD_BOOT \ USE_COHERENT_MEM \ USE_DEBUGFS \ @@ -1397,6 +1407,7 @@ $(eval $(call add_defines,\ CONDITIONAL_CMO \ IMPDEF_SYSREG_TRAP \ SVE_VECTOR_LEN \ + ENABLE_SPMD_LP \ ))) ifeq (${SANITIZE_UB},trap) diff --git a/include/common/bl_common.h b/include/common/bl_common.h index 539280e12..4c8a17c5b 100644 --- a/include/common/bl_common.h +++ b/include/common/bl_common.h @@ -85,6 +85,10 @@ #define __EL3_LP_DESCS_START__ Load$$__EL3_LP_DESCS__$$Base #define __EL3_LP_DESCS_END__ Load$$__EL3_LP_DESCS__$$Limit #endif +#if ENABLE_SPMD_LP +#define __SPMD_LP_DESCS_START__ Load$$__SPMD_LP_DESCS__$$Base +#define __SPMD_LP_DESCS_END__ Load$$__SPMD_LP_DESCS__$$Limit +#endif #define __RW_START__ Load$$LR$$LR_RW_DATA$$Base #define __RW_END__ Load$$LR$$LR_END$$Base #define __SPM_SHIM_EXCEPTIONS_START__ Load$$__SPM_SHIM_EXCEPTIONS__$$Base diff --git a/include/common/bl_common.ld.h b/include/common/bl_common.ld.h index c9bed1ad9..b6dd0f006 100644 --- a/include/common/bl_common.ld.h +++ b/include/common/bl_common.ld.h @@ -49,6 +49,15 @@ #define EL3_LP_DESCS #endif +#if ENABLE_SPMD_LP +#define SPMD_LP_DESCS \ + . = ALIGN(STRUCT_ALIGN); \ + __SPMD_LP_DESCS_START__ = .; \ + KEEP(*(.spmd_lp_descs)) \ + __SPMD_LP_DESCS_END__ = .; +#else +#define SPMD_LP_DESCS +#endif #define PMF_SVC_DESCS \ . = ALIGN(STRUCT_ALIGN); \ __PMF_SVC_DESCS_START__ = .; \ @@ -100,7 +109,8 @@ CPU_OPS \ GOT \ BASE_XLAT_TABLE_RO \ - EL3_LP_DESCS + EL3_LP_DESCS \ + SPMD_LP_DESCS /* * .data must be placed at a lower address than the stacks if the stack diff --git a/include/services/el3_spmd_logical_sp.h b/include/services/el3_spmd_logical_sp.h new file mode 100644 index 000000000..4725c159e --- /dev/null +++ b/include/services/el3_spmd_logical_sp.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef EL3_SPMD_LOGICAL_SP_H +#define EL3_SPMD_LOGICAL_SP_H + +#include +#include +#include + +/******************************************************************************* + * Structure definition, typedefs & constants for the SPMD Logical Partitions. + ******************************************************************************/ + +/* Prototype for SPMD logical partition initializing function. */ +typedef int32_t (*ffa_spmd_lp_init_t)(void); + +/* SPMD Logical Partition Descriptor. */ +struct spmd_lp_desc { + ffa_spmd_lp_init_t init; + uint16_t sp_id; + uint32_t properties; + uint32_t uuid[4]; /* Little Endian. */ + const char *debug_name; +}; + +/* Convenience macro to declare a SPMD logical partition descriptor. */ +#define DECLARE_SPMD_LOGICAL_PARTITION(_name, _init, _sp_id, _uuid, _properties) \ + static const struct spmd_lp_desc __partition_desc_ ## _name \ + __section(".spmd_lp_descs") __used = { \ + .debug_name = #_name, \ + .init = (_init), \ + .sp_id = (_sp_id), \ + .uuid = _uuid, \ + .properties = (_properties), \ + } + +IMPORT_SYM(uintptr_t, __SPMD_LP_DESCS_START__, SPMD_LP_DESCS_START); +IMPORT_SYM(uintptr_t, __SPMD_LP_DESCS_END__, SPMD_LP_DESCS_END); + +#define SPMD_LP_DESCS_COUNT ((SPMD_LP_DESCS_END - SPMD_LP_DESCS_START) \ + / sizeof(struct spmd_lp_desc)) +CASSERT(sizeof(struct spmd_lp_desc) == 40, assert_spmd_lp_desc_size_mismatch); + +/* + * Reserve 63 IDs for SPMD Logical Partitions. Currently, 0xFFC0 to 0xFFFE + * is reserved. + */ +#define SPMD_LP_ID_END (SPMD_DIRECT_MSG_ENDPOINT_ID - 1) +#define SPMD_LP_ID_START (SPMD_LP_ID_END - 62) + +static inline bool is_spmd_lp_id(unsigned int id) +{ + return (id >= SPMD_LP_ID_START && id <= SPMD_LP_ID_END); +} + +void spmd_logical_sp_set_spmc_initialized(void); +void spmc_logical_sp_set_spmc_failure(void); + +int32_t spmd_logical_sp_init(void); + +#endif /* EL3_SPMD_LOGICAL_SP_H */ diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index a06503913..964e0f925 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -508,3 +508,6 @@ DRTM_SUPPORT := 0 # Check platform if cache management operations should be performed. # Disabled by default. CONDITIONAL_CMO := 0 + +# By default, disable SPMD Logical partitions +ENABLE_SPMD_LP := 0 diff --git a/services/std_svc/spmd/spmd.mk b/services/std_svc/spmd/spmd.mk index 6f451c8db..cc0e3922f 100644 --- a/services/std_svc/spmd/spmd.mk +++ b/services/std_svc/spmd/spmd.mk @@ -15,7 +15,8 @@ endif SPMD_SOURCES += $(addprefix services/std_svc/spmd/, \ ${ARCH}/spmd_helpers.S \ spmd_pm.c \ - spmd_main.c) + spmd_main.c \ + spmd_logical_sp.c) # Let the top-level Makefile know that we intend to include a BL32 image NEED_BL32 := yes diff --git a/services/std_svc/spmd/spmd_logical_sp.c b/services/std_svc/spmd/spmd_logical_sp.c new file mode 100644 index 000000000..f3089c659 --- /dev/null +++ b/services/std_svc/spmd/spmd_logical_sp.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include +#include + +#if ENABLE_SPMD_LP +static bool is_spmd_lp_inited; +static bool is_spmc_inited; + +/* + * Helper function to obtain the array storing the EL3 + * SPMD Logical Partition descriptors. + */ +static struct spmd_lp_desc *get_spmd_el3_lp_array(void) +{ + return (struct spmd_lp_desc *) SPMD_LP_DESCS_START; +} + +/******************************************************************************* + * Validate any logical partition descriptors before we initialize. + * Initialization of said partitions will be taken care of during SPMD boot. + ******************************************************************************/ +static int el3_spmd_sp_desc_validate(struct spmd_lp_desc *lp_array) +{ + /* Check the array bounds are valid. */ + assert(SPMD_LP_DESCS_END > SPMD_LP_DESCS_START); + + /* If no SPMD logical partitions are implemented then simply bail out. */ + if (SPMD_LP_DESCS_COUNT == 0U) { + return -1; + } + + for (uint32_t index = 0U; index < SPMD_LP_DESCS_COUNT; index++) { + struct spmd_lp_desc *lp_desc = &lp_array[index]; + + /* Validate our logical partition descriptors. */ + if (lp_desc == NULL) { + ERROR("Invalid SPMD Logical SP Descriptor\n"); + return -EINVAL; + } + + /* + * Ensure the ID follows the convention to indicate it resides + * in the secure world. + */ + if (!ffa_is_secure_world_id(lp_desc->sp_id)) { + ERROR("Invalid SPMD Logical SP ID (0x%x)\n", + lp_desc->sp_id); + return -EINVAL; + } + + /* Ensure SPMD logical partition is in valid range. */ + if (!is_spmd_lp_id(lp_desc->sp_id)) { + ERROR("Invalid SPMD Logical Partition ID (0x%x)\n", + lp_desc->sp_id); + return -EINVAL; + } + + /* Ensure the UUID is not the NULL UUID. */ + if (lp_desc->uuid[0] == 0 && lp_desc->uuid[1] == 0 && + lp_desc->uuid[2] == 0 && lp_desc->uuid[3] == 0) { + ERROR("Invalid UUID for SPMD Logical SP (0x%x)\n", + lp_desc->sp_id); + return -EINVAL; + } + + /* Ensure init function callback is registered. */ + if (lp_desc->init == NULL) { + ERROR("Missing init function for Logical SP(0x%x)\n", + lp_desc->sp_id); + return -EINVAL; + } + + /* Ensure that SPMD LP only supports sending direct requests. */ + if (lp_desc->properties != FFA_PARTITION_DIRECT_REQ_SEND) { + ERROR("Invalid SPMD logical partition properties (0x%x)\n", + lp_desc->properties); + return -EINVAL; + } + + /* Ensure that all partition IDs are unique. */ + for (uint32_t inner_idx = index + 1; + inner_idx < SPMD_LP_DESCS_COUNT; inner_idx++) { + if (lp_desc->sp_id == lp_array[inner_idx].sp_id) { + ERROR("Duplicate SPMD logical SP ID Detected (0x%x)\n", + lp_desc->sp_id); + return -EINVAL; + } + } + } + return 0; +} +#endif + +/* + * Initialize SPMD logical partitions. This function assumes that it is called + * only after the SPMC has successfully initialized. + */ +int32_t spmd_logical_sp_init(void) +{ +#if ENABLE_SPMD_LP + int32_t rc = 0; + struct spmd_lp_desc *spmd_lp_descs; + + if (is_spmd_lp_inited == true) { + return 0; + } + + if (is_spmc_inited == false) { + return -1; + } + + spmd_lp_descs = get_spmd_el3_lp_array(); + + /* Perform initial validation of the SPMD Logical Partitions. */ + rc = el3_spmd_sp_desc_validate(spmd_lp_descs); + if (rc != 0) { + ERROR("Logical SPMD Partition validation failed!\n"); + return rc; + } + + VERBOSE("SPMD Logical Secure Partition init start.\n"); + for (unsigned int i = 0U; i < SPMD_LP_DESCS_COUNT; i++) { + rc = spmd_lp_descs[i].init(); + if (rc != 0) { + ERROR("SPMD Logical SP (0x%x) failed to initialize\n", + spmd_lp_descs[i].sp_id); + return rc; + } + VERBOSE("SPMD Logical SP (0x%x) Initialized\n", + spmd_lp_descs[i].sp_id); + } + + INFO("SPMD Logical Secure Partition init completed.\n"); + if (rc == 0) { + is_spmd_lp_inited = true; + } + return rc; +#else + return 0; +#endif +} + +void spmd_logical_sp_set_spmc_initialized(void) +{ +#if ENABLE_SPMD_LP + is_spmc_inited = true; +#endif +} + +void spmd_logical_sp_set_spmc_failure(void) +{ +#if ENABLE_SPMD_LP + is_spmc_inited = false; +#endif +} diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index 587e60f4f..9b06b55c3 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -190,6 +191,12 @@ static int32_t spmd_init(void) VERBOSE("SPM Core init end.\n"); + spmd_logical_sp_set_spmc_initialized(); + rc = spmd_logical_sp_init(); + if (rc != 0) { + WARN("SPMD Logical partitions failed init.\n"); + } + return 1; } From 66bdfd6e4e6d8e086a30397be6055dbb04846895 Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Fri, 3 Mar 2023 06:41:29 -0800 Subject: [PATCH 2/8] feat(spmd): el3 direct message API This patch implements an API that is exposed to SPMD logical partitions that can be used to send direct messages to a secure partition. It also adds required code in the SPMD smc handler to complete the direct response appropriately. Change-Id: I2d0e38415f13ad4fd28f8984d565036b7d3a9e71 Signed-off-by: Raghu Krishnamurthy --- include/services/el3_spmd_logical_sp.h | 36 ++++ services/std_svc/spmd/spmd_logical_sp.c | 235 +++++++++++++++++++++++- services/std_svc/spmd/spmd_main.c | 57 +++++- services/std_svc/spmd/spmd_private.h | 6 + 4 files changed, 327 insertions(+), 7 deletions(-) diff --git a/include/services/el3_spmd_logical_sp.h b/include/services/el3_spmd_logical_sp.h index 4725c159e..dcbbfabe4 100644 --- a/include/services/el3_spmd_logical_sp.h +++ b/include/services/el3_spmd_logical_sp.h @@ -12,6 +12,7 @@ /******************************************************************************* * Structure definition, typedefs & constants for the SPMD Logical Partitions. ******************************************************************************/ +typedef struct spmd_spm_core_context spmd_spm_core_context_t; /* Prototype for SPMD logical partition initializing function. */ typedef int32_t (*ffa_spmd_lp_init_t)(void); @@ -25,6 +26,17 @@ struct spmd_lp_desc { const char *debug_name; }; +struct ffa_value { + uint64_t func; + uint64_t arg1; + uint64_t arg2; + uint64_t arg3; + uint64_t arg4; + uint64_t arg5; + uint64_t arg6; + uint64_t arg7; +}; + /* Convenience macro to declare a SPMD logical partition descriptor. */ #define DECLARE_SPMD_LOGICAL_PARTITION(_name, _init, _sp_id, _uuid, _properties) \ static const struct spmd_lp_desc __partition_desc_ ## _name \ @@ -52,12 +64,36 @@ CASSERT(sizeof(struct spmd_lp_desc) == 40, assert_spmd_lp_desc_size_mismatch); static inline bool is_spmd_lp_id(unsigned int id) { +#if ENABLE_SPMD_LP return (id >= SPMD_LP_ID_START && id <= SPMD_LP_ID_END); +#else + return false; +#endif +} + +static inline bool is_ffa_error(struct ffa_value *retval) +{ + return retval->func == FFA_ERROR; +} + +static inline bool is_ffa_direct_msg_resp(struct ffa_value *retval) +{ + return (retval->func == FFA_MSG_SEND_DIRECT_RESP_SMC32) || + (retval->func == FFA_MSG_SEND_DIRECT_RESP_SMC64); } void spmd_logical_sp_set_spmc_initialized(void); void spmc_logical_sp_set_spmc_failure(void); int32_t spmd_logical_sp_init(void); +bool is_spmd_logical_sp_dir_req_in_progress( + spmd_spm_core_context_t *ctx); + +bool spmd_el3_ffa_msg_direct_req(uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *handle, + struct ffa_value *retval); #endif /* EL3_SPMD_LOGICAL_SP_H */ diff --git a/services/std_svc/spmd/spmd_logical_sp.c b/services/std_svc/spmd/spmd_logical_sp.c index f3089c659..06b5d1242 100644 --- a/services/std_svc/spmd/spmd_logical_sp.c +++ b/services/std_svc/spmd/spmd_logical_sp.c @@ -7,10 +7,13 @@ #include #include #include +#include "spmd_private.h" #include +#include #include -#include +#include + #if ENABLE_SPMD_LP static bool is_spmd_lp_inited; @@ -34,6 +37,11 @@ static int el3_spmd_sp_desc_validate(struct spmd_lp_desc *lp_array) /* Check the array bounds are valid. */ assert(SPMD_LP_DESCS_END > SPMD_LP_DESCS_START); + /* + * No support for SPMD logical partitions when SPMC is at EL3. + */ + assert(!is_spmc_at_el3()); + /* If no SPMD logical partitions are implemented then simply bail out. */ if (SPMD_LP_DESCS_COUNT == 0U) { return -1; @@ -99,6 +107,60 @@ static int el3_spmd_sp_desc_validate(struct spmd_lp_desc *lp_array) } return 0; } + +static void spmd_encode_ffa_error(struct ffa_value *retval, int32_t error_code) +{ + retval->func = FFA_ERROR; + retval->arg1 = FFA_TARGET_INFO_MBZ; + retval->arg2 = (uint32_t)error_code; + retval->arg3 = FFA_TARGET_INFO_MBZ; + retval->arg4 = FFA_TARGET_INFO_MBZ; + retval->arg5 = FFA_TARGET_INFO_MBZ; + retval->arg6 = FFA_TARGET_INFO_MBZ; + retval->arg7 = FFA_TARGET_INFO_MBZ; +} + +static void spmd_build_direct_message_req(spmd_spm_core_context_t *ctx, + uint64_t x1, uint64_t x2, + uint64_t x3, uint64_t x4) +{ + gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); + + write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32); + write_ctx_reg(gpregs, CTX_GPREG_X1, x1); + write_ctx_reg(gpregs, CTX_GPREG_X2, x2); + write_ctx_reg(gpregs, CTX_GPREG_X3, x3); + write_ctx_reg(gpregs, CTX_GPREG_X4, x4); + write_ctx_reg(gpregs, CTX_GPREG_X5, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X6, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X7, 0U); +} + +static void spmd_encode_ctx_to_ffa_value(spmd_spm_core_context_t *ctx, + struct ffa_value *retval) +{ + gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); + + retval->func = read_ctx_reg(gpregs, CTX_GPREG_X0); + retval->arg1 = read_ctx_reg(gpregs, CTX_GPREG_X1); + retval->arg2 = read_ctx_reg(gpregs, CTX_GPREG_X2); + retval->arg3 = read_ctx_reg(gpregs, CTX_GPREG_X3); + retval->arg4 = read_ctx_reg(gpregs, CTX_GPREG_X4); + retval->arg5 = read_ctx_reg(gpregs, CTX_GPREG_X5); + retval->arg6 = read_ctx_reg(gpregs, CTX_GPREG_X6); + retval->arg7 = read_ctx_reg(gpregs, CTX_GPREG_X7); +} + +static void spmd_logical_sp_set_dir_req_ongoing(spmd_spm_core_context_t *ctx) +{ + ctx->spmd_lp_sync_req_ongoing |= SPMD_LP_FFA_DIR_REQ_ONGOING; +} + +static void spmd_logical_sp_reset_dir_req_ongoing(spmd_spm_core_context_t *ctx) +{ + ctx->spmd_lp_sync_req_ongoing &= ~SPMD_LP_FFA_DIR_REQ_ONGOING; +} + #endif /* @@ -163,3 +225,174 @@ void spmd_logical_sp_set_spmc_failure(void) is_spmc_inited = false; #endif } + +/******************************************************************************* + * This function sends an FF-A Direct Request from a partition in EL3 to a + * partition that may reside under an SPMC (only lower ELs supported). The main + * use of this API is for SPMD logical partitions. + * The API is expected to be used when there are platform specific SMCs that + * need to be routed to a secure partition that is FF-A compliant or when + * there are group 0 interrupts that need to be handled first in EL3 and then + * forwarded to an FF-A compliant secure partition. Therefore, it is expected + * that the handle to the context provided belongs to the non-secure context. + * This also means that interrupts/SMCs that trap to EL3 during secure execution + * cannot use this API. + * x1, x2, x3 and x4 are encoded as specified in the FF-A specification. + * retval is used to pass the direct response values to the caller. + * The function returns true if retval has valid values, and false otherwise. + ******************************************************************************/ +bool spmd_el3_ffa_msg_direct_req(uint64_t x1, + uint64_t x2, + uint64_t x3, + uint64_t x4, + void *handle, + struct ffa_value *retval) +{ +#if ENABLE_SPMD_LP + + uint64_t rc = UINT64_MAX; + spmd_spm_core_context_t *ctx = spmd_get_context(); + + if (retval == NULL) { + return false; + } + + memset(retval, 0, sizeof(*retval)); + + if (!is_spmd_lp_inited || !is_spmc_inited) { + VERBOSE("Cannot send SPMD logical partition direct message," + " Partitions not initialized or SPMC not initialized.\n"); + spmd_encode_ffa_error(retval, FFA_ERROR_DENIED); + return true; + } + + /* + * x2 must be zero, since there is no support for framework message via + * an SPMD logical partition. This is sort of a useless check and it is + * possible to not take parameter. However, as the framework extends it + * may be useful to have x2 and extend this function later with + * functionality based on x2. + */ + if (x2 != 0) { + VERBOSE("x2 must be zero. Cannot send framework message.\n"); + spmd_encode_ffa_error(retval, FFA_ERROR_DENIED); + return true; + } + + /* + * Current context must be non-secure. API is expected to be used + * when entry into EL3 and the SPMD logical partition is via an + * interrupt that occurs when execution is in normal world and + * SMCs from normal world. FF-A compliant SPMCs are expected to + * trap interrupts during secure execution in lower ELs since they + * are usually not re-entrant and SMCs from secure world can be + * handled synchronously. There is no known use case for an SPMD + * logical partition to send a direct message to another partition + * in response to a secure interrupt or SMCs from secure world. + */ + if (handle != cm_get_context(NON_SECURE)) { + VERBOSE("Handle must be for the non-secure context.\n"); + spmd_encode_ffa_error(retval, FFA_ERROR_DENIED); + return true; + } + + if (!is_spmd_lp_id(ffa_endpoint_source(x1))) { + VERBOSE("Source ID must be valid SPMD logical partition" + " ID.\n"); + spmd_encode_ffa_error(retval, + FFA_ERROR_INVALID_PARAMETER); + return true; + } + + if (is_spmd_lp_id(ffa_endpoint_destination(x1))) { + VERBOSE("Destination ID must not be SPMD logical partition" + " ID.\n"); + spmd_encode_ffa_error(retval, + FFA_ERROR_INVALID_PARAMETER); + return true; + } + + if (!ffa_is_secure_world_id(ffa_endpoint_destination(x1))) { + VERBOSE("Destination ID must be secure world ID.\n"); + spmd_encode_ffa_error(retval, + FFA_ERROR_INVALID_PARAMETER); + return true; + } + + if (ffa_endpoint_destination(x1) == SPMD_DIRECT_MSG_ENDPOINT_ID) { + VERBOSE("Destination ID must not be SPMD ID.\n"); + spmd_encode_ffa_error(retval, + FFA_ERROR_INVALID_PARAMETER); + return true; + } + + if (ffa_endpoint_destination(x1) == spmd_spmc_id_get()) { + VERBOSE("Destination ID must not be SPMC ID.\n"); + spmd_encode_ffa_error(retval, + FFA_ERROR_INVALID_PARAMETER); + return true; + } + + /* Save the non-secure context before entering SPMC */ + cm_el1_sysregs_context_save(NON_SECURE); +#if SPMD_SPM_AT_SEL2 + cm_el2_sysregs_context_save(NON_SECURE); +#endif + + /* + * Perform synchronous entry into the SPMC. Synchronous entry is + * required because the spec requires that a direct message request + * from an SPMD LP look like a function call from it's perspective. + */ + spmd_build_direct_message_req(ctx, x1, x2, x3, x4); + spmd_logical_sp_set_dir_req_ongoing(ctx); + + rc = spmd_spm_core_sync_entry(ctx); + + spmd_logical_sp_reset_dir_req_ongoing(ctx); + + if (rc != 0ULL) { + ERROR("%s failed (%lx) on CPU%u\n", __func__, rc, + plat_my_core_pos()); + panic(); + } else { + spmd_encode_ctx_to_ffa_value(ctx, retval); + + /* + * Only expect error or direct response, + * spmd_spm_core_sync_exit should not be called on other paths. + * Checks are asserts since the LSP can fail gracefully if the + * source or destination ids are not the same. Panic'ing would + * not provide any benefit. + */ + assert(is_ffa_error(retval) || is_ffa_direct_msg_resp(retval)); + assert(is_ffa_error(retval) || + (ffa_endpoint_destination(retval->arg1) == + ffa_endpoint_source(x1))); + assert(is_ffa_error(retval) || + (ffa_endpoint_source(retval->arg1) == + ffa_endpoint_destination(x1))); + } + + cm_el1_sysregs_context_restore(NON_SECURE); +#if SPMD_SPM_AT_SEL2 + cm_el2_sysregs_context_restore(NON_SECURE); +#endif + cm_set_next_eret_context(NON_SECURE); + + return true; +#else + return false; +#endif +} + +bool is_spmd_logical_sp_dir_req_in_progress( + spmd_spm_core_context_t *ctx) +{ +#if ENABLE_SPMD_LP + return ((ctx->spmd_lp_sync_req_ongoing & SPMD_LP_FFA_DIR_REQ_ONGOING) + == SPMD_LP_FFA_DIR_REQ_ONGOING); +#else + return false; +#endif +} diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index 9b06b55c3..b1bf795af 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -863,6 +863,16 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, spmd_spm_core_sync_exit(x2); } + /* + * If there was an SPMD logical partition direct request on-going, + * return back to the SPMD logical partition so the error can be + * consumed. + */ + if (is_spmd_logical_sp_dir_req_in_progress(ctx)) { + assert(secure_origin); + spmd_spm_core_sync_exit(0ULL); + } + return spmd_smc_forward(smc_fid, secure_origin, x1, x2, x3, x4, cookie, handle, flags); @@ -1052,6 +1062,31 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, case FFA_MSG_SEND_DIRECT_REQ_SMC32: case FFA_MSG_SEND_DIRECT_REQ_SMC64: + /* + * Regardless of secure_origin, SPMD logical partitions cannot + * handle direct messages. They can only initiate direct + * messages and consume direct responses or errors. + */ + if (is_spmd_lp_id(ffa_endpoint_source(x1)) || + is_spmd_lp_id(ffa_endpoint_destination(x1))) { + return spmd_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER + ); + } + + /* + * When there is an ongoing SPMD logical partition direct + * request, there cannot be another direct request. Return + * error in this case. Panic'ing is an option but that does + * not provide the opportunity for caller to abort based on + * error codes. + */ + if (is_spmd_logical_sp_dir_req_in_progress(ctx)) { + assert(secure_origin); + return spmd_ffa_error_return(handle, + FFA_ERROR_DENIED); + } + if (!secure_origin) { /* Validate source endpoint is non-secure for non-secure caller. */ if (ffa_is_secure_world_id(ffa_endpoint_source(x1))) { @@ -1079,7 +1114,9 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, break; /* Not reached */ case FFA_MSG_SEND_DIRECT_RESP_SMC32: - if (secure_origin && spmd_is_spmc_message(x1)) { + case FFA_MSG_SEND_DIRECT_RESP_SMC64: + if (secure_origin && (spmd_is_spmc_message(x1) || + is_spmd_logical_sp_dir_req_in_progress(ctx))) { spmd_spm_core_sync_exit(0ULL); } else { /* Forward direct message to the other world */ @@ -1119,7 +1156,6 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, /* Forward the call to the other world */ /* fallthrough */ case FFA_MSG_SEND: - case FFA_MSG_SEND_DIRECT_RESP_SMC64: case FFA_MEM_DONATE_SMC32: case FFA_MEM_DONATE_SMC64: case FFA_MEM_LEND_SMC32: @@ -1136,11 +1172,14 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, case FFA_SUCCESS_SMC32: case FFA_SUCCESS_SMC64: /* - * TODO: Assume that no requests originate from EL3 at the - * moment. This will change if a SP service is required in - * response to secure interrupts targeted to EL3. Until then - * simply forward the call to the Normal world. + * If there is an ongoing direct request from an SPMD logical + * partition, return an error. */ + if (is_spmd_logical_sp_dir_req_in_progress(ctx)) { + assert(secure_origin); + return spmd_ffa_error_return(handle, + FFA_ERROR_DENIED); + } return spmd_smc_forward(smc_fid, secure_origin, x1, x2, x3, x4, cookie, @@ -1167,6 +1206,12 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, FFA_ERROR_NOT_SUPPORTED); } + if (is_spmd_logical_sp_dir_req_in_progress(ctx)) { + assert(secure_origin); + return spmd_ffa_error_return(handle, + FFA_ERROR_DENIED); + } + return spmd_smc_forward(smc_fid, secure_origin, x1, x2, x3, x4, cookie, handle, flags); diff --git a/services/std_svc/spmd/spmd_private.h b/services/std_svc/spmd/spmd_private.h index ff6942e41..237879919 100644 --- a/services/std_svc/spmd/spmd_private.h +++ b/services/std_svc/spmd/spmd_private.h @@ -52,8 +52,14 @@ typedef struct spmd_spm_core_context { cpu_context_t cpu_ctx; spmc_state_t state; bool secure_interrupt_ongoing; +#if ENABLE_SPMD_LP + uint8_t spmd_lp_sync_req_ongoing; +#endif } spmd_spm_core_context_t; +/* Flags to indicate ongoing requests for SPMD EL3 logical partitions */ +#define SPMD_LP_FFA_DIR_REQ_ONGOING U(0x1) + /* * Reserve ID for NS physical FFA Endpoint. */ From 5ca1619f86e6d1d85800bb6afb1d2a3c7165af5d Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Sat, 22 Apr 2023 11:21:58 -0700 Subject: [PATCH 3/8] refactor(ff-a): move structure definitions Move ffa_partition_info_get definitions from EL3 SPMC private header files to common header files. The structures are common to FF-A and are useful for the EL3 SPMD logical partitions. Signed-off-by: Raghu Krishnamurthy Change-Id: I19de3f6cb3351afa873022da1397a475a84e3d8b --- include/services/ffa_svc.h | 24 ++++++++++++++++++++++++ services/std_svc/spm/el3_spmc/spmc.h | 24 ------------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h index 64af437bb..de56638c7 100644 --- a/include/services/ffa_svc.h +++ b/include/services/ffa_svc.h @@ -343,4 +343,28 @@ struct ffa_boot_info_header { uint64_t reserved; }; +/* FF-A Partition Info Get related macros. */ +#define FFA_PARTITION_INFO_GET_PROPERTIES_V1_0_MASK U(0x7) +#define FFA_PARTITION_INFO_GET_EXEC_STATE_SHIFT U(8) +#define FFA_PARTITION_INFO_GET_AARCH32_STATE U(0) +#define FFA_PARTITION_INFO_GET_AARCH64_STATE U(1) + +/** + * Holds information returned for each partition by the FFA_PARTITION_INFO_GET + * interface. + */ +struct ffa_partition_info_v1_0 { + uint16_t ep_id; + uint16_t execution_ctx_count; + uint32_t properties; +}; + +/* Extended structure for FF-A v1.1. */ +struct ffa_partition_info_v1_1 { + uint16_t ep_id; + uint16_t execution_ctx_count; + uint32_t properties; + uint32_t uuid[4]; +}; + #endif /* FFA_SVC_H */ diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h index 13875b913..48644ac7f 100644 --- a/services/std_svc/spm/el3_spmc/spmc.h +++ b/services/std_svc/spm/el3_spmc/spmc.h @@ -213,30 +213,6 @@ struct ns_endpoint_desc { uint32_t ffa_version; }; -/** - * Holds information returned for each partition by the FFA_PARTITION_INFO_GET - * interface. - */ -struct ffa_partition_info_v1_0 { - uint16_t ep_id; - uint16_t execution_ctx_count; - uint32_t properties; -}; - -/* Extended structure for v1.1. */ -struct ffa_partition_info_v1_1 { - uint16_t ep_id; - uint16_t execution_ctx_count; - uint32_t properties; - uint32_t uuid[4]; -}; - -/* FF-A Partition Info Get related macros. */ -#define FFA_PARTITION_INFO_GET_PROPERTIES_V1_0_MASK U(0x7) -#define FFA_PARTITION_INFO_GET_EXEC_STATE_SHIFT U(8) -#define FFA_PARTITION_INFO_GET_AARCH32_STATE U(0) -#define FFA_PARTITION_INFO_GET_AARCH64_STATE U(1) - /* Reference to power management hooks */ extern const spd_pm_ops_t spmc_pm; From 0b850e9e7c89667f9a12d49492a60baf44750dd9 Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Sat, 22 Apr 2023 11:28:38 -0700 Subject: [PATCH 4/8] feat(spmd): add partition info get regs This patch adds support for an EL3 SPMD logical partition to discover secure partitions using the FFA_PARTITION_INFO_GET_REGS abi. It also adds helper functions for a logical partition to use the information returned in registers in a meaningful way. Signed-off-by: Raghu Krishnamurthy Change-Id: Id69488e7367e17e2dfa6c8e332be3c8d41f6c773 --- include/services/el3_spmd_logical_sp.h | 51 +++++++ services/std_svc/spmd/spmd_logical_sp.c | 173 ++++++++++++++++++++++++ services/std_svc/spmd/spmd_main.c | 10 ++ services/std_svc/spmd/spmd_private.h | 1 + 4 files changed, 235 insertions(+) diff --git a/include/services/el3_spmd_logical_sp.h b/include/services/el3_spmd_logical_sp.h index dcbbfabe4..c81071431 100644 --- a/include/services/el3_spmd_logical_sp.h +++ b/include/services/el3_spmd_logical_sp.h @@ -35,6 +35,16 @@ struct ffa_value { uint64_t arg5; uint64_t arg6; uint64_t arg7; + uint64_t arg8; + uint64_t arg9; + uint64_t arg10; + uint64_t arg11; + uint64_t arg12; + uint64_t arg13; + uint64_t arg14; + uint64_t arg15; + uint64_t arg16; + uint64_t arg17; }; /* Convenience macro to declare a SPMD logical partition descriptor. */ @@ -76,12 +86,50 @@ static inline bool is_ffa_error(struct ffa_value *retval) return retval->func == FFA_ERROR; } +static inline bool is_ffa_success(struct ffa_value *retval) +{ + return (retval->func == FFA_SUCCESS_SMC32) || + (retval->func == FFA_SUCCESS_SMC64); +} + static inline bool is_ffa_direct_msg_resp(struct ffa_value *retval) { return (retval->func == FFA_MSG_SEND_DIRECT_RESP_SMC32) || (retval->func == FFA_MSG_SEND_DIRECT_RESP_SMC64); } +static inline uint16_t ffa_partition_info_regs_get_last_idx( + struct ffa_value args) +{ + return (uint16_t)(args.arg2 & 0xFFFFU); +} + +static inline uint16_t ffa_partition_info_regs_get_curr_idx( + struct ffa_value args) +{ + return (uint16_t)((args.arg2 >> 16) & 0xFFFFU); +} + +static inline uint16_t ffa_partition_info_regs_get_tag(struct ffa_value args) +{ + return (uint16_t)((args.arg2 >> 32) & 0xFFFFU); +} + +static inline uint16_t ffa_partition_info_regs_get_desc_size( + struct ffa_value args) +{ + return (uint16_t)(args.arg2 >> 48); +} + +bool ffa_partition_info_regs_get_part_info( + struct ffa_value args, uint8_t idx, + struct ffa_partition_info_v1_1 *partition_info); + +bool spmd_el3_invoke_partition_info_get( + const uint32_t target_uuid[4], + const uint16_t start_index, + const uint16_t tag, + struct ffa_value *retval); void spmd_logical_sp_set_spmc_initialized(void); void spmc_logical_sp_set_spmc_failure(void); @@ -89,6 +137,9 @@ int32_t spmd_logical_sp_init(void); bool is_spmd_logical_sp_dir_req_in_progress( spmd_spm_core_context_t *ctx); +bool is_spmd_logical_sp_info_regs_req_in_progress( + spmd_spm_core_context_t *ctx); + bool spmd_el3_ffa_msg_direct_req(uint64_t x1, uint64_t x2, uint64_t x3, diff --git a/services/std_svc/spmd/spmd_logical_sp.c b/services/std_svc/spmd/spmd_logical_sp.c index 06b5d1242..80b49ba85 100644 --- a/services/std_svc/spmd/spmd_logical_sp.c +++ b/services/std_svc/spmd/spmd_logical_sp.c @@ -149,6 +149,16 @@ static void spmd_encode_ctx_to_ffa_value(spmd_spm_core_context_t *ctx, retval->arg5 = read_ctx_reg(gpregs, CTX_GPREG_X5); retval->arg6 = read_ctx_reg(gpregs, CTX_GPREG_X6); retval->arg7 = read_ctx_reg(gpregs, CTX_GPREG_X7); + retval->arg8 = read_ctx_reg(gpregs, CTX_GPREG_X8); + retval->arg9 = read_ctx_reg(gpregs, CTX_GPREG_X9); + retval->arg10 = read_ctx_reg(gpregs, CTX_GPREG_X10); + retval->arg11 = read_ctx_reg(gpregs, CTX_GPREG_X11); + retval->arg12 = read_ctx_reg(gpregs, CTX_GPREG_X12); + retval->arg13 = read_ctx_reg(gpregs, CTX_GPREG_X13); + retval->arg14 = read_ctx_reg(gpregs, CTX_GPREG_X14); + retval->arg15 = read_ctx_reg(gpregs, CTX_GPREG_X15); + retval->arg16 = read_ctx_reg(gpregs, CTX_GPREG_X16); + retval->arg17 = read_ctx_reg(gpregs, CTX_GPREG_X17); } static void spmd_logical_sp_set_dir_req_ongoing(spmd_spm_core_context_t *ctx) @@ -161,6 +171,47 @@ static void spmd_logical_sp_reset_dir_req_ongoing(spmd_spm_core_context_t *ctx) ctx->spmd_lp_sync_req_ongoing &= ~SPMD_LP_FFA_DIR_REQ_ONGOING; } +static void spmd_build_ffa_info_get_regs(spmd_spm_core_context_t *ctx, + const uint32_t uuid[4], + const uint16_t start_index, + const uint16_t tag) +{ + gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); + + uint64_t arg1 = (uint64_t)uuid[1] << 32 | uuid[0]; + uint64_t arg2 = (uint64_t)uuid[3] << 32 | uuid[2]; + uint64_t arg3 = start_index | (uint64_t)tag << 16; + + write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_PARTITION_INFO_GET_REGS_SMC64); + write_ctx_reg(gpregs, CTX_GPREG_X1, arg1); + write_ctx_reg(gpregs, CTX_GPREG_X2, arg2); + write_ctx_reg(gpregs, CTX_GPREG_X3, arg3); + write_ctx_reg(gpregs, CTX_GPREG_X4, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X5, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X6, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X7, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X8, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X9, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X10, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X11, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X12, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X13, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X14, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X15, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X16, 0U); + write_ctx_reg(gpregs, CTX_GPREG_X17, 0U); +} + +static void spmd_logical_sp_set_info_regs_ongoing(spmd_spm_core_context_t *ctx) +{ + ctx->spmd_lp_sync_req_ongoing |= SPMD_LP_FFA_INFO_GET_REG_ONGOING; +} + +static void spmd_logical_sp_reset_info_regs_ongoing( + spmd_spm_core_context_t *ctx) +{ + ctx->spmd_lp_sync_req_ongoing &= ~SPMD_LP_FFA_INFO_GET_REG_ONGOING; +} #endif /* @@ -226,6 +277,117 @@ void spmd_logical_sp_set_spmc_failure(void) #endif } +/* + * This function takes an ffa_value structure populated with partition + * information from an FFA_PARTITION_INFO_GET_REGS ABI call, extracts + * the values and writes it into a ffa_partition_info_v1_1 structure for + * other code to consume. + */ +bool ffa_partition_info_regs_get_part_info( + struct ffa_value args, uint8_t idx, + struct ffa_partition_info_v1_1 *partition_info) +{ + uint64_t *arg_ptrs; + uint64_t info, uuid_lo, uuid_high; + + /* + * Each partition information is encoded in 3 registers, so there can be + * a maximum of 5 entries. + */ + if (idx >= 5 || partition_info == NULL) { + return false; + } + + /* List of pointers to args in return value. */ + arg_ptrs = (uint64_t *)&args + ((idx * 3) + 3); + info = *arg_ptrs; + + arg_ptrs++; + uuid_lo = *arg_ptrs; + + arg_ptrs++; + uuid_high = *arg_ptrs; + + partition_info->ep_id = (uint16_t)(info & 0xFFFFU); + partition_info->execution_ctx_count = (uint16_t)((info >> 16) & 0xFFFFU); + partition_info->properties = (uint32_t)(info >> 32); + partition_info->uuid[0] = (uint32_t)(uuid_lo & 0xFFFFFFFFU); + partition_info->uuid[1] = (uint32_t)((uuid_lo >> 32) & 0xFFFFFFFFU); + partition_info->uuid[2] = (uint32_t)(uuid_high & 0xFFFFFFFFU); + partition_info->uuid[3] = (uint32_t)((uuid_high >> 32) & 0xFFFFFFFFU); + + return true; +} + +/* + * This function can be used by an SPMD logical partition to invoke the + * FFA_PARTITION_INFO_GET_REGS ABI to the SPMC, to discover the secure + * partitions in the system. The function takes a UUID, start index and + * tag and the partition information are returned in an ffa_value structure + * and can be consumed by using appropriate helper functions. + */ +bool spmd_el3_invoke_partition_info_get( + const uint32_t target_uuid[4], + const uint16_t start_index, + const uint16_t tag, + struct ffa_value *retval) +{ +#if ENABLE_SPMD_LP + uint64_t rc = UINT64_MAX; + spmd_spm_core_context_t *ctx = spmd_get_context(); + + if (retval == NULL) { + return false; + } + + memset(retval, 0, sizeof(*retval)); + + if (!is_spmc_inited) { + VERBOSE("Cannot discover partition before," + " SPMC is initialized.\n"); + spmd_encode_ffa_error(retval, FFA_ERROR_DENIED); + return true; + } + + if (tag != 0) { + VERBOSE("Tag must be zero. other tags unsupported\n"); + spmd_encode_ffa_error(retval, + FFA_ERROR_INVALID_PARAMETER); + return true; + } + + /* Save the non-secure context before entering SPMC */ + cm_el1_sysregs_context_save(NON_SECURE); +#if SPMD_SPM_AT_SEL2 + cm_el2_sysregs_context_save(NON_SECURE); +#endif + + spmd_build_ffa_info_get_regs(ctx, target_uuid, start_index, tag); + spmd_logical_sp_set_info_regs_ongoing(ctx); + + rc = spmd_spm_core_sync_entry(ctx); + if (rc != 0ULL) { + ERROR("%s failed (%lx) on CPU%u\n", __func__, rc, + plat_my_core_pos()); + panic(); + } + + spmd_logical_sp_reset_info_regs_ongoing(ctx); + spmd_encode_ctx_to_ffa_value(ctx, retval); + + assert(is_ffa_error(retval) || is_ffa_success(retval)); + + cm_el1_sysregs_context_restore(NON_SECURE); +#if SPMD_SPM_AT_SEL2 + cm_el2_sysregs_context_restore(NON_SECURE); +#endif + cm_set_next_eret_context(NON_SECURE); + return true; +#else + return false; +#endif +} + /******************************************************************************* * This function sends an FF-A Direct Request from a partition in EL3 to a * partition that may reside under an SPMC (only lower ELs supported). The main @@ -386,6 +548,17 @@ bool spmd_el3_ffa_msg_direct_req(uint64_t x1, #endif } +bool is_spmd_logical_sp_info_regs_req_in_progress( + spmd_spm_core_context_t *ctx) +{ +#if ENABLE_SPMD_LP + return ((ctx->spmd_lp_sync_req_ongoing & SPMD_LP_FFA_INFO_GET_REG_ONGOING) + == SPMD_LP_FFA_INFO_GET_REG_ONGOING); +#else + return false; +#endif +} + bool is_spmd_logical_sp_dir_req_in_progress( spmd_spm_core_context_t *ctx) { diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index b1bf795af..d8eb09b90 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -852,6 +852,16 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, SMC_GET_GP(handle, CTX_GPREG_X6), SMC_GET_GP(handle, CTX_GPREG_X7)); + /* + * If there is an on-going info regs from EL3 SPMD LP, unconditionally + * return, we don't expect any other FF-A ABIs to be called between + * calls to FFA_PARTITION_INFO_GET_REGS. + */ + if (is_spmd_logical_sp_info_regs_req_in_progress(ctx)) { + assert(secure_origin); + spmd_spm_core_sync_exit(0ULL); + } + switch (smc_fid) { case FFA_ERROR: /* diff --git a/services/std_svc/spmd/spmd_private.h b/services/std_svc/spmd/spmd_private.h index 237879919..5ac501180 100644 --- a/services/std_svc/spmd/spmd_private.h +++ b/services/std_svc/spmd/spmd_private.h @@ -59,6 +59,7 @@ typedef struct spmd_spm_core_context { /* Flags to indicate ongoing requests for SPMD EL3 logical partitions */ #define SPMD_LP_FFA_DIR_REQ_ONGOING U(0x1) +#define SPMD_LP_FFA_INFO_GET_REG_ONGOING U(0x2) /* * Reserve ID for NS physical FFA Endpoint. From 95f7f6d86a6aadc9d235684fd1aa57ddc4c56ea9 Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Sat, 22 Apr 2023 18:00:02 -0700 Subject: [PATCH 5/8] feat(spmd): get logical partitions info This patch enables FF-A secure partitions and the SPMC to query EL3 SPMD logical partitions that are present in the system via partition get info regs abi. Note that normal world will not be able to see EL3 SPMD logical partitions as per the spec. Signed-off-by: Raghu Krishnamurthy Change-Id: I3fca8aed8ae156a559a74521803324c13ae3d55a --- include/services/el3_spmd_logical_sp.h | 9 ++ services/std_svc/spmd/spmd_logical_sp.c | 175 +++++++++++++++++++++++- services/std_svc/spmd/spmd_main.c | 9 +- services/std_svc/spmd/spmd_private.h | 3 + 4 files changed, 189 insertions(+), 7 deletions(-) diff --git a/include/services/el3_spmd_logical_sp.h b/include/services/el3_spmd_logical_sp.h index c81071431..670474a90 100644 --- a/include/services/el3_spmd_logical_sp.h +++ b/include/services/el3_spmd_logical_sp.h @@ -72,6 +72,12 @@ CASSERT(sizeof(struct spmd_lp_desc) == 40, assert_spmd_lp_desc_size_mismatch); #define SPMD_LP_ID_END (SPMD_DIRECT_MSG_ENDPOINT_ID - 1) #define SPMD_LP_ID_START (SPMD_LP_ID_END - 62) +/* + * TODO: Arbitrary number. Can make this platform specific in the future, + * no known use cases for more LPs at this point. + */ +#define EL3_SPMD_MAX_NUM_LP U(5) + static inline bool is_spmd_lp_id(unsigned int id) { #if ENABLE_SPMD_LP @@ -121,6 +127,9 @@ static inline uint16_t ffa_partition_info_regs_get_desc_size( return (uint16_t)(args.arg2 >> 48); } +uint64_t spmd_el3_populate_logical_partition_info(void *handle, uint64_t x1, + uint64_t x2, uint64_t x3); + bool ffa_partition_info_regs_get_part_info( struct ffa_value args, uint8_t idx, struct ffa_partition_info_v1_1 *partition_info); diff --git a/services/std_svc/spmd/spmd_logical_sp.c b/services/std_svc/spmd/spmd_logical_sp.c index 80b49ba85..964b36b4d 100644 --- a/services/std_svc/spmd/spmd_logical_sp.c +++ b/services/std_svc/spmd/spmd_logical_sp.c @@ -10,11 +10,25 @@ #include "spmd_private.h" #include +#include #include #include #include +#include +/* + * Maximum ffa_partition_info entries that can be returned by an invocation + * of FFA_PARTITION_INFO_GET_REGS_64 is size in bytes, of available + * registers/args in struct ffa_value divided by size of struct + * ffa_partition_info. For this ABI, arg3-arg17 in ffa_value can be used, i.e. + * 15 uint64_t fields. For FF-A v1.1, this value should be 5. + */ +#define MAX_INFO_REGS_ENTRIES_PER_CALL \ + (uint8_t)((15 * sizeof(uint64_t)) / \ + sizeof(struct ffa_partition_info_v1_1)) +CASSERT(MAX_INFO_REGS_ENTRIES_PER_CALL == 5, assert_too_many_info_reg_entries); + #if ENABLE_SPMD_LP static bool is_spmd_lp_inited; static bool is_spmc_inited; @@ -212,6 +226,62 @@ static void spmd_logical_sp_reset_info_regs_ongoing( { ctx->spmd_lp_sync_req_ongoing &= ~SPMD_LP_FFA_INFO_GET_REG_ONGOING; } + +static void spmd_fill_lp_info_array( + struct ffa_partition_info_v1_1 (*partitions)[EL3_SPMD_MAX_NUM_LP], + uint32_t uuid[4], uint16_t *lp_count_out) +{ + uint16_t lp_count = 0; + struct spmd_lp_desc *lp_array; + bool uuid_is_null = is_null_uuid(uuid); + + if (SPMD_LP_DESCS_COUNT == 0U) { + *lp_count_out = 0; + return; + } + + lp_array = get_spmd_el3_lp_array(); + for (uint16_t index = 0; index < SPMD_LP_DESCS_COUNT; ++index) { + struct spmd_lp_desc *lp = &lp_array[index]; + + if (uuid_is_null || uuid_match(uuid, lp->uuid)) { + uint16_t array_index = lp_count; + + ++lp_count; + + (*partitions)[array_index].ep_id = lp->sp_id; + (*partitions)[array_index].execution_ctx_count = 1; + (*partitions)[array_index].properties = lp->properties; + (*partitions)[array_index].properties |= + (FFA_PARTITION_INFO_GET_AARCH64_STATE << + FFA_PARTITION_INFO_GET_EXEC_STATE_SHIFT); + if (uuid_is_null) { + memcpy(&((*partitions)[array_index].uuid), + &lp->uuid, sizeof(lp->uuid)); + } + } + } + + *lp_count_out = lp_count; +} + +static inline void spmd_pack_lp_count_props( + uint64_t *xn, uint16_t ep_id, uint16_t vcpu_count, + uint32_t properties) +{ + *xn = (uint64_t)ep_id; + *xn |= (uint64_t)vcpu_count << 16; + *xn |= (uint64_t)properties << 32; +} + +static inline void spmd_pack_lp_uuid(uint64_t *xn_1, uint64_t *xn_2, + uint32_t uuid[4]) +{ + *xn_1 = (uint64_t)uuid[0]; + *xn_1 |= (uint64_t)uuid[1] << 32; + *xn_2 = (uint64_t)uuid[2]; + *xn_2 |= (uint64_t)uuid[3] << 32; +} #endif /* @@ -224,6 +294,8 @@ int32_t spmd_logical_sp_init(void) int32_t rc = 0; struct spmd_lp_desc *spmd_lp_descs; + assert(SPMD_LP_DESCS_COUNT <= EL3_SPMD_MAX_NUM_LP); + if (is_spmd_lp_inited == true) { return 0; } @@ -298,7 +370,11 @@ bool ffa_partition_info_regs_get_part_info( return false; } - /* List of pointers to args in return value. */ + /* + * List of pointers to args in return value. arg0/func encodes ff-a + * function, arg1 is reserved, arg2 encodes indices. arg3 and greater + * values reflect partition properties. + */ arg_ptrs = (uint64_t *)&args + ((idx * 3) + 3); info = *arg_ptrs; @@ -320,7 +396,102 @@ bool ffa_partition_info_regs_get_part_info( } /* - * This function can be used by an SPMD logical partition to invoke the + * This function is called by the SPMD in response to + * an FFA_PARTITION_INFO_GET_REG ABI invocation by the SPMC. Secure partitions + * are allowed to discover the presence of EL3 SPMD logical partitions by + * invoking the aforementioned ABI and this function populates the required + * information about EL3 SPMD logical partitions. + */ +uint64_t spmd_el3_populate_logical_partition_info(void *handle, uint64_t x1, + uint64_t x2, uint64_t x3) +{ +#if ENABLE_SPMD_LP + uint32_t target_uuid[4] = { 0 }; + uint32_t w0; + uint32_t w1; + uint32_t w2; + uint32_t w3; + uint16_t start_index; + uint16_t tag; + static struct ffa_partition_info_v1_1 partitions[EL3_SPMD_MAX_NUM_LP]; + uint16_t lp_count = 0; + uint16_t max_idx = 0; + uint16_t curr_idx = 0; + uint8_t num_entries_to_ret = 0; + struct ffa_value ret = { 0 }; + uint64_t *arg_ptrs = (uint64_t *)&ret + 3; + + w0 = (uint32_t)(x1 & 0xFFFFFFFFU); + w1 = (uint32_t)(x1 >> 32); + w2 = (uint32_t)(x2 & 0xFFFFFFFFU); + w3 = (uint32_t)(x2 >> 32); + + target_uuid[0] = w0; + target_uuid[1] = w1; + target_uuid[2] = w2; + target_uuid[3] = w3; + + start_index = (uint16_t)(x3 & 0xFFFFU); + tag = (uint16_t)((x3 >> 16) & 0xFFFFU); + + assert(handle == cm_get_context(SECURE)); + + if (tag != 0) { + VERBOSE("Tag is not 0. Cannot return partition info.\n"); + return spmd_ffa_error_return(handle, FFA_ERROR_RETRY); + } + + memset(&partitions, 0, sizeof(partitions)); + + spmd_fill_lp_info_array(&partitions, target_uuid, &lp_count); + + if (lp_count == 0) { + VERBOSE("No SPDM EL3 logical partitions exist.\n"); + return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + } + + if (start_index >= lp_count) { + VERBOSE("start_index = %d, lp_count = %d (start index must be" + " less than partition count.\n", + start_index, lp_count); + return spmd_ffa_error_return(handle, + FFA_ERROR_INVALID_PARAMETER); + } + + max_idx = lp_count - 1; + num_entries_to_ret = (max_idx - start_index) + 1; + num_entries_to_ret = + MIN(num_entries_to_ret, MAX_INFO_REGS_ENTRIES_PER_CALL); + curr_idx = start_index + num_entries_to_ret - 1; + assert(curr_idx <= max_idx); + + ret.func = FFA_SUCCESS_SMC64; + ret.arg2 = (uint64_t)((sizeof(struct ffa_partition_info_v1_1) & 0xFFFFU) << 48); + ret.arg2 |= (uint64_t)(curr_idx << 16); + ret.arg2 |= (uint64_t)max_idx; + + for (uint16_t idx = start_index; idx <= curr_idx; ++idx) { + spmd_pack_lp_count_props(arg_ptrs, partitions[idx].ep_id, + partitions[idx].execution_ctx_count, + partitions[idx].properties); + arg_ptrs++; + if (is_null_uuid(target_uuid)) { + spmd_pack_lp_uuid(arg_ptrs, (arg_ptrs + 1), + partitions[idx].uuid); + } + arg_ptrs += 2; + } + + SMC_RET18(handle, ret.func, ret.arg1, ret.arg2, ret.arg3, ret.arg4, + ret.arg5, ret.arg6, ret.arg7, ret.arg8, ret.arg9, ret.arg10, + ret.arg11, ret.arg12, ret.arg13, ret.arg14, ret.arg15, + ret.arg16, ret.arg17); +#else + return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); +#endif +} + +/* This function can be used by an SPMD logical partition to invoke the * FFA_PARTITION_INFO_GET_REGS ABI to the SPMC, to discover the secure * partitions in the system. The function takes a UUID, start index and * tag and the partition information are returned in an ffa_value structure diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index d8eb09b90..d830403fc 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -87,8 +87,7 @@ uint16_t spmd_spmc_id_get(void) ******************************************************************************/ static int32_t spmd_init(void); static int spmd_spmc_init(void *pm_addr); -static uint64_t spmd_ffa_error_return(void *handle, - int error_code); + static uint64_t spmd_smc_forward(uint32_t smc_fid, bool secure_origin, uint64_t x1, @@ -749,7 +748,7 @@ static uint64_t spmd_smc_forward(uint32_t smc_fid, /******************************************************************************* * Return FFA_ERROR with specified error code ******************************************************************************/ -static uint64_t spmd_ffa_error_return(void *handle, int error_code) +uint64_t spmd_ffa_error_return(void *handle, int error_code) { SMC_RET8(handle, (uint32_t) FFA_ERROR, FFA_TARGET_INFO_MBZ, (uint32_t)error_code, @@ -1237,8 +1236,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid, #if MAKE_FFA_VERSION(1, 1) <= FFA_VERSION_COMPILED case FFA_PARTITION_INFO_GET_REGS_SMC64: if (secure_origin) { - /* TODO: Future patches to enable support for this */ - return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED); + return spmd_el3_populate_logical_partition_info(handle, x1, + x2, x3); } /* Call only supported with SMCCC 1.2+ */ diff --git a/services/std_svc/spmd/spmd_private.h b/services/std_svc/spmd/spmd_private.h index 5ac501180..fef7ef600 100644 --- a/services/std_svc/spmd/spmd_private.h +++ b/services/std_svc/spmd/spmd_private.h @@ -107,6 +107,9 @@ bool spmd_check_address_in_binary_image(uint64_t address); * otherwise it returns a negative value */ int plat_spmd_handle_group0_interrupt(uint32_t id); + +uint64_t spmd_ffa_error_return(void *handle, int error_code); + #endif /* __ASSEMBLER__ */ #endif /* SPMD_PRIVATE_H */ From 5cf311f3a41fc114289265305a6254a8fb412c0e Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Fri, 3 Mar 2023 19:30:25 -0800 Subject: [PATCH 6/8] feat(fvp): add spmd logical partition This patch changes spmd.mk to include one or more SPMD logical partitions specific to a platform. It also adds a basic SPMD logical partition to fvp. Change-Id: I2075e0458c92813913b28cbf4cfffc1f151e65cf Signed-off-by: Raghu Krishnamurthy --- plat/arm/board/fvp/fvp_spmd_logical_sp.c | 30 ++++++++++++++++++++++++ services/std_svc/spmd/spmd.mk | 10 ++++++++ 2 files changed, 40 insertions(+) create mode 100644 plat/arm/board/fvp/fvp_spmd_logical_sp.c diff --git a/plat/arm/board/fvp/fvp_spmd_logical_sp.c b/plat/arm/board/fvp/fvp_spmd_logical_sp.c new file mode 100644 index 000000000..4f8ce1ea8 --- /dev/null +++ b/plat/arm/board/fvp/fvp_spmd_logical_sp.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include +#include + +#define SPMD_LP_PARTITION_ID SPMD_LP_ID_START +#define SPMD_LP_UUID {0xe98e43ad, 0xb7db524f, 0x47a3bf57, 0x1588f4e3} + +/* SPMD Logical SP currently only supports sending direct message. */ +#define SPMD_PARTITION_PROPERTIES FFA_PARTITION_DIRECT_REQ_SEND + +static int32_t fvp_spmd_logical_partition_init(void) +{ + INFO("FVP SPMD LSP: Init function called.\n"); + return 0; +} + +/* Register SPMD logical partition */ +DECLARE_SPMD_LOGICAL_PARTITION( + fvp_spmd_logical_partition, + fvp_spmd_logical_partition_init,/* Init Function */ + SPMD_LP_PARTITION_ID, /* FF-A Partition ID */ + SPMD_LP_UUID, /* UUID */ + SPMD_PARTITION_PROPERTIES /* Partition Properties. */ +); diff --git a/services/std_svc/spmd/spmd.mk b/services/std_svc/spmd/spmd.mk index cc0e3922f..72376f757 100644 --- a/services/std_svc/spmd/spmd.mk +++ b/services/std_svc/spmd/spmd.mk @@ -18,6 +18,16 @@ SPMD_SOURCES += $(addprefix services/std_svc/spmd/, \ spmd_main.c \ spmd_logical_sp.c) +# Specify platform specific SPMD logical partition implementation. +SPMD_LP_SOURCES := $(wildcard $(addprefix ${PLAT_DIR}/, \ + ${PLAT}_spmd_logical_sp*.c)) + +ifeq (${ENABLE_SPMD_LP}, 1) +ifneq ($(wildcard $(SPMD_LP_SOURCES)),) +SPMD_SOURCES += $(SPMD_LP_SOURCES) +endif +endif + # Let the top-level Makefile know that we intend to include a BL32 image NEED_BL32 := yes From a1a9a950713468a734ef3d8da210baf97f7c1071 Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Sun, 9 Apr 2023 07:46:28 -0700 Subject: [PATCH 7/8] feat(fvp): spmd logical partition smc handler This patch adds a basic el3 spmd logical partition to the fvp platform via a platform specific smc handler. One of the use cases for el3 logical partitions is to have the ability to translate sip calls into ff-a direct requests via the use of spmd logical partitions. The smc handler creates a direct request based on the incoming smc parameters and forwards the call as a direct request from the spmd logical partition to the target secure partition. Change-Id: If8ba9aab8203924bd00fc1dcdf9cd05a9a04a147 --- include/services/el3_spmd_logical_sp.h | 9 ++++ plat/arm/board/fvp/fvp_spmd_logical_sp.c | 69 ++++++++++++++++++++++++ plat/arm/common/arm_sip_svc.c | 8 +++ 3 files changed, 86 insertions(+) diff --git a/include/services/el3_spmd_logical_sp.h b/include/services/el3_spmd_logical_sp.h index 670474a90..1f9ef0d4e 100644 --- a/include/services/el3_spmd_logical_sp.h +++ b/include/services/el3_spmd_logical_sp.h @@ -156,4 +156,13 @@ bool spmd_el3_ffa_msg_direct_req(uint64_t x1, void *handle, struct ffa_value *retval); +uintptr_t plat_spmd_logical_sp_smc_handler(unsigned int smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags); + #endif /* EL3_SPMD_LOGICAL_SP_H */ diff --git a/plat/arm/board/fvp/fvp_spmd_logical_sp.c b/plat/arm/board/fvp/fvp_spmd_logical_sp.c index 4f8ce1ea8..37b44669a 100644 --- a/plat/arm/board/fvp/fvp_spmd_logical_sp.c +++ b/plat/arm/board/fvp/fvp_spmd_logical_sp.c @@ -14,12 +14,81 @@ /* SPMD Logical SP currently only supports sending direct message. */ #define SPMD_PARTITION_PROPERTIES FFA_PARTITION_DIRECT_REQ_SEND +#define SPMD_LP_MAX_SUPPORTED_SP 10 +static void fvp_get_partition_info(void) +{ + struct ffa_value ret = { 0 }; + uint32_t target_uuid[4] = { 0 }; + static struct ffa_partition_info_v1_1 + part_info[SPMD_LP_MAX_SUPPORTED_SP] = { 0 }; + + uint16_t num_partitions = 0; + + if (!spmd_el3_invoke_partition_info_get(target_uuid, 0, 0, &ret)) { + panic(); + } + + if (is_ffa_error(&ret)) { + panic(); + } + + num_partitions = ffa_partition_info_regs_get_last_idx(ret) + 1; + if (num_partitions > SPMD_LP_MAX_SUPPORTED_SP) { + panic(); + } + + INFO("Number of secure partitions = %d\n", num_partitions); + + for (uint16_t i = 0; i < num_partitions; i++) { + INFO("***Start Partition***\n"); + if (!ffa_partition_info_regs_get_part_info(ret, i, &part_info[i])) + panic(); + INFO("\tPartition ID: 0x%x\n", part_info[i].ep_id); + INFO("\tvCPU count:0x%x\n", part_info[i].execution_ctx_count); + INFO("\tProperties: 0x%x\n", part_info[i].properties); + INFO("\tUUID: 0x%x 0x%x 0x%x 0x%x\n", part_info[i].uuid[0], + part_info[i].uuid[1], part_info[i].uuid[2], + part_info[i].uuid[3]); + INFO("***End Partition***\n"); + } + +} + static int32_t fvp_spmd_logical_partition_init(void) { INFO("FVP SPMD LSP: Init function called.\n"); + + fvp_get_partition_info(); return 0; } +/* + * Platform specific SMC handler used to translate SIP SMCs or other platform + * specific SMCs into FF-A direct messages. + */ +uintptr_t plat_spmd_logical_sp_smc_handler(unsigned int smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + struct ffa_value retval = { 0 }; + uint64_t send_recv_id = SPMD_LP_PARTITION_ID << 16 | 0x8001; + + /* + * Forward the SMC as direct request. + */ + if (!spmd_el3_ffa_msg_direct_req(send_recv_id, x2, x3, x4, handle, &retval)) { + panic(); + } + + SMC_RET8(handle, retval.func, retval.arg1, retval.arg2, retval.arg3, + retval.arg4, retval.arg5, retval.arg6, retval.arg7); +} + /* Register SPMD logical partition */ DECLARE_SPMD_LOGICAL_PARTITION( fvp_spmd_logical_partition, diff --git a/plat/arm/common/arm_sip_svc.c b/plat/arm/common/arm_sip_svc.c index af8a02fb0..7c3e13d34 100644 --- a/plat/arm/common/arm_sip_svc.c +++ b/plat/arm/common/arm_sip_svc.c @@ -13,6 +13,9 @@ #include #include #include +#if ENABLE_SPMD_LP +#include +#endif #include /* ARM SiP Service UUID */ @@ -133,8 +136,13 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid, SMC_RET2(handle, ARM_SIP_SVC_VERSION_MAJOR, ARM_SIP_SVC_VERSION_MINOR); default: +#if ENABLE_SPMD_LP + return plat_spmd_logical_sp_smc_handler(smc_fid, x1, x2, x3, x4, + cookie, handle, flags); +#else WARN("Unimplemented ARM SiP Service Call: 0x%x \n", smc_fid); SMC_RET1(handle, SMC_UNK); +#endif } } From a83aa72fd0eba0f2e6a9631381fb91c4d399d57f Mon Sep 17 00:00:00 2001 From: Raghu Krishnamurthy Date: Tue, 4 Jul 2023 14:16:22 -0700 Subject: [PATCH 8/8] docs(spm): document new build option Add documentation for the new build option ENABLE_SPMD_LP. Signed-off-by: Raghu Krishnamurthy Change-Id: I808e6c00e3699fc900dc97e889af63cc01cae794 --- docs/getting_started/build-options.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst index c410a8e95..c665715b2 100644 --- a/docs/getting_started/build-options.rst +++ b/docs/getting_started/build-options.rst @@ -899,6 +899,12 @@ Common build options support pre-Armv8.4 platforms (aka not implementing the ``FEAT_SEL2`` extension). +- ``ENABLE_SPMD_LP`` : This boolean option is used jointly with the SPM + Dispatcher option (``SPD=spmd``). When enabled (1) it indicates support + for logical partitions in EL3, managed by the SPMD as defined in the FF-A + 1.2 specification. This flag is disabled by default. This flag must not be + used if ``SPMC_AT_EL3`` is enabled. This is an experimental feature. + - ``SPM_MM`` : Boolean option to enable the Management Mode (MM)-based Secure Partition Manager (SPM) implementation. The default value is ``0`` (disabled). This option cannot be enabled (``1``) when SPM Dispatcher is