From eb1cbb4c8355622c4d8abe444c8af0850bd98e77 Mon Sep 17 00:00:00 2001 From: Antonio Nino Diaz Date: Thu, 8 Nov 2018 14:22:51 +0000 Subject: SPM: Support non-blocking calls Note that the arguments passed during the SMC call don't comply with the SPCI specifications. This will be fixed in following patches, but it is needed to implement a few more SPCI SMCs to be able to do it. The current code allows us to start testing it. Change-Id: Ic13dcc54c40327df03be1b0f52e8a44f468f06b4 Co-authored-by: Jean-Paul Etienne Signed-off-by: Antonio Nino Diaz --- services/std_svc/spm/spci.c | 329 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) (limited to 'services') diff --git a/services/std_svc/spm/spci.c b/services/std_svc/spm/spci.c index 1544ae16..41b0b00c 100644 --- a/services/std_svc/spm/spci.c +++ b/services/std_svc/spm/spci.c @@ -107,6 +107,20 @@ static int spci_create_handle_value(uint16_t *handle) return 0; } +/******************************************************************************* + * Returns a unique token for a Secure Service request. + ******************************************************************************/ +static uint32_t spci_create_token_value(void) +{ + /* + * Trivial implementation that relies on the fact that any response will + * be read before 2^32 more service requests have been done. + */ + static uint32_t token_count; + + return token_count++; +} + /******************************************************************************* * 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 @@ -363,6 +377,290 @@ static uint64_t spci_service_request_blocking(void *handle, SMC_RET4(handle, SPCI_SUCCESS, rx1, rx2, rx3); } +/******************************************************************************* + * This function requests a Secure Service from a given handle and client ID. + ******************************************************************************/ +static uint64_t spci_service_request_start(void *handle, + uint32_t smc_fid, 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) +{ + spci_handle_t *handle_info; + sp_context_t *sp_ctx; + cpu_context_t *cpu_ctx; + uint16_t request_handle, client_id; + uint32_t token; + + /* Get handle array lock */ + spin_lock(&spci_handles_lock); + + /* Get pointer to struct of this open handle and client ID. */ + request_handle = (x7 >> 16U) & 0x0000FFFFU; + client_id = x7 & 0x0000FFFFU; + + handle_info = spci_handle_info_get(request_handle, client_id); + if (handle_info == NULL) { + spin_unlock(&spci_handles_lock); + + WARN("SPCI_SERVICE_TUN_REQUEST_START: Not found.\n" + " Handle 0x%04x. Client ID 0x%04x\n", request_handle, + client_id); + + SMC_RET1(handle, SPCI_INVALID_PARAMETER); + } + + /* Get pointer to the Secure Partition that handles the service */ + sp_ctx = handle_info->sp_ctx; + assert(sp_ctx != NULL); + cpu_ctx = &(sp_ctx->cpu_ctx); + + /* Prevent this handle from being closed */ + handle_info->num_active_requests += 1; + + spm_sp_request_increase(sp_ctx); + + /* Create new token for this request */ + token = spci_create_token_value(); + + /* Release handle lock */ + spin_unlock(&spci_handles_lock); + + /* Pass arguments to the Secure Partition */ + struct sprt_queue_entry_message message = { + .type = SPRT_MSG_TYPE_SERVICE_TUN_REQUEST, + .client_id = client_id, + .service_handle = request_handle, + .session_id = x6, + .token = token, + .args = {smc_fid, x1, x2, x3, x4, x5} + }; + + spin_lock(&(sp_ctx->spm_sp_buffer_lock)); + int rc = sprt_push_message((void *)sp_ctx->spm_sp_buffer_base, &message, + SPRT_QUEUE_NUM_NON_BLOCKING); + spin_unlock(&(sp_ctx->spm_sp_buffer_lock)); + if (rc != 0) { + WARN("SPCI_SERVICE_TUN_REQUEST_START: SPRT queue full.\n" + " Handle 0x%04x. Client ID 0x%04x\n", request_handle, + client_id); + SMC_RET1(handle, SPCI_NO_MEMORY); + } + + /* Try to enter the partition. If it's not possible, simply return. */ + if (sp_state_try_switch(sp_ctx, SP_STATE_IDLE, SP_STATE_BUSY) != 0) { + SMC_RET2(handle, SPCI_SUCCESS, token); + } + + /* Save the Normal world context */ + cm_el1_sysregs_context_save(NON_SECURE); + + /* Jump to the Secure Partition. */ + uint64_t ret = spm_sp_synchronous_entry(sp_ctx); + + /* Verify returned values */ + if (ret == SPRT_PUT_RESPONSE_AARCH64) { + uint32_t token; + uint64_t rx1, rx2, rx3, x6; + + token = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X1); + rx1 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X3); + rx2 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X4); + rx3 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X5); + x6 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X6); + + uint16_t client_id = x6 & 0xFFFFU; + uint16_t service_handle = x6 >> 16; + + int rc = spm_response_add(client_id, service_handle, token, + rx1, rx2, rx3); + if (rc != 0) { + /* + * This is error fatal because we can't return to the SP + * from this SMC. The SP has crashed. + */ + panic(); + } + } else if (ret != SPRT_YIELD_AARCH64) { + ERROR("SPM: %s: Unexpected x0 value 0x%llx\n", __func__, ret); + panic(); + } + + /* Flag Secure Partition as idle. */ + assert(sp_ctx->state == SP_STATE_BUSY); + sp_state_set(sp_ctx, SP_STATE_IDLE); + + /* Restore non-secure state */ + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + + SMC_RET2(handle, SPCI_SUCCESS, token); +} + +/******************************************************************************* + * This function returns the response of a Secure Service given a handle, a + * client ID and a token. If not available, it will schedule a Secure Partition + * and give it CPU time. + ******************************************************************************/ +static uint64_t spci_service_request_resume(void *handle, u_register_t x1, + u_register_t x7) +{ + int rc; + u_register_t rx1 = 0, rx2 = 0, rx3 = 0; + spci_handle_t *handle_info; + sp_context_t *sp_ctx; + cpu_context_t *cpu_ctx; + uint32_t token = (uint32_t) x1; + uint16_t client_id = x7 & 0x0000FFFF; + uint16_t service_handle = (x7 >> 16) & 0x0000FFFF; + + /* Get pointer to struct of this open handle and client ID. */ + 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_SERVICE_REQUEST_RESUME: Not found.\n" + "Handle 0x%04x. Client ID 0x%04x, Token 0x%08x.\n", + client_id, service_handle, token); + + SMC_RET1(handle, SPCI_INVALID_PARAMETER); + } + + /* Get pointer to the Secure Partition that handles the service */ + sp_ctx = handle_info->sp_ctx; + assert(sp_ctx != NULL); + cpu_ctx = &(sp_ctx->cpu_ctx); + + spin_unlock(&spci_handles_lock); + + /* Look for a valid response in the global queue */ + rc = spm_response_get(client_id, service_handle, token, + &rx1, &rx2, &rx3); + if (rc == 0) { + /* Decrease request count */ + spin_lock(&spci_handles_lock); + handle_info->num_active_requests -= 1; + spin_unlock(&spci_handles_lock); + spm_sp_request_decrease(sp_ctx); + + SMC_RET4(handle, SPCI_SUCCESS, rx1, rx2, rx3); + } + + /* Try to enter the partition. If it's not possible, simply return. */ + if (sp_state_try_switch(sp_ctx, SP_STATE_IDLE, SP_STATE_BUSY) != 0) { + SMC_RET1(handle, SPCI_QUEUED); + } + + /* Save the Normal world context */ + cm_el1_sysregs_context_save(NON_SECURE); + + /* Jump to the Secure Partition. */ + uint64_t ret = spm_sp_synchronous_entry(sp_ctx); + + /* Verify returned values */ + if (ret == SPRT_PUT_RESPONSE_AARCH64) { + uint32_t token; + uint64_t rx1, rx2, rx3, x6; + + token = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X1); + rx1 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X3); + rx2 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X4); + rx3 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X5); + x6 = read_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X6); + + uint16_t client_id = x6 & 0xFFFFU; + uint16_t service_handle = x6 >> 16; + + int rc = spm_response_add(client_id, service_handle, token, + rx1, rx2, rx3); + if (rc != 0) { + /* + * This is error fatal because we can't return to the SP + * from this SMC. The SP has crashed. + */ + panic(); + } + } else if (ret != SPRT_YIELD_AARCH64) { + ERROR("SPM: %s: Unexpected x0 value 0x%llx\n", __func__, ret); + panic(); + } + + /* Flag Secure Partition as idle. */ + assert(sp_ctx->state == SP_STATE_BUSY); + sp_state_set(sp_ctx, SP_STATE_IDLE); + + /* Restore non-secure state */ + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + + /* Look for a valid response in the global queue */ + rc = spm_response_get(client_id, service_handle, token, + &rx1, &rx2, &rx3); + if (rc != 0) { + SMC_RET1(handle, SPCI_QUEUED); + } + + /* Decrease request count */ + spin_lock(&spci_handles_lock); + handle_info->num_active_requests -= 1; + spin_unlock(&spci_handles_lock); + spm_sp_request_decrease(sp_ctx); + + /* Return response */ + SMC_RET4(handle, SPCI_SUCCESS, rx1, rx2, rx3); +} + +/******************************************************************************* + * This function returns the response of a Secure Service given a handle, a + * client ID and a token. + ******************************************************************************/ +static uint64_t spci_service_get_response(void *handle, u_register_t x1, + u_register_t x7) + +{ + int rc; + u_register_t rx1 = 0, rx2 = 0, rx3 = 0; + spci_handle_t *handle_info; + uint32_t token = (uint32_t) x1; + uint16_t client_id = x7 & 0x0000FFFF; + uint16_t service_handle = (x7 >> 16) & 0x0000FFFF; + + /* Get pointer to struct of this open handle and client ID. */ + + 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_SERVICE_GET_RESPONSE: Not found.\n" + "Handle 0x%04x. Client ID 0x%04x, Token 0x%08x.\n", + client_id, service_handle, token); + + SMC_RET1(handle, SPCI_INVALID_PARAMETER); + } + + spin_unlock(&spci_handles_lock); + + /* Look for a valid response in the global queue */ + rc = spm_response_get(client_id, service_handle, token, + &rx1, &rx2, &rx3); + + if (rc != 0) { + SMC_RET1(handle, SPCI_QUEUED); + } + + /* Decrease request count */ + spin_lock(&spci_handles_lock); + handle_info->num_active_requests -= 1; + sp_context_t *sp_ctx; + sp_ctx = handle_info->sp_ctx; + spin_unlock(&spci_handles_lock); + spm_sp_request_decrease(sp_ctx); + + /* Return response */ + SMC_RET4(handle, SPCI_SUCCESS, rx1, rx2, rx3); +} + /******************************************************************************* * This function handles all SMCs in the range reserved for SPCI. ******************************************************************************/ @@ -416,6 +714,23 @@ uint64_t spci_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, smc_fid, x1, x2, x3, x4, x5, x6, x7); } + case SPCI_FID_SERVICE_REQUEST_START: + { + 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_request_start(handle, + smc_fid, x1, x2, x3, x4, x5, x6, x7); + } + + case SPCI_FID_SERVICE_GET_RESPONSE: + { + uint64_t x7 = SMC_GET_GP(handle, CTX_GPREG_X7); + + return spci_service_get_response(handle, x1, x7); + } + default: break; } @@ -424,6 +739,20 @@ uint64_t spci_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, /* Tunneled calls */ + spci_fid = (smc_fid >> SPCI_FID_TUN_SHIFT) & SPCI_FID_TUN_MASK; + + switch (spci_fid) { + + case SPCI_FID_SERVICE_REQUEST_RESUME: + { + uint64_t x7 = SMC_GET_GP(handle, CTX_GPREG_X7); + + return spci_service_request_resume(handle, x1, x7); + } + + default: + break; + } } WARN("SPCI: Unsupported call 0x%08x\n", smc_fid); -- cgit v1.2.3