mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-17 10:04:26 +00:00
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 <jean-paul.etienne@arm.com> Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
This commit is contained in:
parent
aa9ae89835
commit
eb1cbb4c83
1 changed files with 329 additions and 0 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue