From 56ae97924dc80fe1f6fea4896b118d0ca3ea8814 Mon Sep 17 00:00:00 2001 From: Antonio Nino Diaz Date: Thu, 8 Nov 2018 14:20:07 +0000 Subject: [PATCH] SPM: Implement SPCI open/close handle SMCs Introduce SMCs that open and close handles according to the SPCI specification. Change-Id: I65f365f15612e01aa445e783e96e48ae275c39fd Signed-off-by: Antonio Nino Diaz --- include/plat/arm/common/arm_spm_def.h | 2 + services/std_svc/spm/spci.c | 248 ++++++++++++++++++++++++++ services/std_svc/spm/spm_main.c | 33 ++++ services/std_svc/spm/spm_private.h | 1 + 4 files changed, 284 insertions(+) diff --git a/include/plat/arm/common/arm_spm_def.h b/include/plat/arm/common/arm_spm_def.h index 630a29c90..5c7ca90ab 100644 --- a/include/plat/arm/common/arm_spm_def.h +++ b/include/plat/arm/common/arm_spm_def.h @@ -138,4 +138,6 @@ #define PLAT_SPM_NOTIFICATIONS_MAX U(30) #define PLAT_SPM_SERVICES_MAX U(30) +#define PLAT_SPCI_HANDLES_MAX_NUM U(20) + #endif /* ARM_SPM_DEF_H */ diff --git a/services/std_svc/spm/spci.c b/services/std_svc/spm/spci.c index 603523f18..cb1b8fdea 100644 --- a/services/std_svc/spm/spci.c +++ b/services/std_svc/spm/spci.c @@ -4,14 +4,244 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include #include #include +#include +#include #include #include "spm_private.h" +/******************************************************************************* + * Macros to print UUIDs. + ******************************************************************************/ +#define PRINT_UUID_FORMAT "%08x-%08x-%08x-%08x" +#define PRINT_UUID_ARGS(x) x[0], x[1], x[2], x[3] + +/******************************************************************************* + * Array of structs that contains information about all handles of Secure + * Services that are currently open. + ******************************************************************************/ +typedef enum spci_handle_status { + HANDLE_STATUS_CLOSED = 0, + HANDLE_STATUS_OPEN, +} spci_handle_status_t; + +typedef struct spci_handle { + /* 16-bit value used as reference in all SPCI calls */ + uint16_t handle; + + /* Client ID of the client that requested the handle */ + uint16_t client_id; + + /* Current status of the handle */ + spci_handle_status_t status; + + /* + * Context of the Secure Partition that provides the Secure Service + * referenced by this handle. + */ + sp_context_t *sp_ctx; + + /* + * The same handle might be used for multiple requests, keep a reference + * counter of them. + */ + unsigned int num_active_requests; +} spci_handle_t; + +static spci_handle_t spci_handles[PLAT_SPCI_HANDLES_MAX_NUM]; +static spinlock_t spci_handles_lock; + +/* + * Given a handle and a client ID, return the element of the spci_handles + * array that contains the information of the handle. It can only return open + * handles. It returns NULL if it couldn't find the element in the array. + */ +static spci_handle_t *spci_handle_info_get(uint16_t handle, uint16_t client_id) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(spci_handles); i++) { + spci_handle_t *h = &(spci_handles[i]); + + /* Only check for open handles */ + if (h->status == HANDLE_STATUS_CLOSED) { + continue; + } + + /* Check if either the handle or the client ID are different */ + if ((h->handle != handle) || (h->client_id != client_id)) { + continue; + } + + return h; + } + + return NULL; +} + +/* + * Returns a unique value for a handle. This function must be called while + * spci_handles_lock is locked. It returns 0 on success, -1 on error. + */ +static int spci_create_handle_value(uint16_t *handle) +{ + /* + * Trivial implementation that relies on the fact that any handle will + * be closed before 2^16 more handles have been opened. + */ + static uint16_t handle_count; + + *handle = handle_count; + + handle_count++; + + return 0; +} + +/******************************************************************************* + * This function looks for a Secure Partition that has a Secure Service + * identified by the given UUID. It returns a handle that the client can use to + * access the service, and an SPCI_*** error code. + ******************************************************************************/ +static uint64_t spci_service_handle_open_poll(void *handle, u_register_t x1, + u_register_t x2, u_register_t x3, u_register_t x4, + u_register_t x5, u_register_t x6, u_register_t x7) +{ + unsigned int i; + sp_context_t *sp_ptr; + uint16_t service_handle; + + /* Bits 31:16 of w7 are reserved (MBZ). */ + assert((x7 & 0xFFFF0000U) == 0); + + uint16_t client_id = x7 & 0x0000FFFFU; + uint32_t uuid[4] = { x1, x2, x3, x4 }; + + /* Get pointer to the Secure Partition that handles this service */ + sp_ptr = spm_sp_get_by_uuid(&uuid); + if (sp_ptr == NULL) { + WARN("SPCI: Service requested by client 0x%04x not found\n", + client_id); + WARN("SPCI: UUID: " PRINT_UUID_FORMAT "\n", + PRINT_UUID_ARGS(uuid)); + + SMC_RET2(handle, SPCI_NOT_PRESENT, 0); + } + + /* Get lock of the array of handles */ + spin_lock(&spci_handles_lock); + + /* + * We need to record the client ID and Secure Partition that correspond + * to this handle. Look for the first free entry in the array. + */ + for (i = 0; i < PLAT_SPCI_HANDLES_MAX_NUM; i++) { + if (spci_handles[i].status == HANDLE_STATUS_CLOSED) { + break; + } + } + + if (i == PLAT_SPCI_HANDLES_MAX_NUM) { + spin_unlock(&spci_handles_lock); + + WARN("SPCI: Can't open more handles. Client 0x%04x\n", + client_id); + WARN("SPCI: UUID: " PRINT_UUID_FORMAT "\n", + PRINT_UUID_ARGS(uuid)); + + SMC_RET2(handle, SPCI_NO_MEMORY, 0); + } + + /* Create new handle value */ + if (spci_create_handle_value(&service_handle) != 0) { + spin_unlock(&spci_handles_lock); + + WARN("SPCI: Can't create a new handle value. Client 0x%04x\n", + client_id); + WARN("SPCI: UUID: " PRINT_UUID_FORMAT "\n", + PRINT_UUID_ARGS(uuid)); + + SMC_RET2(handle, SPCI_NO_MEMORY, 0); + } + + /* Save all information about this handle */ + spci_handles[i].status = HANDLE_STATUS_OPEN; + spci_handles[i].client_id = client_id; + spci_handles[i].handle = service_handle; + spci_handles[i].num_active_requests = 0U; + spci_handles[i].sp_ctx = sp_ptr; + + /* Release lock of the array of handles */ + spin_unlock(&spci_handles_lock); + + VERBOSE("SPCI: Service handle request by client 0x%04x: 0x%04x\n", + client_id, service_handle); + VERBOSE("SPCI: UUID: " PRINT_UUID_FORMAT "\n", PRINT_UUID_ARGS(uuid)); + + /* The handle is returned in the top 16 bits of x1 */ + SMC_RET2(handle, SPCI_SUCCESS, ((uint32_t)service_handle) << 16); +} + +/******************************************************************************* + * This function closes a handle that a specific client uses to access a Secure + * Service. It returns a SPCI_*** error code. + ******************************************************************************/ +static uint64_t spci_service_handle_close(void *handle, u_register_t x1) +{ + spci_handle_t *handle_info; + uint16_t client_id = x1 & 0x0000FFFFU; + uint16_t service_handle = (x1 >> 16) & 0x0000FFFFU; + + spin_lock(&spci_handles_lock); + + handle_info = spci_handle_info_get(service_handle, client_id); + + if (handle_info == NULL) { + spin_unlock(&spci_handles_lock); + + WARN("SPCI: Tried to close invalid handle 0x%04x by client 0x%04x\n", + service_handle, client_id); + + SMC_RET1(handle, SPCI_INVALID_PARAMETER); + } + + if (handle_info->status != HANDLE_STATUS_OPEN) { + spin_unlock(&spci_handles_lock); + + WARN("SPCI: Tried to close handle 0x%04x by client 0x%04x in status %d\n", + service_handle, client_id, handle_info->status); + + SMC_RET1(handle, SPCI_INVALID_PARAMETER); + } + + if (handle_info->num_active_requests != 0U) { + spin_unlock(&spci_handles_lock); + + /* A handle can't be closed if there are requests left */ + WARN("SPCI: Tried to close handle 0x%04x by client 0x%04x with %d requests left\n", + service_handle, client_id, + handle_info->num_active_requests); + + SMC_RET1(handle, SPCI_BUSY); + } + + memset(handle_info, 0, sizeof(spci_handle_t)); + + handle_info->status = HANDLE_STATUS_CLOSED; + + spin_unlock(&spci_handles_lock); + + VERBOSE("SPCI: Closed handle 0x%04x by client 0x%04x.\n", + service_handle, client_id); + + SMC_RET1(handle, SPCI_SUCCESS); +} + /******************************************************************************* * This function handles all SMCs in the range reserved for SPCI. ******************************************************************************/ @@ -37,6 +267,24 @@ uint64_t spci_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, case SPCI_FID_VERSION: SMC_RET1(handle, SPCI_VERSION_COMPILED); + case SPCI_FID_SERVICE_HANDLE_OPEN: + { + if ((smc_fid & SPCI_SERVICE_HANDLE_OPEN_NOTIFY_BIT) != 0) { + /* Not supported for now */ + WARN("SPCI_SERVICE_HANDLE_OPEN_NOTIFY not supported.\n"); + SMC_RET1(handle, SPCI_INVALID_PARAMETER); + } + + uint64_t x5 = SMC_GET_GP(handle, CTX_GPREG_X5); + uint64_t x6 = SMC_GET_GP(handle, CTX_GPREG_X6); + uint64_t x7 = SMC_GET_GP(handle, CTX_GPREG_X7); + + return spci_service_handle_open_poll(handle, x1, x2, x3, + x4, x5, x6, x7); + } + case SPCI_FID_SERVICE_HANDLE_CLOSE: + return spci_service_handle_close(handle, x1); + default: break; } diff --git a/services/std_svc/spm/spm_main.c b/services/std_svc/spm/spm_main.c index e8dda0f82..ed188d48a 100644 --- a/services/std_svc/spm/spm_main.c +++ b/services/std_svc/spm/spm_main.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,38 @@ sp_context_t *spm_cpu_get_sp_ctx(unsigned int linear_id) return cpu_sp_ctx[linear_id]; } +/******************************************************************************* + * This function returns a pointer to the context of the Secure Partition that + * handles the service specified by an UUID. It returns NULL if the UUID wasn't + * found. + ******************************************************************************/ +sp_context_t *spm_sp_get_by_uuid(const uint32_t (*svc_uuid)[4]) +{ + unsigned int i; + + for (i = 0U; i < PLAT_SPM_MAX_PARTITIONS; i++) { + + sp_context_t *sp_ctx = &sp_ctx_array[i]; + + if (sp_ctx->is_present == 0) { + continue; + } + + struct sp_rd_sect_service *rdsvc; + + for (rdsvc = sp_ctx->rd.service; rdsvc != NULL; + rdsvc = rdsvc->next) { + uint32_t *rd_uuid = (uint32_t *)(rdsvc->uuid); + + if (memcmp(rd_uuid, svc_uuid, sizeof(rd_uuid)) == 0) { + return sp_ctx; + } + } + } + + return NULL; +} + /******************************************************************************* * Set state of a Secure Partition context. ******************************************************************************/ diff --git a/services/std_svc/spm/spm_private.h b/services/std_svc/spm/spm_private.h index cfd85a3dc..9a4e82b27 100644 --- a/services/std_svc/spm/spm_private.h +++ b/services/std_svc/spm/spm_private.h @@ -84,6 +84,7 @@ int spm_memory_attributes_set_smc_handler(sp_context_t *sp_ctx, /* Functions to handle Secure Partition contexts */ void spm_cpu_set_sp_ctx(unsigned int linear_id, sp_context_t *sp_ctx); sp_context_t *spm_cpu_get_sp_ctx(unsigned int linear_id); +sp_context_t *spm_sp_get_by_uuid(const uint32_t (*svc_uuid)[4]); #endif /* __ASSEMBLY__ */